GS: Switch CAS from compute shader type to pixel shader type.

Depending on the gpu/driver in some situations it can be a little faster or a little slower. Usually faster on igpus and slower on dgpus, but we want to prioritize igpus since they are on the lower end.

GL: Support has been extended to older hardware GL version 3.3.
Drop requirement of GL_ARB_gpu_shader5, emulate bitfieldExtract and bitfieldInsert in the cas shader instead.
Make GL_ARB_shading_language_packing as a fallback when gl core 4.2 is not available.

DX11: Enable on feature level 10.

DX12/VK co author port by TJnotJT.
Metal co author port by TellowKrinkle.
This commit is contained in:
lightningterror
2026-09-26 15:25:56 +02:00
parent ae2bac2b09
commit 9c1bea4ab6
18 changed files with 250 additions and 289 deletions
+5 -3
View File
@@ -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<<bits)-1', and 'bits' needs to be an immediate.
AU1 ABfiM(AU1 src,AU1 ins,AU1 bits){return bitfieldInsert(src,ins,0,ASU1(bits));}
//------------------------------------------------------------------------------------------------------------------------------
// Emulate bitfieldInsert.
AU1 ABfiM(AU1 src, AU1 ins, AU1 bits){uint mask = (bits >= 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);}
+14 -28
View File
@@ -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<float4> OutputTexture : register(u0);
#define A_GPU 1
#define A_HLSL 1
@@ -34,7 +34,7 @@ RWTexture2D<float4> 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);
}
+16 -21
View File
@@ -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
+17 -27
View File
@@ -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);
}
+3 -3
View File
@@ -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;
}
+11 -27
View File
@@ -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<u32, NUM_CAS_CONSTANTS>& 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<GSTexture11*>(sTex)};
ID3D11UnorderedAccessView* uavs[1] = {*static_cast<GSTexture11*>(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;
}
+2 -2
View File
@@ -271,8 +271,8 @@ private:
struct
{
wil::com_ptr_nothrow<ID3D11Buffer> cb;
wil::com_ptr_nothrow<ID3D11ComputeShader> cs_upscale;
wil::com_ptr_nothrow<ID3D11ComputeShader> cs_sharpen;
wil::com_ptr_nothrow<ID3D11PixelShader> ps_upscale;
wil::com_ptr_nothrow<ID3D11PixelShader> ps_sharpen;
} m_cas;
struct
+46 -64
View File
@@ -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<std::string> 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<ID3DBlob> cs_upscale(m_shader_cache.GetComputeShader(cas_source.value(), nullptr, "main"));
const ComPtr<ID3DBlob> cs_sharpen(m_shader_cache.GetComputeShader(cas_source.value(), sharpen_only_macros, "main"));
const ComPtr<ID3DBlob> cs_upscale(m_shader_cache.GetPixelShader(cas_source.value(), nullptr, "ps_main"));
const ComPtr<ID3DBlob> 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<u32, NUM_CAS_CONSTANTS>& constants)
{
g_perfmon.Put(GSPerfMon::TextureCopies, 1);
GL_PUSH("DoCAS");
EndRenderPass();
GSTexture12* const sTex12 = static_cast<GSTexture12*>(sTex);
GSTexture12* const dTex12 = static_cast<GSTexture12*>(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<GSTexture12*>(sTex), sRect, static_cast<GSTexture12*>(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<ID3DBlob> 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<std::string> source = ReadShaderSource("shaders/dx11/convert.fx");
if (!source)
{
Host::ReportErrorAsync("GS", "Failed to read shaders/dx11/convert.fx.");
return false;
std::optional<std::string> 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<int>(shader.Float32Output()));
sm.AddMacro(entry_point_macro.c_str(), 1);
ComPtr<ID3DBlob> ps(m_shader_cache.GetPixelShader(*source, sm.GetPtr(), shader.EntryPoint()));
ComPtr<ID3DBlob> 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<ID3DBlob> ps(m_shader_cache.GetPixelShader(*source, sm.GetPtr(), entry_point.c_str()));
ComPtr<ID3DBlob> ps(m_shader_cache.GetPixelShader(m_convert_source, sm.GetPtr(), entry_point.c_str()));
if (!ps)
return false;
+1
View File
@@ -445,6 +445,7 @@ private:
D3D12ShaderCache m_shader_cache;
ComPtr<ID3DBlob> 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,
+8 -8
View File
@@ -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;
+1 -1
View File
@@ -32,7 +32,7 @@ public:
ComputeShaderResource,
CopySrc,
CopyDst,
CASShaderUAV, // No Clear UAV Sync
ComputeShaderUAV, // No Clear UAV Sync
PixelShaderUAV,
Count
};
+1 -1
View File
@@ -248,7 +248,7 @@ public:
MRCOwned<id<MTLFence>> m_spin_fence;
// Functions and Pipeline States
MRCOwned<id<MTLComputePipelineState>> m_cas_pipeline[2];
MRCOwned<id<MTLRenderPipelineState>> m_cas_pipeline[2];
std::vector<MRCOwned<id<MTLRenderPipelineState>>> m_convert_pipeline;
MRCOwned<id<MTLRenderPipelineState>> m_present_pipeline[static_cast<int>(PresentShader::Count)];
MRCOwned<id<MTLRenderPipelineState>> m_merge_pipeline[4];
+12 -23
View File
@@ -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<u32, NUM_CAS_CONSTANTS>& 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<MTLComputeCommandEncoder> enc = [GetRenderCmdBuf() computeCommandEncoder];
[enc setLabel:@"CAS"];
[enc setComputePipelineState:m_cas_pipeline[sharpen_only]];
[enc setTexture:static_cast<GSTextureMTL*>(sTex)->GetTexture() atIndex:0];
[enc setTexture:static_cast<GSTextureMTL*>(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);
+13
View File
@@ -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<float, access::read> 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]],
+47 -24
View File
@@ -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<std::string> cas_source = ReadShaderSource("shaders/opengl/cas.glsl");
if (!cas_source.has_value() || !GetCASShaderSource(&cas_source.value()))
std::optional<std::string> 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<std::pair<GLProgram*, const char*>, 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<u32, NUM_CAS_CONSTANTS>& 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<const s32*>(&constants[8]));
PSSetShaderResource(TEXTURE_TEXTURE, sTex);
glBindImageTexture(0, static_cast<GSTextureOGL*>(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<const s32*>(&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;
}
+51 -54
View File
@@ -4154,14 +4154,17 @@ bool GSDeviceVK::CreateRenderPasses()
bool GSDeviceVK::CompileConvertPipelines()
{
const std::optional<std::string> source = ReadShaderSource("shaders/vulkan/convert.glsl");
if (!source)
{
Host::ReportErrorAsync("GS", "Failed to read shaders/vulkan/convert.glsl.");
return false;
const std::optional<std::string> 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<int>(shader.Float32Input()));
macro += fmt::format("#define HAS_FLOAT32_OUTPUT {}\n", static_cast<int>(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<std::string> 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<u32, NUM_CAS_CONSTANTS>& 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<u8>(sharpen_only)];
DoStretchRect(static_cast<GSTextureVK*>(sTex), sRect, static_cast<GSTextureVK*>(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<GSTextureVK*>(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<u8>(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);
+1 -2
View File
@@ -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<VkPipeline, NUM_CAS_PIPELINES> 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;
+1 -1
View File
@@ -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