diff --git a/bin/resources/shaders/common/ffx_a.h b/bin/resources/shaders/common/ffx_a.h index 211734ebd9..5f9b347b3b 100644 --- a/bin/resources/shaders/common/ffx_a.h +++ b/bin/resources/shaders/common/ffx_a.h @@ -640,11 +640,13 @@ AU3 AAbsSU3(AU3 a){return AU3(abs(ASU3(a)));} AU4 AAbsSU4(AU4 a){return AU4(abs(ASU4(a)));} //------------------------------------------------------------------------------------------------------------------------------ - AU1 ABfe(AU1 src,AU1 off,AU1 bits){return bitfieldExtract(src,ASU1(off),ASU1(bits));} + // Emulate bitfieldExtract. + AU1 ABfe(AU1 src, AU1 off, AU1 bits){uint mask = (bits >= 32u) ? 0xFFFFFFFFu : ((1u << bits) - 1u);return (src >> off) & mask;} AU1 ABfi(AU1 src,AU1 ins,AU1 mask){return (ins&mask)|(src&(~mask));} // Proxy for V_BFI_B32 where the 'mask' is set as 'bits', 'mask=(1<= 32u) ? 0xFFFFFFFFu : ((1u << bits) - 1u);return (src & ~mask) | (ins & mask);} + //------------------------------------------------------------------------------------------------------------------------------ // V_FRACT_F32 (note DX frac() is different). AF1 AFractF1(AF1 x){return fract(x);} AF2 AFractF2(AF2 x){return fract(x);} diff --git a/bin/resources/shaders/dx11/cas.hlsl b/bin/resources/shaders/dx11/cas.hlsl index 47552122a9..9c7426b7b6 100644 --- a/bin/resources/shaders/dx11/cas.hlsl +++ b/bin/resources/shaders/dx11/cas.hlsl @@ -19,13 +19,13 @@ cbuffer cb : register(b0) { - uint4 const0; - uint4 const1; - int2 srcOffset; + uint4 const0; + uint4 const1; + int2 srcOffset; + int2 pad; }; Texture2D InputTexture : register(t0); -RWTexture2D OutputTexture : register(u0); #define A_GPU 1 #define A_HLSL 1 @@ -34,7 +34,7 @@ RWTexture2D OutputTexture : register(u0); AF3 CasLoad(ASU2 p) { - return InputTexture.Load(int3(srcOffset, 0) + int3(p, 0)).rgb; + return InputTexture.Load(int3(srcOffset, 0) + int3(p, 0)).rgb; } // Lets you transform input from the load into a linear color space between 0 and 1. See ffx_cas.h @@ -43,33 +43,19 @@ void CasInput(inout AF1 r, inout AF1 g, inout AF1 b) {} #include "ffx_cas.h" -[numthreads(64, 1, 1)] -void main(uint3 LocalThreadId : SV_GroupThreadID, uint3 WorkGroupId : SV_GroupID) +float4 ps_main(float4 position : SV_Position, float2 uv : TEXCOORD0) : SV_Target { - // Do remapping of local xy in workgroup for a more PS-like swizzle pattern. - AU2 gxy = ARmp8x8(LocalThreadId.x) + AU2(WorkGroupId.x << 4u, WorkGroupId.y << 4u); + // Pixel coordinate for the current fragment + AU2 gxy = AU2(position.xy); #if CAS_SHARPEN_ONLY - const bool sharpenOnly = true; + const bool sharpenOnly = true; #else - const bool sharpenOnly = false; + const bool sharpenOnly = false; #endif - // Filter. - AF3 c = (float3)0.0f; + AF3 c = (AF3)0.0f; + CasFilter(c.r, c.g, c.b, gxy, const0, const1, sharpenOnly); - CasFilter(c.r, c.g, c.b, gxy, const0, const1, sharpenOnly); - OutputTexture[ASU2(gxy)] = AF4(c, 1); - gxy.x += 8u; - - CasFilter(c.r, c.g, c.b, gxy, const0, const1, sharpenOnly); - OutputTexture[ASU2(gxy)] = AF4(c, 1); - gxy.y += 8u; - - CasFilter(c.r, c.g, c.b, gxy, const0, const1, sharpenOnly); - OutputTexture[ASU2(gxy)] = AF4(c, 1); - gxy.x -= 8u; - - CasFilter(c.r, c.g, c.b, gxy, const0, const1, sharpenOnly); - OutputTexture[ASU2(gxy)] = AF4(c, 1); -} + return AF4(c, 1.0f); +} \ No newline at end of file diff --git a/bin/resources/shaders/opengl/cas.glsl b/bin/resources/shaders/opengl/cas.glsl index baa1ecd940..967ce04eb5 100644 --- a/bin/resources/shaders/opengl/cas.glsl +++ b/bin/resources/shaders/opengl/cas.glsl @@ -16,12 +16,16 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. +//#version 420 // Keep it for editor detection + +#ifdef FRAGMENT_SHADER uniform uvec4 const0; uniform uvec4 const1; uniform ivec2 srcOffset; -layout(binding=0) uniform sampler2D imgSrc; -layout(binding=0, rgba8) uniform writeonly image2D imgDst; +layout(binding = 0) uniform sampler2D imgSrc; + +out vec4 fragColor; #define A_GPU 1 #define A_GLSL 1 @@ -39,26 +43,17 @@ void CasInput(inout AF1 r, inout AF1 g, inout AF1 b) {} #include "ffx_cas.h" -layout(local_size_x=64) in; void main() { - // Do remapping of local xy in workgroup for a more PS-like swizzle pattern. - AU2 gxy = ARmp8x8(gl_LocalInvocationID.x)+AU2(gl_WorkGroupID.x<<4u,gl_WorkGroupID.y<<4u); + AU2 gxy = AU2(gl_FragCoord.xy); +#if CAS_SHARPEN_ONLY + const bool sharpenOnly = true; +#else + const bool sharpenOnly = false; +#endif + AF3 c = AF3(0.0f); + CasFilter(c.r, c.g, c.b, gxy, const0, const1, sharpenOnly); - // Filter. - AF4 c = vec4(0.0f); - CasFilter(c.r, c.g, c.b, gxy, const0, const1, CAS_SHARPEN_ONLY); - imageStore(imgDst, ASU2(gxy), c); - gxy.x += 8u; - - CasFilter(c.r, c.g, c.b, gxy, const0, const1, CAS_SHARPEN_ONLY); - imageStore(imgDst, ASU2(gxy), c); - gxy.y += 8u; - - CasFilter(c.r, c.g, c.b, gxy, const0, const1, CAS_SHARPEN_ONLY); - imageStore(imgDst, ASU2(gxy), c); - gxy.x -= 8u; - - CasFilter(c.r, c.g, c.b, gxy, const0, const1, CAS_SHARPEN_ONLY); - imageStore(imgDst, ASU2(gxy), c); + fragColor = vec4(c, 1.0f); } +#endif \ No newline at end of file diff --git a/bin/resources/shaders/vulkan/cas.glsl b/bin/resources/shaders/vulkan/cas.glsl index 8a71173095..dd21011321 100644 --- a/bin/resources/shaders/vulkan/cas.glsl +++ b/bin/resources/shaders/vulkan/cas.glsl @@ -1,6 +1,3 @@ -#version 460 core -#extension GL_EXT_samplerless_texture_functions : require - // Based on CAS_Shader.glsl // Copyright(c) 2019 Advanced Micro Devices, Inc.All rights reserved. // Permission is hereby granted, free of charge, to any person obtaining a copy @@ -21,14 +18,12 @@ layout(push_constant) uniform const_buffer { - uvec4 const0; - uvec4 const1; - ivec2 srcOffset; + uvec4 const0; + uvec4 const1; + ivec2 srcOffset; }; layout(set=0, binding=0) uniform texture2D imgSrc; -layout(set=0, binding=1, rgba8) uniform writeonly image2D imgDst; -layout(constant_id=0) const int sharpenOnly = 0; #define A_GPU 1 #define A_GLSL 1 @@ -37,7 +32,7 @@ layout(constant_id=0) const int sharpenOnly = 0; AF3 CasLoad(ASU2 p) { - return texelFetch(imgSrc, srcOffset + ivec2(p), 0).rgb; + return texelFetch(imgSrc, srcOffset + ivec2(p), 0).rgb; } // Lets you transform input from the load into a linear color space between 0 and 1. See ffx_cas.h @@ -46,26 +41,21 @@ void CasInput(inout AF1 r, inout AF1 g, inout AF1 b) {} #include "ffx_cas.h" -layout(local_size_x=64) in; +layout(location = 0) out vec4 o_col0; + void main() { - // Do remapping of local xy in workgroup for a more PS-like swizzle pattern. - AU2 gxy = ARmp8x8(gl_LocalInvocationID.x)+AU2(gl_WorkGroupID.x<<4u,gl_WorkGroupID.y<<4u); + // Pixel coordinate for the current fragment + AU2 gxy = AU2(gl_FragCoord.xy); - // Filter. - AF4 c = vec4(0.0f); - CasFilter(c.r, c.g, c.b, gxy, const0, const1, sharpenOnly != 0); - imageStore(imgDst, ASU2(gxy), c); - gxy.x += 8u; +#if CAS_SHARPEN_ONLY + const bool sharpenOnly = true; +#else + const bool sharpenOnly = false; +#endif - CasFilter(c.r, c.g, c.b, gxy, const0, const1, sharpenOnly != 0); - imageStore(imgDst, ASU2(gxy), c); - gxy.y += 8u; + AF3 c = AF3(0.0f); + CasFilter(c.r, c.g, c.b, gxy, const0, const1, sharpenOnly); - CasFilter(c.r, c.g, c.b, gxy, const0, const1, sharpenOnly != 0); - imageStore(imgDst, ASU2(gxy), c); - gxy.x -= 8u; - - CasFilter(c.r, c.g, c.b, gxy, const0, const1, sharpenOnly != 0); - imageStore(imgDst, ASU2(gxy), c); -} + o_col0 = AF4(c, 1.0f); +} \ No newline at end of file diff --git a/pcsx2/GS/Renderers/Common/GSDevice.cpp b/pcsx2/GS/Renderers/Common/GSDevice.cpp index e302311b74..1ee50ac7ca 100644 --- a/pcsx2/GS/Renderers/Common/GSDevice.cpp +++ b/pcsx2/GS/Renderers/Common/GSDevice.cpp @@ -1247,10 +1247,10 @@ void GSDevice::CAS(GSTexture*& tex, GSVector4i& src_rect, GSVector4& src_uv, con if (!m_cas || m_cas->GetWidth() != dst_width || m_cas->GetHeight() != dst_height) { delete m_cas; - m_cas = CreateSurface(GSTexture::ShaderWriteTexture, dst_width, dst_height, 1, GSTexture::Format::Color); + m_cas = CreateSurface(GSTexture::RenderTarget, dst_width, dst_height, 1, GSTexture::Format::Color); if (!m_cas) { - Console.Error("Failed to allocate CAS RW texture."); + Console.Error("GS: Failed to allocate CAS texture."); return; } } @@ -1265,7 +1265,7 @@ void GSDevice::CAS(GSTexture*& tex, GSVector4i& src_rect, GSVector4& src_uv, con if (!DoCAS(src_tex, m_cas, sharpen_only, consts)) { // leave textures intact if we failed - Console.Warning("Applying CAS failed."); + Console.Warning("GS: Applying CAS failed."); return; } diff --git a/pcsx2/GS/Renderers/DX11/GSDevice11.cpp b/pcsx2/GS/Renderers/DX11/GSDevice11.cpp index bc6349af06..cb2a471cd9 100644 --- a/pcsx2/GS/Renderers/DX11/GSDevice11.cpp +++ b/pcsx2/GS/Renderers/DX11/GSDevice11.cpp @@ -592,7 +592,7 @@ bool GSDevice11::Create(GSVSyncMode vsync_mode, bool allow_present_throttle) return false; } - if (m_features.cas_sharpening && !CreateCASShaders()) + if (!CreateCASShaders()) return false; if (!CreateImGuiResources()) @@ -715,7 +715,6 @@ void GSDevice11::SetFeatures(IDXGIAdapter1* adapter) SupportsTextureFormat(m_dev.get(), DXGI_FORMAT_BC3_UNORM); m_features.bptc_textures = SupportsTextureFormat(m_dev.get(), DXGI_FORMAT_BC7_UNORM); - m_features.cas_sharpening = (m_feature_level >= D3D_FEATURE_LEVEL_11_0); m_features.test_and_sample_depth = (m_feature_level >= D3D_FEATURE_LEVEL_11_0); m_features.depth_feedback = m_features.multidraw_fb_copy && GSConfig.DepthFeedbackMode == GSDepthFeedbackMode::Depth; m_features.aa1 = GSConfig.HWAA1 && m_features.vs_expand && m_features.feedback_loops(); @@ -2273,7 +2272,7 @@ void GSDevice11::SetupOM(OMDepthStencilSelector dssel, OMBlendSelector bsel, u8 bool GSDevice11::CreateCASShaders() { CD3D11_BUFFER_DESC desc(NUM_CAS_CONSTANTS * sizeof(u32), D3D11_BIND_CONSTANT_BUFFER, D3D11_USAGE_DEFAULT); - HRESULT hr = m_dev->CreateBuffer(&desc, nullptr, m_cas.cb.put()); + const HRESULT hr = m_dev->CreateBuffer(&desc, nullptr, m_cas.cb.put()); if (FAILED(hr)) return false; @@ -2281,15 +2280,13 @@ bool GSDevice11::CreateCASShaders() if (!cas_source.has_value() || !GetCASShaderSource(&cas_source.value())) return false; - static constexpr D3D_SHADER_MACRO sharpen_only_macros[] = { - {"CAS_SHARPEN_ONLY", "1"}, - {nullptr, nullptr}}; + static constexpr D3D_SHADER_MACRO sharpen_macros[] = {{"CAS_SHARPEN_ONLY", "1"}, {nullptr, nullptr}}; - m_cas.cs_sharpen = m_shader_cache.GetComputeShader(m_dev.get(), cas_source.value(), sharpen_only_macros, "main"); - m_cas.cs_upscale = m_shader_cache.GetComputeShader(m_dev.get(), cas_source.value(), nullptr, "main"); - if (!m_cas.cs_sharpen || !m_cas.cs_upscale) + m_cas.ps_sharpen = m_shader_cache.GetPixelShader(m_dev.get(), cas_source.value(), sharpen_macros, "ps_main"); + m_cas.ps_upscale = m_shader_cache.GetPixelShader(m_dev.get(), cas_source.value(), nullptr, "ps_main"); + if (!m_cas.ps_sharpen || !m_cas.ps_upscale) { - Console.Error("D3D11: Failed to create CAS compute shaders."); + Console.Error("D3D11: Failed to create CAS pixel shaders."); return false; } @@ -2298,27 +2295,14 @@ bool GSDevice11::CreateCASShaders() bool GSDevice11::DoCAS(GSTexture* sTex, GSTexture* dTex, bool sharpen_only, const std::array& constants) { - g_perfmon.Put(GSPerfMon::TextureCopies, 1); + const GSVector2i s = dTex->GetSize(); - static constexpr int threadGroupWorkRegionDim = 16; - const int dispatchX = (dTex->GetWidth() + (threadGroupWorkRegionDim - 1)) / threadGroupWorkRegionDim; - const int dispatchY = (dTex->GetHeight() + (threadGroupWorkRegionDim - 1)) / threadGroupWorkRegionDim; + const GSVector4 sRect(0, 0, 1, 1); + const GSVector4 dRect(0, 0, s.x, s.y); - ID3D11ShaderResourceView* srvs[1] = {*static_cast(sTex)}; - ID3D11UnorderedAccessView* uavs[1] = {*static_cast(dTex)}; - OMSetRenderTargets(nullptr, nullptr, nullptr); m_ctx->UpdateSubresource(m_cas.cb.get(), 0, nullptr, constants.data(), 0, 0); - m_ctx->CSSetConstantBuffers(0, 1, m_cas.cb.addressof()); - m_ctx->CSSetShader(sharpen_only ? m_cas.cs_sharpen.get() : m_cas.cs_upscale.get(), nullptr, 0); - m_ctx->CSSetShaderResources(0, std::size(srvs), srvs); - m_ctx->CSSetUnorderedAccessViews(0, std::size(uavs), uavs, nullptr); - m_ctx->Dispatch(dispatchX, dispatchY, 1); - // clear bindings out to prevent hazards - uavs[0] = nullptr; - srvs[0] = nullptr; - m_ctx->CSSetShaderResources(0, std::size(srvs), srvs); - m_ctx->CSSetUnorderedAccessViews(0, std::size(uavs), uavs, nullptr); + DoStretchRect(sTex, sRect, dTex, dRect, sharpen_only ? m_cas.ps_sharpen.get() : m_cas.ps_upscale.get(), m_cas.cb.get(), Nearest); return true; } diff --git a/pcsx2/GS/Renderers/DX11/GSDevice11.h b/pcsx2/GS/Renderers/DX11/GSDevice11.h index 3da31414cf..33f47a9cca 100644 --- a/pcsx2/GS/Renderers/DX11/GSDevice11.h +++ b/pcsx2/GS/Renderers/DX11/GSDevice11.h @@ -271,8 +271,8 @@ private: struct { wil::com_ptr_nothrow cb; - wil::com_ptr_nothrow cs_upscale; - wil::com_ptr_nothrow cs_sharpen; + wil::com_ptr_nothrow ps_upscale; + wil::com_ptr_nothrow ps_sharpen; } m_cas; struct diff --git a/pcsx2/GS/Renderers/DX12/GSDevice12.cpp b/pcsx2/GS/Renderers/DX12/GSDevice12.cpp index 879b33edab..cf918b3b23 100644 --- a/pcsx2/GS/Renderers/DX12/GSDevice12.cpp +++ b/pcsx2/GS/Renderers/DX12/GSDevice12.cpp @@ -60,6 +60,14 @@ static D3D12_RENDER_PASS_BEGINNING_ACCESS_TYPE GetLoadOpForTexture(GSTexture12* // clang-format on } +static void AddUtilityVertexAttributes(D3D12::GraphicsPipelineBuilder& gpb) +{ + gpb.AddVertexAttribute("POSITION", 0, DXGI_FORMAT_R32G32B32A32_FLOAT, 0, 0); + gpb.AddVertexAttribute("TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 16); + gpb.AddVertexAttribute("COLOR", 0, DXGI_FORMAT_R8G8B8A8_UNORM, 0, 28); + gpb.SetPrimitiveTopologyType(D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE); +} + GSDevice12::ShaderMacro::ShaderMacro() { mlist.emplace_back("DX12", "1"); @@ -2327,31 +2335,36 @@ void GSDevice12::DoFXAA(GSTexture* sTex, GSTexture* dTex) bool GSDevice12::CompileCASPipelines() { - D3D12::RootSignatureBuilder rsb; - rsb.Add32BitConstants(0, NUM_CAS_CONSTANTS, D3D12_SHADER_VISIBILITY_ALL); - rsb.AddDescriptorTable(D3D12_DESCRIPTOR_RANGE_TYPE_SRV, 0, 1, D3D12_SHADER_VISIBILITY_ALL); - rsb.AddDescriptorTable(D3D12_DESCRIPTOR_RANGE_TYPE_UAV, 0, 1, D3D12_SHADER_VISIBILITY_ALL); - m_cas_root_signature = rsb.Create(false); - if (!m_cas_root_signature) - return false; + D3D12::GraphicsPipelineBuilder gpb; + gpb.SetRootSignature(m_utility_root_signature.get()); + AddUtilityVertexAttributes(gpb); + gpb.SetNoCullRasterizationState(); + gpb.SetNoBlendingState(); + gpb.SetVertexShader(m_convert_vs.get()); std::optional cas_source = ReadShaderSource("shaders/dx11/cas.hlsl"); if (!cas_source.has_value() || !GetCASShaderSource(&cas_source.value())) return false; - static constexpr D3D_SHADER_MACRO sharpen_only_macros[] = {{"CAS_SHARPEN_ONLY", "1"}, {nullptr, nullptr}}; + static constexpr D3D_SHADER_MACRO sharpen_macros[] = {{"CAS_SHARPEN_ONLY", "1"}, {nullptr, nullptr}}; - const ComPtr cs_upscale(m_shader_cache.GetComputeShader(cas_source.value(), nullptr, "main")); - const ComPtr cs_sharpen(m_shader_cache.GetComputeShader(cas_source.value(), sharpen_only_macros, "main")); + const ComPtr cs_upscale(m_shader_cache.GetPixelShader(cas_source.value(), nullptr, "ps_main")); + const ComPtr cs_sharpen(m_shader_cache.GetPixelShader(cas_source.value(), sharpen_macros, "ps_main")); if (!cs_upscale || !cs_sharpen) return false; - D3D12::ComputePipelineBuilder cpb; - cpb.SetRootSignature(m_cas_root_signature.get()); - cpb.SetShader(cs_upscale->GetBufferPointer(), cs_upscale->GetBufferSize()); - m_cas_upscale_pipeline = cpb.Create(m_device.get(), m_shader_cache, false); - cpb.SetShader(cs_sharpen->GetBufferPointer(), cs_sharpen->GetBufferSize()); - m_cas_sharpen_pipeline = cpb.Create(m_device.get(), m_shader_cache, false); + gpb.SetRootSignature(m_utility_root_signature.get()); + gpb.SetRenderTarget(0, DXGI_FORMAT_R8G8B8A8_UNORM); + gpb.SetNoDepthTestState(); + gpb.SetNoStencilState(); + gpb.SetBlendState(0, false, D3D12_BLEND_ONE, D3D12_BLEND_ONE, D3D12_BLEND_OP_ADD, D3D12_BLEND_ZERO, + D3D12_BLEND_ZERO, D3D12_BLEND_OP_ADD, D3D12_COLOR_WRITE_ENABLE_ALL); + + gpb.SetPixelShader(cs_upscale.get()); + m_cas_upscale_pipeline = gpb.Create(m_device.get(), m_shader_cache, false); + + gpb.SetPixelShader(cs_sharpen.get()); + m_cas_sharpen_pipeline = gpb.Create(m_device.get(), m_shader_cache, false); if (!m_cas_upscale_pipeline || !m_cas_sharpen_pipeline) { Console.Error("D3D12: Failed to create CAS pipelines"); @@ -2516,44 +2529,18 @@ void GSDevice12::RenderImGui() bool GSDevice12::DoCAS( GSTexture* sTex, GSTexture* dTex, bool sharpen_only, const std::array& constants) { - g_perfmon.Put(GSPerfMon::TextureCopies, 1); + GL_PUSH("DoCAS"); EndRenderPass(); - GSTexture12* const sTex12 = static_cast(sTex); - GSTexture12* const dTex12 = static_cast(dTex); - D3D12DescriptorHandle sTexDH, dTexDH; - if (!GetTextureGroupDescriptors(&sTexDH, &sTex12->GetSRVDescriptor(), 1) || - !GetTextureGroupDescriptors(&dTexDH, &dTex12->GetUAVDescriptor(), 1)) - { - ExecuteCommandList(false, "Ran out of descriptors for CAS"); - if (!GetTextureGroupDescriptors(&sTexDH, &sTex12->GetSRVDescriptor(), 1) || - !GetTextureGroupDescriptors(&dTexDH, &dTex12->GetUAVDescriptor(), 1)) - { - Console.Error("D3D12: Failed to allocate CAS descriptors."); - return false; - } - } + SetUtilityRootSignature(); + SetUtilityPushConstants(constants.data(), sizeof(constants)); - const D3D12CommandList& cmdlist = GetCommandList(); - const GSTexture12::ResourceState old_state = sTex12->GetResourceState(); - sTex12->TransitionToState(cmdlist, GSTexture12::ResourceState::ComputeShaderResource); - dTex12->TransitionToState(cmdlist, GSTexture12::ResourceState::CASShaderUAV); + const GSVector4 dRect(dTex->GetRect()); + const GSVector4 sRect(0.0f, 0.0f, 1.0f, 1.0f); + ID3D12PipelineState* pipeline = sharpen_only ? m_cas_sharpen_pipeline.get() : m_cas_upscale_pipeline.get(); + DoStretchRect(static_cast(sTex), sRect, static_cast(dTex), dRect, pipeline, Nearest, true); - cmdlist.list4->SetComputeRootSignature(m_cas_root_signature.get()); - cmdlist.list4->SetComputeRoot32BitConstants( - CAS_ROOT_SIGNATURE_PARAM_PUSH_CONSTANTS, NUM_CAS_CONSTANTS, constants.data(), 0); - cmdlist.list4->SetComputeRootDescriptorTable(CAS_ROOT_SIGNATURE_PARAM_SRC_TEXTURE, sTexDH); - cmdlist.list4->SetComputeRootDescriptorTable(CAS_ROOT_SIGNATURE_PARAM_DST_TEXTURE, dTexDH); - cmdlist.list4->SetPipelineState(sharpen_only ? m_cas_sharpen_pipeline.get() : m_cas_upscale_pipeline.get()); - m_dirty_flags |= DIRTY_FLAG_PIPELINE; - - static const int threadGroupWorkRegionDim = 16; - const int dispatchX = (dTex->GetWidth() + (threadGroupWorkRegionDim - 1)) / threadGroupWorkRegionDim; - const int dispatchY = (dTex->GetHeight() + (threadGroupWorkRegionDim - 1)) / threadGroupWorkRegionDim; - cmdlist.list4->Dispatch(dispatchX, dispatchY, 1); - - sTex12->TransitionToState(cmdlist, old_state); return true; } @@ -2742,14 +2729,6 @@ bool GSDevice12::GetTextureGroupDescriptors( return true; } -static void AddUtilityVertexAttributes(D3D12::GraphicsPipelineBuilder& gpb) -{ - gpb.AddVertexAttribute("POSITION", 0, DXGI_FORMAT_R32G32B32A32_FLOAT, 0, 0); - gpb.AddVertexAttribute("TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 16); - gpb.AddVertexAttribute("COLOR", 0, DXGI_FORMAT_R8G8B8A8_UNORM, 0, 28); - gpb.SetPrimitiveTopologyType(D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE); -} - GSDevice12::ComPtr GSDevice12::GetUtilityVertexShader(const std::string& source, const char* entry_point) { ShaderMacro sm_model; @@ -2863,14 +2842,17 @@ bool GSDevice12::CreateRootSignatures() bool GSDevice12::CompileConvertPipelines() { - std::optional source = ReadShaderSource("shaders/dx11/convert.fx"); - if (!source) { - Host::ReportErrorAsync("GS", "Failed to read shaders/dx11/convert.fx."); - return false; + std::optional source = ReadShaderSource("shaders/dx11/convert.fx"); + if (!source) + { + Host::ReportErrorAsync("GS", "Failed to read shaders/dx11/convert.fx."); + return false; + } + m_convert_source = std::move(*source); } - m_convert_vs = GetUtilityVertexShader(*source, "vs_main"); + m_convert_vs = GetUtilityVertexShader(m_convert_source, "vs_main"); if (!m_convert_vs) return false; @@ -2933,7 +2915,7 @@ bool GSDevice12::CompileConvertPipelines() sm.AddMacro("HAS_FLOAT32_OUTPUT", static_cast(shader.Float32Output())); sm.AddMacro(entry_point_macro.c_str(), 1); - ComPtr ps(m_shader_cache.GetPixelShader(*source, sm.GetPtr(), shader.EntryPoint())); + ComPtr ps(m_shader_cache.GetPixelShader(m_convert_source, sm.GetPtr(), shader.EntryPoint())); if (!ps) return false; @@ -2982,7 +2964,7 @@ bool GSDevice12::CompileConvertPipelines() sm.AddMacro("PRIMID_MIN", GSShader::PRIMID_MIN); sm.AddMacro(entry_point_macro.c_str(), "1"); - ComPtr ps(m_shader_cache.GetPixelShader(*source, sm.GetPtr(), entry_point.c_str())); + ComPtr ps(m_shader_cache.GetPixelShader(m_convert_source, sm.GetPtr(), entry_point.c_str())); if (!ps) return false; diff --git a/pcsx2/GS/Renderers/DX12/GSDevice12.h b/pcsx2/GS/Renderers/DX12/GSDevice12.h index acb330386c..3eeab02aba 100644 --- a/pcsx2/GS/Renderers/DX12/GSDevice12.h +++ b/pcsx2/GS/Renderers/DX12/GSDevice12.h @@ -445,6 +445,7 @@ private: D3D12ShaderCache m_shader_cache; ComPtr m_convert_vs; + std::string m_convert_source; std::string m_tfx_source; void LookupNativeFormat(GSTexture::Format format, DXGI_FORMAT* d3d_format, DXGI_FORMAT* srv_format, diff --git a/pcsx2/GS/Renderers/DX12/GSTexture12.cpp b/pcsx2/GS/Renderers/DX12/GSTexture12.cpp index d4fab3f018..3bad8bd4bb 100644 --- a/pcsx2/GS/Renderers/DX12/GSTexture12.cpp +++ b/pcsx2/GS/Renderers/DX12/GSTexture12.cpp @@ -137,7 +137,7 @@ static D3D12_BARRIER_LAYOUT GetD3D12BarrierLayout(GSTexture12::ResourceState sta return D3D12_BARRIER_LAYOUT_DIRECT_QUEUE_COPY_SOURCE; case GSTexture12::ResourceState::CopyDst: return D3D12_BARRIER_LAYOUT_DIRECT_QUEUE_COPY_DEST; - case GSTexture12::ResourceState::CASShaderUAV: + case GSTexture12::ResourceState::ComputeShaderUAV: case GSTexture12::ResourceState::PixelShaderUAV: return D3D12_BARRIER_LAYOUT_DIRECT_QUEUE_UNORDERED_ACCESS; default: @@ -166,7 +166,7 @@ static D3D12_RESOURCE_STATES GetD3D12ResourceState(GSTexture12::ResourceState st return D3D12_RESOURCE_STATE_COPY_SOURCE; case GSTexture12::ResourceState::CopyDst: return D3D12_RESOURCE_STATE_COPY_DEST; - case GSTexture12::ResourceState::CASShaderUAV: + case GSTexture12::ResourceState::ComputeShaderUAV: case GSTexture12::ResourceState::PixelShaderUAV: return D3D12_RESOURCE_STATE_UNORDERED_ACCESS; default: @@ -876,7 +876,7 @@ void GSTexture12::TransitionSubresourceToState(const D3D12CommandList& cmdlist, barrier.AccessBefore = D3D12_BARRIER_ACCESS_COPY_DEST; barrier.SyncBefore = D3D12_BARRIER_SYNC_COPY; break; - case ResourceState::CASShaderUAV: + case ResourceState::ComputeShaderUAV: barrier.LayoutBefore = m_simultaneous_tex ? D3D12_BARRIER_LAYOUT_COMMON : D3D12_BARRIER_LAYOUT_DIRECT_QUEUE_UNORDERED_ACCESS; barrier.AccessBefore = D3D12_BARRIER_ACCESS_UNORDERED_ACCESS; barrier.SyncBefore = D3D12_BARRIER_SYNC_COMPUTE_SHADING; @@ -954,7 +954,7 @@ void GSTexture12::TransitionSubresourceToState(const D3D12CommandList& cmdlist, barrier.AccessAfter = D3D12_BARRIER_ACCESS_COPY_DEST; barrier.SyncAfter = D3D12_BARRIER_SYNC_COPY; break; - case ResourceState::CASShaderUAV: + case ResourceState::ComputeShaderUAV: barrier.LayoutAfter = m_simultaneous_tex ? D3D12_BARRIER_LAYOUT_COMMON : D3D12_BARRIER_LAYOUT_DIRECT_QUEUE_UNORDERED_ACCESS; barrier.AccessAfter = D3D12_BARRIER_ACCESS_UNORDERED_ACCESS; barrier.SyncAfter = D3D12_BARRIER_SYNC_COMPUTE_SHADING; @@ -1042,10 +1042,10 @@ void GSTexture12::TransitionSubresourceToState(const D3D12CommandList& cmdlist, case ResourceState::CopyDst: barrier.Transition.StateBefore = D3D12_RESOURCE_STATE_COPY_DEST; break; - case ResourceState::CASShaderUAV: + case ResourceState::ComputeShaderUAV: case ResourceState::PixelShaderUAV: // Handled in after_state cases. - if (after_state == ResourceState::CASShaderUAV || after_state == ResourceState::PixelShaderUAV) + if (after_state == ResourceState::ComputeShaderUAV || after_state == ResourceState::PixelShaderUAV) break; barrier.Transition.StateBefore = D3D12_RESOURCE_STATE_UNORDERED_ACCESS; @@ -1093,9 +1093,9 @@ void GSTexture12::TransitionSubresourceToState(const D3D12CommandList& cmdlist, case ResourceState::CopyDst: barrier.Transition.StateAfter = D3D12_RESOURCE_STATE_COPY_DEST; break; - case ResourceState::CASShaderUAV: + case ResourceState::ComputeShaderUAV: case ResourceState::PixelShaderUAV: - if (before_state == ResourceState::CASShaderUAV || before_state == ResourceState::PixelShaderUAV) + if (before_state == ResourceState::ComputeShaderUAV || before_state == ResourceState::PixelShaderUAV) { // No state transition, but probably want a barrier instead. barrier.Type = D3D12_RESOURCE_BARRIER_TYPE_UAV; diff --git a/pcsx2/GS/Renderers/DX12/GSTexture12.h b/pcsx2/GS/Renderers/DX12/GSTexture12.h index ecfe618876..969429ae15 100644 --- a/pcsx2/GS/Renderers/DX12/GSTexture12.h +++ b/pcsx2/GS/Renderers/DX12/GSTexture12.h @@ -32,7 +32,7 @@ public: ComputeShaderResource, CopySrc, CopyDst, - CASShaderUAV, // No Clear UAV Sync + ComputeShaderUAV, // No Clear UAV Sync PixelShaderUAV, Count }; diff --git a/pcsx2/GS/Renderers/Metal/GSDeviceMTL.h b/pcsx2/GS/Renderers/Metal/GSDeviceMTL.h index 1192af18b4..71ef14962a 100644 --- a/pcsx2/GS/Renderers/Metal/GSDeviceMTL.h +++ b/pcsx2/GS/Renderers/Metal/GSDeviceMTL.h @@ -248,7 +248,7 @@ public: MRCOwned> m_spin_fence; // Functions and Pipeline States - MRCOwned> m_cas_pipeline[2]; + MRCOwned> m_cas_pipeline[2]; std::vector>> m_convert_pipeline; MRCOwned> m_present_pipeline[static_cast(PresentShader::Count)]; MRCOwned> m_merge_pipeline[4]; diff --git a/pcsx2/GS/Renderers/Metal/GSDeviceMTL.mm b/pcsx2/GS/Renderers/Metal/GSDeviceMTL.mm index f91b6f1629..0701bfcc1a 100644 --- a/pcsx2/GS/Renderers/Metal/GSDeviceMTL.mm +++ b/pcsx2/GS/Renderers/Metal/GSDeviceMTL.mm @@ -749,23 +749,12 @@ void GSDeviceMTL::DoShadeBoost(GSTexture* sTex, GSTexture* dTex, const float par bool GSDeviceMTL::DoCAS(GSTexture* sTex, GSTexture* dTex, bool sharpen_only, const std::array& constants) { @autoreleasepool { - g_perfmon.Put(GSPerfMon::TextureCopies, 1); - - static constexpr int threadGroupWorkRegionDim = 16; - const int dispatchX = (dTex->GetWidth() + (threadGroupWorkRegionDim - 1)) / threadGroupWorkRegionDim; - const int dispatchY = (dTex->GetHeight() + (threadGroupWorkRegionDim - 1)) / threadGroupWorkRegionDim; static_assert(sizeof(constants) == sizeof(GSMTLCASPSUniform)); - - EndRenderPass(); - id enc = [GetRenderCmdBuf() computeCommandEncoder]; - [enc setLabel:@"CAS"]; - [enc setComputePipelineState:m_cas_pipeline[sharpen_only]]; - [enc setTexture:static_cast(sTex)->GetTexture() atIndex:0]; - [enc setTexture:static_cast(dTex)->GetTexture() atIndex:1]; - [enc setBytes:&constants length:sizeof(constants) atIndex:GSMTLBufferIndexUniforms]; - [enc dispatchThreadgroups:MTLSizeMake(dispatchX, dispatchY, 1) - threadsPerThreadgroup:MTLSizeMake(64, 1, 1)]; - [enc endEncoding]; + BeginRenderPass(@"CAS", dTex, MTLLoadActionDontCare, nullptr, MTLLoadActionDontCare); + [m_current_render.encoder setFragmentBytes:&constants + length:sizeof(constants) + atIndex:GSMTLBufferIndexUniforms]; + RenderCopy(sTex, m_cas_pipeline[sharpen_only], GSVector4i(0, 0, dTex->GetSize().x, dTex->GetSize().y)); return true; }} @@ -1095,13 +1084,6 @@ bool GSDeviceMTL::Create(GSVSyncMode vsync_mode, bool allow_present_throttle) [clearSpinBuffer endEncoding]; m_spin_pipeline = MakeComputePipeline(LoadShader(@"waste_time"), @"waste_time"); - for (int sharpen_only = 0; sharpen_only < 2; sharpen_only++) - { - setFnConstantB(m_fn_constants, sharpen_only, GSMTLConstantIndex_CAS_SHARPEN_ONLY); - NSString* shader = m_dev.features.has_fast_half ? @"CASHalf" : @"CASFloat"; - m_cas_pipeline[sharpen_only] = MakeComputePipeline(LoadShader(shader), sharpen_only ? @"CAS Sharpen" : @"CAS Upscale"); - } - m_expand_index_buffer = CreatePrivateBufferWithContent(m_dev.dev, initCommands, MTLResourceHazardTrackingModeUntracked, EXPAND_BUFFER_SIZE, GenerateExpansionIndexBuffer); [m_expand_index_buffer setLabel:@"Point/Sprite Expand Indices"]; @@ -1262,6 +1244,13 @@ bool GSDeviceMTL::Create(GSVSyncMode vsync_mode, bool allow_present_throttle) m_primid_init_pipeline[0][3] = MakePipeline(pdesc, fs_triangle, LoadShader(@"ps_primid_rta_init_datm1"), @"PrimID DATM1 RTA Clear"); pdesc.colorAttachments[0].pixelFormat = ConvertPixelFormat(GSTexture::Format::Color); + + for (int sharpen_only = 0; sharpen_only < 2; sharpen_only++) + { + setFnConstantB(m_fn_constants, sharpen_only, GSMTLConstantIndex_CAS_SHARPEN_ONLY); + m_cas_pipeline[sharpen_only] = MakePipeline(pdesc, fs_triangle, LoadShader(@"CASPS"), sharpen_only ? @"CAS Sharpen" : @"CAS Upscale"); + } + applyAttribute(pdesc.vertexDescriptor, 0, MTLVertexFormatFloat2, offsetof(ConvertShaderVertex, pos), 0); applyAttribute(pdesc.vertexDescriptor, 1, MTLVertexFormatFloat2, offsetof(ConvertShaderVertex, texpos), 0); pdesc.vertexDescriptor.layouts[0].stride = sizeof(ConvertShaderVertex); diff --git a/pcsx2/GS/Renderers/Metal/cas.metal b/pcsx2/GS/Renderers/Metal/cas.metal index 9c8825b4ee..8b5315ad95 100644 --- a/pcsx2/GS/Renderers/Metal/cas.metal +++ b/pcsx2/GS/Renderers/Metal/cas.metal @@ -41,6 +41,19 @@ A_STATIC void CasInputH(inoutAH2 r, inoutAH2 g, inoutAH2 b){} constant bool CAS_SHARPEN_ONLY [[function_constant(GSMTLConstantIndex_CAS_SHARPEN_ONLY)]]; +fragment float4 CASPS( + float4 pos [[position]], + texture2d input [[texture(GSMTLTextureIndexNonHW)]], + constant GSMTLCASPSUniform& cb [[buffer(GSMTLBufferIndexUniforms)]]) +{ + const CASTextureF tex{input, AU2(cb.srcOffset)}; + const AU4 const0 = cb.const0; + const AU4 const1 = cb.const1; + float r, g, b; + CasFilter(tex, r, g, b, uint2(pos.xy), const0, const1, CAS_SHARPEN_ONLY); + return float4(r, g, b, 1); +} + kernel void CASFloat( uint2 localID [[thread_position_in_threadgroup]], uint2 workgroupID [[threadgroup_position_in_grid]], diff --git a/pcsx2/GS/Renderers/OpenGL/GSDeviceOGL.cpp b/pcsx2/GS/Renderers/OpenGL/GSDeviceOGL.cpp index 50014c9796..2ff3ad64d8 100644 --- a/pcsx2/GS/Renderers/OpenGL/GSDeviceOGL.cpp +++ b/pcsx2/GS/Renderers/OpenGL/GSDeviceOGL.cpp @@ -653,8 +653,8 @@ bool GSDeviceOGL::Create(GSVSyncMode vsync_mode, bool allow_present_throttle) if (!CompileShadeBoostProgram() || !CompileFXAAProgram()) return false; - // Image load store and GLSL 420pack is core in GL4.2, no need to check. - m_features.cas_sharpening = ((GLAD_GL_VERSION_4_2 && GLAD_GL_ARB_compute_shader) || GLAD_GL_ES_VERSION_3_2) && CreateCASPrograms(); + if (GLAD_GL_VERSION_4_2 || GLAD_GL_ARB_shading_language_packing) + m_features.cas_sharpening = CreateCASPrograms(); // **************************************************************** // rasterization configuration @@ -1020,6 +1020,19 @@ bool GSDeviceOGL::CheckFeatures() GLAD_GL_AMD_conservative_depth ? "GL_AMD_conservative_depth" : "None"); + Console.WriteLnFmt( + "GL: {1:<{0}} {3:<{2}} => Using: {4}", + LABEL_WIDTH, "CAS Sharpening:", + STATUS_WIDTH, + (GLAD_GL_VERSION_4_2 || GLAD_GL_ARB_shading_language_packing) ? + "Supported" : + "Not Supported", + GLAD_GL_VERSION_4_2 ? + "OpenGL 4.2 Core" : + GLAD_GL_ARB_shading_language_packing ? + "GL_ARB_shading_language_packing" : + "None"); + Console.WriteLnFmt( "GL: {1:<{0}} {3:<{2}} => Using: {4}", LABEL_WIDTH, "Texture Barriers:", @@ -1776,6 +1789,11 @@ std::string GSDeviceOGL::GenGlslHeader(const std::string_view entry, GLenum type else header += "#define HAS_CLIP_CONTROL 0\n"; + if (!GLAD_GL_VERSION_4_2 && GLAD_GL_ARB_shading_language_packing) + { + header += "#extension GL_ARB_shading_language_packing : require\n"; + } + // Allow to puts several shader in 1 files switch (type) { @@ -2583,25 +2601,28 @@ void GSDeviceOGL::ClearSamplerCache() bool GSDeviceOGL::CreateCASPrograms() { - std::optional cas_source = ReadShaderSource("shaders/opengl/cas.glsl"); - if (!cas_source.has_value() || !GetCASShaderSource(&cas_source.value())) + std::optional shader = ReadShaderSource("shaders/opengl/cas.glsl"); + if (!shader.has_value() || !GetCASShaderSource(&shader.value())) { + Console.Error("GL: Failed to read cas.glsl"); m_features.cas_sharpening = false; return false; } - const char* header = - "#version 420\n" - "#extension GL_ARB_compute_shader : require\n"; - const char* sharpen_params[2] = { - "#define CAS_SHARPEN_ONLY false\n", - "#define CAS_SHARPEN_ONLY true\n"}; + const std::array, 2> programs = {{ + {&m_cas.upscale_ps, "#define CAS_SHARPEN_ONLY 0\n"}, + {&m_cas.sharpen_ps, "#define CAS_SHARPEN_ONLY 1\n"}, + }}; - if (!m_shader_cache.GetComputeProgram(&m_cas.upscale_ps, fmt::format("{}{}{}", header, sharpen_params[0], cas_source.value())) || - !m_shader_cache.GetComputeProgram(&m_cas.sharpen_ps, fmt::format("{}{}{}", header, sharpen_params[1], cas_source.value()))) + for (const auto& [prog, macro] : programs) { - m_features.cas_sharpening = false; - return false; + const std::string ps(GetShaderSource("main", GL_FRAGMENT_SHADER, *shader, macro)); + if (!m_shader_cache.GetProgram(prog, m_convert.vs, ps)) + { + Console.Error("GL: Failed to compile CAS program."); + m_features.cas_sharpening = false; + return false; + } } const auto link_uniforms = [](GLProgram& prog) { @@ -2609,6 +2630,7 @@ bool GSDeviceOGL::CreateCASPrograms() prog.RegisterUniform("const1"); prog.RegisterUniform("srcOffset"); }; + link_uniforms(m_cas.upscale_ps); link_uniforms(m_cas.sharpen_ps); @@ -2617,21 +2639,22 @@ bool GSDeviceOGL::CreateCASPrograms() bool GSDeviceOGL::DoCAS(GSTexture* sTex, GSTexture* dTex, bool sharpen_only, const std::array& constants) { - g_perfmon.Put(GSPerfMon::TextureCopies, 1); + GL_PUSH("DoCAS"); + + OMSetColorMaskState(); + + const GSVector2i s = dTex->GetSize(); + const GSVector4 sRect(0, 0, 1, 1); + const GSVector4 dRect(0, 0, s.x, s.y); const GLProgram& prog = sharpen_only ? m_cas.sharpen_ps : m_cas.upscale_ps; prog.Bind(); - prog.Uniform4uiv(0, &constants[0]); - prog.Uniform4uiv(1, &constants[4]); - prog.Uniform2iv(2, reinterpret_cast(&constants[8])); - PSSetShaderResource(TEXTURE_TEXTURE, sTex); - glBindImageTexture(0, static_cast(dTex)->GetID(), 0, GL_FALSE, 0, GL_WRITE_ONLY, GL_RGBA8); + prog.Uniform4uiv(0, &constants[0]); // const0 + prog.Uniform4uiv(1, &constants[4]); // const1 + prog.Uniform2iv(2, reinterpret_cast(&constants[8])); // srcOffset - static const int threadGroupWorkRegionDim = 16; - const int dispatchX = (dTex->GetWidth() + (threadGroupWorkRegionDim - 1)) / threadGroupWorkRegionDim; - const int dispatchY = (dTex->GetHeight() + (threadGroupWorkRegionDim - 1)) / threadGroupWorkRegionDim; - glDispatchCompute(dispatchX, dispatchY, 1); + DoStretchRect(sTex, sRect, dTex, dRect, prog, Nearest); return true; } diff --git a/pcsx2/GS/Renderers/Vulkan/GSDeviceVK.cpp b/pcsx2/GS/Renderers/Vulkan/GSDeviceVK.cpp index 571d27002a..5ef0c4cd52 100644 --- a/pcsx2/GS/Renderers/Vulkan/GSDeviceVK.cpp +++ b/pcsx2/GS/Renderers/Vulkan/GSDeviceVK.cpp @@ -4154,14 +4154,17 @@ bool GSDeviceVK::CreateRenderPasses() bool GSDeviceVK::CompileConvertPipelines() { - const std::optional source = ReadShaderSource("shaders/vulkan/convert.glsl"); - if (!source) { - Host::ReportErrorAsync("GS", "Failed to read shaders/vulkan/convert.glsl."); - return false; + const std::optional source = ReadShaderSource("shaders/vulkan/convert.glsl"); + if (!source) + { + Host::ReportErrorAsync("GS", "Failed to read shaders/vulkan/convert.glsl."); + return false; + } + m_convert_source = std::move(*source); } - VkShaderModule vs = GetUtilityVertexShader(*source); + VkShaderModule vs = GetUtilityVertexShader(m_convert_source); if (vs == VK_NULL_HANDLE) return false; ScopedGuard vs_guard([this, &vs]() { vkDestroyShaderModule(m_device, vs, nullptr); }); @@ -4232,7 +4235,7 @@ bool GSDeviceVK::CompileConvertPipelines() macro += fmt::format("#define HAS_FLOAT32_INPUT {}\n", static_cast(shader.Float32Input())); macro += fmt::format("#define HAS_FLOAT32_OUTPUT {}\n", static_cast(shader.Float32Output())); - std::string shader_with_header = macro + *source; + std::string shader_with_header = macro + m_convert_source; VkShaderModule ps = GetUtilityFragmentShader(shader_with_header, shader.EntryPoint()); if (ps == VK_NULL_HANDLE) @@ -4296,7 +4299,7 @@ bool GSDeviceVK::CompileConvertPipelines() macro += fmt::format("#define PRIMID_MAX {}\n", GSShader::PRIMID_MAX); macro += fmt::format("#define PRIMID_MIN {}\n", GSShader::PRIMID_MIN); - const std::string source_with_header = macro + *source; + const std::string source_with_header = macro + m_convert_source; const std::string entry_point(StringUtil::StdStringFromFormat("ps_primid_image_init_%d", datm)); VkShaderModule ps = GetUtilityFragmentShader(source_with_header, entry_point.c_str()); @@ -4574,40 +4577,47 @@ bool GSDeviceVK::CompilePostProcessingPipelines() bool GSDeviceVK::CompileCASPipelines() { + VkShaderModule vs = GetUtilityVertexShader(m_convert_source); + if (vs == VK_NULL_HANDLE) + return false; + ScopedGuard vs_guard([this, &vs]() { vkDestroyShaderModule(m_device, vs, nullptr); }); + VkDevice dev = m_device; - Vulkan::DescriptorSetLayoutBuilder dslb; - Vulkan::PipelineLayoutBuilder plb; - dslb.SetPushFlag(); - dslb.AddBinding(0, VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, 1, VK_SHADER_STAGE_COMPUTE_BIT); - dslb.AddBinding(1, VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, 1, VK_SHADER_STAGE_COMPUTE_BIT); - if ((m_cas_ds_layout = dslb.Create(dev)) == VK_NULL_HANDLE) - return false; - Vulkan::SetObjectName(dev, m_cas_ds_layout, "CAS descriptor layout"); + Vulkan::GraphicsPipelineBuilder gpb; + SetPipelineProvokingVertex(m_features, gpb); + AddUtilityVertexAttributes(gpb); + gpb.SetPipelineLayout(m_utility_pipeline_layout); + gpb.SetDynamicViewportAndScissorState(); + gpb.AddDynamicState(VK_DYNAMIC_STATE_BLEND_CONSTANTS); + gpb.AddDynamicState(VK_DYNAMIC_STATE_LINE_WIDTH); + gpb.SetNoCullRasterizationState(); + gpb.SetNoBlendingState(); + gpb.SetVertexShader(vs); - plb.AddPushConstants(VK_SHADER_STAGE_COMPUTE_BIT, 0, NUM_CAS_CONSTANTS * sizeof(u32)); - plb.AddDescriptorSet(m_cas_ds_layout); - if ((m_cas_pipeline_layout = plb.Create(dev)) == VK_NULL_HANDLE) - return false; - Vulkan::SetObjectName(dev, m_cas_pipeline_layout, "CAS pipeline layout"); + gpb.SetRenderPass( + GetRenderPass(VK_FORMAT_R8G8B8A8_UNORM, VK_FORMAT_UNDEFINED, VK_ATTACHMENT_LOAD_OP_DONT_CARE), 0); - // we use specialization constants to avoid compiling it twice std::optional cas_source = ReadShaderSource("shaders/vulkan/cas.glsl"); if (!cas_source.has_value() || !GetCASShaderSource(&cas_source.value())) return false; - VkShaderModule mod = g_vulkan_shader_cache->GetComputeShader(cas_source->c_str()); - ScopedGuard mod_guard = [this, &mod]() { vkDestroyShaderModule(m_device, mod, nullptr); }; - if (mod == VK_NULL_HANDLE) - return false; - for (u8 sharpen_only = 0; sharpen_only < 2; sharpen_only++) { - Vulkan::ComputePipelineBuilder cpb; - cpb.SetPipelineLayout(m_cas_pipeline_layout); - cpb.SetShader(mod, "main"); - cpb.SetSpecializationBool(0, sharpen_only != 0); - m_cas_pipelines[sharpen_only] = cpb.Create(dev, g_vulkan_shader_cache->GetPipelineCache(true), false); + std::stringstream source_with_header; + AddMacro(source_with_header, "CAS_SHARPEN_ONLY", sharpen_only); + + source_with_header << *cas_source; + + VkShaderModule mod = GetUtilityFragmentShader(source_with_header.str()); + + ScopedGuard mod_guard = [this, &mod]() { vkDestroyShaderModule(m_device, mod, nullptr); }; + if (mod == VK_NULL_HANDLE) + return false; + + gpb.SetFragmentShader(mod); + + m_cas_pipelines[sharpen_only] = gpb.Create(dev, g_vulkan_shader_cache->GetPipelineCache(true), false); if (!m_cas_pipelines[sharpen_only]) return false; } @@ -4771,7 +4781,7 @@ void GSDeviceVK::RenderBlankFrame() bool GSDeviceVK::DoCAS( GSTexture* sTex, GSTexture* dTex, bool sharpen_only, const std::array& constants) { - g_perfmon.Put(GSPerfMon::TextureCopies, 1); + GL_PUSH("DoCAS"); EndRenderPass(); @@ -4780,27 +4790,17 @@ bool GSDeviceVK::DoCAS( VkCommandBuffer cmdbuf = GetCurrentCommandBuffer(); sTexVK->TransitionToLayout(cmdbuf, GSTextureVK::Layout::ShaderReadOnly); - dTexVK->TransitionToLayout(cmdbuf, GSTextureVK::Layout::ComputeReadWriteImage); + dTexVK->TransitionToLayout(cmdbuf, GSTextureVK::Layout::ColorAttachment); - // only happening once a frame, so the update isn't a huge deal. - Vulkan::DescriptorSetUpdateBuilder dsub; - dsub.AddImageDescriptorWrite(VK_NULL_HANDLE, 0, sTexVK->GetView(), sTexVK->GetVkLayout()); - dsub.AddStorageImageDescriptorWrite(VK_NULL_HANDLE, 1, dTexVK->GetView(), dTexVK->GetVkLayout()); - dsub.PushUpdate(cmdbuf, VK_PIPELINE_BIND_POINT_COMPUTE, m_cas_pipeline_layout, 0, false); + SetUtilityPushConstants(constants.data(), NUM_CAS_CONSTANTS * sizeof(u32)); + const GSVector4 dRect(dTex->GetRect()); + const GSVector4 sRect(0.0f, 0.0f, 1.0f, 1.0f); + const VkPipeline pipeline = m_cas_pipelines[static_cast(sharpen_only)]; + DoStretchRect(static_cast(sTex), sRect, static_cast(dTex), dRect, pipeline, Nearest, true); - // the actual meat and potatoes! only four commands. - static const int threadGroupWorkRegionDim = 16; - const int dispatchX = (dTex->GetWidth() + (threadGroupWorkRegionDim - 1)) / threadGroupWorkRegionDim; - const int dispatchY = (dTex->GetHeight() + (threadGroupWorkRegionDim - 1)) / threadGroupWorkRegionDim; + EndRenderPass(); + static_cast(dTex)->TransitionToLayout(GSTextureVK::Layout::ShaderReadOnly); - vkCmdPushConstants(cmdbuf, m_cas_pipeline_layout, VK_SHADER_STAGE_COMPUTE_BIT, 0, NUM_CAS_CONSTANTS * sizeof(u32), - constants.data()); - vkCmdBindPipeline(cmdbuf, VK_PIPELINE_BIND_POINT_COMPUTE, m_cas_pipelines[static_cast(sharpen_only)]); - vkCmdDispatch(cmdbuf, dispatchX, dispatchY, 1); - - dTexVK->TransitionToLayout(GSTextureVK::Layout::ShaderReadOnly); - - // all done! return true; } @@ -4863,10 +4863,7 @@ void GSDeviceVK::DestroyResources() if (it != VK_NULL_HANDLE) vkDestroyPipeline(m_device, it, nullptr); } - if (m_cas_pipeline_layout != VK_NULL_HANDLE) - vkDestroyPipelineLayout(m_device, m_cas_pipeline_layout, nullptr); - if (m_cas_ds_layout != VK_NULL_HANDLE) - vkDestroyDescriptorSetLayout(m_device, m_cas_ds_layout, nullptr); + if (m_imgui_pipeline != VK_NULL_HANDLE) vkDestroyPipeline(m_device, m_imgui_pipeline, nullptr); diff --git a/pcsx2/GS/Renderers/Vulkan/GSDeviceVK.h b/pcsx2/GS/Renderers/Vulkan/GSDeviceVK.h index 3d3007b3a4..9a89dc0e9f 100644 --- a/pcsx2/GS/Renderers/Vulkan/GSDeviceVK.h +++ b/pcsx2/GS/Renderers/Vulkan/GSDeviceVK.h @@ -472,8 +472,6 @@ private: VkRenderPass m_tfx_render_pass[2][2][2][3][2][2][3][3] = {}; // [rt][ds][colclip][date][fbl][dsp][rt_op][ds_op] - VkDescriptorSetLayout m_cas_ds_layout = VK_NULL_HANDLE; - VkPipelineLayout m_cas_pipeline_layout = VK_NULL_HANDLE; std::array m_cas_pipelines = {}; VkPipeline m_imgui_pipeline = VK_NULL_HANDLE; @@ -481,6 +479,7 @@ private: GSHWDrawConfig::PSConstantBuffer m_ps_cb_cache; GSHWDrawConfig::VSPushConstants m_vs_pc_cache; + std::string m_convert_source; std::string m_tfx_source; GSTexture* CreateSurface(GSTexture::Usage usage, int width, int height, int levels, GSTexture::Format format) override; diff --git a/pcsx2/ShaderCacheVersion.h b/pcsx2/ShaderCacheVersion.h index a8ec78a826..d2367a05e3 100644 --- a/pcsx2/ShaderCacheVersion.h +++ b/pcsx2/ShaderCacheVersion.h @@ -3,4 +3,4 @@ /// Version number for GS and other shaders. Increment whenever any of the contents of the /// shaders change, to invalidate the cache. -static constexpr u32 SHADER_CACHE_VERSION = 115; // Last changed in PR 14985 +static constexpr u32 SHADER_CACHE_VERSION = 116; // Last changed in PR 14992