diff --git a/Common/Data/Text/I18n.cpp b/Common/Data/Text/I18n.cpp index eaa7bd20ab..e9f1683047 100644 --- a/Common/Data/Text/I18n.cpp +++ b/Common/Data/Text/I18n.cpp @@ -84,7 +84,7 @@ std::string_view I18NCategory::T(std::string_view key, std::string_view def) { } if (key != "Font") { // Font is allowed to be missing. - INFO_LOG(Log::UI, "Missing translation [%s] %.*s (%.*s)", name_.c_str(), STR_VIEW(key), STR_VIEW(def)); + DEBUG_LOG(Log::UI, "Missing translation [%s] %.*s (%.*s)", name_.c_str(), STR_VIEW(key), STR_VIEW(def)); std::lock_guard guard(missedKeyLock_); std::string missedKey(key); if (!def.empty()) @@ -107,7 +107,7 @@ const char *I18NCategory::T_cstr(const char *key, const char *def) { } std::string missedKey(key); if (missedKey != "Font") { - INFO_LOG(Log::UI, "Missing translation %s (%s)", key, def); + DEBUG_LOG(Log::UI, "Missing translation %s (%s)", key, def); std::lock_guard guard(missedKeyLock_); if (def) diff --git a/Common/GPU/Vulkan/VulkanBarrier.cpp b/Common/GPU/Vulkan/VulkanBarrier.cpp index 8a6bb0ef1a..abdd50ca2e 100644 --- a/Common/GPU/Vulkan/VulkanBarrier.cpp +++ b/Common/GPU/Vulkan/VulkanBarrier.cpp @@ -13,10 +13,16 @@ VulkanBarrierBatch::~VulkanBarrierBatch() { } void VulkanBarrierBatch::Flush(VkCommandBuffer cmd) { + // TODO: batch together buffer and image barriers if (!imageBarriers_.empty()) { vkCmdPipelineBarrier(cmd, srcStageMask_, dstStageMask_, dependencyFlags_, 0, nullptr, 0, nullptr, (uint32_t)imageBarriers_.size(), imageBarriers_.data()); } imageBarriers_.clear(); + if (!bufferBarriers_.empty()) { + vkCmdPipelineBarrier(cmd, srcStageMask_, dstStageMask_, dependencyFlags_, 0, nullptr, (uint32_t)bufferBarriers_.size(), bufferBarriers_.data(), 0, nullptr); + } + bufferBarriers_.clear(); + srcStageMask_ = 0; dstStageMask_ = 0; dependencyFlags_ = 0; @@ -51,6 +57,22 @@ void VulkanBarrierBatch::TransitionImage( imageBarrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; } +void VulkanBarrierBatch::TransitionBufferToShaderRead(VkBuffer buffer, VkDeviceSize offset, VkDeviceSize size) { + + srcStageMask_ |= VK_PIPELINE_STAGE_TRANSFER_BIT; + dstStageMask_ |= VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT; + VkBufferMemoryBarrier &bufferBarrier = bufferBarriers_.push_uninitialized(); + bufferBarrier.sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER; + bufferBarrier.pNext = nullptr; + bufferBarrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; + bufferBarrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT; + bufferBarrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + bufferBarrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + bufferBarrier.buffer = buffer; + bufferBarrier.offset = offset; + bufferBarrier.size = size; +} + void VulkanBarrierBatch::TransitionColorImageAuto( VkImage image, VkImageLayout *imageLayout, VkImageLayout newImageLayout, int baseMip, int numMipLevels, int numLayers) { _dbg_assert_(image != VK_NULL_HANDLE); diff --git a/Common/GPU/Vulkan/VulkanBarrier.h b/Common/GPU/Vulkan/VulkanBarrier.h index 884e0919c7..e41df8f9b1 100644 --- a/Common/GPU/Vulkan/VulkanBarrier.h +++ b/Common/GPU/Vulkan/VulkanBarrier.h @@ -52,10 +52,13 @@ public: void TransitionColorImageAuto(VKRImage *image, VkImageLayout newImageLayout); void TransitionDepthStencilImageAuto(VKRImage *image, VkImageLayout newImageLayout); + void TransitionBufferToShaderRead(VkBuffer buffer, VkDeviceSize offset, VkDeviceSize size); + void Flush(VkCommandBuffer cmd); private: FastVec imageBarriers_; + FastVec bufferBarriers_; VkPipelineStageFlags srcStageMask_ = 0; VkPipelineStageFlags dstStageMask_ = 0; VkDependencyFlags dependencyFlags_ = 0; diff --git a/Common/GPU/Vulkan/VulkanContext.cpp b/Common/GPU/Vulkan/VulkanContext.cpp index 2d7f74d4ba..50b4f724a2 100644 --- a/Common/GPU/Vulkan/VulkanContext.cpp +++ b/Common/GPU/Vulkan/VulkanContext.cpp @@ -692,6 +692,8 @@ VkResult VulkanContext::CreateDevice(int physical_device) { extensionsLookup_.KHR_maintenance4 = EnableDeviceExtension("VK_KHR_maintenance4", VK_API_VERSION_1_3); extensionsLookup_.KHR_multiview = EnableDeviceExtension(VK_KHR_MULTIVIEW_EXTENSION_NAME, VK_API_VERSION_1_1); + extensionsLookup_.EXT_scalar_block_layout = EnableDeviceExtension(VK_EXT_SCALAR_BLOCK_LAYOUT_EXTENSION_NAME, VK_API_VERSION_1_2); + if (EnableDeviceExtension(VK_KHR_GET_MEMORY_REQUIREMENTS_2_EXTENSION_NAME, VK_API_VERSION_1_1)) { extensionsLookup_.KHR_get_memory_requirements2 = true; extensionsLookup_.KHR_dedicated_allocation = EnableDeviceExtension(VK_KHR_DEDICATED_ALLOCATION_EXTENSION_NAME, VK_API_VERSION_1_1); @@ -730,6 +732,7 @@ VkResult VulkanContext::CreateDevice(int physical_device) { VkPhysicalDevicePresentIdFeaturesKHR presentIdFeatures{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PRESENT_ID_FEATURES_KHR }; VkPhysicalDeviceProvokingVertexFeaturesEXT provokingVertexFeatures{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROVOKING_VERTEX_FEATURES_EXT }; VkPhysicalDevicePresentModeFifoLatestReadyFeaturesKHR presentModeFifoProps{VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PRESENT_MODE_FIFO_LATEST_READY_FEATURES_KHR}; + VkPhysicalDeviceScalarBlockLayoutFeatures scalarBlockLayoutFeatures = {VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SCALAR_BLOCK_LAYOUT_FEATURES}; ChainStruct(features2, &multiViewFeatures); if (extensionsLookup_.KHR_present_wait) { @@ -744,9 +747,15 @@ VkResult VulkanContext::CreateDevice(int physical_device) { if (extensionsLookup_.KHR_present_mode_fifo_latest_ready) { ChainStruct(features2, &presentModeFifoProps); } + if (extensionsLookup_.EXT_scalar_block_layout) { + ChainStruct(features2, &scalarBlockLayoutFeatures); + } vkGetPhysicalDeviceFeatures2(physical_devices_[physical_device_], &features2); deviceFeatures_.available.standard = features2.features; deviceFeatures_.available.multiview = multiViewFeatures; + if (extensionsLookup_.EXT_scalar_block_layout) { + deviceFeatures_.available.scalarBlockLayout = scalarBlockLayoutFeatures; + } if (extensionsLookup_.KHR_present_wait) { deviceFeatures_.available.presentWait = presentWaitFeatures; } @@ -803,6 +812,11 @@ VkResult VulkanContext::CreateDevice(int physical_device) { deviceFeatures_.enabled.presentModeFifoProps.presentModeFifoLatestReady = deviceFeatures_.available.presentModeFifoProps.presentModeFifoLatestReady; } + deviceFeatures_.enabled.scalarBlockLayout = {VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SCALAR_BLOCK_LAYOUT_FEATURES}; + if (extensionsLookup_.EXT_scalar_block_layout) { + deviceFeatures_.enabled.scalarBlockLayout.scalarBlockLayout = deviceFeatures_.available.scalarBlockLayout.scalarBlockLayout; + } + // deviceFeatures_.enabled.multiview.multiviewGeometryShader = deviceFeatures_.available.multiview.multiviewGeometryShader; VkPhysicalDeviceFeatures2 features2{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2 }; @@ -828,6 +842,9 @@ VkResult VulkanContext::CreateDevice(int physical_device) { if (extensionsLookup_.EXT_provoking_vertex) { ChainStruct(features2, &deviceFeatures_.enabled.provokingVertex); } + if (extensionsLookup_.EXT_scalar_block_layout) { + ChainStruct(features2, &deviceFeatures_.enabled.scalarBlockLayout); + } if (extensionsLookup_.KHR_present_mode_fifo_latest_ready) { ChainStruct(features2, &deviceFeatures_.enabled.presentModeFifoProps); } diff --git a/Common/GPU/Vulkan/VulkanContext.h b/Common/GPU/Vulkan/VulkanContext.h index 842bcdf6b0..1777b5732f 100644 --- a/Common/GPU/Vulkan/VulkanContext.h +++ b/Common/GPU/Vulkan/VulkanContext.h @@ -289,6 +289,7 @@ public: VkPhysicalDevicePresentIdFeaturesKHR presentId; VkPhysicalDeviceProvokingVertexFeaturesEXT provokingVertex; VkPhysicalDevicePresentModeFifoLatestReadyFeaturesKHR presentModeFifoProps; + VkPhysicalDeviceScalarBlockLayoutFeatures scalarBlockLayout; }; const PhysicalDeviceProps &GetPhysicalDeviceProperties(int i = -1) const { diff --git a/Common/GPU/Vulkan/VulkanDescSet.cpp b/Common/GPU/Vulkan/VulkanDescSet.cpp index b28f501525..7a0e3f4fb8 100644 --- a/Common/GPU/Vulkan/VulkanDescSet.cpp +++ b/Common/GPU/Vulkan/VulkanDescSet.cpp @@ -20,6 +20,7 @@ void VulkanDescSetPool::Create(VulkanContext *vulkan, const BindingType *binding uint32_t storageImageCount = 0; uint32_t storageBufferCount = 0; uint32_t combinedImageSamplerCount = 0; + uint32_t uniformBufferCount = 0; uint32_t uniformBufferDynamicCount = 0; for (uint32_t i = 0; i < bindingTypesCount; i++) { switch (bindingTypes[i]) { @@ -29,6 +30,9 @@ void VulkanDescSetPool::Create(VulkanContext *vulkan, const BindingType *binding case BindingType::STORAGE_BUFFER_VERTEX: case BindingType::STORAGE_BUFFER_COMPUTE: storageBufferCount++; break; case BindingType::STORAGE_IMAGE_COMPUTE: storageImageCount++; break; + case BindingType::UNIFORM_BUFFER_COMPUTE: uniformBufferCount++; break; + default: + _dbg_assert_(false); } } if (combinedImageSamplerCount) { @@ -37,6 +41,9 @@ void VulkanDescSetPool::Create(VulkanContext *vulkan, const BindingType *binding if (uniformBufferDynamicCount) { sizes_.push_back(VkDescriptorPoolSize{ VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC, uniformBufferDynamicCount * descriptorCount }); } + if (uniformBufferCount) { + sizes_.push_back(VkDescriptorPoolSize{VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, uniformBufferCount * descriptorCount}); + } if (storageBufferCount) { sizes_.push_back(VkDescriptorPoolSize{ VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, storageBufferCount * descriptorCount }); } diff --git a/Common/GPU/Vulkan/VulkanDescSet.h b/Common/GPU/Vulkan/VulkanDescSet.h index bf62bec4fa..5123ba3460 100644 --- a/Common/GPU/Vulkan/VulkanDescSet.h +++ b/Common/GPU/Vulkan/VulkanDescSet.h @@ -12,6 +12,7 @@ enum class BindingType { STORAGE_BUFFER_VERTEX, STORAGE_BUFFER_COMPUTE, STORAGE_IMAGE_COMPUTE, + UNIFORM_BUFFER_COMPUTE, }; // Only appropriate for use in a per-frame pool. diff --git a/Common/GPU/Vulkan/VulkanLoader.h b/Common/GPU/Vulkan/VulkanLoader.h index 83836b6d1c..132e096403 100644 --- a/Common/GPU/Vulkan/VulkanLoader.h +++ b/Common/GPU/Vulkan/VulkanLoader.h @@ -271,6 +271,7 @@ struct VulkanExtensions { bool GOOGLE_display_timing; bool EXT_provoking_vertex; bool KHR_present_mode_fifo_latest_ready; + bool EXT_scalar_block_layout; // bool EXT_depth_range_unrestricted; // Allows depth outside [0.0, 1.0] in 32-bit float depth buffers. }; diff --git a/Common/GPU/Vulkan/VulkanMemory.cpp b/Common/GPU/Vulkan/VulkanMemory.cpp index 23af6ebf72..e54f385b7a 100644 --- a/Common/GPU/Vulkan/VulkanMemory.cpp +++ b/Common/GPU/Vulkan/VulkanMemory.cpp @@ -176,3 +176,35 @@ void VulkanPushPool::GetDebugString(char *buffer, size_t bufSize) const { snprintf(buffer, bufSize, "Pool %s: %s / %s (%d extra blocks)", name_, NiceSizeFormat(used).c_str(), NiceSizeFormat(capacity).c_str(), (int)blocks_.size() - 3); } + +void VulkanBuffer::Create(VulkanContext *vulkan, const char *name, VkDeviceSize size, VkBufferUsageFlags usage) { + if (buffer_) { + Destroy(vulkan); + } + + size_ = size; + + if (size == 0) { + return; + } + + VkBufferCreateInfo b{VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO}; + b.size = size; + b.usage = usage; + b.sharingMode = VK_SHARING_MODE_EXCLUSIVE; + VmaAllocationCreateInfo allocCreateInfo{}; + + allocCreateInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY; + allocCreateInfo.requiredFlags = VK_MEMORY_PROPERTY_HOST_COHERENT_BIT; // required to not get memory where we have to manually flush. + VmaAllocationInfo allocInfo{}; + + VkResult result = vmaCreateBuffer(vulkan->Allocator(), &b, &allocCreateInfo, &buffer_, &allocation_, &allocInfo); + _dbg_assert_(result == VK_SUCCESS); + vulkan->SetDebugName(buffer_, VK_OBJECT_TYPE_BUFFER, name); +} + +void VulkanBuffer::Destroy(VulkanContext *vulkan) { + if (buffer_) { + vulkan->Delete().QueueDeleteBufferAllocation(buffer_, allocation_); + } +} diff --git a/Common/GPU/Vulkan/VulkanMemory.h b/Common/GPU/Vulkan/VulkanMemory.h index 14e3b78436..fe0bc576d5 100644 --- a/Common/GPU/Vulkan/VulkanMemory.h +++ b/Common/GPU/Vulkan/VulkanMemory.h @@ -94,3 +94,20 @@ private: int curBlockIndex_ = -1; const char *name_; }; + +// Just a plain old static buffer in GPU memory. You can upload to it +// using a PushPool. +class VulkanBuffer { +public: + // Can be called again to resize/recreate. Will not preserve any data. + void Create(VulkanContext *vulkan, const char *name, VkDeviceSize size, VkBufferUsageFlags usage); + void Destroy(VulkanContext *vulkan); + + VkBuffer Buffer() const { return buffer_; } + VkDeviceSize Size() const { return size_; } + +private: + VkBuffer buffer_ = VK_NULL_HANDLE; + VmaAllocation allocation_ = VK_NULL_HANDLE; + VkDeviceSize size_ = 0; // convenience +}; diff --git a/Common/GPU/Vulkan/VulkanRenderManager.cpp b/Common/GPU/Vulkan/VulkanRenderManager.cpp index 711e2a3fb5..10abc5cbec 100644 --- a/Common/GPU/Vulkan/VulkanRenderManager.cpp +++ b/Common/GPU/Vulkan/VulkanRenderManager.cpp @@ -1780,6 +1780,10 @@ VKRPipelineLayout *VulkanRenderManager::CreatePipelineLayout(BindingType *bindin bindings[i].descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE; bindings[i].stageFlags = VK_SHADER_STAGE_COMPUTE_BIT; break; + case BindingType::UNIFORM_BUFFER_COMPUTE: + bindings[i].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; + bindings[i].stageFlags = VK_SHADER_STAGE_COMPUTE_BIT; + break; default: UNREACHABLE(); break; @@ -1962,12 +1966,22 @@ void VKRPipelineLayout::FlushDescSets(VulkanContext *vulkan, int frame, QueuePro _dbg_assert_(data[i].buffer.buffer != VK_NULL_HANDLE); bufferInfo[numBuffers].buffer = data[i].buffer.buffer; bufferInfo[numBuffers].range = data[i].buffer.range; - bufferInfo[numBuffers].offset = 0; // This is supplied by the dynamic offset. + bufferInfo[numBuffers].offset = 0; // This is supplied by the dynamic offset, if available. writes[numWrites].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC; writes[numWrites].pBufferInfo = &bufferInfo[numBuffers]; writes[numWrites].pImageInfo = nullptr; numBuffers++; break; + case BindingType::UNIFORM_BUFFER_COMPUTE: + _dbg_assert_(data[i].buffer.buffer != VK_NULL_HANDLE); + bufferInfo[numBuffers].buffer = data[i].buffer.buffer; + bufferInfo[numBuffers].range = data[i].buffer.range; + bufferInfo[numBuffers].offset = 0; // This is supplied by the dynamic offset, if available. + writes[numWrites].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; + writes[numWrites].pBufferInfo = &bufferInfo[numBuffers]; + writes[numWrites].pImageInfo = nullptr; + numBuffers++; + break; } writes[numWrites].dstBinding = i; writes[numWrites].dstSet = d.set; diff --git a/GPU/Common/PostShader.cpp b/GPU/Common/PostShader.cpp index 6335625dfd..0e6ca85419 100644 --- a/GPU/Common/PostShader.cpp +++ b/GPU/Common/PostShader.cpp @@ -212,6 +212,13 @@ void LoadPostShaderInfo(Draw::DrawContext *draw, const std::vector &direct info.scaleFactor = 0; section.Get("Name", &info.name); section.Get("Scale", &info.scaleFactor); + bool hidden = false; + section.Get("Hidden", &info.hidden); + std::string cbufferFilename; + if (section.Get("ConstantBuffer", &cbufferFilename)) { + Path cbufferPath = path / cbufferFilename; + info.constantBuffer = cbufferPath; + } if (section.Get("Compute", &temp)) { info.computeShaderFile = path / temp; info.computeShaderFiles.push_back(info.computeShaderFile); diff --git a/GPU/Common/PostShader.h b/GPU/Common/PostShader.h index 280a04dac5..77fdbb94ee 100644 --- a/GPU/Common/PostShader.h +++ b/GPU/Common/PostShader.h @@ -90,6 +90,10 @@ struct TextureShaderInfo { // Upscaling shaders have a fixed scale factor. int scaleFactor; + // Some shaders need to pass some data to the shader, and this is the buffer for that. It's up to the shader to interpret it. + Path constantBuffer; + bool hidden = false; + bool operator == (const std::string &other) const { return name == other; } diff --git a/GPU/GPU.vcxproj b/GPU/GPU.vcxproj index 96206312cf..1ae4f38491 100644 --- a/GPU/GPU.vcxproj +++ b/GPU/GPU.vcxproj @@ -492,6 +492,62 @@ + + true + true + true + true + true + true + + + true + true + true + true + true + true + + + true + true + true + true + true + true + + + true + true + true + true + true + true + + + true + true + true + true + true + true + + + true + true + true + true + true + true + + + true + true + true + true + true + true + true true @@ -516,8 +572,192 @@ true true + + true + true + true + true + true + true + + + true + true + true + true + true + true + + + true + true + true + true + true + true + + + true + true + true + true + true + true + + + true + true + true + true + true + true + + + true + true + true + true + true + true + + + true + true + true + true + true + true + + + true + true + true + true + true + true + + + true + true + true + true + true + true + + + true + true + true + true + true + true + + + true + true + true + true + true + true + + + true + true + true + true + true + true + + + true + true + true + true + true + true + + + true + true + true + true + true + true + + + true + true + true + true + true + true + + + true + true + true + true + true + true + + + true + true + true + true + true + true + + + true + true + true + true + true + true + + + true + true + true + true + true + true + + + true + true + true + true + true + true + + + true + true + true + true + true + true + + + true + true + true + true + true + true + + + true + true + true + true + true + true + true true @@ -526,6 +766,110 @@ true true + + true + true + true + true + true + true + + + true + true + true + true + true + true + + + true + true + true + true + true + true + + + true + true + true + true + true + true + + + true + true + true + true + true + true + + + true + true + true + true + true + true + + + true + true + true + true + true + true + + + true + true + true + true + true + true + + + true + true + true + true + true + true + + + true + true + true + true + true + true + + + true + true + true + true + true + true + + + true + true + true + true + true + true + + + true + true + true + true + true + true + true true @@ -542,6 +886,46 @@ true true + + true + true + true + true + true + true + + + true + true + true + true + true + true + + + true + true + true + true + true + true + + + true + true + true + true + true + true + + + true + true + true + true + true + true + diff --git a/GPU/GPU.vcxproj.filters b/GPU/GPU.vcxproj.filters index 04c674b20a..389b88b0b5 100644 --- a/GPU/GPU.vcxproj.filters +++ b/GPU/GPU.vcxproj.filters @@ -538,6 +538,69 @@ Shaders + + Shaders + + + Shaders + + + Shaders + + + Shaders + + + Shaders + + + Shaders + + + Shaders + + + Shaders + + + Shaders + + + Shaders + + + Shaders + + + Shaders + + + Shaders + + + Shaders + + + Shaders + + + Shaders + + + Shaders + + + Shaders + + + Shaders + + + Shaders + + + Shaders + @@ -549,5 +612,86 @@ Shaders + + Shaders + + + Shaders + + + Shaders + + + Shaders + + + Shaders + + + Shaders + + + Shaders + + + Shaders + + + Shaders + + + Shaders + + + Shaders + + + Shaders + + + Shaders + + + Shaders + + + Shaders + + + Shaders + + + Shaders + + + Shaders + + + Shaders + + + Shaders + + + Shaders + + + Shaders + + + Shaders + + + Shaders + + + Shaders + + + Shaders + + + Shaders + \ No newline at end of file diff --git a/GPU/Vulkan/GPU_Vulkan.cpp b/GPU/Vulkan/GPU_Vulkan.cpp index d681d1a7d6..a36f54c68c 100644 --- a/GPU/Vulkan/GPU_Vulkan.cpp +++ b/GPU/Vulkan/GPU_Vulkan.cpp @@ -55,9 +55,9 @@ GPU_Vulkan::GPU_Vulkan(GraphicsContext *gfxCtx, Draw::DrawContext *draw) pipelineManager_ = new PipelineManagerVulkan(vulkan); framebufferManagerVulkan_ = new FramebufferManagerVulkan(draw); framebufferManager_ = framebufferManagerVulkan_; + drawEngineCommon_ = &drawEngine_; textureCacheVulkan_ = new TextureCacheVulkan(draw, framebufferManager_->GetDraw2D(), vulkan); textureCache_ = textureCacheVulkan_; - drawEngineCommon_ = &drawEngine_; shaderManager_ = shaderManagerVulkan_; drawEngine_.SetGPUCommon(this); diff --git a/GPU/Vulkan/TextureCacheVulkan.cpp b/GPU/Vulkan/TextureCacheVulkan.cpp index 0ebe937b2d..37d975b0f0 100644 --- a/GPU/Vulkan/TextureCacheVulkan.cpp +++ b/GPU/Vulkan/TextureCacheVulkan.cpp @@ -62,7 +62,8 @@ const char *uploadShader = R"( #extension GL_ARB_separate_shader_objects : enable // 8x8 is the most common compute shader workgroup size, and works great on all major -// hardware vendors. +// hardware vendors. TODO: However, we should probably change to 16x16, as Qualcomm now has +// support for bigger groups... layout (local_size_x = 8, local_size_y = 8, local_size_z = 1) in; uniform layout(set = 0, binding = 0, rgba8) writeonly image2D img; @@ -76,6 +77,10 @@ layout(push_constant) uniform Params { int height; } params; +// The cbuffer, if present, is self-declared for layout flexibility +#define CBUFFER_SET 0 +#define CBUFFER_BINDING 4 + uint readColoru(uvec2 p) { return buf.data[p.y * params.width + p.x]; } @@ -251,14 +256,16 @@ void TextureCacheVulkan::DeviceRestore(Draw::DrawContext *draw) { VkResult res = vkCreateSampler(vulkan->GetDevice(), &samp, nullptr, &samplerNearest_); _assert_(res == VK_SUCCESS); - CompileScalingShader(); + VkCommandBuffer cmdInit = (VkCommandBuffer)draw_->GetNativeObject(Draw::NativeObject::INIT_COMMANDBUFFER); + CompileScalingShader(cmdInit); computeShaderManager_.DeviceRestore(draw); } void TextureCacheVulkan::NotifyConfigChanged() { TextureCacheCommon::NotifyConfigChanged(); - CompileScalingShader(); + VkCommandBuffer cmdInit = (VkCommandBuffer)draw_->GetNativeObject(Draw::NativeObject::INIT_COMMANDBUFFER); + CompileScalingShader(cmdInit); } static std::string ReadShaderSrc(const Path &filename) { @@ -294,6 +301,7 @@ void TextureCacheVulkan::ClearScalingShaders(VulkanContext *vulkan) { multipassScratchDescs_.clear(); multipassStageDescs_.clear(); textureScalePipeline_ = TextureScalePipelineType::NONE; + textureScaleCBuffer_.Destroy(vulkan); } bool TextureCacheVulkan::CompileMultipassShader(VulkanContext *vulkan, const TextureShaderInfo &shaderInfo, std::string *error) { @@ -356,7 +364,7 @@ bool TextureCacheVulkan::CompileMultipassShader(VulkanContext *vulkan, const Tex return true; } -void TextureCacheVulkan::CompileScalingShader() { +void TextureCacheVulkan::CompileScalingShader(VkCommandBuffer cmdInit) { if (!draw_) { // Something is very wrong. return; @@ -379,8 +387,9 @@ void TextureCacheVulkan::CompileScalingShader() { return; } - if (!g_Config.bTexHardwareScaling) + if (!g_Config.bTexHardwareScaling) { return; + } ReloadAllPostShaderInfo(draw_); const TextureShaderInfo *shaderInfo = GetTextureShaderInfo(g_Config.sTextureShaderName); @@ -402,6 +411,10 @@ void TextureCacheVulkan::CompileScalingShader() { textureScalePipeline_ = TextureScalePipelineType::SINGLE_PASS; } + // if it's empty, we're already done. otherwise we need to load it on first use. + cbufferInited_ = shaderInfo->constantBuffer.empty(); + cbufferPath_ = shaderInfo->constantBuffer; + textureShader_ = g_Config.sTextureShaderName; shaderScaleFactor_ = shaderInfo->scaleFactor; } @@ -421,6 +434,35 @@ static void BarrierComputeImage(VkCommandBuffer cmd, VkImage image) { batch.Flush(cmd); } +void TextureCacheVulkan::LoadConstantBuffer(VulkanContext *vulkan, VkCommandBuffer cmdInit) { + if (cbufferInited_) { + return; + } + + _dbg_assert_(!cbufferPath_.empty()); + + std::string temp; + size_t constantsSize; + uint8_t *contents = g_VFS.ReadFile(cbufferPath_.c_str(), &constantsSize); + if (!contents) { + ERROR_LOG(Log::G3D, "Failed to read constant buffer file '%s'", cbufferPath_.c_str()); + return; + } + textureScaleCBuffer_.Create(vulkan, "TextureScale CBuffer", constantsSize, VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT); + VulkanPushPool *pushPool = drawEngine_->GetPushBufferForTextureData(); + VkBuffer srcBuf = VK_NULL_HANDLE; + VkDeviceSize offset = pushPool->Push(contents, constantsSize, vulkan->GetPhysicalDeviceProperties().properties.limits.minUniformBufferOffsetAlignment, &srcBuf); + VkBufferCopy copyRegion{offset, 0, constantsSize}; + vkCmdCopyBuffer(cmdInit, srcBuf, textureScaleCBuffer_.Buffer(), 1, ©Region); + VulkanBarrierBatch barrier; + barrier.TransitionBufferToShaderRead(textureScaleCBuffer_.Buffer(), 0, constantsSize); + barrier.Flush(cmdInit); + + delete[] contents; + + cbufferInited_ = true; +} + bool TextureCacheVulkan::RunMultipassCompute(VulkanContext *vulkan, VkCommandBuffer cmdInit, VkImageView dstView, VkBuffer texBuf, uint32_t bufferOffset, int srcSize, int srcWidth, int srcHeight, int dstWidth, int dstHeight) { const bool fourX = dstWidth > srcWidth * 2 || dstHeight > srcHeight * 2; VulkanBarrierBatch barrier; @@ -428,6 +470,8 @@ bool TextureCacheVulkan::RunMultipassCompute(VulkanContext *vulkan, VkCommandBuf std::vector> scratchTextures; scratchTextures.reserve(multipassScratchDescs_.size()); + LoadConstantBuffer(vulkan, cmdInit); + for (const MultipassScratchDesc &scratchDesc : multipassScratchDescs_) { auto scratch = std::make_unique(vulkan, scratchDesc.tag); if (!scratch->CreateDirect(srcWidth * scratchDesc.widthScale, srcHeight * scratchDesc.heightScale, 1, 1, VK_FORMAT_R16G16B16A16_SFLOAT, VK_IMAGE_LAYOUT_GENERAL, scratchUsage, &barrier)) { @@ -479,7 +523,7 @@ bool TextureCacheVulkan::RunMultipassCompute(VulkanContext *vulkan, VkCommandBuf stage.useFinalOutputSize ? dstWidth : srcWidth * stage.dstWidthScale, stage.useFinalOutputSize ? dstHeight : srcHeight * stage.dstHeightScale, }; - VkDescriptorSet stageSet = computeShaderManager_.GetDescriptorSet(outputView, inputBuffer, inputOffset, inputRange, VK_NULL_HANDLE, 0, 0, inputImage); + VkDescriptorSet stageSet = computeShaderManager_.GetDescriptorSet(outputView, inputBuffer, inputOffset, inputRange, VK_NULL_HANDLE, 0, 0, inputImage, textureScaleCBuffer_.Buffer(), textureScaleCBuffer_.Size()); vkCmdBindPipeline(cmdInit, VK_PIPELINE_BIND_POINT_COMPUTE, computeShaderManager_.GetPipeline(multipassCS_[stage.shaderIndex])); vkCmdBindDescriptorSets(cmdInit, VK_PIPELINE_BIND_POINT_COMPUTE, computeShaderManager_.GetPipelineLayout(), 0, 1, &stageSet, 0, nullptr); vkCmdPushConstants(cmdInit, computeShaderManager_.GetPipelineLayout(), VK_SHADER_STAGE_COMPUTE_BIT, 0, sizeof(params), ¶ms); @@ -492,18 +536,19 @@ bool TextureCacheVulkan::RunMultipassCompute(VulkanContext *vulkan, VkCommandBuf return true; } -bool TextureCacheVulkan::ScaleBufferToImage(VkCommandBuffer cmdInit, VkImageView dstView, VkBuffer texBuf, uint32_t bufferOffset, int srcSize, int srcWidth, int srcHeight, int dstWidth, int dstHeight) { +bool TextureCacheVulkan::ScaleBufferToImage(VulkanContext *vulkan, VkCommandBuffer cmdInit, VkImageView dstView, VkBuffer texBuf, uint32_t bufferOffset, int srcSize, int srcWidth, int srcHeight, int dstWidth, int dstHeight) { if (!draw_ || cmdInit == VK_NULL_HANDLE || dstView == VK_NULL_HANDLE) { return false; } - VulkanContext *vulkan = (VulkanContext *)draw_->GetNativeObject(Draw::NativeObject::CONTEXT); + LoadConstantBuffer(vulkan, cmdInit); + switch (textureScalePipeline_) { case TextureScalePipelineType::SINGLE_PASS: { if (singlePassCS_ == VK_NULL_HANDLE) { return false; } - VkDescriptorSet descSet = computeShaderManager_.GetDescriptorSet(dstView, texBuf, bufferOffset, srcSize); + VkDescriptorSet descSet = computeShaderManager_.GetDescriptorSet(dstView, texBuf, bufferOffset, srcSize, VK_NULL_HANDLE, 0, 0, VK_NULL_HANDLE, textureScaleCBuffer_.Buffer(), textureScaleCBuffer_.Size()); struct Params { int x; int y; } params{ srcWidth, srcHeight }; vkCmdBindPipeline(cmdInit, VK_PIPELINE_BIND_POINT_COMPUTE, computeShaderManager_.GetPipeline(singlePassCS_)); vkCmdBindDescriptorSets(cmdInit, VK_PIPELINE_BIND_POINT_COMPUTE, computeShaderManager_.GetPipelineLayout(), 0, 1, &descSet, 0, nullptr); @@ -857,7 +902,7 @@ void TextureCacheVulkan::BuildTexture(TexCacheEntry *const entry) { VkImageView view = entry->vkTex->CreateViewForMip(i); VK_PROFILE_BEGIN(vulkan, cmdInit, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, "Compute Upload: %dx%d->%dx%d", mipUnscaledWidth, mipUnscaledHeight, mipWidth, mipHeight); - ScaleBufferToImage(cmdInit, view, texBuf, bufferOffset, srcSize, mipUnscaledWidth, mipUnscaledHeight, mipWidth, mipHeight); + ScaleBufferToImage(vulkan, cmdInit, view, texBuf, bufferOffset, srcSize, mipUnscaledWidth, mipUnscaledHeight, mipWidth, mipHeight); VK_PROFILE_END(vulkan, cmdInit, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT); vulkan->Delete().QueueDeleteImageView(view); } else { diff --git a/GPU/Vulkan/TextureCacheVulkan.h b/GPU/Vulkan/TextureCacheVulkan.h index 79c1cf3d3f..520567eace 100644 --- a/GPU/Vulkan/TextureCacheVulkan.h +++ b/GPU/Vulkan/TextureCacheVulkan.h @@ -23,6 +23,7 @@ #include "GPU/Common/TextureCacheCommon.h" #include "GPU/Common/TextureShaderCommon.h" #include "GPU/Vulkan/VulkanUtil.h" +#include "GPU/Vulkan/VulkanMemory.h" struct VirtualFramebuffer; struct TextureShaderInfo; @@ -121,12 +122,14 @@ private: void BuildTexture(TexCacheEntry *const entry) override; - void CompileScalingShader(); + void CompileScalingShader(VkCommandBuffer cmdInit); bool CompileMultipassShader(VulkanContext *vulkan, const TextureShaderInfo &shaderInfo, std::string *error); void ClearScalingShaders(VulkanContext *vulkan); bool HasScalingShader() const; bool RunMultipassCompute(VulkanContext *vulkan, VkCommandBuffer cmdInit, VkImageView dstView, VkBuffer texBuf, uint32_t bufferOffset, int srcSize, int srcWidth, int srcHeight, int dstWidth, int dstHeight); - bool ScaleBufferToImage(VkCommandBuffer cmdInit, VkImageView dstView, VkBuffer texBuf, uint32_t bufferOffset, int srcSize, int srcWidth, int srcHeight, int dstWidth, int dstHeight); + bool ScaleBufferToImage(VulkanContext *vulkan, VkCommandBuffer cmdInit, VkImageView dstView, VkBuffer texBuf, uint32_t bufferOffset, int srcSize, int srcWidth, int srcHeight, int dstWidth, int dstHeight); + + void LoadConstantBuffer(VulkanContext *vulkan, VkCommandBuffer cmdInit); VulkanComputeShaderManager computeShaderManager_; @@ -146,6 +149,11 @@ private: VkSampler curSampler_ = VK_NULL_HANDLE; VkSampler samplerNearest_ = VK_NULL_HANDLE; + + Path cbufferPath_; + VulkanBuffer textureScaleCBuffer_; + bool cbufferInited_ = true; + bool cbufferFailed_ = false; }; VkFormat getClutDestFormatVulkan(GEPaletteFormat format); diff --git a/GPU/Vulkan/VulkanUtil.cpp b/GPU/Vulkan/VulkanUtil.cpp index 1e2cfa75d2..ab67da1f11 100644 --- a/GPU/Vulkan/VulkanUtil.cpp +++ b/GPU/Vulkan/VulkanUtil.cpp @@ -39,6 +39,14 @@ const VkComponentMapping VULKAN_1555_SWIZZLE = { VK_COMPONENT_SWIZZLE_B, VK_COMP const VkComponentMapping VULKAN_565_SWIZZLE = { VK_COMPONENT_SWIZZLE_B, VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_IDENTITY }; const VkComponentMapping VULKAN_8888_SWIZZLE = { VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY }; +static const BindingType g_bindingTypes[] = { + BindingType::STORAGE_IMAGE_COMPUTE, + BindingType::STORAGE_BUFFER_COMPUTE, + BindingType::STORAGE_BUFFER_COMPUTE, + BindingType::STORAGE_IMAGE_COMPUTE, + BindingType::UNIFORM_BUFFER_COMPUTE, +}; + VkPresentModeKHR ConfigPresentModeToVulkan(Draw::DrawContext *draw) { g_frameTiming.ComputePresentMode(draw, false); Draw::PresentMode presentMode = g_frameTiming.PresentMode(); @@ -107,14 +115,7 @@ void VulkanComputeShaderManager::InitDeviceObjects(Draw::DrawContext *draw) { VkResult res = vkCreatePipelineCache(vulkan_->GetDevice(), &pc, nullptr, &pipelineCache_); _assert_(VK_SUCCESS == res); - static const BindingType bindingTypes[4] = { - BindingType::STORAGE_IMAGE_COMPUTE, - BindingType::STORAGE_BUFFER_COMPUTE, - BindingType::STORAGE_BUFFER_COMPUTE, - BindingType::STORAGE_IMAGE_COMPUTE, - }; - - VkDescriptorSetLayoutBinding bindings[4] = {}; + VkDescriptorSetLayoutBinding bindings[ARRAY_SIZE(g_bindingTypes)] = {}; bindings[0].descriptorCount = 1; bindings[0].descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE; bindings[0].stageFlags = VK_SHADER_STAGE_COMPUTE_BIT; @@ -131,6 +132,10 @@ void VulkanComputeShaderManager::InitDeviceObjects(Draw::DrawContext *draw) { bindings[3].descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE; bindings[3].stageFlags = VK_SHADER_STAGE_COMPUTE_BIT; bindings[3].binding = 3; + bindings[4].descriptorCount = 1; + bindings[4].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; + bindings[4].stageFlags = VK_SHADER_STAGE_COMPUTE_BIT; + bindings[4].binding = 4; VkDevice device = vulkan_->GetDevice(); @@ -141,7 +146,7 @@ void VulkanComputeShaderManager::InitDeviceObjects(Draw::DrawContext *draw) { _assert_(VK_SUCCESS == res); for (int i = 0; i < ARRAY_SIZE(frameData_); i++) { - frameData_[i].descPool.Create(vulkan_, bindingTypes, ARRAY_SIZE(bindingTypes), 4096); + frameData_[i].descPool.Create(vulkan_, g_bindingTypes, ARRAY_SIZE(g_bindingTypes), 4096); frameData_[i].descPoolUsed = false; } @@ -181,7 +186,7 @@ void VulkanComputeShaderManager::DestroyDeviceObjects() { } } -VkDescriptorSet VulkanComputeShaderManager::GetDescriptorSet(VkImageView image, VkBuffer buffer, VkDeviceSize offset, VkDeviceSize range, VkBuffer buffer2, VkDeviceSize offset2, VkDeviceSize range2, VkImageView image2) { +VkDescriptorSet VulkanComputeShaderManager::GetDescriptorSet(VkImageView image, VkBuffer buffer, VkDeviceSize offset, VkDeviceSize range, VkBuffer buffer2, VkDeviceSize offset2, VkDeviceSize range2, VkImageView image2, VkBuffer cbuffer, VkDeviceSize cbufferSize) { int curFrame = vulkan_->GetCurFrame(); FrameData &frameData = frameData_[curFrame]; frameData.descPoolUsed = true; @@ -189,10 +194,10 @@ VkDescriptorSet VulkanComputeShaderManager::GetDescriptorSet(VkImageView image, frameData.descPool.Allocate(&desc, 1, &descriptorSetLayout_); _assert_(desc != VK_NULL_HANDLE); - VkWriteDescriptorSet writes[4]{}; + VkWriteDescriptorSet writes[5]{}; int n = 0; VkDescriptorImageInfo imageInfo[2] = {}; - VkDescriptorBufferInfo bufferInfo[2] = {}; + VkDescriptorBufferInfo bufferInfo[3] = {}; if (image) { imageInfo[0].imageLayout = VK_IMAGE_LAYOUT_GENERAL; imageInfo[0].imageView = image; @@ -241,6 +246,18 @@ VkDescriptorSet VulkanComputeShaderManager::GetDescriptorSet(VkImageView image, writes[n].dstSet = desc; n++; } + if (cbuffer) { + bufferInfo[2].buffer = cbuffer; + bufferInfo[2].offset = 0; + bufferInfo[2].range = cbufferSize; + writes[n].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; + writes[n].dstBinding = 4; + writes[n].pBufferInfo = &bufferInfo[2]; + writes[n].descriptorCount = 1; + writes[n].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; + writes[n].dstSet = desc; + n++; + } vkUpdateDescriptorSets(vulkan_->GetDevice(), n, writes, 0, nullptr); return desc; } diff --git a/GPU/Vulkan/VulkanUtil.h b/GPU/Vulkan/VulkanUtil.h index fca4f2b847..6e6d683d29 100644 --- a/GPU/Vulkan/VulkanUtil.h +++ b/GPU/Vulkan/VulkanUtil.h @@ -55,7 +55,7 @@ public: } // Note: This doesn't cache. The descriptor is for immediate use only. - VkDescriptorSet GetDescriptorSet(VkImageView image, VkBuffer buffer, VkDeviceSize offset, VkDeviceSize range, VkBuffer buffer2 = VK_NULL_HANDLE, VkDeviceSize offset2 = 0, VkDeviceSize range2 = 0, VkImageView image2 = VK_NULL_HANDLE); + VkDescriptorSet GetDescriptorSet(VkImageView image, VkBuffer buffer, VkDeviceSize offset, VkDeviceSize range, VkBuffer buffer2, VkDeviceSize offset2, VkDeviceSize range2, VkImageView image2, VkBuffer cbuffer, VkDeviceSize cbufferSize); // This of course caches though. VkPipeline GetPipeline(VkShaderModule cs); diff --git a/UI/MiscScreens.cpp b/UI/MiscScreens.cpp index 1dedbefa2f..7bd09b9fe7 100644 --- a/UI/MiscScreens.cpp +++ b/UI/MiscScreens.cpp @@ -244,6 +244,9 @@ void TextureShaderScreen::CreateViews() { std::vector items; int selected = -1; for (int i = 0; i < (int)shaders_.size(); i++) { + if (shaders_[i].hidden) { + continue; + } if (shaders_[i].section == g_Config.sTextureShaderName) selected = i; items.emplace_back(ps->T(shaders_[i].section, shaders_[i].name)); diff --git a/Windows/PPSSPP.vcxproj b/Windows/PPSSPP.vcxproj index 55cd42e4e1..dc415e0c67 100644 --- a/Windows/PPSSPP.vcxproj +++ b/Windows/PPSSPP.vcxproj @@ -1205,6 +1205,14 @@ true true + + true + true + true + true + true + true + diff --git a/Windows/PPSSPP.vcxproj.filters b/Windows/PPSSPP.vcxproj.filters index 07285507bc..4bc460a744 100644 --- a/Windows/PPSSPP.vcxproj.filters +++ b/Windows/PPSSPP.vcxproj.filters @@ -842,6 +842,9 @@ assets + + assets + diff --git a/assets/shaders/defaultshaders.ini b/assets/shaders/defaultshaders.ini index f322a6a8b4..460e1db049 100644 --- a/assets/shaders/defaultshaders.ini +++ b/assets/shaders/defaultshaders.ini @@ -260,6 +260,14 @@ Name=NNEDI3 Single (4x) Author=tritical, mpv community Compute=tex_nnedi3_4x_single.csh Scale=4 +[TexSmiley] +Type=Texture +Name=Smiley (2x) +Author=PPSSPP contributors +Compute=tex_smiley_2x.csh +Scale=2 +ConstantBuffer=smiley_16x16_rgba.bin +Hidden=True # this is an example shader that should not be shown in the list. [RedBlue] Type=StereoToMono Name=Red/Blue glasses (anaglyph) diff --git a/assets/shaders/smiley.py b/assets/shaders/smiley.py new file mode 100644 index 0000000000..eedf77d773 --- /dev/null +++ b/assets/shaders/smiley.py @@ -0,0 +1,53 @@ +import struct + +# Vibe coded nonsense to initialize a cbuffer + +# Constants +WIDTH, HEIGHT = 16, 16 +OUTPUT_FILE = "smiley_16x16_rgba.bin" + +# Define the colors (RGBA uint32) +# Format: 0xAABBGGRR (Little Endian will write this as R, G, B, A) +YELLOW = 0xFF00FFFF # Bright Yellow +BLACK = 0xFF000000 # Opaque Black +TRANS = 0x00000000 # Fully Transparent + +# A simple 16x16 map +# Y = Yellow, B = Black, . = Transparent +smiley_map = [ + " YYYY ", + " YYYBYYYY ", + " YYYYYYYYYY ", + " YYYYYYYYYYYY ", + " YYYBYYYYBBYYY ", + " YYYBYYYYBBYYY ", + "YYYYYYYYYYYYYYYY", + "YYYYYYYYYYYYYYYY", + "YYYYBYYYYYYBYYYY", + "YYYYYBBYBBBYYYYY", + " YYYYYYBBBYYYYY ", + " YYYYYYYYYYYYYY ", + " YYYYYYYYYYYY ", + " YYYYYYYYYY ", + " YYYYYYYY ", + " YYYY ", +] + +def generate_binary(): + with open(OUTPUT_FILE, "wb") as f: + for row in smiley_map: + for char in row: + if char == "Y": + color = YELLOW + elif char == "B": + color = BLACK + else: + color = TRANS + + # 'I' is unsigned int (32-bit), '<' is Little Endian + f.write(struct.pack('