vk: Move MSAA resolve/unresolve shaders to common GPU program layer

This commit is contained in:
kd-11 2025-02-04 02:53:26 +03:00
parent 19bb98da79
commit e48262f5be
13 changed files with 367 additions and 177 deletions

View file

@ -1,5 +1,7 @@
R"(
#version 420
#extension GL_ARB_separate_shader_objects: enable
layout(location=0) out vec2 tc0;
#ifdef VULKAN

View file

@ -0,0 +1,37 @@
R"(
#version 430
layout(local_size_x=%WORKGROUP_SIZE_X, local_size_y=%WORKGROUP_SIZE_Y, local_size_z=1) in;
#ifdef VULKAN
layout(set=0, binding=0, %IMAGE_FORMAT) uniform readonly restrict image2DMS multisampled;
layout(set=0, binding=1) uniform writeonly restrict image2D resolve;
#else
layout(binding=0, %IMAGE_FORMAT) uniform readonly restrict image2DMS multisampled;
layout(binding=1) uniform writeonly restrict image2D resolve;
#endif
#if %BGRA_SWAP
#define shuffle(x) (x.bgra)
#else
#define shuffle(x) (x)
#endif
void main()
{
ivec2 resolve_size = imageSize(resolve);
ivec2 aa_size = imageSize(multisampled);
ivec2 sample_count = resolve_size / aa_size;
if (any(greaterThanEqual(gl_GlobalInvocationID.xy, uvec2(resolve_size)))) return;
ivec2 resolve_coords = ivec2(gl_GlobalInvocationID.xy);
ivec2 aa_coords = resolve_coords / sample_count;
ivec2 sample_loc = ivec2(resolve_coords % sample_count);
int sample_index = sample_loc.x + (sample_loc.y * sample_count.y);
vec4 aa_sample = imageLoad(multisampled, aa_coords, sample_index);
imageStore(resolve, resolve_coords, shuffle(aa_sample));
}
)"

View file

@ -0,0 +1,37 @@
R"(
#version 430
layout(local_size_x=%WORKGROUP_SIZE_X, local_size_y=%WORKGROUP_SIZE_Y, local_size_z=1) in;
#ifdef VULKAN
layout(set=0, binding=0) uniform writeonly restrict image2DMS multisampled;
layout(set=0, binding=1, %IMAGE_FORMAT) uniform readonly restrict image2D resolve;
#else
layout(binding=30) uniform readonly restrict image2DMS multisampled;
layout(binding=31, %IMAGE_FORMAT) uniform readonly restrict image2D resolve;
#endif
#if %BGRA_SWAP
#define shuffle(x) (x.bgra)
#else
#define shuffle(x) (x)
#endif
void main()
{
ivec2 resolve_size = imageSize(resolve);
ivec2 aa_size = imageSize(multisampled);
ivec2 sample_count = resolve_size / aa_size;
if (any(greaterThanEqual(gl_GlobalInvocationID.xy, uvec2(resolve_size)))) return;
ivec2 resolve_coords = ivec2(gl_GlobalInvocationID.xy);
ivec2 aa_coords = resolve_coords / sample_count;
ivec2 sample_loc = ivec2(resolve_coords % sample_count);
int sample_index = sample_loc.x + (sample_loc.y * sample_count.y);
vec4 resolved_sample = imageLoad(resolve, resolve_coords);
imageStore(multisampled, aa_coords, sample_index, shuffle(resolved_sample));
}
)"

View file

@ -0,0 +1,23 @@
R"(
#version 420
#extension GL_ARB_separate_shader_objects: enable
#ifdef VULKAN
layout(set=0, binding=0) uniform sampler2DMS fs0;
layout(push_constant) uniform static_data { ivec2 sample_count; };
#else
layout(binding=31) uniform sampler2DMS fs0;
uniform ivec2 sample_count;
#endif
void main()
{
ivec2 out_coord = ivec2(gl_FragCoord.xy);
ivec2 in_coord = (out_coord / sample_count.xy);
ivec2 sample_loc = out_coord % sample_count.xy;
int sample_index = sample_loc.x + (sample_loc.y * sample_count.y);
float frag_depth = texelFetch(fs0, in_coord, sample_index).x;
gl_FragDepth = frag_depth;
}
)"

View file

@ -0,0 +1,28 @@
R"(
#version 420
#extension GL_ARB_separate_shader_objects: enable
#extension GL_ARB_shader_stencil_export : enable
#ifdef VULKAN
layout(set=0, binding=0) uniform sampler2DMS fs0;
layout(set=0, binding=1) uniform usampler2DMS fs1;
layout(push_constant) uniform static_data { ivec2 sample_count; };
#else
layout(binding=31) uniform sampler2DMS fs0;
layout(binding=30) uniform usampler2DMS fs1;
uniform ivec2 sample_count;
#endif
void main()
{
ivec2 out_coord = ivec2(gl_FragCoord.xy);
ivec2 in_coord = (out_coord / sample_count.xy);
ivec2 sample_loc = out_coord % ivec2(sample_count.xy);
int sample_index = sample_loc.x + (sample_loc.y * sample_count.y);
float frag_depth = texelFetch(fs0, in_coord, sample_index).x;
uint frag_stencil = texelFetch(fs1, in_coord, sample_index).x;
gl_FragDepth = frag_depth;
gl_FragStencilRefARB = int(frag_stencil);
}
)"

View file

@ -0,0 +1,28 @@
R"(
#version 420
#extension GL_ARB_separate_shader_objects: enable
#extension GL_ARB_shader_stencil_export : enable
#ifdef VULKAN
layout(set=0, binding=0) uniform sampler2D fs0;
layout(set=0, binding=1) uniform usampler2D fs1;
layout(push_constant) uniform static_data { ivec2 sample_count; };
#else
layout(binding=31) uniform sampler2D fs0;
layout(binding=30) uniform usampler2D fs1;
uniform ivec2 sample_count;
#endif
void main()
{
ivec2 pixel_coord = ivec2(gl_FragCoord.xy);
pixel_coord *= sample_count.xy;
pixel_coord.x += (gl_SampleID % sample_count.x);
pixel_coord.y += (gl_SampleID / sample_count.x);
float frag_depth = texelFetch(fs0, pixel_coord, 0).x;
uint frag_stencil = texelFetch(fs1, pixel_coord, 0).x;
gl_FragDepth = frag_depth;
gl_FragStencilRefARB = int(frag_stencil);
}
)"

View file

@ -0,0 +1,23 @@
R"(
#version 420
#extension GL_ARB_separate_shader_objects: enable
#ifdef VULKAN
layout(set=0, binding=0) uniform sampler2D fs0;
layout(push_constant) uniform static_data { ivec2 sample_count; };
#else
layout(binding=31) uniform sampler2D fs0;
uniform ivec2 sample_count;
#endif
void main()
{
ivec2 pixel_coord = ivec2(gl_FragCoord.xy);
pixel_coord *= sample_count.xy;
pixel_coord.x += (gl_SampleID % sample_count.x);
pixel_coord.y += (gl_SampleID / sample_count.x);
float frag_depth = texelFetch(fs0, pixel_coord, 0).x;
gl_FragDepth = frag_depth;
}
)"

View file

@ -0,0 +1,28 @@
R"(
#version 420
#extension GL_ARB_separate_shader_objects: enable
#ifdef VULKAN
layout(set=0, binding=0) uniform usampler2DMS fs0;
layout(push_constant) uniform static_data
{
layout(offset=0) ivec2 sample_count;
layout(offset=8) int stencil_mask;
}
#else
layout(binding=31) uniform usampler2DMS fs0;
uniform ivec2 sample_count;
uniform int stencil_mask;
#endif
void main()
{
ivec2 out_coord = ivec2(gl_FragCoord.xy);
ivec2 in_coord = (out_coord / sample_count.xy);
ivec2 sample_loc = out_coord % sample_count.xy;
int sample_index = sample_loc.x + (sample_loc.y * sample_count.y);
uint frag_stencil = texelFetch(fs0, in_coord, sample_index).x;
if ((frag_stencil & uint(stencil_mask)) == 0) discard;
}
)"

View file

@ -0,0 +1,28 @@
R"(
#version 420
#extension GL_ARB_separate_shader_objects: enable
#ifdef VULKAN
layout(set=0, binding=0) uniform usampler2D fs0;
layout(push_constant) uniform static_data
{
layout(offset=0) ivec2 sample_count;
layout(offset=8) int stencil_mask;
}
#else
layout(binding=31) uniform usampler2D fs0;
uniform ivec2 sample_count;
uniform int stencil_mask;
#endif
void main()
{
ivec2 pixel_coord = ivec2(gl_FragCoord.xy);
pixel_coord *= sample_count.xy;
pixel_coord.x += (gl_SampleID % sample_count.x);
pixel_coord.y += (gl_SampleID / sample_count.x);
uint frag_stencil = texelFetch(fs0, pixel_coord, 0).x;
if ((frag_stencil & uint(stencil_mask)) == 0) discard;
}
)"

View file

@ -253,4 +253,90 @@ namespace vk
if (g_depthstencil_resolver) g_depthstencil_resolver->free_resources();
if (g_depthstencil_unresolver) g_depthstencil_unresolver->free_resources();
}
void cs_resolve_base::build(const std::string& format_prefix, bool unresolve, bool bgra_swap)
{
create();
switch (optimal_group_size)
{
default:
case 64:
cs_wave_x = 8;
cs_wave_y = 8;
break;
case 32:
cs_wave_x = 8;
cs_wave_y = 4;
break;
}
static const char* resolve_kernel =
#include "Emu/RSX/Program/MSAA/ColorResolvePass.glsl"
;
static const char* unresolve_kernel =
#include "Emu/RSX/Program/MSAA/ColorUnresolvePass.glsl"
;
const std::pair<std::string_view, std::string> syntax_replace[] =
{
{ "%WORKGROUP_SIZE_X", std::to_string(cs_wave_x) },
{ "%WORKGROUP_SIZE_Y", std::to_string(cs_wave_y) },
{ "%IMAGE_FORMAT", format_prefix },
{ "%BGRA_SWAP", bgra_swap ? "1" : "0" }
};
m_src = unresolve ? unresolve_kernel : resolve_kernel;
m_src = fmt::replace_all(m_src, syntax_replace);
rsx_log.notice("Compute shader:\n%s", m_src);
}
void depth_resolve_base::build(bool resolve_depth, bool resolve_stencil, bool is_unresolve)
{
vs_src =
#include "Emu/RSX/Program/GLSLSnippets/GenericVSPassthrough.glsl"
;
static const char* depth_resolver =
#include "Emu/RSX/Program/MSAA/DepthResolvePass.glsl"
;
static const char* depth_unresolver =
#include "Emu/RSX/Program/MSAA/DepthUnresolvePass.glsl"
;
static const char* stencil_resolver =
#include "Emu/RSX/Program/MSAA/StencilResolvePass.glsl"
;
static const char* stencil_unresolver =
#include "Emu/RSX/Program/MSAA/StencilUnresolvePass.glsl"
;
static const char* depth_stencil_resolver =
#include "Emu/RSX/Program/MSAA/DepthStencilResolvePass.glsl"
;
static const char* depth_stencil_unresolver =
#include "Emu/RSX/Program/MSAA/DepthStencilUnresolvePass.glsl"
;
if (resolve_depth && resolve_stencil)
{
fs_src = is_unresolve ? depth_stencil_unresolver : depth_stencil_resolver;
}
else if (resolve_depth)
{
fs_src = is_unresolve ? depth_unresolver : depth_resolver;
}
else if (resolve_stencil)
{
fs_src = is_unresolve ? stencil_unresolver : stencil_resolver;
}
rsx_log.notice("Resolve shader:\n%s", fs_src);
}
}

View file

@ -21,70 +21,7 @@ namespace vk
virtual ~cs_resolve_base()
{}
// FIXME: move body to cpp
void build(const std::string& kernel, const std::string& format_prefix, int direction)
{
create();
// TODO: Tweak occupancy
switch (optimal_group_size)
{
default:
case 64:
cs_wave_x = 8;
cs_wave_y = 8;
break;
case 32:
cs_wave_x = 8;
cs_wave_y = 4;
break;
}
const std::pair<std::string_view, std::string> syntax_replace[] =
{
{ "%wx", std::to_string(cs_wave_x) },
{ "%wy", std::to_string(cs_wave_y) },
};
m_src =
"#version 430\n"
"layout(local_size_x=%wx, local_size_y=%wy, local_size_z=1) in;\n"
"\n";
m_src = fmt::replace_all(m_src, syntax_replace);
if (direction == 0)
{
m_src +=
"layout(set=0, binding=0, " + format_prefix + ") uniform readonly restrict image2DMS multisampled;\n"
"layout(set=0, binding=1) uniform writeonly restrict image2D resolve;\n";
}
else
{
m_src +=
"layout(set=0, binding=0) uniform writeonly restrict image2DMS multisampled;\n"
"layout(set=0, binding=1, " + format_prefix + ") uniform readonly restrict image2D resolve;\n";
}
m_src +=
"\n"
"void main()\n"
"{\n"
" ivec2 resolve_size = imageSize(resolve);\n"
" ivec2 aa_size = imageSize(multisampled);\n"
" ivec2 sample_count = resolve_size / aa_size;\n"
"\n"
" if (any(greaterThanEqual(gl_GlobalInvocationID.xy, uvec2(resolve_size)))) return;"
"\n"
" ivec2 resolve_coords = ivec2(gl_GlobalInvocationID.xy);\n"
" ivec2 aa_coords = resolve_coords / sample_count;\n"
" ivec2 sample_loc = ivec2(resolve_coords % sample_count);\n"
" int sample_index = sample_loc.x + (sample_loc.y * sample_count.y);\n"
+ kernel +
"}\n";
rsx_log.notice("Compute shader:\n%s", m_src);
}
void build(const std::string& format_prefix, bool unresolve, bool bgra_swap);
std::vector<std::pair<VkDescriptorType, u8>> get_descriptor_layout() override
{
@ -144,14 +81,8 @@ namespace vk
{
cs_resolve_task(const std::string& format_prefix, bool bgra_swap = false)
{
// Allow rgba->bgra transformation for old GeForce cards
const std::string swizzle = bgra_swap? ".bgra" : "";
std::string kernel =
" vec4 aa_sample = imageLoad(multisampled, aa_coords, sample_index);\n"
" imageStore(resolve, resolve_coords, aa_sample" + swizzle + ");\n";
build(kernel, format_prefix, 0);
// BGRA-swap flag is a workaround to swap channels for old GeForce cards with broken compute image handling
build(format_prefix, false, bgra_swap);
}
};
@ -159,14 +90,8 @@ namespace vk
{
cs_unresolve_task(const std::string& format_prefix, bool bgra_swap = false)
{
// Allow rgba->bgra transformation for old GeForce cards
const std::string swizzle = bgra_swap? ".bgra" : "";
std::string kernel =
" vec4 resolved_sample = imageLoad(resolve, resolve_coords);\n"
" imageStore(multisampled, aa_coords, sample_index, resolved_sample" + swizzle + ");\n";
build(kernel, format_prefix, 1);
// BGRA-swap flag is a workaround to swap channels for old GeForce cards with broken compute image handling
build(format_prefix, true, bgra_swap);
}
};
@ -186,41 +111,7 @@ namespace vk
m_sampler_filter = VK_FILTER_NEAREST;
}
void build(const std::string& kernel, const std::string& extensions, const std::vector<const char*>& inputs)
{
vs_src =
"#version 450\n"
"#extension GL_ARB_separate_shader_objects : enable\n\n"
"\n"
"void main()\n"
"{\n"
" vec2 positions[] = {vec2(-1., -1.), vec2(1., -1.), vec2(-1., 1.), vec2(1., 1.)};\n"
" gl_Position = vec4(positions[gl_VertexIndex % 4], 0., 1.);\n"
"}\n";
fs_src =
"#version 420\n"
"#extension GL_ARB_separate_shader_objects : enable\n";
fs_src += extensions +
"\n"
"layout(push_constant) uniform static_data{ ivec" + std::to_string(static_parameters_width) + " regs[1]; };\n";
int binding = 1;
for (const auto& input : inputs)
{
fs_src += "layout(set=0, binding=" + std::to_string(binding++) + ") uniform " + input + ";\n";
}
fs_src +=
"//layout(pixel_center_integer) in vec4 gl_FragCoord;\n"
"\n"
"void main()\n"
"{\n";
fs_src += kernel +
"}\n";
rsx_log.notice("Resolve shader:\n%s", fs_src);
}
void build(bool resolve_depth, bool resolve_stencil, bool unresolve);
std::vector<VkPushConstantRange> get_push_constants() override
{
@ -263,15 +154,7 @@ namespace vk
{
depthonly_resolve()
{
build(
" ivec2 out_coord = ivec2(gl_FragCoord.xy);\n"
" ivec2 in_coord = (out_coord / regs[0].xy);\n"
" ivec2 sample_loc = out_coord % regs[0].xy;\n"
" int sample_index = sample_loc.x + (sample_loc.y * regs[0].y);\n"
" float frag_depth = texelFetch(fs0, in_coord, sample_index).x;\n"
" gl_FragDepth = frag_depth;\n",
"",
{ "sampler2DMS fs0" });
build(true, false, false);
}
void run(vk::command_buffer& cmd, vk::viewable_image* msaa_image, vk::viewable_image* resolve_image, VkRenderPass render_pass)
@ -291,15 +174,7 @@ namespace vk
{
depthonly_unresolve()
{
build(
" ivec2 pixel_coord = ivec2(gl_FragCoord.xy);\n"
" pixel_coord *= regs[0].xy;\n"
" pixel_coord.x += (gl_SampleID % regs[0].x);\n"
" pixel_coord.y += (gl_SampleID / regs[0].x);\n"
" float frag_depth = texelFetch(fs0, pixel_coord, 0).x;\n"
" gl_FragDepth = frag_depth;\n",
"",
{ "sampler2D fs0" });
build(true, false, true);
}
void run(vk::command_buffer& cmd, vk::viewable_image* msaa_image, vk::viewable_image* resolve_image, VkRenderPass render_pass)
@ -340,15 +215,7 @@ namespace vk
static_parameters_width = 3;
build(
" ivec2 out_coord = ivec2(gl_FragCoord.xy);\n"
" ivec2 in_coord = (out_coord / regs[0].xy);\n"
" ivec2 sample_loc = out_coord % regs[0].xy;\n"
" int sample_index = sample_loc.x + (sample_loc.y * regs[0].y);\n"
" uint frag_stencil = texelFetch(fs0, in_coord, sample_index).x;\n"
" if ((frag_stencil & uint(regs[0].z)) == 0) discard;\n",
"",
{"usampler2DMS fs0"});
build(false, true, false);
}
void get_dynamic_state_entries(std::vector<VkDynamicState>& state_descriptors) override
@ -407,15 +274,7 @@ namespace vk
static_parameters_width = 3;
build(
" ivec2 pixel_coord = ivec2(gl_FragCoord.xy);\n"
" pixel_coord *= regs[0].xy;\n"
" pixel_coord.x += (gl_SampleID % regs[0].x);\n"
" pixel_coord.y += (gl_SampleID / regs[0].x);\n"
" uint frag_stencil = texelFetch(fs0, pixel_coord, 0).x;\n"
" if ((frag_stencil & uint(regs[0].z)) == 0) discard;\n",
"",
{ "usampler2D fs0" });
build(false, true, false);
}
void get_dynamic_state_entries(std::vector<VkDynamicState>& state_descriptors) override
@ -468,19 +327,7 @@ namespace vk
renderpass_config.set_stencil_mask(0xFF);
m_num_usable_samplers = 2;
build(
" ivec2 out_coord = ivec2(gl_FragCoord.xy);\n"
" ivec2 in_coord = (out_coord / regs[0].xy);\n"
" ivec2 sample_loc = out_coord % ivec2(regs[0].xy);\n"
" int sample_index = sample_loc.x + (sample_loc.y * regs[0].y);\n"
" float frag_depth = texelFetch(fs0, in_coord, sample_index).x;\n"
" uint frag_stencil = texelFetch(fs1, in_coord, sample_index).x;\n"
" gl_FragDepth = frag_depth;\n"
" gl_FragStencilRefARB = int(frag_stencil);\n",
"#extension GL_ARB_shader_stencil_export : enable\n",
{ "sampler2DMS fs0", "usampler2DMS fs1" });
build(true, true, false);
}
void run(vk::command_buffer& cmd, vk::viewable_image* msaa_image, vk::viewable_image* resolve_image, VkRenderPass render_pass)
@ -510,19 +357,7 @@ namespace vk
renderpass_config.set_stencil_mask(0xFF);
m_num_usable_samplers = 2;
build(
" ivec2 pixel_coord = ivec2(gl_FragCoord.xy);\n"
" pixel_coord *= regs[0].xy;\n"
" pixel_coord.x += (gl_SampleID % regs[0].x);\n"
" pixel_coord.y += (gl_SampleID / regs[0].x);\n"
" float frag_depth = texelFetch(fs0, pixel_coord, 0).x;\n"
" uint frag_stencil = texelFetch(fs1, pixel_coord, 0).x;\n"
" gl_FragDepth = frag_depth;\n"
" gl_FragStencilRefARB = int(frag_stencil);\n",
"#extension GL_ARB_shader_stencil_export : enable\n",
{ "sampler2D fs0", "usampler2D fs1" });
build(true, true, true);
}
void run(vk::command_buffer& cmd, vk::viewable_image* msaa_image, vk::viewable_image* resolve_image, VkRenderPass render_pass)

View file

@ -1030,6 +1030,14 @@
<None Include="Emu\RSX\Program\GLSLSnippets\RSXProg\RSXVertexPrologue.glsl" />
<None Include="Emu\RSX\Program\GLSLSnippets\ShuffleBytes.glsl" />
<None Include="Emu\RSX\Program\GLSLSnippets\VideoOutCalibrationPass.glsl" />
<None Include="Emu\RSX\Program\MSAA\ColorResolvePass.glsl" />
<None Include="Emu\RSX\Program\MSAA\ColorUnresolvePass.glsl" />
<None Include="Emu\RSX\Program\MSAA\DepthResolvePass.glsl" />
<None Include="Emu\RSX\Program\MSAA\DepthStencilResolvePass.glsl" />
<None Include="Emu\RSX\Program\MSAA\DepthStencilUnresolvePass.glsl" />
<None Include="Emu\RSX\Program\MSAA\DepthUnresolvePass.glsl" />
<None Include="Emu\RSX\Program\MSAA\StencilResolvePass.glsl" />
<None Include="Emu\RSX\Program\MSAA\StencilUnresolvePass.glsl" />
<None Include="Emu\RSX\Program\Upscalers\FSR1\fsr_ffx_a_flattened.inc" />
<None Include="Emu\RSX\Program\Upscalers\FSR1\fsr_ffx_fsr1_flattened.inc" />
<None Include="Emu\RSX\Program\Upscalers\FSR1\fsr_ubershader.glsl" />

View file

@ -130,6 +130,9 @@
<Filter Include="Emu\GPU\RSX\Overlays\Trophies">
<UniqueIdentifier>{caf84300-5c45-4340-bd9a-8ac859409351}</UniqueIdentifier>
</Filter>
<Filter Include="Emu\GPU\RSX\Program\MSAA">
<UniqueIdentifier>{ce6d6b90-8313-4273-b46c-d92bd450c002}</UniqueIdentifier>
</Filter>
</ItemGroup>
<ItemGroup>
<ClCompile Include="Crypto\aes.cpp">
@ -2802,5 +2805,29 @@
<None Include="Emu\CPU\Backends\AArch64\AArch64Signal.cpp">
<Filter>Emu\CPU\Backends\AArch64</Filter>
</None>
<None Include="Emu\RSX\Program\MSAA\ColorResolvePass.glsl">
<Filter>Emu\GPU\RSX\Program\MSAA</Filter>
</None>
<None Include="Emu\RSX\Program\MSAA\ColorUnresolvePass.glsl">
<Filter>Emu\GPU\RSX\Program\MSAA</Filter>
</None>
<None Include="Emu\RSX\Program\MSAA\DepthResolvePass.glsl">
<Filter>Emu\GPU\RSX\Program\MSAA</Filter>
</None>
<None Include="Emu\RSX\Program\MSAA\DepthStencilResolvePass.glsl">
<Filter>Emu\GPU\RSX\Program\MSAA</Filter>
</None>
<None Include="Emu\RSX\Program\MSAA\DepthStencilUnresolvePass.glsl">
<Filter>Emu\GPU\RSX\Program\MSAA</Filter>
</None>
<None Include="Emu\RSX\Program\MSAA\DepthUnresolvePass.glsl">
<Filter>Emu\GPU\RSX\Program\MSAA</Filter>
</None>
<None Include="Emu\RSX\Program\MSAA\StencilResolvePass.glsl">
<Filter>Emu\GPU\RSX\Program\MSAA</Filter>
</None>
<None Include="Emu\RSX\Program\MSAA\StencilUnresolvePass.glsl">
<Filter>Emu\GPU\RSX\Program\MSAA</Filter>
</None>
</ItemGroup>
</Project>