diff --git a/GPU/Vulkan/TextureCacheVulkan.cpp b/GPU/Vulkan/TextureCacheVulkan.cpp index d092893d40..55e400eb9b 100644 --- a/GPU/Vulkan/TextureCacheVulkan.cpp +++ b/GPU/Vulkan/TextureCacheVulkan.cpp @@ -141,7 +141,8 @@ std::vector SamplerCache::DebugGetSamplerIDs() const { TextureCacheVulkan::TextureCacheVulkan(Draw::DrawContext *draw, VulkanContext *vulkan) : TextureCacheCommon(draw), vulkan_(vulkan), - samplerCache_(vulkan) { + samplerCache_(vulkan), + upload_(vulkan) { timesInvalidatedAllThisFrame_ = 0; DeviceRestore(vulkan, draw); SetupTextureDecoder(); @@ -180,6 +181,8 @@ void TextureCacheVulkan::DeviceLost() { if (samplerNearest_) vulkan_->Delete().QueueDeleteSampler(samplerNearest_); + upload_.DeviceLost(); + nextTexture_ = nullptr; } @@ -200,6 +203,8 @@ void TextureCacheVulkan::DeviceRestore(VulkanContext *vulkan, Draw::DrawContext samp.minFilter = VK_FILTER_NEAREST; samp.mipmapMode = VK_SAMPLER_MIPMAP_MODE_NEAREST; vkCreateSampler(vulkan_->GetDevice(), &samp, nullptr, &samplerNearest_); + + upload_.DeviceRestore(vulkan); } void TextureCacheVulkan::ReleaseTexture(TexCacheEntry *entry, bool delete_them) { diff --git a/GPU/Vulkan/TextureCacheVulkan.h b/GPU/Vulkan/TextureCacheVulkan.h index 77f55aaea8..49862fde3c 100644 --- a/GPU/Vulkan/TextureCacheVulkan.h +++ b/GPU/Vulkan/TextureCacheVulkan.h @@ -25,6 +25,7 @@ #include "Common/Vulkan/VulkanContext.h" #include "GPU/Vulkan/TextureScalerVulkan.h" #include "GPU/Common/TextureCacheCommon.h" +#include "GPU/Vulkan/VulkanUtil.h" struct VirtualFramebuffer; class FramebufferManagerVulkan; @@ -126,6 +127,8 @@ private: VulkanDeviceAllocator *allocator_ = nullptr; VulkanPushBuffer *push_ = nullptr; + VulkanComputeUploader upload_; + SamplerCache samplerCache_; TextureScalerVulkan scaler; diff --git a/GPU/Vulkan/VulkanUtil.cpp b/GPU/Vulkan/VulkanUtil.cpp index f598086235..1f14f0eefa 100644 --- a/GPU/Vulkan/VulkanUtil.cpp +++ b/GPU/Vulkan/VulkanUtil.cpp @@ -51,17 +51,14 @@ void Vulkan2D::DestroyDeviceObjects() { VkDevice device = vulkan_->GetDevice(); if (descriptorSetLayout_ != VK_NULL_HANDLE) { vulkan_->Delete().QueueDeleteDescriptorSetLayout(descriptorSetLayout_); - descriptorSetLayout_ = VK_NULL_HANDLE; } if (pipelineLayout_ != VK_NULL_HANDLE) { vulkan_->Delete().QueueDeletePipelineLayout(pipelineLayout_); - pipelineLayout_ = VK_NULL_HANDLE; } // pipelineBasicTex_ and pipelineBasicTex_ come from vulkan2D_. if (pipelineCache_ != VK_NULL_HANDLE) { vulkan_->Delete().QueueDeletePipelineCache(pipelineCache_); - pipelineCache_ = VK_NULL_HANDLE; } } @@ -401,3 +398,139 @@ VkShaderModule CompileShaderModule(VulkanContext *vulkan, VkShaderStageFlagBits } } } + +VulkanComputeUploader::VulkanComputeUploader(VulkanContext *vulkan) : vulkan_(vulkan), pipelines_(8) { +} +VulkanComputeUploader::~VulkanComputeUploader() {} + +void VulkanComputeUploader::InitDeviceObjects() { + pipelineCache_ = vulkan_->CreatePipelineCache(); + + VkDescriptorSetLayoutBinding bindings[2] = {}; + bindings[0].descriptorCount = 1; + bindings[0].descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER; + bindings[0].stageFlags = VK_SHADER_STAGE_COMPUTE_BIT; + bindings[0].binding = 0; + bindings[1].descriptorCount = 1; + bindings[1].descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE; + bindings[1].stageFlags = VK_SHADER_STAGE_COMPUTE_BIT; + bindings[1].binding = 1; + + VkDevice device = vulkan_->GetDevice(); + + VkDescriptorSetLayoutCreateInfo dsl = { VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO }; + dsl.bindingCount = 2; + dsl.pBindings = bindings; + VkResult res = vkCreateDescriptorSetLayout(device, &dsl, nullptr, &descriptorSetLayout_); + assert(VK_SUCCESS == res); + + VkDescriptorPoolSize dpTypes[2]; + dpTypes[0].descriptorCount = 300; + dpTypes[0].type = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER; + dpTypes[1].descriptorCount = 300; + dpTypes[1].type = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE; + + VkDescriptorPoolCreateInfo dp = { VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO }; + dp.flags = 0; // Don't want to mess around with individually freeing these, let's go fixed each frame and zap the whole array. Might try the dynamic approach later. + dp.maxSets = 300; + dp.pPoolSizes = dpTypes; + dp.poolSizeCount = ARRAY_SIZE(dpTypes); + for (int i = 0; i < ARRAY_SIZE(frameData_); i++) { + VkResult res = vkCreateDescriptorPool(vulkan_->GetDevice(), &dp, nullptr, &frameData_[i].descPool); + assert(VK_SUCCESS == res); + } + + VkPushConstantRange push = {}; + push.offset = 0; + push.size = 48; + push.stageFlags = VK_SHADER_STAGE_COMPUTE_BIT; + + VkPipelineLayoutCreateInfo pl = { VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO }; + pl.pPushConstantRanges = &push; + pl.pushConstantRangeCount = 1; + pl.setLayoutCount = 1; + pl.pSetLayouts = &descriptorSetLayout_; + pl.flags = 0; + res = vkCreatePipelineLayout(device, &pl, nullptr, &pipelineLayout_); + assert(VK_SUCCESS == res); +} + +void VulkanComputeUploader::DestroyDeviceObjects() { + for (int i = 0; i < ARRAY_SIZE(frameData_); i++) { + vulkan_->Delete().QueueDeleteDescriptorPool(frameData_[i].descPool); + } + if (descriptorSetLayout_) { + vulkan_->Delete().QueueDeleteDescriptorSetLayout(descriptorSetLayout_); + } + pipelines_.Iterate([&](const PipelineKey &key, VkPipeline pipeline) { + vulkan_->Delete().QueueDeletePipeline(pipeline); + }); + pipelines_.Clear(); + + if (pipelineLayout_) { + vulkan_->Delete().QueueDeletePipelineLayout(pipelineLayout_); + } + if (pipelineCache_ != VK_NULL_HANDLE) { + vulkan_->Delete().QueueDeletePipelineCache(pipelineCache_); + } +} + +VkDescriptorSet VulkanComputeUploader::GetDescriptorSet(VkBuffer buffer, VkDeviceSize offset, VkDeviceSize range, VkImageView image) { + int curFrame = vulkan_->GetCurFrame(); + FrameData *frame = &frameData_[curFrame]; + + VkDescriptorSet desc; + VkDescriptorSetAllocateInfo descAlloc = { VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO }; + descAlloc.pSetLayouts = &descriptorSetLayout_; + descAlloc.descriptorPool = frame->descPool; + descAlloc.descriptorSetCount = 1; + VkResult result = vkAllocateDescriptorSets(vulkan_->GetDevice(), &descAlloc, &desc); + assert(result == VK_SUCCESS); + + VkWriteDescriptorSet writes[2]{}; + int n = 0; + VkDescriptorBufferInfo bufferInfo = {}; + VkDescriptorImageInfo imageInfo = {}; + bufferInfo.buffer = buffer; + bufferInfo.offset = offset; + bufferInfo.range = range; + writes[n].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; + writes[n].dstBinding = 0; + writes[n].pBufferInfo = &bufferInfo; + writes[n].descriptorCount = 1; + writes[n].descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER; + writes[n].dstSet = desc; + n++; + imageInfo.imageLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; + imageInfo.imageView = image; + imageInfo.sampler = VK_NULL_HANDLE; + writes[n].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; + writes[n].dstBinding = 1; + writes[n].pImageInfo = &imageInfo; + writes[n].descriptorCount = 1; + writes[n].descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE; + writes[n].dstSet = desc; + n++; + + vkUpdateDescriptorSets(vulkan_->GetDevice(), n, writes, 0, nullptr); + return desc; +} + +VkPipeline VulkanComputeUploader::GetPipeline(VkShaderModule cs) { + PipelineKey key{ cs }; + VkPipeline pipeline = pipelines_.Get(key); + if (pipeline) + return pipeline; + + VkComputePipelineCreateInfo pci{ VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO }; + pci.stage.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; + pci.stage.module = cs; + pci.stage.pName = "main"; + pci.layout = pipelineLayout_; + pci.flags = 0; + + vkCreateComputePipelines(vulkan_->GetDevice(), pipelineCache_, 1, &pci, nullptr, &pipeline); + + pipelines_.Insert(key, pipeline); + return pipeline; +} \ No newline at end of file diff --git a/GPU/Vulkan/VulkanUtil.h b/GPU/Vulkan/VulkanUtil.h index 19b1ef4a37..abce1ff8a3 100644 --- a/GPU/Vulkan/VulkanUtil.h +++ b/GPU/Vulkan/VulkanUtil.h @@ -20,6 +20,7 @@ #include #include +#include "Common/Hashmaps.h" #include "Common/Vulkan/VulkanContext.h" #include "Common/Vulkan/VulkanLoader.h" #include "Common/Vulkan/VulkanImage.h" @@ -124,5 +125,48 @@ private: std::vector keptPipelines_; }; +// Manager for compute shaders that upload things (and those have two bindings: a storage buffer to read from and an image to write to). +class VulkanComputeUploader { +public: + VulkanComputeUploader(VulkanContext *vulkan); + ~VulkanComputeUploader(); + + void DeviceLost() { + DestroyDeviceObjects(); + } + void DeviceRestore(VulkanContext *vulkan) { + vulkan_ = vulkan; + InitDeviceObjects(); + } + + // Note: This doesn't cache. The descriptor is for immediate use only. + VkDescriptorSet GetDescriptorSet(VkBuffer buffer, VkDeviceSize offset, VkDeviceSize range, VkImageView image); + + // This of course caches though. + VkPipeline GetPipeline(VkShaderModule cs); + VkPipelineLayout GetPipelineLayout() const { return pipelineLayout_; } + +private: + void InitDeviceObjects(); + void DestroyDeviceObjects(); + + VulkanContext *vulkan_ = nullptr; + VkPipelineCache cache_ = VK_NULL_HANDLE; + VkDescriptorSetLayout descriptorSetLayout_ = VK_NULL_HANDLE; + VkPipelineLayout pipelineLayout_ = VK_NULL_HANDLE; + VkPipelineCache pipelineCache_ = VK_NULL_HANDLE; + + struct FrameData { + VkDescriptorPool descPool; + }; + FrameData frameData_[VulkanContext::MAX_INFLIGHT_FRAMES]; + + struct PipelineKey { + VkShaderModule module; + }; + + DenseHashMap pipelines_; +}; + VkShaderModule CompileShaderModule(VulkanContext *vulkan, VkShaderStageFlagBits stage, const char *code, std::string *error);