From 4335a327cd4b4e1ce1e8e7995ed0ff9f15d1c304 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Fri, 10 Apr 2026 11:43:00 -0600 Subject: [PATCH 1/3] Hackery to prepare for constant buffers in compute --- Common/GPU/Vulkan/VulkanBarrier.cpp | 19 ++ Common/GPU/Vulkan/VulkanBarrier.h | 3 + Common/GPU/Vulkan/VulkanDescSet.h | 1 + Common/GPU/Vulkan/VulkanMemory.cpp | 26 ++ Common/GPU/Vulkan/VulkanMemory.h | 15 + Common/GPU/Vulkan/VulkanRenderManager.cpp | 16 +- GPU/Common/PostShader.cpp | 10 + GPU/Common/PostShader.h | 3 + GPU/GPU.vcxproj | 384 ++++++++++++++++++++++ GPU/GPU.vcxproj.filters | 144 ++++++++ GPU/Vulkan/TextureCacheVulkan.cpp | 26 +- GPU/Vulkan/TextureCacheVulkan.h | 4 +- assets/shaders/defaultshaders.ini | 7 + assets/shaders/smiley.py | 53 +++ assets/shaders/smiley_16x16_rgba.bin | Bin 0 -> 1024 bytes assets/shaders/tex_smiley_2x.csh | 30 ++ 16 files changed, 735 insertions(+), 6 deletions(-) create mode 100644 assets/shaders/smiley.py create mode 100644 assets/shaders/smiley_16x16_rgba.bin create mode 100644 assets/shaders/tex_smiley_2x.csh diff --git a/Common/GPU/Vulkan/VulkanBarrier.cpp b/Common/GPU/Vulkan/VulkanBarrier.cpp index 8a6bb0ef1a..822fd5640b 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,19 @@ void VulkanBarrierBatch::TransitionImage( imageBarrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; } +void VulkanBarrierBatch::TransitionBufferToShaderRead(VkBuffer buffer, VkDeviceSize offset, VkDeviceSize size) { + 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/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/VulkanMemory.cpp b/Common/GPU/Vulkan/VulkanMemory.cpp index 23af6ebf72..451960bf1b 100644 --- a/Common/GPU/Vulkan/VulkanMemory.cpp +++ b/Common/GPU/Vulkan/VulkanMemory.cpp @@ -176,3 +176,29 @@ 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); + } + + 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..f7e265046f 100644 --- a/Common/GPU/Vulkan/VulkanMemory.h +++ b/Common/GPU/Vulkan/VulkanMemory.h @@ -94,3 +94,18 @@ 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_; } + +private: + VkBuffer buffer_; + VmaAllocation allocation_; +}; 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..66bdfa25ab 100644 --- a/GPU/Common/PostShader.cpp +++ b/GPU/Common/PostShader.cpp @@ -212,6 +212,16 @@ void LoadPostShaderInfo(Draw::DrawContext *draw, const std::vector &direct info.scaleFactor = 0; section.Get("Name", &info.name); section.Get("Scale", &info.scaleFactor); + std::string cbufferFilename; + if (section.Get("ConstantBuffer", &cbufferFilename)) { + Path cbufferPath = path / cbufferFilename; + std::string temp; + if (File::ReadBinaryFileToString(cbufferPath, &temp)) { + info.constantBuffer.assign(temp.begin(), temp.end()); + } else { + ERROR_LOG(Log::G3D, "Failed to read constant buffer file '%s' for texture shader '%s'", cbufferPath.c_str(), info.name.c_str()); + } + } 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..7a8231afde 100644 --- a/GPU/Common/PostShader.h +++ b/GPU/Common/PostShader.h @@ -90,6 +90,9 @@ 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. + std::vector constantBuffer; + 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/TextureCacheVulkan.cpp b/GPU/Vulkan/TextureCacheVulkan.cpp index 0ebe937b2d..624dff7c47 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,8 @@ layout(push_constant) uniform Params { int height; } params; +#define CBUFFER_BINDING 4 + uint readColoru(uvec2 p) { return buf.data[p.y * params.width + p.x]; } @@ -251,14 +254,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 +299,7 @@ void TextureCacheVulkan::ClearScalingShaders(VulkanContext *vulkan) { multipassScratchDescs_.clear(); multipassStageDescs_.clear(); textureScalePipeline_ = TextureScalePipelineType::NONE; + textureSceleCBuffer_.Destroy(vulkan); } bool TextureCacheVulkan::CompileMultipassShader(VulkanContext *vulkan, const TextureShaderInfo &shaderInfo, std::string *error) { @@ -356,7 +362,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; @@ -402,6 +408,18 @@ void TextureCacheVulkan::CompileScalingShader() { textureScalePipeline_ = TextureScalePipelineType::SINGLE_PASS; } + if (!shaderInfo->constantBuffer.empty()) { + textureSceleCBuffer_.Create(vulkan, "TextureScale CBuffer", shaderInfo->constantBuffer.size(), VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT); + VulkanPushPool *pushPool = drawEngine_->GetPushBufferForTextureData(); + VkBuffer srcBuf; + VkDeviceSize offset = pushPool->Push(shaderInfo->constantBuffer.data(), shaderInfo->constantBuffer.size(), vulkan->GetPhysicalDeviceProperties().properties.limits.minUniformBufferOffsetAlignment, &srcBuf); + VkBufferCopy copyRegion{offset, 0, shaderInfo->constantBuffer.size()}; + vkCmdCopyBuffer(cmdInit, srcBuf, textureSceleCBuffer_.Buffer(), 1, ©Region); + VulkanBarrierBatch barrier; + barrier.TransitionBufferToShaderRead(textureSceleCBuffer_.Buffer(), 0, shaderInfo->constantBuffer.size()); + barrier.Flush(cmdInit); + } + textureShader_ = g_Config.sTextureShaderName; shaderScaleFactor_ = shaderInfo->scaleFactor; } diff --git a/GPU/Vulkan/TextureCacheVulkan.h b/GPU/Vulkan/TextureCacheVulkan.h index 79c1cf3d3f..dd01b2ac78 100644 --- a/GPU/Vulkan/TextureCacheVulkan.h +++ b/GPU/Vulkan/TextureCacheVulkan.h @@ -121,7 +121,7 @@ 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; @@ -146,6 +146,8 @@ private: VkSampler curSampler_ = VK_NULL_HANDLE; VkSampler samplerNearest_ = VK_NULL_HANDLE; + + VulkanBuffer textureSceleCBuffer_; }; VkFormat getClutDestFormatVulkan(GEPaletteFormat format); diff --git a/assets/shaders/defaultshaders.ini b/assets/shaders/defaultshaders.ini index f322a6a8b4..bd30fdaf6a 100644 --- a/assets/shaders/defaultshaders.ini +++ b/assets/shaders/defaultshaders.ini @@ -260,6 +260,13 @@ 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 [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('^uZ+)EIr7anOY*5=)jrAt?Eqks3KIYT literal 0 HcmV?d00001 diff --git a/assets/shaders/tex_smiley_2x.csh b/assets/shaders/tex_smiley_2x.csh new file mode 100644 index 0000000000..c9f6f718ee --- /dev/null +++ b/assets/shaders/tex_smiley_2x.csh @@ -0,0 +1,30 @@ +// Test shader for constant buffers in texture upscalers + +#extension GL_EXT_scalar_block_layout : enable + +layout(scalar, set = 0, binding = 3) uniform PixelData { + uint pixels[256]; +}; + +void applyScaling(uvec2 origxy) { + // 1. Flatten 2D coordinates to 1D index + // Use bitwise & 15 to wrap/clamp to the 16x16 bounds + uint x = origxy.x & 15u; + uint y = origxy.y & 15u; + uint index = (y * 16u) + x; + + // 2. Access the scalar array + uint color = pixels[index]; + + // 3. Unpack (Note: unpackUnorm4x8 expects a uint) + vec4 rgba = unpackUnorm4x8(color); + + // Assuming destXY is derived from origxy (e.g., origxy * 2) + ivec2 destXY = ivec2(origxy) * 2; + + // 4. Output calls + writeColorf(destXY, rgba); + writeColorf(destXY + ivec2(1, 0), rgba); + writeColorf(destXY + ivec2(0, 1), rgba); + writeColorf(destXY + ivec2(1, 1), rgba); +} From 4c2be7f8afd9f44aec791750b403c84785044406 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Fri, 10 Apr 2026 12:43:19 -0600 Subject: [PATCH 2/3] More work. Some initialization order problem. --- Common/GPU/Vulkan/VulkanContext.cpp | 17 ++++++++++++ Common/GPU/Vulkan/VulkanContext.h | 1 + Common/GPU/Vulkan/VulkanDescSet.cpp | 7 +++++ Common/GPU/Vulkan/VulkanLoader.h | 1 + Common/GPU/Vulkan/VulkanMemory.cpp | 6 +++++ Common/GPU/Vulkan/VulkanMemory.h | 6 +++-- GPU/Common/PostShader.cpp | 8 ++++-- GPU/Vulkan/GPU_Vulkan.cpp | 2 +- GPU/Vulkan/TextureCacheVulkan.cpp | 17 +++++++----- GPU/Vulkan/TextureCacheVulkan.h | 4 +-- GPU/Vulkan/VulkanUtil.cpp | 40 ++++++++++++++++++++--------- GPU/Vulkan/VulkanUtil.h | 2 +- Windows/PPSSPP.vcxproj | 8 ++++++ Windows/PPSSPP.vcxproj.filters | 3 +++ assets/shaders/tex_smiley_2x.csh | 2 +- 15 files changed, 96 insertions(+), 28 deletions(-) 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/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 451960bf1b..e54f385b7a 100644 --- a/Common/GPU/Vulkan/VulkanMemory.cpp +++ b/Common/GPU/Vulkan/VulkanMemory.cpp @@ -182,6 +182,12 @@ void VulkanBuffer::Create(VulkanContext *vulkan, const char *name, VkDeviceSize Destroy(vulkan); } + size_ = size; + + if (size == 0) { + return; + } + VkBufferCreateInfo b{VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO}; b.size = size; b.usage = usage; diff --git a/Common/GPU/Vulkan/VulkanMemory.h b/Common/GPU/Vulkan/VulkanMemory.h index f7e265046f..fe0bc576d5 100644 --- a/Common/GPU/Vulkan/VulkanMemory.h +++ b/Common/GPU/Vulkan/VulkanMemory.h @@ -104,8 +104,10 @@ public: void Destroy(VulkanContext *vulkan); VkBuffer Buffer() const { return buffer_; } + VkDeviceSize Size() const { return size_; } private: - VkBuffer buffer_; - VmaAllocation allocation_; + VkBuffer buffer_ = VK_NULL_HANDLE; + VmaAllocation allocation_ = VK_NULL_HANDLE; + VkDeviceSize size_ = 0; // convenience }; diff --git a/GPU/Common/PostShader.cpp b/GPU/Common/PostShader.cpp index 66bdfa25ab..fb33de7858 100644 --- a/GPU/Common/PostShader.cpp +++ b/GPU/Common/PostShader.cpp @@ -216,10 +216,14 @@ void LoadPostShaderInfo(Draw::DrawContext *draw, const std::vector &direct if (section.Get("ConstantBuffer", &cbufferFilename)) { Path cbufferPath = path / cbufferFilename; std::string temp; - if (File::ReadBinaryFileToString(cbufferPath, &temp)) { - info.constantBuffer.assign(temp.begin(), temp.end()); + size_t constantsSize; + uint8_t *contents = g_VFS.ReadFile(cbufferPath.c_str(), &constantsSize); + if (contents) { + info.constantBuffer.assign(contents, contents + constantsSize); + delete[] contents; } else { ERROR_LOG(Log::G3D, "Failed to read constant buffer file '%s' for texture shader '%s'", cbufferPath.c_str(), info.name.c_str()); + continue; } } if (section.Get("Compute", &temp)) { 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 624dff7c47..b94fe3063c 100644 --- a/GPU/Vulkan/TextureCacheVulkan.cpp +++ b/GPU/Vulkan/TextureCacheVulkan.cpp @@ -77,6 +77,8 @@ 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) { @@ -299,7 +301,7 @@ void TextureCacheVulkan::ClearScalingShaders(VulkanContext *vulkan) { multipassScratchDescs_.clear(); multipassStageDescs_.clear(); textureScalePipeline_ = TextureScalePipelineType::NONE; - textureSceleCBuffer_.Destroy(vulkan); + textureScaleCBuffer_.Destroy(vulkan); } bool TextureCacheVulkan::CompileMultipassShader(VulkanContext *vulkan, const TextureShaderInfo &shaderInfo, std::string *error) { @@ -385,8 +387,9 @@ void TextureCacheVulkan::CompileScalingShader(VkCommandBuffer cmdInit) { return; } - if (!g_Config.bTexHardwareScaling) + if (!g_Config.bTexHardwareScaling) { return; + } ReloadAllPostShaderInfo(draw_); const TextureShaderInfo *shaderInfo = GetTextureShaderInfo(g_Config.sTextureShaderName); @@ -409,14 +412,14 @@ void TextureCacheVulkan::CompileScalingShader(VkCommandBuffer cmdInit) { } if (!shaderInfo->constantBuffer.empty()) { - textureSceleCBuffer_.Create(vulkan, "TextureScale CBuffer", shaderInfo->constantBuffer.size(), VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT); + textureScaleCBuffer_.Create(vulkan, "TextureScale CBuffer", shaderInfo->constantBuffer.size(), VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT); VulkanPushPool *pushPool = drawEngine_->GetPushBufferForTextureData(); VkBuffer srcBuf; VkDeviceSize offset = pushPool->Push(shaderInfo->constantBuffer.data(), shaderInfo->constantBuffer.size(), vulkan->GetPhysicalDeviceProperties().properties.limits.minUniformBufferOffsetAlignment, &srcBuf); VkBufferCopy copyRegion{offset, 0, shaderInfo->constantBuffer.size()}; - vkCmdCopyBuffer(cmdInit, srcBuf, textureSceleCBuffer_.Buffer(), 1, ©Region); + vkCmdCopyBuffer(cmdInit, srcBuf, textureScaleCBuffer_.Buffer(), 1, ©Region); VulkanBarrierBatch barrier; - barrier.TransitionBufferToShaderRead(textureSceleCBuffer_.Buffer(), 0, shaderInfo->constantBuffer.size()); + barrier.TransitionBufferToShaderRead(textureScaleCBuffer_.Buffer(), 0, shaderInfo->constantBuffer.size()); barrier.Flush(cmdInit); } @@ -497,7 +500,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); @@ -521,7 +524,7 @@ bool TextureCacheVulkan::ScaleBufferToImage(VkCommandBuffer cmdInit, VkImageView 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); diff --git a/GPU/Vulkan/TextureCacheVulkan.h b/GPU/Vulkan/TextureCacheVulkan.h index dd01b2ac78..8721caadb9 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; @@ -146,8 +147,7 @@ private: VkSampler curSampler_ = VK_NULL_HANDLE; VkSampler samplerNearest_ = VK_NULL_HANDLE; - - VulkanBuffer textureSceleCBuffer_; + VulkanBuffer textureScaleCBuffer_; }; VkFormat getClutDestFormatVulkan(GEPaletteFormat format); diff --git a/GPU/Vulkan/VulkanUtil.cpp b/GPU/Vulkan/VulkanUtil.cpp index 1e2cfa75d2..60d34e1d61 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,9 @@ 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].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; + bindings[4].stageFlags = VK_SHADER_STAGE_COMPUTE_BIT; + bindings[4].binding = 4; VkDevice device = vulkan_->GetDevice(); @@ -141,7 +145,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 +185,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 +193,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 +245,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/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/tex_smiley_2x.csh b/assets/shaders/tex_smiley_2x.csh index c9f6f718ee..555f201bc6 100644 --- a/assets/shaders/tex_smiley_2x.csh +++ b/assets/shaders/tex_smiley_2x.csh @@ -2,7 +2,7 @@ #extension GL_EXT_scalar_block_layout : enable -layout(scalar, set = 0, binding = 3) uniform PixelData { +layout(scalar, set = CBUFFER_SET, binding = CBUFFER_BINDING) uniform PixelData { uint pixels[256]; }; From d108fe25d10b36c3f6ee3ebf57018910a6c6b7a4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Sun, 12 Apr 2026 09:16:29 -0600 Subject: [PATCH 3/3] Rework constant buffer loading, barrier fix --- Common/Data/Text/I18n.cpp | 4 +-- Common/GPU/Vulkan/VulkanBarrier.cpp | 3 ++ GPU/Common/PostShader.cpp | 13 ++------ GPU/Common/PostShader.h | 3 +- GPU/Vulkan/TextureCacheVulkan.cpp | 52 +++++++++++++++++++++-------- GPU/Vulkan/TextureCacheVulkan.h | 8 ++++- GPU/Vulkan/VulkanUtil.cpp | 3 +- UI/MiscScreens.cpp | 3 ++ assets/shaders/defaultshaders.ini | 1 + 9 files changed, 61 insertions(+), 29 deletions(-) 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 822fd5640b..abdd50ca2e 100644 --- a/Common/GPU/Vulkan/VulkanBarrier.cpp +++ b/Common/GPU/Vulkan/VulkanBarrier.cpp @@ -58,6 +58,9 @@ void VulkanBarrierBatch::TransitionImage( } 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; diff --git a/GPU/Common/PostShader.cpp b/GPU/Common/PostShader.cpp index fb33de7858..0e6ca85419 100644 --- a/GPU/Common/PostShader.cpp +++ b/GPU/Common/PostShader.cpp @@ -212,19 +212,12 @@ 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; - std::string temp; - size_t constantsSize; - uint8_t *contents = g_VFS.ReadFile(cbufferPath.c_str(), &constantsSize); - if (contents) { - info.constantBuffer.assign(contents, contents + constantsSize); - delete[] contents; - } else { - ERROR_LOG(Log::G3D, "Failed to read constant buffer file '%s' for texture shader '%s'", cbufferPath.c_str(), info.name.c_str()); - continue; - } + info.constantBuffer = cbufferPath; } if (section.Get("Compute", &temp)) { info.computeShaderFile = path / temp; diff --git a/GPU/Common/PostShader.h b/GPU/Common/PostShader.h index 7a8231afde..77fdbb94ee 100644 --- a/GPU/Common/PostShader.h +++ b/GPU/Common/PostShader.h @@ -91,7 +91,8 @@ struct TextureShaderInfo { 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. - std::vector constantBuffer; + Path constantBuffer; + bool hidden = false; bool operator == (const std::string &other) const { return name == other; diff --git a/GPU/Vulkan/TextureCacheVulkan.cpp b/GPU/Vulkan/TextureCacheVulkan.cpp index b94fe3063c..37d975b0f0 100644 --- a/GPU/Vulkan/TextureCacheVulkan.cpp +++ b/GPU/Vulkan/TextureCacheVulkan.cpp @@ -411,17 +411,9 @@ void TextureCacheVulkan::CompileScalingShader(VkCommandBuffer cmdInit) { textureScalePipeline_ = TextureScalePipelineType::SINGLE_PASS; } - if (!shaderInfo->constantBuffer.empty()) { - textureScaleCBuffer_.Create(vulkan, "TextureScale CBuffer", shaderInfo->constantBuffer.size(), VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT); - VulkanPushPool *pushPool = drawEngine_->GetPushBufferForTextureData(); - VkBuffer srcBuf; - VkDeviceSize offset = pushPool->Push(shaderInfo->constantBuffer.data(), shaderInfo->constantBuffer.size(), vulkan->GetPhysicalDeviceProperties().properties.limits.minUniformBufferOffsetAlignment, &srcBuf); - VkBufferCopy copyRegion{offset, 0, shaderInfo->constantBuffer.size()}; - vkCmdCopyBuffer(cmdInit, srcBuf, textureScaleCBuffer_.Buffer(), 1, ©Region); - VulkanBarrierBatch barrier; - barrier.TransitionBufferToShaderRead(textureScaleCBuffer_.Buffer(), 0, shaderInfo->constantBuffer.size()); - barrier.Flush(cmdInit); - } + // 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; @@ -442,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; @@ -449,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)) { @@ -513,12 +536,13 @@ 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) { @@ -878,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 8721caadb9..520567eace 100644 --- a/GPU/Vulkan/TextureCacheVulkan.h +++ b/GPU/Vulkan/TextureCacheVulkan.h @@ -127,7 +127,9 @@ private: 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_; @@ -147,7 +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 60d34e1d61..ab67da1f11 100644 --- a/GPU/Vulkan/VulkanUtil.cpp +++ b/GPU/Vulkan/VulkanUtil.cpp @@ -132,7 +132,8 @@ 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].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; + bindings[4].descriptorCount = 1; + bindings[4].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; bindings[4].stageFlags = VK_SHADER_STAGE_COMPUTE_BIT; bindings[4].binding = 4; 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/assets/shaders/defaultshaders.ini b/assets/shaders/defaultshaders.ini index bd30fdaf6a..460e1db049 100644 --- a/assets/shaders/defaultshaders.ini +++ b/assets/shaders/defaultshaders.ini @@ -267,6 +267,7 @@ 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)