ShaderGenCommon: Rename WriteFmt() to Write()

Now that we've converted all of the shader generators over to using fmt,
we can drop the old Write() member function and perform a rename
operation on the WriteFmt() to turn it into the new Write() function.

All changes within this are the removal of a <cstdarg> header, since the
previous printf-based Write() required it, and renaming. No functional
changes are made at all.
This commit is contained in:
Lioncash 2020-11-09 02:23:32 -05:00
parent a9ef7e0e43
commit a5b28f1f07
12 changed files with 2340 additions and 2381 deletions

View File

@ -27,9 +27,9 @@ APIType GetAPIType()
void EmitUniformBufferDeclaration(ShaderCode& code) void EmitUniformBufferDeclaration(ShaderCode& code)
{ {
if (GetAPIType() == APIType::D3D) if (GetAPIType() == APIType::D3D)
code.WriteFmt("cbuffer PSBlock : register(b0)\n"); code.Write("cbuffer PSBlock : register(b0)\n");
else else
code.WriteFmt("UBO_BINDING(std140, 1) uniform PSBlock\n"); code.Write("UBO_BINDING(std140, 1) uniform PSBlock\n");
} }
void EmitSamplerDeclarations(ShaderCode& code, u32 start = 0, u32 end = 1, void EmitSamplerDeclarations(ShaderCode& code, u32 start = 0, u32 end = 1,
@ -43,8 +43,8 @@ void EmitSamplerDeclarations(ShaderCode& code, u32 start = 0, u32 end = 1,
for (u32 i = start; i < end; i++) for (u32 i = start; i < end; i++)
{ {
code.WriteFmt("{} tex{} : register(t{});\n", array_type, i, i); code.Write("{} tex{} : register(t{});\n", array_type, i, i);
code.WriteFmt("SamplerState samp{} : register(s{});\n", i, i); code.Write("SamplerState samp{} : register(s{});\n", i, i);
} }
} }
break; break;
@ -56,7 +56,7 @@ void EmitSamplerDeclarations(ShaderCode& code, u32 start = 0, u32 end = 1,
for (u32 i = start; i < end; i++) for (u32 i = start; i < end; i++)
{ {
code.WriteFmt("SAMPLER_BINDING({}) uniform {} samp{};\n", i, array_type, i); code.Write("SAMPLER_BINDING({}) uniform {} samp{};\n", i, array_type, i);
} }
} }
break; break;
@ -70,12 +70,12 @@ void EmitSampleTexture(ShaderCode& code, u32 n, std::string_view coords)
switch (GetAPIType()) switch (GetAPIType())
{ {
case APIType::D3D: case APIType::D3D:
code.WriteFmt("tex{}.Sample(samp{}, {})", n, n, coords); code.Write("tex{}.Sample(samp{}, {})", n, n, coords);
break; break;
case APIType::OpenGL: case APIType::OpenGL:
case APIType::Vulkan: case APIType::Vulkan:
code.WriteFmt("texture(samp{}, {})", n, coords); code.Write("texture(samp{}, {})", n, coords);
break; break;
default: default:
@ -90,12 +90,12 @@ void EmitTextureLoad(ShaderCode& code, u32 n, std::string_view coords)
switch (GetAPIType()) switch (GetAPIType())
{ {
case APIType::D3D: case APIType::D3D:
code.WriteFmt("tex{}.Load({})", n, coords); code.Write("tex{}.Load({})", n, coords);
break; break;
case APIType::OpenGL: case APIType::OpenGL:
case APIType::Vulkan: case APIType::Vulkan:
code.WriteFmt("texelFetch(samp{}, ({}).xyz, ({}).w)", n, coords, coords); code.Write("texelFetch(samp{}, ({}).xyz, ({}).w)", n, coords, coords);
break; break;
default: default:
@ -111,19 +111,19 @@ void EmitVertexMainDeclaration(ShaderCode& code, u32 num_tex_inputs, u32 num_col
{ {
case APIType::D3D: case APIType::D3D:
{ {
code.WriteFmt("void main("); code.Write("void main(");
for (u32 i = 0; i < num_tex_inputs; i++) for (u32 i = 0; i < num_tex_inputs; i++)
code.WriteFmt("in float3 rawtex{} : TEXCOORD{}, ", i, i); code.Write("in float3 rawtex{} : TEXCOORD{}, ", i, i);
for (u32 i = 0; i < num_color_inputs; i++) for (u32 i = 0; i < num_color_inputs; i++)
code.WriteFmt("in float4 rawcolor{} : COLOR{}, ", i, i); code.Write("in float4 rawcolor{} : COLOR{}, ", i, i);
if (position_input) if (position_input)
code.WriteFmt("in float4 rawpos : POSITION, "); code.Write("in float4 rawpos : POSITION, ");
code.WriteFmt("{}", extra_inputs); code.Write("{}", extra_inputs);
for (u32 i = 0; i < num_tex_outputs; i++) for (u32 i = 0; i < num_tex_outputs; i++)
code.WriteFmt("out float3 v_tex{} : TEXCOORD{}, ", i, i); code.Write("out float3 v_tex{} : TEXCOORD{}, ", i, i);
for (u32 i = 0; i < num_color_outputs; i++) for (u32 i = 0; i < num_color_outputs; i++)
code.WriteFmt("out float4 v_col{} : COLOR{}, ", i, i); code.Write("out float4 v_col{} : COLOR{}, ", i, i);
code.WriteFmt("out float4 opos : SV_Position)\n"); code.Write("out float4 opos : SV_Position)\n");
} }
break; break;
@ -133,35 +133,35 @@ void EmitVertexMainDeclaration(ShaderCode& code, u32 num_tex_inputs, u32 num_col
for (u32 i = 0; i < num_tex_inputs; i++) for (u32 i = 0; i < num_tex_inputs; i++)
{ {
const auto attribute = SHADER_TEXTURE0_ATTRIB + i; const auto attribute = SHADER_TEXTURE0_ATTRIB + i;
code.WriteFmt("ATTRIBUTE_LOCATION({}) in float3 rawtex{};\n", attribute, i); code.Write("ATTRIBUTE_LOCATION({}) in float3 rawtex{};\n", attribute, i);
} }
for (u32 i = 0; i < num_color_inputs; i++) for (u32 i = 0; i < num_color_inputs; i++)
{ {
const auto attribute = SHADER_COLOR0_ATTRIB + i; const auto attribute = SHADER_COLOR0_ATTRIB + i;
code.WriteFmt("ATTRIBUTE_LOCATION({}) in float4 rawcolor{};\n", attribute, i); code.Write("ATTRIBUTE_LOCATION({}) in float4 rawcolor{};\n", attribute, i);
} }
if (position_input) if (position_input)
code.WriteFmt("ATTRIBUTE_LOCATION({}) in float4 rawpos;\n", SHADER_POSITION_ATTRIB); code.Write("ATTRIBUTE_LOCATION({}) in float4 rawpos;\n", SHADER_POSITION_ATTRIB);
if (g_ActiveConfig.backend_info.bSupportsGeometryShaders) if (g_ActiveConfig.backend_info.bSupportsGeometryShaders)
{ {
code.WriteFmt("VARYING_LOCATION(0) out VertexData {{\n"); code.Write("VARYING_LOCATION(0) out VertexData {{\n");
for (u32 i = 0; i < num_tex_outputs; i++) for (u32 i = 0; i < num_tex_outputs; i++)
code.WriteFmt(" float3 v_tex{};\n", i); code.Write(" float3 v_tex{};\n", i);
for (u32 i = 0; i < num_color_outputs; i++) for (u32 i = 0; i < num_color_outputs; i++)
code.WriteFmt(" float4 v_col{};\n", i); code.Write(" float4 v_col{};\n", i);
code.WriteFmt("}};\n"); code.Write("}};\n");
} }
else else
{ {
for (u32 i = 0; i < num_tex_outputs; i++) for (u32 i = 0; i < num_tex_outputs; i++)
code.WriteFmt("VARYING_LOCATION({}) out float3 v_tex{};\n", i, i); code.Write("VARYING_LOCATION({}) out float3 v_tex{};\n", i, i);
for (u32 i = 0; i < num_color_outputs; i++) for (u32 i = 0; i < num_color_outputs; i++)
code.WriteFmt("VARYING_LOCATION({}) out float4 v_col{};\n", num_tex_inputs + i, i); code.Write("VARYING_LOCATION({}) out float4 v_col{};\n", num_tex_inputs + i, i);
} }
code.WriteFmt("#define opos gl_Position\n"); code.Write("#define opos gl_Position\n");
code.WriteFmt("{}\n", extra_inputs); code.Write("{}\n", extra_inputs);
code.WriteFmt("void main()\n"); code.Write("void main()\n");
} }
break; break;
default: default:
@ -177,14 +177,14 @@ void EmitPixelMainDeclaration(ShaderCode& code, u32 num_tex_inputs, u32 num_colo
{ {
case APIType::D3D: case APIType::D3D:
{ {
code.WriteFmt("void main("); code.Write("void main(");
for (u32 i = 0; i < num_tex_inputs; i++) for (u32 i = 0; i < num_tex_inputs; i++)
code.WriteFmt("in float3 v_tex{} : TEXCOORD{}, ", i, i); code.Write("in float3 v_tex{} : TEXCOORD{}, ", i, i);
for (u32 i = 0; i < num_color_inputs; i++) for (u32 i = 0; i < num_color_inputs; i++)
code.WriteFmt("in float4 v_col{} : COLOR{}, ", i, i); code.Write("in float4 v_col{} : COLOR{}, ", i, i);
if (emit_frag_coord) if (emit_frag_coord)
code.WriteFmt("in float4 frag_coord : SV_Position, "); code.Write("in float4 frag_coord : SV_Position, ");
code.WriteFmt("{}out {} ocol0 : SV_Target)\n", extra_vars, output_type); code.Write("{}out {} ocol0 : SV_Target)\n", extra_vars, output_type);
} }
break; break;
@ -193,26 +193,26 @@ void EmitPixelMainDeclaration(ShaderCode& code, u32 num_tex_inputs, u32 num_colo
{ {
if (g_ActiveConfig.backend_info.bSupportsGeometryShaders) if (g_ActiveConfig.backend_info.bSupportsGeometryShaders)
{ {
code.WriteFmt("VARYING_LOCATION(0) in VertexData {{\n"); code.Write("VARYING_LOCATION(0) in VertexData {{\n");
for (u32 i = 0; i < num_tex_inputs; i++) for (u32 i = 0; i < num_tex_inputs; i++)
code.WriteFmt(" in float3 v_tex{};\n", i); code.Write(" in float3 v_tex{};\n", i);
for (u32 i = 0; i < num_color_inputs; i++) for (u32 i = 0; i < num_color_inputs; i++)
code.WriteFmt(" in float4 v_col{};\n", i); code.Write(" in float4 v_col{};\n", i);
code.WriteFmt("}};\n"); code.Write("}};\n");
} }
else else
{ {
for (u32 i = 0; i < num_tex_inputs; i++) for (u32 i = 0; i < num_tex_inputs; i++)
code.WriteFmt("VARYING_LOCATION({}) in float3 v_tex{};\n", i, i); code.Write("VARYING_LOCATION({}) in float3 v_tex{};\n", i, i);
for (u32 i = 0; i < num_color_inputs; i++) for (u32 i = 0; i < num_color_inputs; i++)
code.WriteFmt("VARYING_LOCATION({}) in float4 v_col{};\n", num_tex_inputs + i, i); code.Write("VARYING_LOCATION({}) in float4 v_col{};\n", num_tex_inputs + i, i);
} }
code.WriteFmt("FRAGMENT_OUTPUT_LOCATION(0) out {} ocol0;\n", output_type); code.Write("FRAGMENT_OUTPUT_LOCATION(0) out {} ocol0;\n", output_type);
code.WriteFmt("{}\n", extra_vars); code.Write("{}\n", extra_vars);
if (emit_frag_coord) if (emit_frag_coord)
code.WriteFmt("#define frag_coord gl_FragCoord\n"); code.Write("#define frag_coord gl_FragCoord\n");
code.WriteFmt("void main()\n"); code.Write("void main()\n");
} }
break; break;
@ -228,16 +228,16 @@ std::string GenerateScreenQuadVertexShader()
EmitVertexMainDeclaration(code, 0, 0, false, 1, 0, EmitVertexMainDeclaration(code, 0, 0, false, 1, 0,
GetAPIType() == APIType::D3D ? "in uint id : SV_VertexID, " : GetAPIType() == APIType::D3D ? "in uint id : SV_VertexID, " :
"#define id gl_VertexID\n"); "#define id gl_VertexID\n");
code.WriteFmt( code.Write(
"{{\n" "{{\n"
" v_tex0 = float3(float((id << 1) & 2), float(id & 2), 0.0f);\n" " v_tex0 = float3(float((id << 1) & 2), float(id & 2), 0.0f);\n"
" opos = float4(v_tex0.xy * float2(2.0f, -2.0f) + float2(-1.0f, 1.0f), 0.0f, 1.0f);\n"); " opos = float4(v_tex0.xy * float2(2.0f, -2.0f) + float2(-1.0f, 1.0f), 0.0f, 1.0f);\n");
// NDC space is flipped in Vulkan. We also flip in GL so that (0,0) is in the lower-left. // NDC space is flipped in Vulkan. We also flip in GL so that (0,0) is in the lower-left.
if (GetAPIType() == APIType::Vulkan || GetAPIType() == APIType::OpenGL) if (GetAPIType() == APIType::Vulkan || GetAPIType() == APIType::OpenGL)
code.WriteFmt(" opos.y = -opos.y;\n"); code.Write(" opos.y = -opos.y;\n");
code.WriteFmt("}}\n"); code.Write("}}\n");
return code.GetBuffer(); return code.GetBuffer();
} }
@ -247,88 +247,88 @@ std::string GeneratePassthroughGeometryShader(u32 num_tex, u32 num_colors)
ShaderCode code; ShaderCode code;
if (GetAPIType() == APIType::D3D) if (GetAPIType() == APIType::D3D)
{ {
code.WriteFmt("struct VS_OUTPUT\n" code.Write("struct VS_OUTPUT\n"
"{{\n"); "{{\n");
for (u32 i = 0; i < num_tex; i++) for (u32 i = 0; i < num_tex; i++)
code.WriteFmt(" float3 tex{} : TEXCOORD{};\n", i, i); code.Write(" float3 tex{} : TEXCOORD{};\n", i, i);
for (u32 i = 0; i < num_colors; i++) for (u32 i = 0; i < num_colors; i++)
code.WriteFmt(" float4 color{} : COLOR{};\n", i, i); code.Write(" float4 color{} : COLOR{};\n", i, i);
code.WriteFmt(" float4 position : SV_Position;\n" code.Write(" float4 position : SV_Position;\n"
"}};\n"); "}};\n");
code.WriteFmt("struct GS_OUTPUT\n" code.Write("struct GS_OUTPUT\n"
"{{"); "{{");
for (u32 i = 0; i < num_tex; i++) for (u32 i = 0; i < num_tex; i++)
code.WriteFmt(" float3 tex{} : TEXCOORD{};\n", i, i); code.Write(" float3 tex{} : TEXCOORD{};\n", i, i);
for (u32 i = 0; i < num_colors; i++) for (u32 i = 0; i < num_colors; i++)
code.WriteFmt(" float4 color{} : COLOR{};\n", i, i); code.Write(" float4 color{} : COLOR{};\n", i, i);
code.WriteFmt(" float4 position : SV_Position;\n" code.Write(" float4 position : SV_Position;\n"
" uint slice : SV_RenderTargetArrayIndex;\n" " uint slice : SV_RenderTargetArrayIndex;\n"
"}};\n\n"); "}};\n\n");
code.WriteFmt("[maxvertexcount(6)]\n" code.Write("[maxvertexcount(6)]\n"
"void main(triangle VS_OUTPUT vso[3], inout TriangleStream<GS_OUTPUT> output)\n" "void main(triangle VS_OUTPUT vso[3], inout TriangleStream<GS_OUTPUT> output)\n"
"{{\n" "{{\n"
" for (uint slice = 0; slice < 2u; slice++)\n" " for (uint slice = 0; slice < 2u; slice++)\n"
" {{\n" " {{\n"
" for (int i = 0; i < 3; i++)\n" " for (int i = 0; i < 3; i++)\n"
" {{\n" " {{\n"
" GS_OUTPUT gso;\n" " GS_OUTPUT gso;\n"
" gso.position = vso[i].position;\n"); " gso.position = vso[i].position;\n");
for (u32 i = 0; i < num_tex; i++) for (u32 i = 0; i < num_tex; i++)
code.WriteFmt(" gso.tex{} = float3(vso[i].tex{}.xy, float(slice));\n", i, i); code.Write(" gso.tex{} = float3(vso[i].tex{}.xy, float(slice));\n", i, i);
for (u32 i = 0; i < num_colors; i++) for (u32 i = 0; i < num_colors; i++)
code.WriteFmt(" gso.color{} = vso[i].color{};\n", i, i); code.Write(" gso.color{} = vso[i].color{};\n", i, i);
code.WriteFmt(" gso.slice = slice;\n" code.Write(" gso.slice = slice;\n"
" output.Append(gso);\n" " output.Append(gso);\n"
" }}\n" " }}\n"
" output.RestartStrip();\n" " output.RestartStrip();\n"
" }}\n" " }}\n"
"}}\n"); "}}\n");
} }
else if (GetAPIType() == APIType::OpenGL || GetAPIType() == APIType::Vulkan) else if (GetAPIType() == APIType::OpenGL || GetAPIType() == APIType::Vulkan)
{ {
code.WriteFmt("layout(triangles) in;\n" code.Write("layout(triangles) in;\n"
"layout(triangle_strip, max_vertices = 6) out;\n"); "layout(triangle_strip, max_vertices = 6) out;\n");
if (num_tex > 0 || num_colors > 0) if (num_tex > 0 || num_colors > 0)
{ {
code.WriteFmt("VARYING_LOCATION(0) in VertexData {{\n"); code.Write("VARYING_LOCATION(0) in VertexData {{\n");
for (u32 i = 0; i < num_tex; i++) for (u32 i = 0; i < num_tex; i++)
code.WriteFmt(" float3 v_tex{};\n", i); code.Write(" float3 v_tex{};\n", i);
for (u32 i = 0; i < num_colors; i++) for (u32 i = 0; i < num_colors; i++)
code.WriteFmt(" float4 v_col{};\n", i); code.Write(" float4 v_col{};\n", i);
code.WriteFmt("}} v_in[];\n"); code.Write("}} v_in[];\n");
code.WriteFmt("VARYING_LOCATION(0) out VertexData {{\n"); code.Write("VARYING_LOCATION(0) out VertexData {{\n");
for (u32 i = 0; i < num_tex; i++) for (u32 i = 0; i < num_tex; i++)
code.WriteFmt(" float3 v_tex{};\n", i); code.Write(" float3 v_tex{};\n", i);
for (u32 i = 0; i < num_colors; i++) for (u32 i = 0; i < num_colors; i++)
code.WriteFmt(" float4 v_col{};\n", i); code.Write(" float4 v_col{};\n", i);
code.WriteFmt("}} v_out;\n"); code.Write("}} v_out;\n");
} }
code.WriteFmt("\n" code.Write("\n"
"void main()\n" "void main()\n"
"{{\n" "{{\n"
" for (int j = 0; j < 2; j++)\n" " for (int j = 0; j < 2; j++)\n"
" {{\n" " {{\n"
" gl_Layer = j;\n"); " gl_Layer = j;\n");
// We have to explicitly unroll this loop otherwise the GL compiler gets cranky. // We have to explicitly unroll this loop otherwise the GL compiler gets cranky.
for (u32 v = 0; v < 3; v++) for (u32 v = 0; v < 3; v++)
{ {
code.WriteFmt(" gl_Position = gl_in[{}].gl_Position;\n", v); code.Write(" gl_Position = gl_in[{}].gl_Position;\n", v);
for (u32 i = 0; i < num_tex; i++) for (u32 i = 0; i < num_tex; i++)
{ {
code.WriteFmt(" v_out.v_tex{} = float3(v_in[{}].v_tex{}.xy, float(j));\n", i, v, i); code.Write(" v_out.v_tex{} = float3(v_in[{}].v_tex{}.xy, float(j));\n", i, v, i);
} }
for (u32 i = 0; i < num_colors; i++) for (u32 i = 0; i < num_colors; i++)
code.WriteFmt(" v_out.v_col{} = v_in[{}].v_col{};\n", i, v, i); code.Write(" v_out.v_col{} = v_in[{}].v_col{};\n", i, v, i);
code.WriteFmt(" EmitVertex();\n\n"); code.Write(" EmitVertex();\n\n");
} }
code.WriteFmt(" EndPrimitive();\n" code.Write(" EndPrimitive();\n"
" }}\n" " }}\n"
"}}\n"); "}}\n");
} }
return code.GetBuffer(); return code.GetBuffer();
@ -338,25 +338,24 @@ std::string GenerateTextureCopyVertexShader()
{ {
ShaderCode code; ShaderCode code;
EmitUniformBufferDeclaration(code); EmitUniformBufferDeclaration(code);
code.WriteFmt("{{" code.Write("{{"
" float2 src_offset;\n" " float2 src_offset;\n"
" float2 src_size;\n" " float2 src_size;\n"
"}};\n\n"); "}};\n\n");
EmitVertexMainDeclaration(code, 0, 0, false, 1, 0, EmitVertexMainDeclaration(code, 0, 0, false, 1, 0,
GetAPIType() == APIType::D3D ? "in uint id : SV_VertexID, " : GetAPIType() == APIType::D3D ? "in uint id : SV_VertexID, " :
"#define id gl_VertexID"); "#define id gl_VertexID");
code.WriteFmt( code.Write("{{\n"
"{{\n" " v_tex0 = float3(float((id << 1) & 2), float(id & 2), 0.0f);\n"
" v_tex0 = float3(float((id << 1) & 2), float(id & 2), 0.0f);\n" " opos = float4(v_tex0.xy * float2(2.0f, -2.0f) + float2(-1.0f, 1.0f), 0.0f, 1.0f);\n"
" opos = float4(v_tex0.xy * float2(2.0f, -2.0f) + float2(-1.0f, 1.0f), 0.0f, 1.0f);\n" " v_tex0 = float3(src_offset + (src_size * v_tex0.xy), 0.0f);\n");
" v_tex0 = float3(src_offset + (src_size * v_tex0.xy), 0.0f);\n");
// NDC space is flipped in Vulkan. We also flip in GL so that (0,0) is in the lower-left. // NDC space is flipped in Vulkan. We also flip in GL so that (0,0) is in the lower-left.
if (GetAPIType() == APIType::Vulkan || GetAPIType() == APIType::OpenGL) if (GetAPIType() == APIType::Vulkan || GetAPIType() == APIType::OpenGL)
code.WriteFmt(" opos.y = -opos.y;\n"); code.Write(" opos.y = -opos.y;\n");
code.WriteFmt("}}\n"); code.Write("}}\n");
return code.GetBuffer(); return code.GetBuffer();
} }
@ -366,11 +365,11 @@ std::string GenerateTextureCopyPixelShader()
ShaderCode code; ShaderCode code;
EmitSamplerDeclarations(code, 0, 1, false); EmitSamplerDeclarations(code, 0, 1, false);
EmitPixelMainDeclaration(code, 1, 0); EmitPixelMainDeclaration(code, 1, 0);
code.WriteFmt("{{\n" code.Write("{{\n"
" ocol0 = "); " ocol0 = ");
EmitSampleTexture(code, 0, "v_tex0"); EmitSampleTexture(code, 0, "v_tex0");
code.WriteFmt(";\n" code.Write(";\n"
"}}\n"); "}}\n");
return code.GetBuffer(); return code.GetBuffer();
} }
@ -378,9 +377,9 @@ std::string GenerateColorPixelShader()
{ {
ShaderCode code; ShaderCode code;
EmitPixelMainDeclaration(code, 0, 1); EmitPixelMainDeclaration(code, 0, 1);
code.WriteFmt("{{\n" code.Write("{{\n"
" ocol0 = v_col0;\n" " ocol0 = v_col0;\n"
"}}\n"); "}}\n");
return code.GetBuffer(); return code.GetBuffer();
} }
@ -390,25 +389,25 @@ std::string GenerateResolveDepthPixelShader(u32 samples)
EmitSamplerDeclarations(code, 0, 1, true); EmitSamplerDeclarations(code, 0, 1, true);
EmitPixelMainDeclaration(code, 1, 0, "float", EmitPixelMainDeclaration(code, 1, 0, "float",
GetAPIType() == APIType::D3D ? "in float4 ipos : SV_Position, " : ""); GetAPIType() == APIType::D3D ? "in float4 ipos : SV_Position, " : "");
code.WriteFmt("{{\n" code.Write("{{\n"
" int layer = int(v_tex0.z);\n"); " int layer = int(v_tex0.z);\n");
if (GetAPIType() == APIType::D3D) if (GetAPIType() == APIType::D3D)
code.WriteFmt(" int3 coords = int3(int2(ipos.xy), layer);\n"); code.Write(" int3 coords = int3(int2(ipos.xy), layer);\n");
else else
code.WriteFmt(" int3 coords = int3(int2(gl_FragCoord.xy), layer);\n"); code.Write(" int3 coords = int3(int2(gl_FragCoord.xy), layer);\n");
// Take the minimum of all depth samples. // Take the minimum of all depth samples.
if (GetAPIType() == APIType::D3D) if (GetAPIType() == APIType::D3D)
code.WriteFmt(" ocol0 = tex0.Load(coords, 0).r;\n"); code.Write(" ocol0 = tex0.Load(coords, 0).r;\n");
else else
code.WriteFmt(" ocol0 = texelFetch(samp0, coords, 0).r;\n"); code.Write(" ocol0 = texelFetch(samp0, coords, 0).r;\n");
code.WriteFmt(" for (int i = 1; i < {}; i++)\n", samples); code.Write(" for (int i = 1; i < {}; i++)\n", samples);
if (GetAPIType() == APIType::D3D) if (GetAPIType() == APIType::D3D)
code.WriteFmt(" ocol0 = min(ocol0, tex0.Load(coords, i).r);\n"); code.Write(" ocol0 = min(ocol0, tex0.Load(coords, i).r);\n");
else else
code.WriteFmt(" ocol0 = min(ocol0, texelFetch(samp0, coords, i).r);\n"); code.Write(" ocol0 = min(ocol0, texelFetch(samp0, coords, i).r);\n");
code.WriteFmt("}}\n"); code.Write("}}\n");
return code.GetBuffer(); return code.GetBuffer();
} }
@ -416,15 +415,15 @@ std::string GenerateClearVertexShader()
{ {
ShaderCode code; ShaderCode code;
EmitUniformBufferDeclaration(code); EmitUniformBufferDeclaration(code);
code.WriteFmt("{{\n" code.Write("{{\n"
" float4 clear_color;\n" " float4 clear_color;\n"
" float clear_depth;\n" " float clear_depth;\n"
"}};\n"); "}};\n");
EmitVertexMainDeclaration(code, 0, 0, false, 0, 1, EmitVertexMainDeclaration(code, 0, 0, false, 0, 1,
GetAPIType() == APIType::D3D ? "in uint id : SV_VertexID, " : GetAPIType() == APIType::D3D ? "in uint id : SV_VertexID, " :
"#define id gl_VertexID\n"); "#define id gl_VertexID\n");
code.WriteFmt( code.Write(
"{{\n" "{{\n"
" float2 coord = float2(float((id << 1) & 2), float(id & 2));\n" " float2 coord = float2(float((id << 1) & 2), float(id & 2));\n"
" opos = float4(coord * float2(2.0f, -2.0f) + float2(-1.0f, 1.0f), clear_depth, 1.0f);\n" " opos = float4(coord * float2(2.0f, -2.0f) + float2(-1.0f, 1.0f), clear_depth, 1.0f);\n"
@ -432,9 +431,9 @@ std::string GenerateClearVertexShader()
// NDC space is flipped in Vulkan // NDC space is flipped in Vulkan
if (GetAPIType() == APIType::Vulkan) if (GetAPIType() == APIType::Vulkan)
code.WriteFmt(" opos.y = -opos.y;\n"); code.Write(" opos.y = -opos.y;\n");
code.WriteFmt("}}\n"); code.Write("}}\n");
return code.GetBuffer(); return code.GetBuffer();
} }
@ -443,17 +442,17 @@ std::string GenerateEFBPokeVertexShader()
{ {
ShaderCode code; ShaderCode code;
EmitVertexMainDeclaration(code, 0, 1, true, 0, 1); EmitVertexMainDeclaration(code, 0, 1, true, 0, 1);
code.WriteFmt("{{\n" code.Write("{{\n"
" v_col0 = rawcolor0;\n" " v_col0 = rawcolor0;\n"
" opos = float4(rawpos.xyz, 1.0f);\n"); " opos = float4(rawpos.xyz, 1.0f);\n");
if (g_ActiveConfig.backend_info.bSupportsLargePoints) if (g_ActiveConfig.backend_info.bSupportsLargePoints)
code.WriteFmt(" gl_PointSize = rawpos.w;\n"); code.Write(" gl_PointSize = rawpos.w;\n");
// NDC space is flipped in Vulkan. // NDC space is flipped in Vulkan.
if (GetAPIType() == APIType::Vulkan) if (GetAPIType() == APIType::Vulkan)
code.WriteFmt(" opos.y = -opos.y;\n"); code.Write(" opos.y = -opos.y;\n");
code.WriteFmt("}}\n"); code.Write("}}\n");
return code.GetBuffer(); return code.GetBuffer();
} }
@ -468,82 +467,82 @@ std::string GenerateFormatConversionShader(EFBReinterpretType convtype, u32 samp
"in float4 ipos : SV_Position, in uint isample : SV_SampleIndex, " : "in float4 ipos : SV_Position, in uint isample : SV_SampleIndex, " :
"in float4 ipos : SV_Position, ") : "in float4 ipos : SV_Position, ") :
""); "");
code.WriteFmt("{{\n" code.Write("{{\n"
" int layer = int(v_tex0.z);\n"); " int layer = int(v_tex0.z);\n");
if (GetAPIType() == APIType::D3D) if (GetAPIType() == APIType::D3D)
code.WriteFmt(" int3 coords = int3(int2(ipos.xy), layer);\n"); code.Write(" int3 coords = int3(int2(ipos.xy), layer);\n");
else else
code.WriteFmt(" int3 coords = int3(int2(gl_FragCoord.xy), layer);\n"); code.Write(" int3 coords = int3(int2(gl_FragCoord.xy), layer);\n");
if (samples == 1) if (samples == 1)
{ {
// No MSAA at all. // No MSAA at all.
if (GetAPIType() == APIType::D3D) if (GetAPIType() == APIType::D3D)
code.WriteFmt(" float4 val = tex0.Load(int4(coords, 0));\n"); code.Write(" float4 val = tex0.Load(int4(coords, 0));\n");
else else
code.WriteFmt(" float4 val = texelFetch(samp0, coords, 0);\n"); code.Write(" float4 val = texelFetch(samp0, coords, 0);\n");
} }
else if (g_ActiveConfig.bSSAA) else if (g_ActiveConfig.bSSAA)
{ {
// Sample shading, shader runs once per sample // Sample shading, shader runs once per sample
if (GetAPIType() == APIType::D3D) if (GetAPIType() == APIType::D3D)
code.WriteFmt(" float4 val = tex0.Load(coords, isample);"); code.Write(" float4 val = tex0.Load(coords, isample);");
else else
code.WriteFmt(" float4 val = texelFetch(samp0, coords, gl_SampleID);"); code.Write(" float4 val = texelFetch(samp0, coords, gl_SampleID);");
} }
else else
{ {
// MSAA without sample shading, average out all samples. // MSAA without sample shading, average out all samples.
code.WriteFmt(" float4 val = float4(0.0f, 0.0f, 0.0f, 0.0f);\n"); code.Write(" float4 val = float4(0.0f, 0.0f, 0.0f, 0.0f);\n");
code.WriteFmt(" for (int i = 0; i < {}; i++)\n", samples); code.Write(" for (int i = 0; i < {}; i++)\n", samples);
if (GetAPIType() == APIType::D3D) if (GetAPIType() == APIType::D3D)
code.WriteFmt(" val += tex0.Load(coords, i);\n"); code.Write(" val += tex0.Load(coords, i);\n");
else else
code.WriteFmt(" val += texelFetch(samp0, coords, i);\n"); code.Write(" val += texelFetch(samp0, coords, i);\n");
code.WriteFmt(" val /= float({});\n", samples); code.Write(" val /= float({});\n", samples);
} }
switch (convtype) switch (convtype)
{ {
case EFBReinterpretType::RGB8ToRGBA6: case EFBReinterpretType::RGB8ToRGBA6:
code.WriteFmt(" int4 src8 = int4(round(val * 255.f));\n" code.Write(" int4 src8 = int4(round(val * 255.f));\n"
" int4 dst6;\n" " int4 dst6;\n"
" dst6.r = src8.r >> 2;\n" " dst6.r = src8.r >> 2;\n"
" dst6.g = ((src8.r & 0x3) << 4) | (src8.g >> 4);\n" " dst6.g = ((src8.r & 0x3) << 4) | (src8.g >> 4);\n"
" dst6.b = ((src8.g & 0xF) << 2) | (src8.b >> 6);\n" " dst6.b = ((src8.g & 0xF) << 2) | (src8.b >> 6);\n"
" dst6.a = src8.b & 0x3F;\n" " dst6.a = src8.b & 0x3F;\n"
" ocol0 = float4(dst6) / 63.f;\n"); " ocol0 = float4(dst6) / 63.f;\n");
break; break;
case EFBReinterpretType::RGB8ToRGB565: case EFBReinterpretType::RGB8ToRGB565:
code.WriteFmt(" ocol0 = val;\n"); code.Write(" ocol0 = val;\n");
break; break;
case EFBReinterpretType::RGBA6ToRGB8: case EFBReinterpretType::RGBA6ToRGB8:
code.WriteFmt(" int4 src6 = int4(round(val * 63.f));\n" code.Write(" int4 src6 = int4(round(val * 63.f));\n"
" int4 dst8;\n" " int4 dst8;\n"
" dst8.r = (src6.r << 2) | (src6.g >> 4);\n" " dst8.r = (src6.r << 2) | (src6.g >> 4);\n"
" dst8.g = ((src6.g & 0xF) << 4) | (src6.b >> 2);\n" " dst8.g = ((src6.g & 0xF) << 4) | (src6.b >> 2);\n"
" dst8.b = ((src6.b & 0x3) << 6) | src6.a;\n" " dst8.b = ((src6.b & 0x3) << 6) | src6.a;\n"
" dst8.a = 255;\n" " dst8.a = 255;\n"
" ocol0 = float4(dst8) / 255.f;\n"); " ocol0 = float4(dst8) / 255.f;\n");
break; break;
case EFBReinterpretType::RGBA6ToRGB565: case EFBReinterpretType::RGBA6ToRGB565:
code.WriteFmt(" ocol0 = val;\n"); code.Write(" ocol0 = val;\n");
break; break;
case EFBReinterpretType::RGB565ToRGB8: case EFBReinterpretType::RGB565ToRGB8:
code.WriteFmt(" ocol0 = val;\n"); code.Write(" ocol0 = val;\n");
break; break;
case EFBReinterpretType::RGB565ToRGBA6: case EFBReinterpretType::RGB565ToRGBA6:
// //
code.WriteFmt(" ocol0 = val;\n"); code.Write(" ocol0 = val;\n");
break; break;
} }
code.WriteFmt("}}\n"); code.Write("}}\n");
return code.GetBuffer(); return code.GetBuffer();
} }
@ -552,71 +551,70 @@ std::string GenerateTextureReinterpretShader(TextureFormat from_format, TextureF
ShaderCode code; ShaderCode code;
EmitSamplerDeclarations(code, 0, 1, false); EmitSamplerDeclarations(code, 0, 1, false);
EmitPixelMainDeclaration(code, 1, 0, "float4", "", true); EmitPixelMainDeclaration(code, 1, 0, "float4", "", true);
code.WriteFmt("{{\n" code.Write("{{\n"
" int layer = int(v_tex0.z);\n" " int layer = int(v_tex0.z);\n"
" int4 coords = int4(int2(frag_coord.xy), layer, 0);\n"); " int4 coords = int4(int2(frag_coord.xy), layer, 0);\n");
// Convert to a 32-bit value encompassing all channels, filling the most significant bits with // Convert to a 32-bit value encompassing all channels, filling the most significant bits with
// zeroes. // zeroes.
code.WriteFmt(" uint raw_value;\n"); code.Write(" uint raw_value;\n");
switch (from_format) switch (from_format)
{ {
case TextureFormat::I8: case TextureFormat::I8:
case TextureFormat::C8: case TextureFormat::C8:
{ {
code.WriteFmt(" float4 temp_value = "); code.Write(" float4 temp_value = ");
EmitTextureLoad(code, 0, "coords"); EmitTextureLoad(code, 0, "coords");
code.WriteFmt(";\n" code.Write(";\n"
" raw_value = uint(temp_value.r * 255.0);\n"); " raw_value = uint(temp_value.r * 255.0);\n");
} }
break; break;
case TextureFormat::IA8: case TextureFormat::IA8:
{ {
code.WriteFmt(" float4 temp_value = "); code.Write(" float4 temp_value = ");
EmitTextureLoad(code, 0, "coords"); EmitTextureLoad(code, 0, "coords");
code.WriteFmt( code.Write(";\n"
";\n" " raw_value = uint(temp_value.r * 255.0) | (uint(temp_value.a * 255.0) << 8);\n");
" raw_value = uint(temp_value.r * 255.0) | (uint(temp_value.a * 255.0) << 8);\n");
} }
break; break;
case TextureFormat::I4: case TextureFormat::I4:
{ {
code.WriteFmt(" float4 temp_value = "); code.Write(" float4 temp_value = ");
EmitTextureLoad(code, 0, "coords"); EmitTextureLoad(code, 0, "coords");
code.WriteFmt(";\n" code.Write(";\n"
" raw_value = uint(temp_value.r * 15.0);\n"); " raw_value = uint(temp_value.r * 15.0);\n");
} }
break; break;
case TextureFormat::IA4: case TextureFormat::IA4:
{ {
code.WriteFmt(" float4 temp_value = "); code.Write(" float4 temp_value = ");
EmitTextureLoad(code, 0, "coords"); EmitTextureLoad(code, 0, "coords");
code.WriteFmt(";\n" code.Write(";\n"
" raw_value = uint(temp_value.r * 15.0) | (uint(temp_value.a * 15.0) << 4);\n"); " raw_value = uint(temp_value.r * 15.0) | (uint(temp_value.a * 15.0) << 4);\n");
} }
break; break;
case TextureFormat::RGB565: case TextureFormat::RGB565:
{ {
code.WriteFmt(" float4 temp_value = "); code.Write(" float4 temp_value = ");
EmitTextureLoad(code, 0, "coords"); EmitTextureLoad(code, 0, "coords");
code.WriteFmt(";\n" code.Write(";\n"
" raw_value = uint(temp_value.b * 31.0) | (uint(temp_value.g * 63.0) << 5) |\n" " raw_value = uint(temp_value.b * 31.0) | (uint(temp_value.g * 63.0) << 5) |\n"
" (uint(temp_value.r * 31.0) << 11);\n"); " (uint(temp_value.r * 31.0) << 11);\n");
} }
break; break;
case TextureFormat::RGB5A3: case TextureFormat::RGB5A3:
{ {
code.WriteFmt(" float4 temp_value = "); code.Write(" float4 temp_value = ");
EmitTextureLoad(code, 0, "coords"); EmitTextureLoad(code, 0, "coords");
code.WriteFmt(";\n"); code.Write(";\n");
// 0.8784 = 224 / 255 which is the maximum alpha value that can be represented in 3 bits // 0.8784 = 224 / 255 which is the maximum alpha value that can be represented in 3 bits
code.WriteFmt( code.Write(
" if (temp_value.a > 0.878f) {{\n" " if (temp_value.a > 0.878f) {{\n"
" raw_value = (uint(temp_value.b * 31.0)) | (uint(temp_value.g * 31.0) << 5) |\n" " raw_value = (uint(temp_value.b * 31.0)) | (uint(temp_value.g * 31.0) << 5) |\n"
" (uint(temp_value.r * 31.0) << 10) | 0x8000u;\n" " (uint(temp_value.r * 31.0) << 10) | 0x8000u;\n"
@ -638,45 +636,45 @@ std::string GenerateTextureReinterpretShader(TextureFormat from_format, TextureF
case TextureFormat::I8: case TextureFormat::I8:
case TextureFormat::C8: case TextureFormat::C8:
{ {
code.WriteFmt(" float orgba = float(raw_value & 0xFFu) / 255.0;\n" code.Write(" float orgba = float(raw_value & 0xFFu) / 255.0;\n"
" ocol0 = float4(orgba, orgba, orgba, orgba);\n"); " ocol0 = float4(orgba, orgba, orgba, orgba);\n");
} }
break; break;
case TextureFormat::IA8: case TextureFormat::IA8:
{ {
code.WriteFmt(" float orgb = float(raw_value & 0xFFu) / 255.0;\n" code.Write(" float orgb = float(raw_value & 0xFFu) / 255.0;\n"
" ocol0 = float4(orgb, orgb, orgb, float((raw_value >> 8) & 0xFFu) / 255.0);\n"); " ocol0 = float4(orgb, orgb, orgb, float((raw_value >> 8) & 0xFFu) / 255.0);\n");
} }
break; break;
case TextureFormat::IA4: case TextureFormat::IA4:
{ {
code.WriteFmt(" float orgb = float(raw_value & 0xFu) / 15.0;\n" code.Write(" float orgb = float(raw_value & 0xFu) / 15.0;\n"
" ocol0 = float4(orgb, orgb, orgb, float((raw_value >> 4) & 0xFu) / 15.0);\n"); " ocol0 = float4(orgb, orgb, orgb, float((raw_value >> 4) & 0xFu) / 15.0);\n");
} }
break; break;
case TextureFormat::RGB565: case TextureFormat::RGB565:
{ {
code.WriteFmt(" ocol0 = float4(float((raw_value >> 10) & 0x1Fu) / 31.0,\n" code.Write(" ocol0 = float4(float((raw_value >> 10) & 0x1Fu) / 31.0,\n"
" float((raw_value >> 5) & 0x1Fu) / 31.0,\n" " float((raw_value >> 5) & 0x1Fu) / 31.0,\n"
" float(raw_value & 0x1Fu) / 31.0, 1.0);\n"); " float(raw_value & 0x1Fu) / 31.0, 1.0);\n");
} }
break; break;
case TextureFormat::RGB5A3: case TextureFormat::RGB5A3:
{ {
code.WriteFmt(" if ((raw_value & 0x8000u) != 0u) {{\n" code.Write(" if ((raw_value & 0x8000u) != 0u) {{\n"
" ocol0 = float4(float((raw_value >> 10) & 0x1Fu) / 31.0,\n" " ocol0 = float4(float((raw_value >> 10) & 0x1Fu) / 31.0,\n"
" float((raw_value >> 5) & 0x1Fu) / 31.0,\n" " float((raw_value >> 5) & 0x1Fu) / 31.0,\n"
" float(raw_value & 0x1Fu) / 31.0, 1.0);\n" " float(raw_value & 0x1Fu) / 31.0, 1.0);\n"
" }} else {{\n" " }} else {{\n"
" ocol0 = float4(float((raw_value >> 8) & 0x0Fu) / 15.0,\n" " ocol0 = float4(float((raw_value >> 8) & 0x0Fu) / 15.0,\n"
" float((raw_value >> 4) & 0x0Fu) / 15.0,\n" " float((raw_value >> 4) & 0x0Fu) / 15.0,\n"
" float(raw_value & 0x0Fu) / 15.0,\n" " float(raw_value & 0x0Fu) / 15.0,\n"
" float((raw_value >> 12) & 0x07u) / 7.0);\n" " float((raw_value >> 12) & 0x07u) / 7.0);\n"
" }}\n"); " }}\n");
} }
break; break;
default: default:
@ -684,7 +682,7 @@ std::string GenerateTextureReinterpretShader(TextureFormat from_format, TextureF
return "{}\n"; return "{}\n";
} }
code.WriteFmt("}}\n"); code.Write("}}\n");
return code.GetBuffer(); return code.GetBuffer();
} }
@ -694,14 +692,14 @@ std::string GenerateEFBRestorePixelShader()
EmitSamplerDeclarations(code, 0, 2, false); EmitSamplerDeclarations(code, 0, 2, false);
EmitPixelMainDeclaration(code, 1, 0, "float4", EmitPixelMainDeclaration(code, 1, 0, "float4",
GetAPIType() == APIType::D3D ? "out float depth : SV_Depth, " : ""); GetAPIType() == APIType::D3D ? "out float depth : SV_Depth, " : "");
code.WriteFmt("{{\n" code.Write("{{\n"
" ocol0 = "); " ocol0 = ");
EmitSampleTexture(code, 0, "v_tex0"); EmitSampleTexture(code, 0, "v_tex0");
code.WriteFmt(";\n"); code.Write(";\n");
code.WriteFmt(" {} = ", GetAPIType() == APIType::D3D ? "depth" : "gl_FragDepth"); code.Write(" {} = ", GetAPIType() == APIType::D3D ? "depth" : "gl_FragDepth");
EmitSampleTexture(code, 1, "v_tex0"); EmitSampleTexture(code, 1, "v_tex0");
code.WriteFmt(".r;\n" code.Write(".r;\n"
"}}\n"); "}}\n");
return code.GetBuffer(); return code.GetBuffer();
} }
@ -711,22 +709,22 @@ std::string GenerateImGuiVertexShader()
// Uniform buffer contains the viewport size, and we transform in the vertex shader. // Uniform buffer contains the viewport size, and we transform in the vertex shader.
EmitUniformBufferDeclaration(code); EmitUniformBufferDeclaration(code);
code.WriteFmt("{{\n" code.Write("{{\n"
"float2 u_rcp_viewport_size_mul2;\n" "float2 u_rcp_viewport_size_mul2;\n"
"}};\n\n"); "}};\n\n");
EmitVertexMainDeclaration(code, 1, 1, true, 1, 1); EmitVertexMainDeclaration(code, 1, 1, true, 1, 1);
code.WriteFmt("{{\n" code.Write("{{\n"
" v_tex0 = float3(rawtex0.xy, 0.0);\n" " v_tex0 = float3(rawtex0.xy, 0.0);\n"
" v_col0 = rawcolor0;\n" " v_col0 = rawcolor0;\n"
" opos = float4(rawpos.x * u_rcp_viewport_size_mul2.x - 1.0," " opos = float4(rawpos.x * u_rcp_viewport_size_mul2.x - 1.0,"
" 1.0 - rawpos.y * u_rcp_viewport_size_mul2.y, 0.0, 1.0);\n"); " 1.0 - rawpos.y * u_rcp_viewport_size_mul2.y, 0.0, 1.0);\n");
// NDC space is flipped in Vulkan. // NDC space is flipped in Vulkan.
if (GetAPIType() == APIType::Vulkan) if (GetAPIType() == APIType::Vulkan)
code.WriteFmt(" opos.y = -opos.y;\n"); code.Write(" opos.y = -opos.y;\n");
code.WriteFmt("}}\n"); code.Write("}}\n");
return code.GetBuffer(); return code.GetBuffer();
} }
@ -735,11 +733,11 @@ std::string GenerateImGuiPixelShader()
ShaderCode code; ShaderCode code;
EmitSamplerDeclarations(code, 0, 1, false); EmitSamplerDeclarations(code, 0, 1, false);
EmitPixelMainDeclaration(code, 1, 1); EmitPixelMainDeclaration(code, 1, 1);
code.WriteFmt("{{\n" code.Write("{{\n"
" ocol0 = "); " ocol0 = ");
EmitSampleTexture(code, 0, "float3(v_tex0.xy, 0.0)"); EmitSampleTexture(code, 0, "float3(v_tex0.xy, 0.0)");
code.WriteFmt(" * v_col0;\n" code.Write(" * v_col0;\n"
"}}\n"); "}}\n");
return code.GetBuffer(); return code.GetBuffer();
} }

View File

@ -73,133 +73,131 @@ ShaderCode GenerateGeometryShaderCode(APIType ApiType, const ShaderHostConfig& h
// Insert layout parameters // Insert layout parameters
if (host_config.backend_gs_instancing) if (host_config.backend_gs_instancing)
{ {
out.WriteFmt("layout({}, invocations = {}) in;\n", primitives_ogl[primitive_type_index], out.Write("layout({}, invocations = {}) in;\n", primitives_ogl[primitive_type_index],
stereo ? 2 : 1); stereo ? 2 : 1);
out.WriteFmt("layout({}_strip, max_vertices = {}) out;\n", wireframe ? "line" : "triangle", out.Write("layout({}_strip, max_vertices = {}) out;\n", wireframe ? "line" : "triangle",
vertex_out); vertex_out);
} }
else else
{ {
out.WriteFmt("layout({}) in;\n", primitives_ogl[primitive_type_index]); out.Write("layout({}) in;\n", primitives_ogl[primitive_type_index]);
out.WriteFmt("layout({}_strip, max_vertices = {}) out;\n", wireframe ? "line" : "triangle", out.Write("layout({}_strip, max_vertices = {}) out;\n", wireframe ? "line" : "triangle",
stereo ? vertex_out * 2 : vertex_out); stereo ? vertex_out * 2 : vertex_out);
} }
} }
out.WriteFmt("{}", s_lighting_struct); out.Write("{}", s_lighting_struct);
// uniforms // uniforms
if (ApiType == APIType::OpenGL || ApiType == APIType::Vulkan) if (ApiType == APIType::OpenGL || ApiType == APIType::Vulkan)
out.WriteFmt("UBO_BINDING(std140, 3) uniform GSBlock {{\n"); out.Write("UBO_BINDING(std140, 3) uniform GSBlock {{\n");
else else
out.WriteFmt("cbuffer GSBlock {{\n"); out.Write("cbuffer GSBlock {{\n");
out.WriteFmt("\tfloat4 " I_STEREOPARAMS ";\n" out.Write("\tfloat4 " I_STEREOPARAMS ";\n"
"\tfloat4 " I_LINEPTPARAMS ";\n" "\tfloat4 " I_LINEPTPARAMS ";\n"
"\tint4 " I_TEXOFFSET ";\n" "\tint4 " I_TEXOFFSET ";\n"
"}};\n"); "}};\n");
out.WriteFmt("struct VS_OUTPUT {{\n"); out.Write("struct VS_OUTPUT {{\n");
GenerateVSOutputMembers(out, ApiType, uid_data->numTexGens, host_config, ""); GenerateVSOutputMembers(out, ApiType, uid_data->numTexGens, host_config, "");
out.WriteFmt("}};\n"); out.Write("}};\n");
if (ApiType == APIType::OpenGL || ApiType == APIType::Vulkan) if (ApiType == APIType::OpenGL || ApiType == APIType::Vulkan)
{ {
if (host_config.backend_gs_instancing) if (host_config.backend_gs_instancing)
out.WriteFmt("#define InstanceID gl_InvocationID\n"); out.Write("#define InstanceID gl_InvocationID\n");
out.WriteFmt("VARYING_LOCATION(0) in VertexData {{\n"); out.Write("VARYING_LOCATION(0) in VertexData {{\n");
GenerateVSOutputMembers(out, ApiType, uid_data->numTexGens, host_config, GenerateVSOutputMembers(out, ApiType, uid_data->numTexGens, host_config,
GetInterpolationQualifier(msaa, ssaa, true, true)); GetInterpolationQualifier(msaa, ssaa, true, true));
out.WriteFmt("}} vs[{}];\n", vertex_in); out.Write("}} vs[{}];\n", vertex_in);
out.WriteFmt("VARYING_LOCATION(0) out VertexData {{\n"); out.Write("VARYING_LOCATION(0) out VertexData {{\n");
GenerateVSOutputMembers(out, ApiType, uid_data->numTexGens, host_config, GenerateVSOutputMembers(out, ApiType, uid_data->numTexGens, host_config,
GetInterpolationQualifier(msaa, ssaa, true, false)); GetInterpolationQualifier(msaa, ssaa, true, false));
if (stereo) if (stereo)
out.WriteFmt("\tflat int layer;\n"); out.Write("\tflat int layer;\n");
out.WriteFmt("}} ps;\n"); out.Write("}} ps;\n");
out.WriteFmt("void main()\n{{\n"); out.Write("void main()\n{{\n");
} }
else // D3D else // D3D
{ {
out.WriteFmt("struct VertexData {{\n"); out.Write("struct VertexData {{\n");
out.WriteFmt("\tVS_OUTPUT o;\n"); out.Write("\tVS_OUTPUT o;\n");
if (stereo) if (stereo)
out.WriteFmt("\tuint layer : SV_RenderTargetArrayIndex;\n"); out.Write("\tuint layer : SV_RenderTargetArrayIndex;\n");
out.WriteFmt("}};\n"); out.Write("}};\n");
if (host_config.backend_gs_instancing) if (host_config.backend_gs_instancing)
{ {
out.WriteFmt("[maxvertexcount({})]\n[instance({})]\n", vertex_out, stereo ? 2 : 1); out.Write("[maxvertexcount({})]\n[instance({})]\n", vertex_out, stereo ? 2 : 1);
out.WriteFmt("void main({} VS_OUTPUT o[{}], inout {}Stream<VertexData> output, in uint " out.Write("void main({} VS_OUTPUT o[{}], inout {}Stream<VertexData> output, in uint "
"InstanceID : SV_GSInstanceID)\n{{\n", "InstanceID : SV_GSInstanceID)\n{{\n",
primitives_d3d[primitive_type_index], vertex_in, primitives_d3d[primitive_type_index], vertex_in, wireframe ? "Line" : "Triangle");
wireframe ? "Line" : "Triangle");
} }
else else
{ {
out.WriteFmt("[maxvertexcount({})]\n", stereo ? vertex_out * 2 : vertex_out); out.Write("[maxvertexcount({})]\n", stereo ? vertex_out * 2 : vertex_out);
out.WriteFmt("void main({} VS_OUTPUT o[{}], inout {}Stream<VertexData> output)\n{{\n", out.Write("void main({} VS_OUTPUT o[{}], inout {}Stream<VertexData> output)\n{{\n",
primitives_d3d[primitive_type_index], vertex_in, primitives_d3d[primitive_type_index], vertex_in, wireframe ? "Line" : "Triangle");
wireframe ? "Line" : "Triangle");
} }
out.WriteFmt("\tVertexData ps;\n"); out.Write("\tVertexData ps;\n");
} }
if (primitive_type == PrimitiveType::Lines) if (primitive_type == PrimitiveType::Lines)
{ {
if (ApiType == APIType::OpenGL || ApiType == APIType::Vulkan) if (ApiType == APIType::OpenGL || ApiType == APIType::Vulkan)
{ {
out.WriteFmt("\tVS_OUTPUT start, end;\n"); out.Write("\tVS_OUTPUT start, end;\n");
AssignVSOutputMembers(out, "start", "vs[0]", uid_data->numTexGens, host_config); AssignVSOutputMembers(out, "start", "vs[0]", uid_data->numTexGens, host_config);
AssignVSOutputMembers(out, "end", "vs[1]", uid_data->numTexGens, host_config); AssignVSOutputMembers(out, "end", "vs[1]", uid_data->numTexGens, host_config);
} }
else else
{ {
out.WriteFmt("\tVS_OUTPUT start = o[0];\n" out.Write("\tVS_OUTPUT start = o[0];\n"
"\tVS_OUTPUT end = o[1];\n"); "\tVS_OUTPUT end = o[1];\n");
} }
// GameCube/Wii's line drawing algorithm is a little quirky. It does not // GameCube/Wii's line drawing algorithm is a little quirky. It does not
// use the correct line caps. Instead, the line caps are vertical or // use the correct line caps. Instead, the line caps are vertical or
// horizontal depending the slope of the line. // horizontal depending the slope of the line.
out.WriteFmt("\tfloat2 offset;\n" out.Write("\tfloat2 offset;\n"
"\tfloat2 to = abs(end.pos.xy / end.pos.w - start.pos.xy / start.pos.w);\n" "\tfloat2 to = abs(end.pos.xy / end.pos.w - start.pos.xy / start.pos.w);\n"
// FIXME: What does real hardware do when line is at a 45-degree angle? // FIXME: What does real hardware do when line is at a 45-degree angle?
// FIXME: Lines aren't drawn at the correct width. See Twilight Princess map. // FIXME: Lines aren't drawn at the correct width. See Twilight Princess map.
"\tif (" I_LINEPTPARAMS ".y * to.y > " I_LINEPTPARAMS ".x * to.x) {{\n" "\tif (" I_LINEPTPARAMS ".y * to.y > " I_LINEPTPARAMS ".x * to.x) {{\n"
// Line is more tall. Extend geometry left and right. // Line is more tall. Extend geometry left and right.
// Lerp LineWidth/2 from [0..VpWidth] to [-1..1] // Lerp LineWidth/2 from [0..VpWidth] to [-1..1]
"\t\toffset = float2(" I_LINEPTPARAMS ".z / " I_LINEPTPARAMS ".x, 0);\n" "\t\toffset = float2(" I_LINEPTPARAMS ".z / " I_LINEPTPARAMS ".x, 0);\n"
"\t}} else {{\n" "\t}} else {{\n"
// Line is more wide. Extend geometry up and down. // Line is more wide. Extend geometry up and down.
// Lerp LineWidth/2 from [0..VpHeight] to [1..-1] // Lerp LineWidth/2 from [0..VpHeight] to [1..-1]
"\t\toffset = float2(0, -" I_LINEPTPARAMS ".z / " I_LINEPTPARAMS ".y);\n" "\t\toffset = float2(0, -" I_LINEPTPARAMS ".z / " I_LINEPTPARAMS ".y);\n"
"\t}}\n"); "\t}}\n");
} }
else if (primitive_type == PrimitiveType::Points) else if (primitive_type == PrimitiveType::Points)
{ {
if (ApiType == APIType::OpenGL || ApiType == APIType::Vulkan) if (ApiType == APIType::OpenGL || ApiType == APIType::Vulkan)
{ {
out.WriteFmt("\tVS_OUTPUT center;\n"); out.Write("\tVS_OUTPUT center;\n");
AssignVSOutputMembers(out, "center", "vs[0]", uid_data->numTexGens, host_config); AssignVSOutputMembers(out, "center", "vs[0]", uid_data->numTexGens, host_config);
} }
else else
{ {
out.WriteFmt("\tVS_OUTPUT center = o[0];\n"); out.Write("\tVS_OUTPUT center = o[0];\n");
} }
// Offset from center to upper right vertex // Offset from center to upper right vertex
// Lerp PointSize/2 from [0,0..VpWidth,VpHeight] to [-1,1..1,-1] // Lerp PointSize/2 from [0,0..VpWidth,VpHeight] to [-1,1..1,-1]
out.WriteFmt("\tfloat2 offset = float2(" I_LINEPTPARAMS ".w / " I_LINEPTPARAMS out.Write("\tfloat2 offset = float2(" I_LINEPTPARAMS ".w / " I_LINEPTPARAMS
".x, -" I_LINEPTPARAMS ".w / " I_LINEPTPARAMS ".y) * center.pos.w;\n"); ".x, -" I_LINEPTPARAMS ".w / " I_LINEPTPARAMS ".y) * center.pos.w;\n");
} }
if (stereo) if (stereo)
@ -207,19 +205,19 @@ ShaderCode GenerateGeometryShaderCode(APIType ApiType, const ShaderHostConfig& h
// If the GPU supports invocation we don't need a for loop and can simply use the // If the GPU supports invocation we don't need a for loop and can simply use the
// invocation identifier to determine which layer we're rendering. // invocation identifier to determine which layer we're rendering.
if (host_config.backend_gs_instancing) if (host_config.backend_gs_instancing)
out.WriteFmt("\tint eye = InstanceID;\n"); out.Write("\tint eye = InstanceID;\n");
else else
out.WriteFmt("\tfor (int eye = 0; eye < 2; ++eye) {{\n"); out.Write("\tfor (int eye = 0; eye < 2; ++eye) {{\n");
} }
if (wireframe) if (wireframe)
out.WriteFmt("\tVS_OUTPUT first;\n"); out.Write("\tVS_OUTPUT first;\n");
out.WriteFmt("\tfor (int i = 0; i < {}; ++i) {{\n", vertex_in); out.Write("\tfor (int i = 0; i < {}; ++i) {{\n", vertex_in);
if (ApiType == APIType::OpenGL || ApiType == APIType::Vulkan) if (ApiType == APIType::OpenGL || ApiType == APIType::Vulkan)
{ {
out.WriteFmt("\tVS_OUTPUT f;\n"); out.Write("\tVS_OUTPUT f;\n");
AssignVSOutputMembers(out, "f", "vs[i]", uid_data->numTexGens, host_config); AssignVSOutputMembers(out, "f", "vs[i]", uid_data->numTexGens, host_config);
if (host_config.backend_depth_clamp && if (host_config.backend_depth_clamp &&
@ -227,21 +225,21 @@ ShaderCode GenerateGeometryShaderCode(APIType ApiType, const ShaderHostConfig& h
{ {
// On certain GPUs we have to consume the clip distance from the vertex shader // On certain GPUs we have to consume the clip distance from the vertex shader
// or else the other vertex shader outputs will get corrupted. // or else the other vertex shader outputs will get corrupted.
out.WriteFmt("\tf.clipDist0 = gl_in[i].gl_ClipDistance[0];\n" out.Write("\tf.clipDist0 = gl_in[i].gl_ClipDistance[0];\n"
"\tf.clipDist1 = gl_in[i].gl_ClipDistance[1];\n"); "\tf.clipDist1 = gl_in[i].gl_ClipDistance[1];\n");
} }
} }
else else
{ {
out.WriteFmt("\tVS_OUTPUT f = o[i];\n"); out.Write("\tVS_OUTPUT f = o[i];\n");
} }
if (stereo) if (stereo)
{ {
// Select the output layer // Select the output layer
out.WriteFmt("\tps.layer = eye;\n"); out.Write("\tps.layer = eye;\n");
if (ApiType == APIType::OpenGL || ApiType == APIType::Vulkan) if (ApiType == APIType::OpenGL || ApiType == APIType::Vulkan)
out.WriteFmt("\tgl_Layer = eye;\n"); out.Write("\tgl_Layer = eye;\n");
// For stereoscopy add a small horizontal offset in Normalized Device Coordinates proportional // For stereoscopy add a small horizontal offset in Normalized Device Coordinates proportional
// to the depth of the vertex. We retrieve the depth value from the w-component of the projected // to the depth of the vertex. We retrieve the depth value from the w-component of the projected
@ -250,56 +248,56 @@ ShaderCode GenerateGeometryShaderCode(APIType ApiType, const ShaderHostConfig& h
// the depth value. This results in objects at a distance smaller than the convergence // the depth value. This results in objects at a distance smaller than the convergence
// distance to seemingly appear in front of the screen. // distance to seemingly appear in front of the screen.
// This formula is based on page 13 of the "Nvidia 3D Vision Automatic, Best Practices Guide" // This formula is based on page 13 of the "Nvidia 3D Vision Automatic, Best Practices Guide"
out.WriteFmt("\tfloat hoffset = (eye == 0) ? " I_STEREOPARAMS ".x : " I_STEREOPARAMS ".y;\n"); out.Write("\tfloat hoffset = (eye == 0) ? " I_STEREOPARAMS ".x : " I_STEREOPARAMS ".y;\n");
out.WriteFmt("\tf.pos.x += hoffset * (f.pos.w - " I_STEREOPARAMS ".z);\n"); out.Write("\tf.pos.x += hoffset * (f.pos.w - " I_STEREOPARAMS ".z);\n");
} }
if (primitive_type == PrimitiveType::Lines) if (primitive_type == PrimitiveType::Lines)
{ {
out.WriteFmt("\tVS_OUTPUT l = f;\n" out.Write("\tVS_OUTPUT l = f;\n"
"\tVS_OUTPUT r = f;\n"); "\tVS_OUTPUT r = f;\n");
out.WriteFmt("\tl.pos.xy -= offset * l.pos.w;\n" out.Write("\tl.pos.xy -= offset * l.pos.w;\n"
"\tr.pos.xy += offset * r.pos.w;\n"); "\tr.pos.xy += offset * r.pos.w;\n");
out.WriteFmt("\tif (" I_TEXOFFSET "[2] != 0) {{\n"); out.Write("\tif (" I_TEXOFFSET "[2] != 0) {{\n");
out.WriteFmt("\tfloat texOffset = 1.0 / float(" I_TEXOFFSET "[2]);\n"); out.Write("\tfloat texOffset = 1.0 / float(" I_TEXOFFSET "[2]);\n");
for (u32 i = 0; i < uid_data->numTexGens; ++i) for (u32 i = 0; i < uid_data->numTexGens; ++i)
{ {
out.WriteFmt("\tif (((" I_TEXOFFSET "[0] >> {}) & 0x1) != 0)\n", i); out.Write("\tif (((" I_TEXOFFSET "[0] >> {}) & 0x1) != 0)\n", i);
out.WriteFmt("\t\tr.tex{}.x += texOffset;\n", i); out.Write("\t\tr.tex{}.x += texOffset;\n", i);
} }
out.WriteFmt("\t}}\n"); out.Write("\t}}\n");
EmitVertex(out, host_config, uid_data, "l", ApiType, wireframe, true); EmitVertex(out, host_config, uid_data, "l", ApiType, wireframe, true);
EmitVertex(out, host_config, uid_data, "r", ApiType, wireframe); EmitVertex(out, host_config, uid_data, "r", ApiType, wireframe);
} }
else if (primitive_type == PrimitiveType::Points) else if (primitive_type == PrimitiveType::Points)
{ {
out.WriteFmt("\tVS_OUTPUT ll = f;\n" out.Write("\tVS_OUTPUT ll = f;\n"
"\tVS_OUTPUT lr = f;\n" "\tVS_OUTPUT lr = f;\n"
"\tVS_OUTPUT ul = f;\n" "\tVS_OUTPUT ul = f;\n"
"\tVS_OUTPUT ur = f;\n"); "\tVS_OUTPUT ur = f;\n");
out.WriteFmt("\tll.pos.xy += float2(-1,-1) * offset;\n" out.Write("\tll.pos.xy += float2(-1,-1) * offset;\n"
"\tlr.pos.xy += float2(1,-1) * offset;\n" "\tlr.pos.xy += float2(1,-1) * offset;\n"
"\tul.pos.xy += float2(-1,1) * offset;\n" "\tul.pos.xy += float2(-1,1) * offset;\n"
"\tur.pos.xy += offset;\n"); "\tur.pos.xy += offset;\n");
out.WriteFmt("\tif (" I_TEXOFFSET "[3] != 0) {{\n"); out.Write("\tif (" I_TEXOFFSET "[3] != 0) {{\n");
out.WriteFmt("\tfloat2 texOffset = float2(1.0 / float(" I_TEXOFFSET out.Write("\tfloat2 texOffset = float2(1.0 / float(" I_TEXOFFSET
"[3]), 1.0 / float(" I_TEXOFFSET "[3]));\n"); "[3]), 1.0 / float(" I_TEXOFFSET "[3]));\n");
for (u32 i = 0; i < uid_data->numTexGens; ++i) for (u32 i = 0; i < uid_data->numTexGens; ++i)
{ {
out.WriteFmt("\tif (((" I_TEXOFFSET "[1] >> {}) & 0x1) != 0) {{\n", i); out.Write("\tif (((" I_TEXOFFSET "[1] >> {}) & 0x1) != 0) {{\n", i);
out.WriteFmt("\t\tul.tex{}.xy += float2(0,1) * texOffset;\n", i); out.Write("\t\tul.tex{}.xy += float2(0,1) * texOffset;\n", i);
out.WriteFmt("\t\tur.tex{}.xy += texOffset;\n", i); out.Write("\t\tur.tex{}.xy += texOffset;\n", i);
out.WriteFmt("\t\tlr.tex{}.xy += float2(1,0) * texOffset;\n", i); out.Write("\t\tlr.tex{}.xy += float2(1,0) * texOffset;\n", i);
out.WriteFmt("\t}}\n"); out.Write("\t}}\n");
} }
out.WriteFmt("\t}}\n"); out.Write("\t}}\n");
EmitVertex(out, host_config, uid_data, "ll", ApiType, wireframe, true); EmitVertex(out, host_config, uid_data, "ll", ApiType, wireframe, true);
EmitVertex(out, host_config, uid_data, "lr", ApiType, wireframe); EmitVertex(out, host_config, uid_data, "lr", ApiType, wireframe);
@ -311,14 +309,14 @@ ShaderCode GenerateGeometryShaderCode(APIType ApiType, const ShaderHostConfig& h
EmitVertex(out, host_config, uid_data, "f", ApiType, wireframe, true); EmitVertex(out, host_config, uid_data, "f", ApiType, wireframe, true);
} }
out.WriteFmt("\t}}\n"); out.Write("\t}}\n");
EndPrimitive(out, host_config, uid_data, ApiType, wireframe); EndPrimitive(out, host_config, uid_data, ApiType, wireframe);
if (stereo && !host_config.backend_gs_instancing) if (stereo && !host_config.backend_gs_instancing)
out.WriteFmt("\t}}\n"); out.Write("\t}}\n");
out.WriteFmt("}}\n"); out.Write("}}\n");
return out; return out;
} }
@ -328,34 +326,34 @@ static void EmitVertex(ShaderCode& out, const ShaderHostConfig& host_config,
APIType ApiType, bool wireframe, bool first_vertex) APIType ApiType, bool wireframe, bool first_vertex)
{ {
if (wireframe && first_vertex) if (wireframe && first_vertex)
out.WriteFmt("\tif (i == 0) first = {};\n", vertex); out.Write("\tif (i == 0) first = {};\n", vertex);
if (ApiType == APIType::OpenGL) if (ApiType == APIType::OpenGL)
{ {
out.WriteFmt("\tgl_Position = {}.pos;\n", vertex); out.Write("\tgl_Position = {}.pos;\n", vertex);
if (host_config.backend_depth_clamp) if (host_config.backend_depth_clamp)
{ {
out.WriteFmt("\tgl_ClipDistance[0] = {}.clipDist0;\n", vertex); out.Write("\tgl_ClipDistance[0] = {}.clipDist0;\n", vertex);
out.WriteFmt("\tgl_ClipDistance[1] = {}.clipDist1;\n", vertex); out.Write("\tgl_ClipDistance[1] = {}.clipDist1;\n", vertex);
} }
AssignVSOutputMembers(out, "ps", vertex, uid_data->numTexGens, host_config); AssignVSOutputMembers(out, "ps", vertex, uid_data->numTexGens, host_config);
} }
else if (ApiType == APIType::Vulkan) else if (ApiType == APIType::Vulkan)
{ {
// Vulkan NDC space has Y pointing down (right-handed NDC space). // Vulkan NDC space has Y pointing down (right-handed NDC space).
out.WriteFmt("\tgl_Position = {}.pos;\n", vertex); out.Write("\tgl_Position = {}.pos;\n", vertex);
out.WriteFmt("\tgl_Position.y = -gl_Position.y;\n"); out.Write("\tgl_Position.y = -gl_Position.y;\n");
AssignVSOutputMembers(out, "ps", vertex, uid_data->numTexGens, host_config); AssignVSOutputMembers(out, "ps", vertex, uid_data->numTexGens, host_config);
} }
else else
{ {
out.WriteFmt("\tps.o = {};\n", vertex); out.Write("\tps.o = {};\n", vertex);
} }
if (ApiType == APIType::OpenGL || ApiType == APIType::Vulkan) if (ApiType == APIType::OpenGL || ApiType == APIType::Vulkan)
out.WriteFmt("\tEmitVertex();\n"); out.Write("\tEmitVertex();\n");
else else
out.WriteFmt("\toutput.Append(ps);\n"); out.Write("\toutput.Append(ps);\n");
} }
static void EndPrimitive(ShaderCode& out, const ShaderHostConfig& host_config, static void EndPrimitive(ShaderCode& out, const ShaderHostConfig& host_config,
@ -365,9 +363,9 @@ static void EndPrimitive(ShaderCode& out, const ShaderHostConfig& host_config,
EmitVertex(out, host_config, uid_data, "first", ApiType, wireframe); EmitVertex(out, host_config, uid_data, "first", ApiType, wireframe);
if (ApiType == APIType::OpenGL || ApiType == APIType::Vulkan) if (ApiType == APIType::OpenGL || ApiType == APIType::Vulkan)
out.WriteFmt("\tEndPrimitive();\n"); out.Write("\tEndPrimitive();\n");
else else
out.WriteFmt("\toutput.RestartStrip();\n"); out.Write("\toutput.RestartStrip();\n");
} }
void EnumerateGeometryShaderUids(const std::function<void(const GeometryShaderUid&)>& callback) void EnumerateGeometryShaderUids(const std::function<void(const GeometryShaderUid&)>& callback)

View File

@ -24,54 +24,53 @@ static void GenerateLightShader(ShaderCode& object, const LightingUidData& uid_d
{ {
case LIGHTATTN_NONE: case LIGHTATTN_NONE:
case LIGHTATTN_DIR: case LIGHTATTN_DIR:
object.WriteFmt("ldir = normalize(" LIGHT_POS ".xyz - pos.xyz);\n", LIGHT_POS_PARAMS(index)); object.Write("ldir = normalize(" LIGHT_POS ".xyz - pos.xyz);\n", LIGHT_POS_PARAMS(index));
object.WriteFmt("attn = 1.0;\n"); object.Write("attn = 1.0;\n");
object.WriteFmt("if (length(ldir) == 0.0)\n\t ldir = _norm0;\n"); object.Write("if (length(ldir) == 0.0)\n\t ldir = _norm0;\n");
break; break;
case LIGHTATTN_SPEC: case LIGHTATTN_SPEC:
object.WriteFmt("ldir = normalize(" LIGHT_POS ".xyz - pos.xyz);\n", LIGHT_POS_PARAMS(index)); object.Write("ldir = normalize(" LIGHT_POS ".xyz - pos.xyz);\n", LIGHT_POS_PARAMS(index));
object.WriteFmt("attn = (dot(_norm0, ldir) >= 0.0) ? max(0.0, dot(_norm0, " LIGHT_DIR object.Write("attn = (dot(_norm0, ldir) >= 0.0) ? max(0.0, dot(_norm0, " LIGHT_DIR
".xyz)) : 0.0;\n", ".xyz)) : 0.0;\n",
LIGHT_DIR_PARAMS(index)); LIGHT_DIR_PARAMS(index));
object.WriteFmt("cosAttn = " LIGHT_COSATT ".xyz;\n", LIGHT_COSATT_PARAMS(index)); object.Write("cosAttn = " LIGHT_COSATT ".xyz;\n", LIGHT_COSATT_PARAMS(index));
object.WriteFmt("distAttn = {}(" LIGHT_DISTATT ".xyz);\n", object.Write("distAttn = {}(" LIGHT_DISTATT ".xyz);\n",
(diffusefunc == LIGHTDIF_NONE) ? "" : "normalize", LIGHT_DISTATT_PARAMS(index)); (diffusefunc == LIGHTDIF_NONE) ? "" : "normalize", LIGHT_DISTATT_PARAMS(index));
object.WriteFmt("attn = max(0.0f, dot(cosAttn, float3(1.0, attn, attn*attn))) / dot(distAttn, " object.Write("attn = max(0.0f, dot(cosAttn, float3(1.0, attn, attn*attn))) / dot(distAttn, "
"float3(1.0, attn, attn*attn));\n"); "float3(1.0, attn, attn*attn));\n");
break; break;
case LIGHTATTN_SPOT: case LIGHTATTN_SPOT:
object.WriteFmt("ldir = " LIGHT_POS ".xyz - pos.xyz;\n", LIGHT_POS_PARAMS(index)); object.Write("ldir = " LIGHT_POS ".xyz - pos.xyz;\n", LIGHT_POS_PARAMS(index));
object.WriteFmt("dist2 = dot(ldir, ldir);\n" object.Write("dist2 = dot(ldir, ldir);\n"
"dist = sqrt(dist2);\n" "dist = sqrt(dist2);\n"
"ldir = ldir / dist;\n" "ldir = ldir / dist;\n"
"attn = max(0.0, dot(ldir, " LIGHT_DIR ".xyz));\n", "attn = max(0.0, dot(ldir, " LIGHT_DIR ".xyz));\n",
LIGHT_DIR_PARAMS(index)); LIGHT_DIR_PARAMS(index));
// attn*attn may overflow // attn*attn may overflow
object.WriteFmt("attn = max(0.0, " LIGHT_COSATT ".x + " LIGHT_COSATT ".y*attn + " LIGHT_COSATT object.Write("attn = max(0.0, " LIGHT_COSATT ".x + " LIGHT_COSATT ".y*attn + " LIGHT_COSATT
".z*attn*attn) / dot(" LIGHT_DISTATT ".xyz, float3(1.0,dist,dist2));\n", ".z*attn*attn) / dot(" LIGHT_DISTATT ".xyz, float3(1.0,dist,dist2));\n",
LIGHT_COSATT_PARAMS(index), LIGHT_COSATT_PARAMS(index), LIGHT_COSATT_PARAMS(index), LIGHT_COSATT_PARAMS(index), LIGHT_COSATT_PARAMS(index),
LIGHT_COSATT_PARAMS(index), LIGHT_DISTATT_PARAMS(index)); LIGHT_DISTATT_PARAMS(index));
break; break;
} }
switch (diffusefunc) switch (diffusefunc)
{ {
case LIGHTDIF_NONE: case LIGHTDIF_NONE:
object.WriteFmt("lacc.{} += int{}(round(attn * float{}(" LIGHT_COL ")));\n", swizzle, object.Write("lacc.{} += int{}(round(attn * float{}(" LIGHT_COL ")));\n", swizzle,
swizzle_components, swizzle_components, LIGHT_COL_PARAMS(index, swizzle)); swizzle_components, swizzle_components, LIGHT_COL_PARAMS(index, swizzle));
break; break;
case LIGHTDIF_SIGN: case LIGHTDIF_SIGN:
case LIGHTDIF_CLAMP: case LIGHTDIF_CLAMP:
object.WriteFmt("lacc.{} += int{}(round(attn * {}dot(ldir, _norm0)) * float{}(" LIGHT_COL object.Write("lacc.{} += int{}(round(attn * {}dot(ldir, _norm0)) * float{}(" LIGHT_COL ")));\n",
")));\n", swizzle, swizzle_components, diffusefunc != LIGHTDIF_SIGN ? "max(0.0," : "(",
swizzle, swizzle_components, diffusefunc != LIGHTDIF_SIGN ? "max(0.0," : "(", swizzle_components, LIGHT_COL_PARAMS(index, swizzle));
swizzle_components, LIGHT_COL_PARAMS(index, swizzle));
break; break;
default: default:
ASSERT(0); ASSERT(0);
} }
object.WriteFmt("\n"); object.Write("\n");
} }
// vertex shader // vertex shader
@ -84,21 +83,21 @@ void GenerateLightingShaderCode(ShaderCode& object, const LightingUidData& uid_d
{ {
for (u32 j = 0; j < NUM_XF_COLOR_CHANNELS; j++) for (u32 j = 0; j < NUM_XF_COLOR_CHANNELS; j++)
{ {
object.WriteFmt("{{\n"); object.Write("{{\n");
const bool colormatsource = !!(uid_data.matsource & (1 << j)); const bool colormatsource = !!(uid_data.matsource & (1 << j));
if (colormatsource) // from vertex if (colormatsource) // from vertex
{ {
if ((components & (VB_HAS_COL0 << j)) != 0) if ((components & (VB_HAS_COL0 << j)) != 0)
object.WriteFmt("int4 mat = int4(round({}{} * 255.0));\n", in_color_name, j); object.Write("int4 mat = int4(round({}{} * 255.0));\n", in_color_name, j);
else if ((components & VB_HAS_COL0) != 0) else if ((components & VB_HAS_COL0) != 0)
object.WriteFmt("int4 mat = int4(round({}0 * 255.0));\n", in_color_name); object.Write("int4 mat = int4(round({}0 * 255.0));\n", in_color_name);
else else
object.WriteFmt("int4 mat = int4(255, 255, 255, 255);\n"); object.Write("int4 mat = int4(255, 255, 255, 255);\n");
} }
else // from color else // from color
{ {
object.WriteFmt("int4 mat = {}[{}];\n", I_MATERIALS, j + 2); object.Write("int4 mat = {}[{}];\n", I_MATERIALS, j + 2);
} }
if ((uid_data.enablelighting & (1 << j)) != 0) if ((uid_data.enablelighting & (1 << j)) != 0)
@ -107,28 +106,28 @@ void GenerateLightingShaderCode(ShaderCode& object, const LightingUidData& uid_d
{ {
if ((components & (VB_HAS_COL0 << j)) != 0) if ((components & (VB_HAS_COL0 << j)) != 0)
{ {
object.WriteFmt("lacc = int4(round({}{} * 255.0));\n", in_color_name, j); object.Write("lacc = int4(round({}{} * 255.0));\n", in_color_name, j);
} }
else if ((components & VB_HAS_COL0) != 0) else if ((components & VB_HAS_COL0) != 0)
{ {
object.WriteFmt("lacc = int4(round({}0 * 255.0));\n", in_color_name); object.Write("lacc = int4(round({}0 * 255.0));\n", in_color_name);
} }
else else
{ {
// TODO: this isn't verified. Here we want to read the ambient from the vertex, // TODO: this isn't verified. Here we want to read the ambient from the vertex,
// but the vertex itself has no color. So we don't know which value to read. // but the vertex itself has no color. So we don't know which value to read.
// Returning 1.0 is the same as disabled lightning, so this could be fine // Returning 1.0 is the same as disabled lightning, so this could be fine
object.WriteFmt("lacc = int4(255, 255, 255, 255);\n"); object.Write("lacc = int4(255, 255, 255, 255);\n");
} }
} }
else // from color else // from color
{ {
object.WriteFmt("lacc = {}[{}];\n", I_MATERIALS, j); object.Write("lacc = {}[{}];\n", I_MATERIALS, j);
} }
} }
else else
{ {
object.WriteFmt("lacc = int4(255, 255, 255, 255);\n"); object.Write("lacc = int4(255, 255, 255, 255);\n");
} }
// check if alpha is different // check if alpha is different
@ -138,15 +137,15 @@ void GenerateLightingShaderCode(ShaderCode& object, const LightingUidData& uid_d
if (alphamatsource) // from vertex if (alphamatsource) // from vertex
{ {
if ((components & (VB_HAS_COL0 << j)) != 0) if ((components & (VB_HAS_COL0 << j)) != 0)
object.WriteFmt("mat.w = int(round({}{}.w * 255.0));\n", in_color_name, j); object.Write("mat.w = int(round({}{}.w * 255.0));\n", in_color_name, j);
else if ((components & VB_HAS_COL0) != 0) else if ((components & VB_HAS_COL0) != 0)
object.WriteFmt("mat.w = int(round({}0.w * 255.0));\n", in_color_name); object.Write("mat.w = int(round({}0.w * 255.0));\n", in_color_name);
else else
object.WriteFmt("mat.w = 255;\n"); object.Write("mat.w = 255;\n");
} }
else // from color else // from color
{ {
object.WriteFmt("mat.w = {}[{}].w;\n", I_MATERIALS, j + 2); object.Write("mat.w = {}[{}].w;\n", I_MATERIALS, j + 2);
} }
} }
@ -156,26 +155,26 @@ void GenerateLightingShaderCode(ShaderCode& object, const LightingUidData& uid_d
{ {
if ((components & (VB_HAS_COL0 << j)) != 0) if ((components & (VB_HAS_COL0 << j)) != 0)
{ {
object.WriteFmt("lacc.w = int(round({}{}.w * 255.0));\n", in_color_name, j); object.Write("lacc.w = int(round({}{}.w * 255.0));\n", in_color_name, j);
} }
else if ((components & VB_HAS_COL0) != 0) else if ((components & VB_HAS_COL0) != 0)
{ {
object.WriteFmt("lacc.w = int(round({}0.w * 255.0));\n", in_color_name); object.Write("lacc.w = int(round({}0.w * 255.0));\n", in_color_name);
} }
else else
{ {
// TODO: The same for alpha: We want to read from vertex, but the vertex has no color // TODO: The same for alpha: We want to read from vertex, but the vertex has no color
object.WriteFmt("lacc.w = 255;\n"); object.Write("lacc.w = 255;\n");
} }
} }
else // from color else // from color
{ {
object.WriteFmt("lacc.w = {}[{}].w;\n", I_MATERIALS, j); object.Write("lacc.w = {}[{}].w;\n", I_MATERIALS, j);
} }
} }
else else
{ {
object.WriteFmt("lacc.w = 255;\n"); object.Write("lacc.w = 255;\n");
} }
if ((uid_data.enablelighting & (1 << j)) != 0) // Color lights if ((uid_data.enablelighting & (1 << j)) != 0) // Color lights
@ -194,9 +193,9 @@ void GenerateLightingShaderCode(ShaderCode& object, const LightingUidData& uid_d
GenerateLightShader(object, uid_data, i, j + 2, true); GenerateLightShader(object, uid_data, i, j + 2, true);
} }
} }
object.WriteFmt("lacc = clamp(lacc, 0, 255);\n"); object.Write("lacc = clamp(lacc, 0, 255);\n");
object.WriteFmt("{}{} = float4((mat * (lacc + (lacc >> 7))) >> 8) / 255.0;\n", dest, j); object.Write("{}{} = float4((mat * (lacc + (lacc >> 7))) >> 8) / 255.0;\n", dest, j);
object.WriteFmt("}}\n"); object.Write("}}\n");
} }
} }

File diff suppressed because it is too large Load Diff

View File

@ -91,20 +91,20 @@ static void DefineOutputMember(ShaderCode& object, APIType api_type, std::string
std::string_view type, std::string_view name, int var_index, std::string_view type, std::string_view name, int var_index,
std::string_view semantic = {}, int semantic_index = -1) std::string_view semantic = {}, int semantic_index = -1)
{ {
object.WriteFmt("\t{} {} {}", qualifier, type, name); object.Write("\t{} {} {}", qualifier, type, name);
if (var_index != -1) if (var_index != -1)
object.WriteFmt("{}", var_index); object.Write("{}", var_index);
if (api_type == APIType::D3D && !semantic.empty()) if (api_type == APIType::D3D && !semantic.empty())
{ {
if (semantic_index != -1) if (semantic_index != -1)
object.WriteFmt(" : {}{}", semantic, semantic_index); object.Write(" : {}{}", semantic, semantic_index);
else else
object.WriteFmt(" : {}", semantic); object.Write(" : {}", semantic);
} }
object.WriteFmt(";\n"); object.Write(";\n");
} }
void GenerateVSOutputMembers(ShaderCode& object, APIType api_type, u32 texgens, void GenerateVSOutputMembers(ShaderCode& object, APIType api_type, u32 texgens,
@ -138,26 +138,26 @@ void GenerateVSOutputMembers(ShaderCode& object, APIType api_type, u32 texgens,
void AssignVSOutputMembers(ShaderCode& object, std::string_view a, std::string_view b, u32 texgens, void AssignVSOutputMembers(ShaderCode& object, std::string_view a, std::string_view b, u32 texgens,
const ShaderHostConfig& host_config) const ShaderHostConfig& host_config)
{ {
object.WriteFmt("\t{}.pos = {}.pos;\n", a, b); object.Write("\t{}.pos = {}.pos;\n", a, b);
object.WriteFmt("\t{}.colors_0 = {}.colors_0;\n", a, b); object.Write("\t{}.colors_0 = {}.colors_0;\n", a, b);
object.WriteFmt("\t{}.colors_1 = {}.colors_1;\n", a, b); object.Write("\t{}.colors_1 = {}.colors_1;\n", a, b);
for (unsigned int i = 0; i < texgens; ++i) for (unsigned int i = 0; i < texgens; ++i)
object.WriteFmt("\t{}.tex{} = {}.tex{};\n", a, i, b, i); object.Write("\t{}.tex{} = {}.tex{};\n", a, i, b, i);
if (!host_config.fast_depth_calc) if (!host_config.fast_depth_calc)
object.WriteFmt("\t{}.clipPos = {}.clipPos;\n", a, b); object.Write("\t{}.clipPos = {}.clipPos;\n", a, b);
if (host_config.per_pixel_lighting) if (host_config.per_pixel_lighting)
{ {
object.WriteFmt("\t{}.Normal = {}.Normal;\n", a, b); object.Write("\t{}.Normal = {}.Normal;\n", a, b);
object.WriteFmt("\t{}.WorldPos = {}.WorldPos;\n", a, b); object.Write("\t{}.WorldPos = {}.WorldPos;\n", a, b);
} }
if (host_config.backend_geometry_shaders) if (host_config.backend_geometry_shaders)
{ {
object.WriteFmt("\t{}.clipDist0 = {}.clipDist0;\n", a, b); object.Write("\t{}.clipDist0 = {}.clipDist0;\n", a, b);
object.WriteFmt("\t{}.clipDist1 = {}.clipDist1;\n", a, b); object.Write("\t{}.clipDist1 = {}.clipDist1;\n", a, b);
} }
} }

View File

@ -4,7 +4,6 @@
#pragma once #pragma once
#include <cstdarg>
#include <cstring> #include <cstring>
#include <iterator> #include <iterator>
#include <string> #include <string>
@ -104,21 +103,9 @@ public:
ShaderCode() { m_buffer.reserve(16384); } ShaderCode() { m_buffer.reserve(16384); }
const std::string& GetBuffer() const { return m_buffer; } const std::string& GetBuffer() const { return m_buffer; }
// Deprecated: Writes format strings using traditional printf format strings.
void Write(const char* fmt, ...)
#ifdef __GNUC__
__attribute__((format(printf, 2, 3)))
#endif
{
va_list arglist;
va_start(arglist, fmt);
m_buffer += StringFromFormatV(fmt, arglist);
va_end(arglist);
}
// Writes format strings using fmtlib format strings. // Writes format strings using fmtlib format strings.
template <typename... Args> template <typename... Args>
void WriteFmt(std::string_view format, Args&&... args) void Write(std::string_view format, Args&&... args)
{ {
fmt::format_to(std::back_inserter(m_buffer), format, std::forward<Args>(args)...); fmt::format_to(std::back_inserter(m_buffer), format, std::forward<Args>(args)...);
} }

View File

@ -60,63 +60,63 @@ static void WriteHeader(ShaderCode& code, APIType api_type)
{ {
// left, top, of source rectangle within source texture // left, top, of source rectangle within source texture
// width of the destination rectangle, scale_factor (1 or 2) // width of the destination rectangle, scale_factor (1 or 2)
code.WriteFmt("UBO_BINDING(std140, 1) uniform PSBlock {{\n" code.Write("UBO_BINDING(std140, 1) uniform PSBlock {{\n"
" int4 position;\n" " int4 position;\n"
" float y_scale;\n" " float y_scale;\n"
" float gamma_rcp;\n" " float gamma_rcp;\n"
" float2 clamp_tb;\n" " float2 clamp_tb;\n"
" float3 filter_coefficients;\n" " float3 filter_coefficients;\n"
"}};\n"); "}};\n");
if (g_ActiveConfig.backend_info.bSupportsGeometryShaders) if (g_ActiveConfig.backend_info.bSupportsGeometryShaders)
{ {
code.WriteFmt("VARYING_LOCATION(0) in VertexData {{\n" code.Write("VARYING_LOCATION(0) in VertexData {{\n"
" float3 v_tex0;\n" " float3 v_tex0;\n"
"}};\n"); "}};\n");
} }
else else
{ {
code.WriteFmt("VARYING_LOCATION(0) in float3 v_tex0;\n"); code.Write("VARYING_LOCATION(0) in float3 v_tex0;\n");
} }
code.WriteFmt("SAMPLER_BINDING(0) uniform sampler2DArray samp0;\n" code.Write("SAMPLER_BINDING(0) uniform sampler2DArray samp0;\n"
"FRAGMENT_OUTPUT_LOCATION(0) out float4 ocol0;\n"); "FRAGMENT_OUTPUT_LOCATION(0) out float4 ocol0;\n");
} }
else // D3D else // D3D
{ {
code.WriteFmt("cbuffer PSBlock : register(b0) {{\n" code.Write("cbuffer PSBlock : register(b0) {{\n"
" int4 position;\n" " int4 position;\n"
" float y_scale;\n" " float y_scale;\n"
" float gamma_rcp;\n" " float gamma_rcp;\n"
" float2 clamp_tb;\n" " float2 clamp_tb;\n"
" float3 filter_coefficients;\n" " float3 filter_coefficients;\n"
"}};\n" "}};\n"
"sampler samp0 : register(s0);\n" "sampler samp0 : register(s0);\n"
"Texture2DArray Tex0 : register(t0);\n"); "Texture2DArray Tex0 : register(t0);\n");
} }
// D3D does not have roundEven(), only round(), which is specified "to the nearest integer". // D3D does not have roundEven(), only round(), which is specified "to the nearest integer".
// This differs from the roundEven() behavior, but to get consistency across drivers in OpenGL // This differs from the roundEven() behavior, but to get consistency across drivers in OpenGL
// we need to use roundEven(). // we need to use roundEven().
if (api_type == APIType::D3D) if (api_type == APIType::D3D)
code.WriteFmt("#define roundEven(x) round(x)\n"); code.Write("#define roundEven(x) round(x)\n");
// Alpha channel in the copy is set to 1 the EFB format does not have an alpha channel. // Alpha channel in the copy is set to 1 the EFB format does not have an alpha channel.
code.WriteFmt("float4 RGBA8ToRGB8(float4 src)\n" code.Write("float4 RGBA8ToRGB8(float4 src)\n"
"{{\n" "{{\n"
" return float4(src.xyz, 1.0);\n" " return float4(src.xyz, 1.0);\n"
"}}\n" "}}\n"
"float4 RGBA8ToRGBA6(float4 src)\n" "float4 RGBA8ToRGBA6(float4 src)\n"
"{{\n" "{{\n"
" int4 val = int4(roundEven(src * 255.0)) >> 2;\n" " int4 val = int4(roundEven(src * 255.0)) >> 2;\n"
" return float4(val) / 63.0;\n" " return float4(val) / 63.0;\n"
"}}\n" "}}\n"
"float4 RGBA8ToRGB565(float4 src)\n" "float4 RGBA8ToRGB565(float4 src)\n"
"{{\n" "{{\n"
" int4 val = int4(roundEven(src * 255.0));\n" " int4 val = int4(roundEven(src * 255.0));\n"
" val = int4(val.r >> 3, val.g >> 2, val.b >> 3, 1);\n" " val = int4(val.r >> 3, val.g >> 2, val.b >> 3, 1);\n"
" return float4(val) / float4(31.0, 63.0, 31.0, 1.0);\n" " return float4(val) / float4(31.0, 63.0, 31.0, 1.0);\n"
"}}\n"); "}}\n");
} }
static void WriteSampleFunction(ShaderCode& code, const EFBCopyParams& params, APIType api_type) static void WriteSampleFunction(ShaderCode& code, const EFBCopyParams& params, APIType api_type)
@ -127,16 +127,16 @@ static void WriteSampleFunction(ShaderCode& code, const EFBCopyParams& params, A
switch (params.efb_format) switch (params.efb_format)
{ {
case PEControl::RGB8_Z24: case PEControl::RGB8_Z24:
code.WriteFmt("RGBA8ToRGB8("); code.Write("RGBA8ToRGB8(");
break; break;
case PEControl::RGBA6_Z24: case PEControl::RGBA6_Z24:
code.WriteFmt("RGBA8ToRGBA6("); code.Write("RGBA8ToRGBA6(");
break; break;
case PEControl::RGB565_Z16: case PEControl::RGB565_Z16:
code.WriteFmt("RGBA8ToRGB565("); code.Write("RGBA8ToRGB565(");
break; break;
default: default:
code.WriteFmt("("); code.Write("(");
break; break;
} }
} }
@ -144,63 +144,63 @@ static void WriteSampleFunction(ShaderCode& code, const EFBCopyParams& params, A
{ {
// Handle D3D depth inversion. // Handle D3D depth inversion.
if (!g_ActiveConfig.backend_info.bSupportsReversedDepthRange) if (!g_ActiveConfig.backend_info.bSupportsReversedDepthRange)
code.WriteFmt("1.0 - ("); code.Write("1.0 - (");
else else
code.WriteFmt("("); code.Write("(");
} }
if (api_type == APIType::OpenGL || api_type == APIType::Vulkan) if (api_type == APIType::OpenGL || api_type == APIType::Vulkan)
code.WriteFmt("texture(samp0, float3("); code.Write("texture(samp0, float3(");
else else
code.WriteFmt("Tex0.Sample(samp0, float3("); code.Write("Tex0.Sample(samp0, float3(");
code.WriteFmt("uv.x + float(xoffset) * pixel_size.x, "); code.Write("uv.x + float(xoffset) * pixel_size.x, ");
// Reverse the direction for OpenGL, since positive numbers are distance from the bottom row. // Reverse the direction for OpenGL, since positive numbers are distance from the bottom row.
if (yoffset != 0) if (yoffset != 0)
{ {
if (api_type == APIType::OpenGL) if (api_type == APIType::OpenGL)
code.WriteFmt("clamp(uv.y - float({}) * pixel_size.y, clamp_tb.x, clamp_tb.y)", yoffset); code.Write("clamp(uv.y - float({}) * pixel_size.y, clamp_tb.x, clamp_tb.y)", yoffset);
else else
code.WriteFmt("clamp(uv.y + float({}) * pixel_size.y, clamp_tb.x, clamp_tb.y)", yoffset); code.Write("clamp(uv.y + float({}) * pixel_size.y, clamp_tb.x, clamp_tb.y)", yoffset);
} }
else else
{ {
code.WriteFmt("uv.y"); code.Write("uv.y");
} }
code.WriteFmt(", 0.0)))"); code.Write(", 0.0)))");
}; };
// The copy filter applies to both color and depth copies. This has been verified on hardware. // The copy filter applies to both color and depth copies. This has been verified on hardware.
// The filter is only applied to the RGB channels, the alpha channel is left intact. // The filter is only applied to the RGB channels, the alpha channel is left intact.
code.WriteFmt("float4 SampleEFB(float2 uv, float2 pixel_size, int xoffset)\n" code.Write("float4 SampleEFB(float2 uv, float2 pixel_size, int xoffset)\n"
"{{\n"); "{{\n");
if (params.copy_filter) if (params.copy_filter)
{ {
code.WriteFmt(" float4 prev_row = "); code.Write(" float4 prev_row = ");
WriteSampleOp(-1); WriteSampleOp(-1);
code.WriteFmt(";\n" code.Write(";\n"
" float4 current_row = "); " float4 current_row = ");
WriteSampleOp(0); WriteSampleOp(0);
code.WriteFmt(";\n" code.Write(";\n"
" float4 next_row = "); " float4 next_row = ");
WriteSampleOp(1); WriteSampleOp(1);
code.WriteFmt(";\n" code.Write(";\n"
" return float4(min(prev_row.rgb * filter_coefficients[0] +\n" " return float4(min(prev_row.rgb * filter_coefficients[0] +\n"
" current_row.rgb * filter_coefficients[1] +\n" " current_row.rgb * filter_coefficients[1] +\n"
" next_row.rgb * filter_coefficients[2], \n" " next_row.rgb * filter_coefficients[2], \n"
" float3(1, 1, 1)), current_row.a);\n"); " float3(1, 1, 1)), current_row.a);\n");
} }
else else
{ {
code.WriteFmt(" float4 current_row = "); code.Write(" float4 current_row = ");
WriteSampleOp(0); WriteSampleOp(0);
code.WriteFmt(";\n" code.Write(";\n"
"return float4(min(current_row.rgb * filter_coefficients[1], float3(1, 1, 1)),\n" "return float4(min(current_row.rgb * filter_coefficients[1], float3(1, 1, 1)),\n"
" current_row.a);\n"); " current_row.a);\n");
} }
code.WriteFmt("}}\n"); code.Write("}}\n");
} }
// Block dimensions : widthStride, heightStride // Block dimensions : widthStride, heightStride
@ -213,101 +213,101 @@ static void WriteSwizzler(ShaderCode& code, const EFBCopyParams& params, EFBCopy
if (api_type == APIType::OpenGL || api_type == APIType::Vulkan) if (api_type == APIType::OpenGL || api_type == APIType::Vulkan)
{ {
code.WriteFmt("void main()\n" code.Write("void main()\n"
"{{\n" "{{\n"
" int2 sampleUv;\n" " int2 sampleUv;\n"
" int2 uv1 = int2(gl_FragCoord.xy);\n"); " int2 uv1 = int2(gl_FragCoord.xy);\n");
} }
else // D3D else // D3D
{ {
code.WriteFmt("void main(\n" code.Write("void main(\n"
" in float3 v_tex0 : TEXCOORD0,\n" " in float3 v_tex0 : TEXCOORD0,\n"
" in float4 rawpos : SV_Position,\n" " in float4 rawpos : SV_Position,\n"
" out float4 ocol0 : SV_Target)\n" " out float4 ocol0 : SV_Target)\n"
"{{\n" "{{\n"
" int2 sampleUv;\n" " int2 sampleUv;\n"
" int2 uv1 = int2(rawpos.xy);\n"); " int2 uv1 = int2(rawpos.xy);\n");
} }
const int blkW = TexDecoder_GetEFBCopyBlockWidthInTexels(format); const int blkW = TexDecoder_GetEFBCopyBlockWidthInTexels(format);
const int blkH = TexDecoder_GetEFBCopyBlockHeightInTexels(format); const int blkH = TexDecoder_GetEFBCopyBlockHeightInTexels(format);
int samples = GetEncodedSampleCount(format); int samples = GetEncodedSampleCount(format);
code.WriteFmt(" int x_block_position = (uv1.x >> {}) << {};\n", IntLog2(blkH * blkW / samples), code.Write(" int x_block_position = (uv1.x >> {}) << {};\n", IntLog2(blkH * blkW / samples),
IntLog2(blkW)); IntLog2(blkW));
code.WriteFmt(" int y_block_position = uv1.y << {};\n", IntLog2(blkH)); code.Write(" int y_block_position = uv1.y << {};\n", IntLog2(blkH));
if (samples == 1) if (samples == 1)
{ {
// With samples == 1, we write out pairs of blocks; one A8R8, one G8B8. // With samples == 1, we write out pairs of blocks; one A8R8, one G8B8.
code.WriteFmt(" bool first = (uv1.x & {}) == 0;\n", blkH * blkW / 2); code.Write(" bool first = (uv1.x & {}) == 0;\n", blkH * blkW / 2);
samples = 2; samples = 2;
} }
code.WriteFmt(" int offset_in_block = uv1.x & {};\n", (blkH * blkW / samples) - 1); code.Write(" int offset_in_block = uv1.x & {};\n", (blkH * blkW / samples) - 1);
code.WriteFmt(" int y_offset_in_block = offset_in_block >> {};\n", IntLog2(blkW / samples)); code.Write(" int y_offset_in_block = offset_in_block >> {};\n", IntLog2(blkW / samples));
code.WriteFmt(" int x_offset_in_block = (offset_in_block & {}) << {};\n", (blkW / samples) - 1, code.Write(" int x_offset_in_block = (offset_in_block & {}) << {};\n", (blkW / samples) - 1,
IntLog2(samples)); IntLog2(samples));
code.WriteFmt(" sampleUv.x = x_block_position + x_offset_in_block;\n" code.Write(" sampleUv.x = x_block_position + x_offset_in_block;\n"
" sampleUv.y = y_block_position + y_offset_in_block;\n"); " sampleUv.y = y_block_position + y_offset_in_block;\n");
// sampleUv is the sample position in (int)gx_coords // sampleUv is the sample position in (int)gx_coords
code.WriteFmt(" float2 uv0 = float2(sampleUv);\n"); code.Write(" float2 uv0 = float2(sampleUv);\n");
// Move to center of pixel // Move to center of pixel
code.WriteFmt(" uv0 += float2(0.5, 0.5);\n"); code.Write(" uv0 += float2(0.5, 0.5);\n");
// Scale by two if needed (also move to pixel borders // Scale by two if needed (also move to pixel borders
// so that linear filtering will average adjacent // so that linear filtering will average adjacent
// pixel) // pixel)
code.WriteFmt(" uv0 *= float(position.w);\n"); code.Write(" uv0 *= float(position.w);\n");
// Move to copied rect // Move to copied rect
code.WriteFmt(" uv0 += float2(position.xy);\n"); code.Write(" uv0 += float2(position.xy);\n");
// Normalize to [0:1] // Normalize to [0:1]
code.WriteFmt(" uv0 /= float2({}, {});\n", EFB_WIDTH, EFB_HEIGHT); code.Write(" uv0 /= float2({}, {});\n", EFB_WIDTH, EFB_HEIGHT);
// Apply the y scaling // Apply the y scaling
code.WriteFmt(" uv0 /= float2(1, y_scale);\n"); code.Write(" uv0 /= float2(1, y_scale);\n");
// OGL has to flip up and down // OGL has to flip up and down
if (api_type == APIType::OpenGL) if (api_type == APIType::OpenGL)
{ {
code.WriteFmt(" uv0.y = 1.0-uv0.y;\n"); code.Write(" uv0.y = 1.0-uv0.y;\n");
} }
code.WriteFmt(" float2 pixel_size = float2(position.w, position.w) / float2({}, {});\n", code.Write(" float2 pixel_size = float2(position.w, position.w) / float2({}, {});\n", EFB_WIDTH,
EFB_WIDTH, EFB_HEIGHT); EFB_HEIGHT);
} }
static void WriteSampleColor(ShaderCode& code, std::string_view color_comp, std::string_view dest, static void WriteSampleColor(ShaderCode& code, std::string_view color_comp, std::string_view dest,
int x_offset, APIType api_type, const EFBCopyParams& params) int x_offset, APIType api_type, const EFBCopyParams& params)
{ {
code.WriteFmt(" {} = SampleEFB(uv0, pixel_size, {}).{};\n", dest, x_offset, color_comp); code.Write(" {} = SampleEFB(uv0, pixel_size, {}).{};\n", dest, x_offset, color_comp);
} }
static void WriteColorToIntensity(ShaderCode& code, std::string_view src, std::string_view dest) static void WriteColorToIntensity(ShaderCode& code, std::string_view src, std::string_view dest)
{ {
if (!IntensityConstantAdded) if (!IntensityConstantAdded)
{ {
code.WriteFmt(" float4 IntensityConst = float4(0.257f,0.504f,0.098f,0.0625f);\n"); code.Write(" float4 IntensityConst = float4(0.257f,0.504f,0.098f,0.0625f);\n");
IntensityConstantAdded = true; IntensityConstantAdded = true;
} }
code.WriteFmt(" {} = dot(IntensityConst.rgb, {}.rgb);\n", dest, src); code.Write(" {} = dot(IntensityConst.rgb, {}.rgb);\n", dest, src);
// don't add IntensityConst.a yet, because doing it later is faster and uses less instructions, // don't add IntensityConst.a yet, because doing it later is faster and uses less instructions,
// due to vectorization // due to vectorization
} }
static void WriteToBitDepth(ShaderCode& code, u8 depth, std::string_view src, std::string_view dest) static void WriteToBitDepth(ShaderCode& code, u8 depth, std::string_view src, std::string_view dest)
{ {
code.WriteFmt(" {} = floor({} * 255.0 / exp2(8.0 - {}.0));\n", dest, src, depth); code.Write(" {} = floor({} * 255.0 / exp2(8.0 - {}.0));\n", dest, src, depth);
} }
static void WriteEncoderEnd(ShaderCode& code) static void WriteEncoderEnd(ShaderCode& code)
{ {
code.WriteFmt("}}\n"); code.Write("}}\n");
IntensityConstantAdded = false; IntensityConstantAdded = false;
} }
static void WriteI8Encoder(ShaderCode& code, APIType api_type, const EFBCopyParams& params) static void WriteI8Encoder(ShaderCode& code, APIType api_type, const EFBCopyParams& params)
{ {
WriteSwizzler(code, params, EFBCopyFormat::R8, api_type); WriteSwizzler(code, params, EFBCopyFormat::R8, api_type);
code.WriteFmt(" float3 texSample;\n"); code.Write(" float3 texSample;\n");
WriteSampleColor(code, "rgb", "texSample", 0, api_type, params); WriteSampleColor(code, "rgb", "texSample", 0, api_type, params);
WriteColorToIntensity(code, "texSample", "ocol0.b"); WriteColorToIntensity(code, "texSample", "ocol0.b");
@ -322,7 +322,7 @@ static void WriteI8Encoder(ShaderCode& code, APIType api_type, const EFBCopyPara
WriteColorToIntensity(code, "texSample", "ocol0.a"); WriteColorToIntensity(code, "texSample", "ocol0.a");
// See WriteColorToIntensity // See WriteColorToIntensity
code.WriteFmt(" ocol0.rgba += IntensityConst.aaaa;\n"); code.Write(" ocol0.rgba += IntensityConst.aaaa;\n");
WriteEncoderEnd(code); WriteEncoderEnd(code);
} }
@ -330,9 +330,9 @@ static void WriteI8Encoder(ShaderCode& code, APIType api_type, const EFBCopyPara
static void WriteI4Encoder(ShaderCode& code, APIType api_type, const EFBCopyParams& params) static void WriteI4Encoder(ShaderCode& code, APIType api_type, const EFBCopyParams& params)
{ {
WriteSwizzler(code, params, EFBCopyFormat::R4, api_type); WriteSwizzler(code, params, EFBCopyFormat::R4, api_type);
code.WriteFmt(" float3 texSample;\n" code.Write(" float3 texSample;\n"
" float4 color0;\n" " float4 color0;\n"
" float4 color1;\n"); " float4 color1;\n");
WriteSampleColor(code, "rgb", "texSample", 0, api_type, params); WriteSampleColor(code, "rgb", "texSample", 0, api_type, params);
WriteColorToIntensity(code, "texSample", "color0.b"); WriteColorToIntensity(code, "texSample", "color0.b");
@ -358,30 +358,30 @@ static void WriteI4Encoder(ShaderCode& code, APIType api_type, const EFBCopyPara
WriteSampleColor(code, "rgb", "texSample", 7, api_type, params); WriteSampleColor(code, "rgb", "texSample", 7, api_type, params);
WriteColorToIntensity(code, "texSample", "color1.a"); WriteColorToIntensity(code, "texSample", "color1.a");
code.WriteFmt(" color0.rgba += IntensityConst.aaaa;\n" code.Write(" color0.rgba += IntensityConst.aaaa;\n"
" color1.rgba += IntensityConst.aaaa;\n"); " color1.rgba += IntensityConst.aaaa;\n");
WriteToBitDepth(code, 4, "color0", "color0"); WriteToBitDepth(code, 4, "color0", "color0");
WriteToBitDepth(code, 4, "color1", "color1"); WriteToBitDepth(code, 4, "color1", "color1");
code.WriteFmt(" ocol0 = (color0 * 16.0 + color1) / 255.0;\n"); code.Write(" ocol0 = (color0 * 16.0 + color1) / 255.0;\n");
WriteEncoderEnd(code); WriteEncoderEnd(code);
} }
static void WriteIA8Encoder(ShaderCode& code, APIType api_type, const EFBCopyParams& params) static void WriteIA8Encoder(ShaderCode& code, APIType api_type, const EFBCopyParams& params)
{ {
WriteSwizzler(code, params, EFBCopyFormat::RA8, api_type); WriteSwizzler(code, params, EFBCopyFormat::RA8, api_type);
code.WriteFmt(" float4 texSample;\n"); code.Write(" float4 texSample;\n");
WriteSampleColor(code, "rgba", "texSample", 0, api_type, params); WriteSampleColor(code, "rgba", "texSample", 0, api_type, params);
code.WriteFmt(" ocol0.b = texSample.a;\n"); code.Write(" ocol0.b = texSample.a;\n");
WriteColorToIntensity(code, "texSample", "ocol0.g"); WriteColorToIntensity(code, "texSample", "ocol0.g");
WriteSampleColor(code, "rgba", "texSample", 1, api_type, params); WriteSampleColor(code, "rgba", "texSample", 1, api_type, params);
code.WriteFmt(" ocol0.r = texSample.a;\n"); code.Write(" ocol0.r = texSample.a;\n");
WriteColorToIntensity(code, "texSample", "ocol0.a"); WriteColorToIntensity(code, "texSample", "ocol0.a");
code.WriteFmt(" ocol0.ga += IntensityConst.aa;\n"); code.Write(" ocol0.ga += IntensityConst.aa;\n");
WriteEncoderEnd(code); WriteEncoderEnd(code);
} }
@ -389,57 +389,57 @@ static void WriteIA8Encoder(ShaderCode& code, APIType api_type, const EFBCopyPar
static void WriteIA4Encoder(ShaderCode& code, APIType api_type, const EFBCopyParams& params) static void WriteIA4Encoder(ShaderCode& code, APIType api_type, const EFBCopyParams& params)
{ {
WriteSwizzler(code, params, EFBCopyFormat::RA4, api_type); WriteSwizzler(code, params, EFBCopyFormat::RA4, api_type);
code.WriteFmt(" float4 texSample;\n" code.Write(" float4 texSample;\n"
" float4 color0;\n" " float4 color0;\n"
" float4 color1;\n"); " float4 color1;\n");
WriteSampleColor(code, "rgba", "texSample", 0, api_type, params); WriteSampleColor(code, "rgba", "texSample", 0, api_type, params);
code.WriteFmt(" color0.b = texSample.a;\n"); code.Write(" color0.b = texSample.a;\n");
WriteColorToIntensity(code, "texSample", "color1.b"); WriteColorToIntensity(code, "texSample", "color1.b");
WriteSampleColor(code, "rgba", "texSample", 1, api_type, params); WriteSampleColor(code, "rgba", "texSample", 1, api_type, params);
code.WriteFmt(" color0.g = texSample.a;\n"); code.Write(" color0.g = texSample.a;\n");
WriteColorToIntensity(code, "texSample", "color1.g"); WriteColorToIntensity(code, "texSample", "color1.g");
WriteSampleColor(code, "rgba", "texSample", 2, api_type, params); WriteSampleColor(code, "rgba", "texSample", 2, api_type, params);
code.WriteFmt(" color0.r = texSample.a;\n"); code.Write(" color0.r = texSample.a;\n");
WriteColorToIntensity(code, "texSample", "color1.r"); WriteColorToIntensity(code, "texSample", "color1.r");
WriteSampleColor(code, "rgba", "texSample", 3, api_type, params); WriteSampleColor(code, "rgba", "texSample", 3, api_type, params);
code.WriteFmt(" color0.a = texSample.a;\n"); code.Write(" color0.a = texSample.a;\n");
WriteColorToIntensity(code, "texSample", "color1.a"); WriteColorToIntensity(code, "texSample", "color1.a");
code.WriteFmt(" color1.rgba += IntensityConst.aaaa;\n"); code.Write(" color1.rgba += IntensityConst.aaaa;\n");
WriteToBitDepth(code, 4, "color0", "color0"); WriteToBitDepth(code, 4, "color0", "color0");
WriteToBitDepth(code, 4, "color1", "color1"); WriteToBitDepth(code, 4, "color1", "color1");
code.WriteFmt(" ocol0 = (color0 * 16.0 + color1) / 255.0;\n"); code.Write(" ocol0 = (color0 * 16.0 + color1) / 255.0;\n");
WriteEncoderEnd(code); WriteEncoderEnd(code);
} }
static void WriteRGB565Encoder(ShaderCode& code, APIType api_type, const EFBCopyParams& params) static void WriteRGB565Encoder(ShaderCode& code, APIType api_type, const EFBCopyParams& params)
{ {
WriteSwizzler(code, params, EFBCopyFormat::RGB565, api_type); WriteSwizzler(code, params, EFBCopyFormat::RGB565, api_type);
code.WriteFmt(" float3 texSample0;\n" code.Write(" float3 texSample0;\n"
" float3 texSample1;\n"); " float3 texSample1;\n");
WriteSampleColor(code, "rgb", "texSample0", 0, api_type, params); WriteSampleColor(code, "rgb", "texSample0", 0, api_type, params);
WriteSampleColor(code, "rgb", "texSample1", 1, api_type, params); WriteSampleColor(code, "rgb", "texSample1", 1, api_type, params);
code.WriteFmt(" float2 texRs = float2(texSample0.r, texSample1.r);\n" code.Write(" float2 texRs = float2(texSample0.r, texSample1.r);\n"
" float2 texGs = float2(texSample0.g, texSample1.g);\n" " float2 texGs = float2(texSample0.g, texSample1.g);\n"
" float2 texBs = float2(texSample0.b, texSample1.b);\n"); " float2 texBs = float2(texSample0.b, texSample1.b);\n");
WriteToBitDepth(code, 6, "texGs", "float2 gInt"); WriteToBitDepth(code, 6, "texGs", "float2 gInt");
code.WriteFmt(" float2 gUpper = floor(gInt / 8.0);\n" code.Write(" float2 gUpper = floor(gInt / 8.0);\n"
" float2 gLower = gInt - gUpper * 8.0;\n"); " float2 gLower = gInt - gUpper * 8.0;\n");
WriteToBitDepth(code, 5, "texRs", "ocol0.br"); WriteToBitDepth(code, 5, "texRs", "ocol0.br");
code.WriteFmt(" ocol0.br = ocol0.br * 8.0 + gUpper;\n"); code.Write(" ocol0.br = ocol0.br * 8.0 + gUpper;\n");
WriteToBitDepth(code, 5, "texBs", "ocol0.ga"); WriteToBitDepth(code, 5, "texBs", "ocol0.ga");
code.WriteFmt(" ocol0.ga = ocol0.ga + gLower * 32.0;\n"); code.Write(" ocol0.ga = ocol0.ga + gLower * 32.0;\n");
code.WriteFmt(" ocol0 = ocol0 / 255.0;\n"); code.Write(" ocol0 = ocol0 / 255.0;\n");
WriteEncoderEnd(code); WriteEncoderEnd(code);
} }
@ -447,63 +447,63 @@ static void WriteRGB5A3Encoder(ShaderCode& code, APIType api_type, const EFBCopy
{ {
WriteSwizzler(code, params, EFBCopyFormat::RGB5A3, api_type); WriteSwizzler(code, params, EFBCopyFormat::RGB5A3, api_type);
code.WriteFmt(" float4 texSample;\n" code.Write(" float4 texSample;\n"
" float color0;\n" " float color0;\n"
" float gUpper;\n" " float gUpper;\n"
" float gLower;\n"); " float gLower;\n");
WriteSampleColor(code, "rgba", "texSample", 0, api_type, params); WriteSampleColor(code, "rgba", "texSample", 0, api_type, params);
// 0.8784 = 224 / 255 which is the maximum alpha value that can be represented in 3 bits // 0.8784 = 224 / 255 which is the maximum alpha value that can be represented in 3 bits
code.WriteFmt("if(texSample.a > 0.878f) {{\n"); code.Write("if(texSample.a > 0.878f) {{\n");
WriteToBitDepth(code, 5, "texSample.g", "color0"); WriteToBitDepth(code, 5, "texSample.g", "color0");
code.WriteFmt(" gUpper = floor(color0 / 8.0);\n" code.Write(" gUpper = floor(color0 / 8.0);\n"
" gLower = color0 - gUpper * 8.0;\n"); " gLower = color0 - gUpper * 8.0;\n");
WriteToBitDepth(code, 5, "texSample.r", "ocol0.b"); WriteToBitDepth(code, 5, "texSample.r", "ocol0.b");
code.WriteFmt(" ocol0.b = ocol0.b * 4.0 + gUpper + 128.0;\n"); code.Write(" ocol0.b = ocol0.b * 4.0 + gUpper + 128.0;\n");
WriteToBitDepth(code, 5, "texSample.b", "ocol0.g"); WriteToBitDepth(code, 5, "texSample.b", "ocol0.g");
code.WriteFmt(" ocol0.g = ocol0.g + gLower * 32.0;\n"); code.Write(" ocol0.g = ocol0.g + gLower * 32.0;\n");
code.WriteFmt("}} else {{\n"); code.Write("}} else {{\n");
WriteToBitDepth(code, 4, "texSample.r", "ocol0.b"); WriteToBitDepth(code, 4, "texSample.r", "ocol0.b");
WriteToBitDepth(code, 4, "texSample.b", "ocol0.g"); WriteToBitDepth(code, 4, "texSample.b", "ocol0.g");
WriteToBitDepth(code, 3, "texSample.a", "color0"); WriteToBitDepth(code, 3, "texSample.a", "color0");
code.WriteFmt("ocol0.b = ocol0.b + color0 * 16.0;\n"); code.Write("ocol0.b = ocol0.b + color0 * 16.0;\n");
WriteToBitDepth(code, 4, "texSample.g", "color0"); WriteToBitDepth(code, 4, "texSample.g", "color0");
code.WriteFmt("ocol0.g = ocol0.g + color0 * 16.0;\n"); code.Write("ocol0.g = ocol0.g + color0 * 16.0;\n");
code.WriteFmt("}}\n"); code.Write("}}\n");
WriteSampleColor(code, "rgba", "texSample", 1, api_type, params); WriteSampleColor(code, "rgba", "texSample", 1, api_type, params);
code.WriteFmt("if(texSample.a > 0.878f) {{\n"); code.Write("if(texSample.a > 0.878f) {{\n");
WriteToBitDepth(code, 5, "texSample.g", "color0"); WriteToBitDepth(code, 5, "texSample.g", "color0");
code.WriteFmt(" gUpper = floor(color0 / 8.0);\n" code.Write(" gUpper = floor(color0 / 8.0);\n"
" gLower = color0 - gUpper * 8.0;\n"); " gLower = color0 - gUpper * 8.0;\n");
WriteToBitDepth(code, 5, "texSample.r", "ocol0.r"); WriteToBitDepth(code, 5, "texSample.r", "ocol0.r");
code.WriteFmt(" ocol0.r = ocol0.r * 4.0 + gUpper + 128.0;\n"); code.Write(" ocol0.r = ocol0.r * 4.0 + gUpper + 128.0;\n");
WriteToBitDepth(code, 5, "texSample.b", "ocol0.a"); WriteToBitDepth(code, 5, "texSample.b", "ocol0.a");
code.WriteFmt(" ocol0.a = ocol0.a + gLower * 32.0;\n"); code.Write(" ocol0.a = ocol0.a + gLower * 32.0;\n");
code.WriteFmt("}} else {{\n"); code.Write("}} else {{\n");
WriteToBitDepth(code, 4, "texSample.r", "ocol0.r"); WriteToBitDepth(code, 4, "texSample.r", "ocol0.r");
WriteToBitDepth(code, 4, "texSample.b", "ocol0.a"); WriteToBitDepth(code, 4, "texSample.b", "ocol0.a");
WriteToBitDepth(code, 3, "texSample.a", "color0"); WriteToBitDepth(code, 3, "texSample.a", "color0");
code.WriteFmt("ocol0.r = ocol0.r + color0 * 16.0;\n"); code.Write("ocol0.r = ocol0.r + color0 * 16.0;\n");
WriteToBitDepth(code, 4, "texSample.g", "color0"); WriteToBitDepth(code, 4, "texSample.g", "color0");
code.WriteFmt("ocol0.a = ocol0.a + color0 * 16.0;\n"); code.Write("ocol0.a = ocol0.a + color0 * 16.0;\n");
code.WriteFmt("}}\n"); code.Write("}}\n");
code.WriteFmt(" ocol0 = ocol0 / 255.0;\n"); code.Write(" ocol0 = ocol0 / 255.0;\n");
WriteEncoderEnd(code); WriteEncoderEnd(code);
} }
@ -511,23 +511,23 @@ static void WriteRGBA8Encoder(ShaderCode& code, APIType api_type, const EFBCopyP
{ {
WriteSwizzler(code, params, EFBCopyFormat::RGBA8, api_type); WriteSwizzler(code, params, EFBCopyFormat::RGBA8, api_type);
code.WriteFmt(" float4 texSample;\n" code.Write(" float4 texSample;\n"
" float4 color0;\n" " float4 color0;\n"
" float4 color1;\n"); " float4 color1;\n");
WriteSampleColor(code, "rgba", "texSample", 0, api_type, params); WriteSampleColor(code, "rgba", "texSample", 0, api_type, params);
code.WriteFmt(" color0.b = texSample.a;\n" code.Write(" color0.b = texSample.a;\n"
" color0.g = texSample.r;\n" " color0.g = texSample.r;\n"
" color1.b = texSample.g;\n" " color1.b = texSample.g;\n"
" color1.g = texSample.b;\n"); " color1.g = texSample.b;\n");
WriteSampleColor(code, "rgba", "texSample", 1, api_type, params); WriteSampleColor(code, "rgba", "texSample", 1, api_type, params);
code.WriteFmt(" color0.r = texSample.a;\n" code.Write(" color0.r = texSample.a;\n"
" color0.a = texSample.r;\n" " color0.a = texSample.r;\n"
" color1.r = texSample.g;\n" " color1.r = texSample.g;\n"
" color1.a = texSample.b;\n"); " color1.a = texSample.b;\n");
code.WriteFmt(" ocol0 = first ? color0 : color1;\n"); code.Write(" ocol0 = first ? color0 : color1;\n");
WriteEncoderEnd(code); WriteEncoderEnd(code);
} }
@ -536,8 +536,8 @@ static void WriteC4Encoder(ShaderCode& code, std::string_view comp, APIType api_
const EFBCopyParams& params) const EFBCopyParams& params)
{ {
WriteSwizzler(code, params, EFBCopyFormat::R4, api_type); WriteSwizzler(code, params, EFBCopyFormat::R4, api_type);
code.WriteFmt(" float4 color0;\n" code.Write(" float4 color0;\n"
" float4 color1;\n"); " float4 color1;\n");
WriteSampleColor(code, comp, "color0.b", 0, api_type, params); WriteSampleColor(code, comp, "color0.b", 0, api_type, params);
WriteSampleColor(code, comp, "color1.b", 1, api_type, params); WriteSampleColor(code, comp, "color1.b", 1, api_type, params);
@ -551,7 +551,7 @@ static void WriteC4Encoder(ShaderCode& code, std::string_view comp, APIType api_
WriteToBitDepth(code, 4, "color0", "color0"); WriteToBitDepth(code, 4, "color0", "color0");
WriteToBitDepth(code, 4, "color1", "color1"); WriteToBitDepth(code, 4, "color1", "color1");
code.WriteFmt(" ocol0 = (color0 * 16.0 + color1) / 255.0;\n"); code.Write(" ocol0 = (color0 * 16.0 + color1) / 255.0;\n");
WriteEncoderEnd(code); WriteEncoderEnd(code);
} }
@ -572,30 +572,30 @@ static void WriteCC4Encoder(ShaderCode& code, std::string_view comp, APIType api
const EFBCopyParams& params) const EFBCopyParams& params)
{ {
WriteSwizzler(code, params, EFBCopyFormat::RA4, api_type); WriteSwizzler(code, params, EFBCopyFormat::RA4, api_type);
code.WriteFmt(" float2 texSample;\n" code.Write(" float2 texSample;\n"
" float4 color0;\n" " float4 color0;\n"
" float4 color1;\n"); " float4 color1;\n");
WriteSampleColor(code, comp, "texSample", 0, api_type, params); WriteSampleColor(code, comp, "texSample", 0, api_type, params);
code.WriteFmt(" color0.b = texSample.x;\n" code.Write(" color0.b = texSample.x;\n"
" color1.b = texSample.y;\n"); " color1.b = texSample.y;\n");
WriteSampleColor(code, comp, "texSample", 1, api_type, params); WriteSampleColor(code, comp, "texSample", 1, api_type, params);
code.WriteFmt(" color0.g = texSample.x;\n" code.Write(" color0.g = texSample.x;\n"
" color1.g = texSample.y;\n"); " color1.g = texSample.y;\n");
WriteSampleColor(code, comp, "texSample", 2, api_type, params); WriteSampleColor(code, comp, "texSample", 2, api_type, params);
code.WriteFmt(" color0.r = texSample.x;\n" code.Write(" color0.r = texSample.x;\n"
" color1.r = texSample.y;\n"); " color1.r = texSample.y;\n");
WriteSampleColor(code, comp, "texSample", 3, api_type, params); WriteSampleColor(code, comp, "texSample", 3, api_type, params);
code.WriteFmt(" color0.a = texSample.x;\n" code.Write(" color0.a = texSample.x;\n"
" color1.a = texSample.y;\n"); " color1.a = texSample.y;\n");
WriteToBitDepth(code, 4, "color0", "color0"); WriteToBitDepth(code, 4, "color0", "color0");
WriteToBitDepth(code, 4, "color1", "color1"); WriteToBitDepth(code, 4, "color1", "color1");
code.WriteFmt(" ocol0 = (color0 * 16.0 + color1) / 255.0;\n"); code.Write(" ocol0 = (color0 * 16.0 + color1) / 255.0;\n");
WriteEncoderEnd(code); WriteEncoderEnd(code);
} }
@ -615,19 +615,19 @@ static void WriteZ8Encoder(ShaderCode& code, std::string_view multiplier, APITyp
{ {
WriteSwizzler(code, params, EFBCopyFormat::G8, api_type); WriteSwizzler(code, params, EFBCopyFormat::G8, api_type);
code.WriteFmt(" float depth;\n"); code.Write(" float depth;\n");
WriteSampleColor(code, "r", "depth", 0, api_type, params); WriteSampleColor(code, "r", "depth", 0, api_type, params);
code.WriteFmt("ocol0.b = frac(depth * {});\n", multiplier); code.Write("ocol0.b = frac(depth * {});\n", multiplier);
WriteSampleColor(code, "r", "depth", 1, api_type, params); WriteSampleColor(code, "r", "depth", 1, api_type, params);
code.WriteFmt("ocol0.g = frac(depth * {});\n", multiplier); code.Write("ocol0.g = frac(depth * {});\n", multiplier);
WriteSampleColor(code, "r", "depth", 2, api_type, params); WriteSampleColor(code, "r", "depth", 2, api_type, params);
code.WriteFmt("ocol0.r = frac(depth * {});\n", multiplier); code.Write("ocol0.r = frac(depth * {});\n", multiplier);
WriteSampleColor(code, "r", "depth", 3, api_type, params); WriteSampleColor(code, "r", "depth", 3, api_type, params);
code.WriteFmt("ocol0.a = frac(depth * {});\n", multiplier); code.Write("ocol0.a = frac(depth * {});\n", multiplier);
WriteEncoderEnd(code); WriteEncoderEnd(code);
} }
@ -636,30 +636,30 @@ static void WriteZ16Encoder(ShaderCode& code, APIType api_type, const EFBCopyPar
{ {
WriteSwizzler(code, params, EFBCopyFormat::RA8, api_type); WriteSwizzler(code, params, EFBCopyFormat::RA8, api_type);
code.WriteFmt(" float depth;\n" code.Write(" float depth;\n"
" float3 expanded;\n"); " float3 expanded;\n");
// Byte order is reversed // Byte order is reversed
WriteSampleColor(code, "r", "depth", 0, api_type, params); WriteSampleColor(code, "r", "depth", 0, api_type, params);
code.WriteFmt(" depth *= 16777216.0;\n" code.Write(" depth *= 16777216.0;\n"
" expanded.r = floor(depth / (256.0 * 256.0));\n" " expanded.r = floor(depth / (256.0 * 256.0));\n"
" depth -= expanded.r * 256.0 * 256.0;\n" " depth -= expanded.r * 256.0 * 256.0;\n"
" expanded.g = floor(depth / 256.0);\n"); " expanded.g = floor(depth / 256.0);\n");
code.WriteFmt(" ocol0.b = expanded.g / 255.0;\n" code.Write(" ocol0.b = expanded.g / 255.0;\n"
" ocol0.g = expanded.r / 255.0;\n"); " ocol0.g = expanded.r / 255.0;\n");
WriteSampleColor(code, "r", "depth", 1, api_type, params); WriteSampleColor(code, "r", "depth", 1, api_type, params);
code.WriteFmt(" depth *= 16777216.0;\n" code.Write(" depth *= 16777216.0;\n"
" expanded.r = floor(depth / (256.0 * 256.0));\n" " expanded.r = floor(depth / (256.0 * 256.0));\n"
" depth -= expanded.r * 256.0 * 256.0;\n" " depth -= expanded.r * 256.0 * 256.0;\n"
" expanded.g = floor(depth / 256.0);\n"); " expanded.g = floor(depth / 256.0);\n");
code.WriteFmt(" ocol0.r = expanded.g / 255.0;\n" code.Write(" ocol0.r = expanded.g / 255.0;\n"
" ocol0.a = expanded.r / 255.0;\n"); " ocol0.a = expanded.r / 255.0;\n");
WriteEncoderEnd(code); WriteEncoderEnd(code);
} }
@ -668,34 +668,34 @@ static void WriteZ16LEncoder(ShaderCode& code, APIType api_type, const EFBCopyPa
{ {
WriteSwizzler(code, params, EFBCopyFormat::GB8, api_type); WriteSwizzler(code, params, EFBCopyFormat::GB8, api_type);
code.WriteFmt(" float depth;\n" code.Write(" float depth;\n"
" float3 expanded;\n"); " float3 expanded;\n");
// Byte order is reversed // Byte order is reversed
WriteSampleColor(code, "r", "depth", 0, api_type, params); WriteSampleColor(code, "r", "depth", 0, api_type, params);
code.WriteFmt(" depth *= 16777216.0;\n" code.Write(" depth *= 16777216.0;\n"
" expanded.r = floor(depth / (256.0 * 256.0));\n" " expanded.r = floor(depth / (256.0 * 256.0));\n"
" depth -= expanded.r * 256.0 * 256.0;\n" " depth -= expanded.r * 256.0 * 256.0;\n"
" expanded.g = floor(depth / 256.0);\n" " expanded.g = floor(depth / 256.0);\n"
" depth -= expanded.g * 256.0;\n" " depth -= expanded.g * 256.0;\n"
" expanded.b = depth;\n"); " expanded.b = depth;\n");
code.WriteFmt(" ocol0.b = expanded.b / 255.0;\n" code.Write(" ocol0.b = expanded.b / 255.0;\n"
" ocol0.g = expanded.g / 255.0;\n"); " ocol0.g = expanded.g / 255.0;\n");
WriteSampleColor(code, "r", "depth", 1, api_type, params); WriteSampleColor(code, "r", "depth", 1, api_type, params);
code.WriteFmt(" depth *= 16777216.0;\n" code.Write(" depth *= 16777216.0;\n"
" expanded.r = floor(depth / (256.0 * 256.0));\n" " expanded.r = floor(depth / (256.0 * 256.0));\n"
" depth -= expanded.r * 256.0 * 256.0;\n" " depth -= expanded.r * 256.0 * 256.0;\n"
" expanded.g = floor(depth / 256.0);\n" " expanded.g = floor(depth / 256.0);\n"
" depth -= expanded.g * 256.0;\n" " depth -= expanded.g * 256.0;\n"
" expanded.b = depth;\n"); " expanded.b = depth;\n");
code.WriteFmt(" ocol0.r = expanded.b / 255.0;\n" code.Write(" ocol0.r = expanded.b / 255.0;\n"
" ocol0.a = expanded.g / 255.0;\n"); " ocol0.a = expanded.g / 255.0;\n");
WriteEncoderEnd(code); WriteEncoderEnd(code);
} }
@ -704,38 +704,38 @@ static void WriteZ24Encoder(ShaderCode& code, APIType api_type, const EFBCopyPar
{ {
WriteSwizzler(code, params, EFBCopyFormat::RGBA8, api_type); WriteSwizzler(code, params, EFBCopyFormat::RGBA8, api_type);
code.WriteFmt(" float depth0;\n" code.Write(" float depth0;\n"
" float depth1;\n" " float depth1;\n"
" float3 expanded0;\n" " float3 expanded0;\n"
" float3 expanded1;\n"); " float3 expanded1;\n");
WriteSampleColor(code, "r", "depth0", 0, api_type, params); WriteSampleColor(code, "r", "depth0", 0, api_type, params);
WriteSampleColor(code, "r", "depth1", 1, api_type, params); WriteSampleColor(code, "r", "depth1", 1, api_type, params);
for (int i = 0; i < 2; i++) for (int i = 0; i < 2; i++)
{ {
code.WriteFmt(" depth{} *= 16777216.0;\n", i); code.Write(" depth{} *= 16777216.0;\n", i);
code.WriteFmt(" expanded{}.r = floor(depth{} / (256.0 * 256.0));\n", i, i); code.Write(" expanded{}.r = floor(depth{} / (256.0 * 256.0));\n", i, i);
code.WriteFmt(" depth{} -= expanded{}.r * 256.0 * 256.0;\n", i, i); code.Write(" depth{} -= expanded{}.r * 256.0 * 256.0;\n", i, i);
code.WriteFmt(" expanded{}.g = floor(depth{} / 256.0);\n", i, i); code.Write(" expanded{}.g = floor(depth{} / 256.0);\n", i, i);
code.WriteFmt(" depth{} -= expanded{}.g * 256.0;\n", i, i); code.Write(" depth{} -= expanded{}.g * 256.0;\n", i, i);
code.WriteFmt(" expanded{}.b = depth{};\n", i, i); code.Write(" expanded{}.b = depth{};\n", i, i);
} }
code.WriteFmt(" if (!first) {{\n"); code.Write(" if (!first) {{\n");
// Upper 16 // Upper 16
code.WriteFmt(" ocol0.b = expanded0.g / 255.0;\n" code.Write(" ocol0.b = expanded0.g / 255.0;\n"
" ocol0.g = expanded0.b / 255.0;\n" " ocol0.g = expanded0.b / 255.0;\n"
" ocol0.r = expanded1.g / 255.0;\n" " ocol0.r = expanded1.g / 255.0;\n"
" ocol0.a = expanded1.b / 255.0;\n" " ocol0.a = expanded1.b / 255.0;\n"
" }} else {{\n"); " }} else {{\n");
// Lower 8 // Lower 8
code.WriteFmt(" ocol0.b = 1.0;\n" code.Write(" ocol0.b = 1.0;\n"
" ocol0.g = expanded0.r / 255.0;\n" " ocol0.g = expanded0.r / 255.0;\n"
" ocol0.r = 1.0;\n" " ocol0.r = 1.0;\n"
" ocol0.a = expanded1.r / 255.0;\n" " ocol0.a = expanded1.r / 255.0;\n"
" }}\n"); " }}\n");
WriteEncoderEnd(code); WriteEncoderEnd(code);
} }
@ -744,23 +744,23 @@ static void WriteXFBEncoder(ShaderCode& code, APIType api_type, const EFBCopyPar
{ {
WriteSwizzler(code, params, EFBCopyFormat::XFB, api_type); WriteSwizzler(code, params, EFBCopyFormat::XFB, api_type);
code.WriteFmt("float3 color0, color1;\n"); code.Write("float3 color0, color1;\n");
WriteSampleColor(code, "rgb", "color0", 0, api_type, params); WriteSampleColor(code, "rgb", "color0", 0, api_type, params);
WriteSampleColor(code, "rgb", "color1", 1, api_type, params); WriteSampleColor(code, "rgb", "color1", 1, api_type, params);
// Gamma is only applied to XFB copies. // Gamma is only applied to XFB copies.
code.WriteFmt(" color0 = pow(color0, float3(gamma_rcp, gamma_rcp, gamma_rcp));\n" code.Write(" color0 = pow(color0, float3(gamma_rcp, gamma_rcp, gamma_rcp));\n"
" color1 = pow(color1, float3(gamma_rcp, gamma_rcp, gamma_rcp));\n"); " color1 = pow(color1, float3(gamma_rcp, gamma_rcp, gamma_rcp));\n");
// Convert to YUV. // Convert to YUV.
code.WriteFmt(" const float3 y_const = float3(0.257, 0.504, 0.098);\n" code.Write(" const float3 y_const = float3(0.257, 0.504, 0.098);\n"
" const float3 u_const = float3(-0.148, -0.291, 0.439);\n" " const float3 u_const = float3(-0.148, -0.291, 0.439);\n"
" const float3 v_const = float3(0.439, -0.368, -0.071);\n" " const float3 v_const = float3(0.439, -0.368, -0.071);\n"
" float3 average = (color0 + color1) * 0.5;\n" " float3 average = (color0 + color1) * 0.5;\n"
" ocol0.b = dot(color0, y_const) + 0.0625;\n" " ocol0.b = dot(color0, y_const) + 0.0625;\n"
" ocol0.g = dot(average, u_const) + 0.5;\n" " ocol0.g = dot(average, u_const) + 0.5;\n"
" ocol0.r = dot(color1, y_const) + 0.0625;\n" " ocol0.r = dot(color1, y_const) + 0.0625;\n"
" ocol0.a = dot(average, v_const) + 0.5;\n"); " ocol0.a = dot(average, v_const) + 0.5;\n");
WriteEncoderEnd(code); WriteEncoderEnd(code);
} }

View File

@ -32,23 +32,23 @@ static void WriteHeader(APIType api_type, ShaderCode& out)
{ {
if (api_type == APIType::D3D) if (api_type == APIType::D3D)
{ {
out.WriteFmt("cbuffer PSBlock : register(b0) {{\n" out.Write("cbuffer PSBlock : register(b0) {{\n"
" float2 src_offset, src_size;\n" " float2 src_offset, src_size;\n"
" float3 filter_coefficients;\n" " float3 filter_coefficients;\n"
" float gamma_rcp;\n" " float gamma_rcp;\n"
" float2 clamp_tb;\n" " float2 clamp_tb;\n"
" float pixel_height;\n" " float pixel_height;\n"
"}};\n\n"); "}};\n\n");
} }
else if (api_type == APIType::OpenGL || api_type == APIType::Vulkan) else if (api_type == APIType::OpenGL || api_type == APIType::Vulkan)
{ {
out.WriteFmt("UBO_BINDING(std140, 1) uniform PSBlock {{\n" out.Write("UBO_BINDING(std140, 1) uniform PSBlock {{\n"
" float2 src_offset, src_size;\n" " float2 src_offset, src_size;\n"
" float3 filter_coefficients;\n" " float3 filter_coefficients;\n"
" float gamma_rcp;\n" " float gamma_rcp;\n"
" float2 clamp_tb;\n" " float2 clamp_tb;\n"
" float pixel_height;\n" " float pixel_height;\n"
"}};\n"); "}};\n");
} }
} }
@ -59,35 +59,35 @@ ShaderCode GenerateVertexShader(APIType api_type)
if (api_type == APIType::D3D) if (api_type == APIType::D3D)
{ {
out.WriteFmt("void main(in uint id : SV_VertexID, out float3 v_tex0 : TEXCOORD0,\n" out.Write("void main(in uint id : SV_VertexID, out float3 v_tex0 : TEXCOORD0,\n"
" out float4 opos : SV_Position) {{\n"); " out float4 opos : SV_Position) {{\n");
} }
else if (api_type == APIType::OpenGL || api_type == APIType::Vulkan) else if (api_type == APIType::OpenGL || api_type == APIType::Vulkan)
{ {
if (g_ActiveConfig.backend_info.bSupportsGeometryShaders) if (g_ActiveConfig.backend_info.bSupportsGeometryShaders)
{ {
out.WriteFmt("VARYING_LOCATION(0) out VertexData {{\n" out.Write("VARYING_LOCATION(0) out VertexData {{\n"
" float3 v_tex0;\n" " float3 v_tex0;\n"
"}};\n"); "}};\n");
} }
else else
{ {
out.WriteFmt("VARYING_LOCATION(0) out float3 v_tex0;\n"); out.Write("VARYING_LOCATION(0) out float3 v_tex0;\n");
} }
out.WriteFmt("#define id gl_VertexID\n" out.Write("#define id gl_VertexID\n"
"#define opos gl_Position\n" "#define opos gl_Position\n"
"void main() {{\n"); "void main() {{\n");
} }
out.WriteFmt(" v_tex0 = float3(float((id << 1) & 2), float(id & 2), 0.0f);\n"); out.Write(" v_tex0 = float3(float((id << 1) & 2), float(id & 2), 0.0f);\n");
out.WriteFmt( out.Write(
" opos = float4(v_tex0.xy * float2(2.0f, -2.0f) + float2(-1.0f, 1.0f), 0.0f, 1.0f);\n"); " opos = float4(v_tex0.xy * float2(2.0f, -2.0f) + float2(-1.0f, 1.0f), 0.0f, 1.0f);\n");
out.WriteFmt(" v_tex0 = float3(src_offset + (src_size * v_tex0.xy), 0.0f);\n"); out.Write(" v_tex0 = float3(src_offset + (src_size * v_tex0.xy), 0.0f);\n");
// NDC space is flipped in Vulkan // NDC space is flipped in Vulkan
if (api_type == APIType::Vulkan) if (api_type == APIType::Vulkan)
out.WriteFmt(" opos.y = -opos.y;\n"); out.Write(" opos.y = -opos.y;\n");
out.WriteFmt("}}\n"); out.Write("}}\n");
return out; return out;
} }
@ -101,52 +101,52 @@ ShaderCode GeneratePixelShader(APIType api_type, const UidData* uid_data)
if (api_type == APIType::D3D) if (api_type == APIType::D3D)
{ {
out.WriteFmt("Texture2DArray tex0 : register(t0);\n" out.Write("Texture2DArray tex0 : register(t0);\n"
"SamplerState samp0 : register(s0);\n" "SamplerState samp0 : register(s0);\n"
"float4 SampleEFB(float3 uv, float y_offset) {{\n" "float4 SampleEFB(float3 uv, float y_offset) {{\n"
" return tex0.Sample(samp0, float3(uv.x, clamp(uv.y + (y_offset * pixel_height), " " return tex0.Sample(samp0, float3(uv.x, clamp(uv.y + (y_offset * pixel_height), "
"clamp_tb.x, clamp_tb.y), {}));\n" "clamp_tb.x, clamp_tb.y), {}));\n"
"}}\n\n", "}}\n\n",
mono_depth ? "0.0" : "uv.z"); mono_depth ? "0.0" : "uv.z");
out.WriteFmt("void main(in float3 v_tex0 : TEXCOORD0, out float4 ocol0 : SV_Target)\n{{\n"); out.Write("void main(in float3 v_tex0 : TEXCOORD0, out float4 ocol0 : SV_Target)\n{{\n");
} }
else if (api_type == APIType::OpenGL || api_type == APIType::Vulkan) else if (api_type == APIType::OpenGL || api_type == APIType::Vulkan)
{ {
out.WriteFmt("SAMPLER_BINDING(0) uniform sampler2DArray samp0;\n"); out.Write("SAMPLER_BINDING(0) uniform sampler2DArray samp0;\n");
out.WriteFmt("float4 SampleEFB(float3 uv, float y_offset) {{\n" out.Write("float4 SampleEFB(float3 uv, float y_offset) {{\n"
" return texture(samp0, float3(uv.x, clamp(uv.y + (y_offset * pixel_height), " " return texture(samp0, float3(uv.x, clamp(uv.y + (y_offset * pixel_height), "
"clamp_tb.x, clamp_tb.y), {}));\n" "clamp_tb.x, clamp_tb.y), {}));\n"
"}}\n", "}}\n",
mono_depth ? "0.0" : "uv.z"); mono_depth ? "0.0" : "uv.z");
if (g_ActiveConfig.backend_info.bSupportsGeometryShaders) if (g_ActiveConfig.backend_info.bSupportsGeometryShaders)
{ {
out.WriteFmt("VARYING_LOCATION(0) in VertexData {{\n" out.Write("VARYING_LOCATION(0) in VertexData {{\n"
" float3 v_tex0;\n" " float3 v_tex0;\n"
"}};\n"); "}};\n");
} }
else else
{ {
out.WriteFmt("VARYING_LOCATION(0) in vec3 v_tex0;\n"); out.Write("VARYING_LOCATION(0) in vec3 v_tex0;\n");
} }
out.WriteFmt("FRAGMENT_OUTPUT_LOCATION(0) out vec4 ocol0;" out.Write("FRAGMENT_OUTPUT_LOCATION(0) out vec4 ocol0;"
"void main()\n{{\n"); "void main()\n{{\n");
} }
// The copy filter applies to both color and depth copies. This has been verified on hardware. // The copy filter applies to both color and depth copies. This has been verified on hardware.
// The filter is only applied to the RGB channels, the alpha channel is left intact. // The filter is only applied to the RGB channels, the alpha channel is left intact.
if (uid_data->copy_filter) if (uid_data->copy_filter)
{ {
out.WriteFmt(" float4 prev_row = SampleEFB(v_tex0, -1.0f);\n" out.Write(" float4 prev_row = SampleEFB(v_tex0, -1.0f);\n"
" float4 current_row = SampleEFB(v_tex0, 0.0f);\n" " float4 current_row = SampleEFB(v_tex0, 0.0f);\n"
" float4 next_row = SampleEFB(v_tex0, 1.0f);\n" " float4 next_row = SampleEFB(v_tex0, 1.0f);\n"
" float4 texcol = float4(min(prev_row.rgb * filter_coefficients[0] +\n" " float4 texcol = float4(min(prev_row.rgb * filter_coefficients[0] +\n"
" current_row.rgb * filter_coefficients[1] +\n" " current_row.rgb * filter_coefficients[1] +\n"
" next_row.rgb * filter_coefficients[2], \n" " next_row.rgb * filter_coefficients[2], \n"
" float3(1, 1, 1)), current_row.a);\n"); " float3(1, 1, 1)), current_row.a);\n");
} }
else else
{ {
out.WriteFmt( out.Write(
" float4 current_row = SampleEFB(v_tex0, 0.0f);\n" " float4 current_row = SampleEFB(v_tex0, 0.0f);\n"
" float4 texcol = float4(min(current_row.rgb * filter_coefficients[1], float3(1, 1, 1)),\n" " float4 texcol = float4(min(current_row.rgb * filter_coefficients[1], float3(1, 1, 1)),\n"
" current_row.a);\n"); " current_row.a);\n");
@ -155,62 +155,62 @@ ShaderCode GeneratePixelShader(APIType api_type, const UidData* uid_data)
if (uid_data->is_depth_copy) if (uid_data->is_depth_copy)
{ {
if (!g_ActiveConfig.backend_info.bSupportsReversedDepthRange) if (!g_ActiveConfig.backend_info.bSupportsReversedDepthRange)
out.WriteFmt("texcol.x = 1.0 - texcol.x;\n"); out.Write("texcol.x = 1.0 - texcol.x;\n");
out.WriteFmt(" int depth = int(texcol.x * 16777216.0);\n" out.Write(" int depth = int(texcol.x * 16777216.0);\n"
// Convert to Z24 format // Convert to Z24 format
" int4 workspace;\n" " int4 workspace;\n"
" workspace.r = (depth >> 16) & 255;\n" " workspace.r = (depth >> 16) & 255;\n"
" workspace.g = (depth >> 8) & 255;\n" " workspace.g = (depth >> 8) & 255;\n"
" workspace.b = depth & 255;\n" " workspace.b = depth & 255;\n"
// Convert to Z4 format // Convert to Z4 format
" workspace.a = (depth >> 16) & 0xF0;\n" " workspace.a = (depth >> 16) & 0xF0;\n"
// Normalize components to [0.0..1.0] // Normalize components to [0.0..1.0]
" texcol = float4(workspace) / 255.0;\n"); " texcol = float4(workspace) / 255.0;\n");
switch (uid_data->dst_format) switch (uid_data->dst_format)
{ {
case EFBCopyFormat::R4: // Z4 case EFBCopyFormat::R4: // Z4
out.WriteFmt(" ocol0 = texcol.aaaa;\n"); out.Write(" ocol0 = texcol.aaaa;\n");
break; break;
case EFBCopyFormat::R8_0x1: // Z8 case EFBCopyFormat::R8_0x1: // Z8
case EFBCopyFormat::R8: // Z8H case EFBCopyFormat::R8: // Z8H
out.WriteFmt(" ocol0 = texcol.rrrr;\n"); out.Write(" ocol0 = texcol.rrrr;\n");
break; break;
case EFBCopyFormat::RA8: // Z16 case EFBCopyFormat::RA8: // Z16
out.WriteFmt(" ocol0 = texcol.gggr;\n"); out.Write(" ocol0 = texcol.gggr;\n");
break; break;
case EFBCopyFormat::RG8: // Z16 (reverse order) case EFBCopyFormat::RG8: // Z16 (reverse order)
out.WriteFmt(" ocol0 = texcol.rrrg;\n"); out.Write(" ocol0 = texcol.rrrg;\n");
break; break;
case EFBCopyFormat::RGBA8: // Z24X8 case EFBCopyFormat::RGBA8: // Z24X8
out.WriteFmt(" ocol0 = float4(texcol.rgb, 1.0);\n"); out.Write(" ocol0 = float4(texcol.rgb, 1.0);\n");
break; break;
case EFBCopyFormat::G8: // Z8M case EFBCopyFormat::G8: // Z8M
out.WriteFmt(" ocol0 = texcol.gggg;\n"); out.Write(" ocol0 = texcol.gggg;\n");
break; break;
case EFBCopyFormat::B8: // Z8L case EFBCopyFormat::B8: // Z8L
out.WriteFmt(" ocol0 = texcol.bbbb;\n"); out.Write(" ocol0 = texcol.bbbb;\n");
break; break;
case EFBCopyFormat::GB8: // Z16L - copy lower 16 depth bits case EFBCopyFormat::GB8: // Z16L - copy lower 16 depth bits
// expected to be used as an IA8 texture (upper 8 bits stored as intensity, lower 8 bits // expected to be used as an IA8 texture (upper 8 bits stored as intensity, lower 8 bits
// stored as alpha) // stored as alpha)
// Used e.g. in Zelda: Skyward Sword // Used e.g. in Zelda: Skyward Sword
out.WriteFmt(" ocol0 = texcol.gggb;\n"); out.Write(" ocol0 = texcol.gggb;\n");
break; break;
default: default:
ERROR_LOG(VIDEO, "Unknown copy zbuf format: 0x%X", static_cast<int>(uid_data->dst_format)); ERROR_LOG(VIDEO, "Unknown copy zbuf format: 0x%X", static_cast<int>(uid_data->dst_format));
out.WriteFmt(" ocol0 = float4(texcol.bgr, 0.0);\n"); out.Write(" ocol0 = float4(texcol.bgr, 0.0);\n");
break; break;
} }
} }
@ -229,99 +229,99 @@ ShaderCode GeneratePixelShader(APIType api_type, const UidData* uid_data)
case EFBCopyFormat::RA4: // IA4 case EFBCopyFormat::RA4: // IA4
case EFBCopyFormat::RA8: // IA8 case EFBCopyFormat::RA8: // IA8
if (has_four_bits) if (has_four_bits)
out.WriteFmt(" texcol = float4(int4(texcol * 255.0) & 0xF0) * (1.0 / 240.0);\n"); out.Write(" texcol = float4(int4(texcol * 255.0) & 0xF0) * (1.0 / 240.0);\n");
// TODO - verify these coefficients // TODO - verify these coefficients
out.WriteFmt(" const float3 coefficients = float3(0.257, 0.504, 0.098);\n" out.Write(" const float3 coefficients = float3(0.257, 0.504, 0.098);\n"
" float intensity = dot(texcol.rgb, coefficients) + 16.0 / 255.0;\n" " float intensity = dot(texcol.rgb, coefficients) + 16.0 / 255.0;\n"
" ocol0 = float4(intensity, intensity, intensity, {});\n", " ocol0 = float4(intensity, intensity, intensity, {});\n",
has_alpha ? "texcol.a" : "intensity"); has_alpha ? "texcol.a" : "intensity");
break; break;
default: default:
ERROR_LOG(VIDEO, "Unknown copy intensity format: 0x%X", ERROR_LOG(VIDEO, "Unknown copy intensity format: 0x%X",
static_cast<int>(uid_data->dst_format)); static_cast<int>(uid_data->dst_format));
out.WriteFmt(" ocol0 = texcol;\n"); out.Write(" ocol0 = texcol;\n");
break; break;
} }
} }
else else
{ {
if (!uid_data->efb_has_alpha) if (!uid_data->efb_has_alpha)
out.WriteFmt(" texcol.a = 1.0;\n"); out.Write(" texcol.a = 1.0;\n");
switch (uid_data->dst_format) switch (uid_data->dst_format)
{ {
case EFBCopyFormat::R4: // R4 case EFBCopyFormat::R4: // R4
out.WriteFmt(" float red = float(int(texcol.r * 255.0) & 0xF0) * (1.0 / 240.0);\n" out.Write(" float red = float(int(texcol.r * 255.0) & 0xF0) * (1.0 / 240.0);\n"
" ocol0 = float4(red, red, red, red);\n"); " ocol0 = float4(red, red, red, red);\n");
break; break;
case EFBCopyFormat::R8_0x1: // R8 case EFBCopyFormat::R8_0x1: // R8
case EFBCopyFormat::R8: // R8 case EFBCopyFormat::R8: // R8
out.WriteFmt(" ocol0 = texcol.rrrr;\n"); out.Write(" ocol0 = texcol.rrrr;\n");
break; break;
case EFBCopyFormat::RA4: // RA4 case EFBCopyFormat::RA4: // RA4
out.WriteFmt(" float2 red_alpha = float2(int2(texcol.ra * 255.0) & 0xF0) * (1.0 / 240.0);\n" out.Write(" float2 red_alpha = float2(int2(texcol.ra * 255.0) & 0xF0) * (1.0 / 240.0);\n"
" ocol0 = red_alpha.rrrg;\n"); " ocol0 = red_alpha.rrrg;\n");
break; break;
case EFBCopyFormat::RA8: // RA8 case EFBCopyFormat::RA8: // RA8
out.WriteFmt(" ocol0 = texcol.rrra;\n"); out.Write(" ocol0 = texcol.rrra;\n");
break; break;
case EFBCopyFormat::A8: // A8 case EFBCopyFormat::A8: // A8
out.WriteFmt(" ocol0 = texcol.aaaa;\n"); out.Write(" ocol0 = texcol.aaaa;\n");
break; break;
case EFBCopyFormat::G8: // G8 case EFBCopyFormat::G8: // G8
out.WriteFmt(" ocol0 = texcol.gggg;\n"); out.Write(" ocol0 = texcol.gggg;\n");
break; break;
case EFBCopyFormat::B8: // B8 case EFBCopyFormat::B8: // B8
out.WriteFmt(" ocol0 = texcol.bbbb;\n"); out.Write(" ocol0 = texcol.bbbb;\n");
break; break;
case EFBCopyFormat::RG8: // RG8 case EFBCopyFormat::RG8: // RG8
out.WriteFmt(" ocol0 = texcol.rrrg;\n"); out.Write(" ocol0 = texcol.rrrg;\n");
break; break;
case EFBCopyFormat::GB8: // GB8 case EFBCopyFormat::GB8: // GB8
out.WriteFmt(" ocol0 = texcol.gggb;\n"); out.Write(" ocol0 = texcol.gggb;\n");
break; break;
case EFBCopyFormat::RGB565: // RGB565 case EFBCopyFormat::RGB565: // RGB565
out.WriteFmt(" float2 red_blue = float2(int2(texcol.rb * 255.0) & 0xF8) * (1.0 / 248.0);\n" out.Write(" float2 red_blue = float2(int2(texcol.rb * 255.0) & 0xF8) * (1.0 / 248.0);\n"
" float green = float(int(texcol.g * 255.0) & 0xFC) * (1.0 / 252.0);\n" " float green = float(int(texcol.g * 255.0) & 0xFC) * (1.0 / 252.0);\n"
" ocol0 = float4(red_blue.r, green, red_blue.g, 1.0);\n"); " ocol0 = float4(red_blue.r, green, red_blue.g, 1.0);\n");
break; break;
case EFBCopyFormat::RGB5A3: // RGB5A3 case EFBCopyFormat::RGB5A3: // RGB5A3
// TODO: The MSB controls whether we have RGB5 or RGB4A3, this selection // TODO: The MSB controls whether we have RGB5 or RGB4A3, this selection
// will need to be implemented once we move away from floats. // will need to be implemented once we move away from floats.
out.WriteFmt(" float3 color = float3(int3(texcol.rgb * 255.0) & 0xF8) * (1.0 / 248.0);\n" out.Write(" float3 color = float3(int3(texcol.rgb * 255.0) & 0xF8) * (1.0 / 248.0);\n"
" float alpha = float(int(texcol.a * 255.0) & 0xE0) * (1.0 / 224.0);\n" " float alpha = float(int(texcol.a * 255.0) & 0xE0) * (1.0 / 224.0);\n"
" ocol0 = float4(color, alpha);\n"); " ocol0 = float4(color, alpha);\n");
break; break;
case EFBCopyFormat::RGBA8: // RGBA8 case EFBCopyFormat::RGBA8: // RGBA8
out.WriteFmt(" ocol0 = texcol;\n"); out.Write(" ocol0 = texcol;\n");
break; break;
case EFBCopyFormat::XFB: case EFBCopyFormat::XFB:
out.WriteFmt( out.Write(
" ocol0 = float4(pow(texcol.rgb, float3(gamma_rcp, gamma_rcp, gamma_rcp)), 1.0f);\n"); " ocol0 = float4(pow(texcol.rgb, float3(gamma_rcp, gamma_rcp, gamma_rcp)), 1.0f);\n");
break; break;
default: default:
ERROR_LOG(VIDEO, "Unknown copy color format: 0x%X", static_cast<int>(uid_data->dst_format)); ERROR_LOG(VIDEO, "Unknown copy color format: 0x%X", static_cast<int>(uid_data->dst_format));
out.WriteFmt(" ocol0 = texcol;\n"); out.Write(" ocol0 = texcol;\n");
break; break;
} }
} }
out.WriteFmt("}}\n"); out.Write("}}\n");
return out; return out;
} }

View File

@ -18,14 +18,13 @@ void WriteUberShaderCommonHeader(ShaderCode& out, APIType api_type,
// ============================================== // ==============================================
if (!host_config.backend_bitfield) if (!host_config.backend_bitfield)
{ {
out.WriteFmt( out.Write("uint bitfieldExtract(uint val, int off, int size) {{\n"
"uint bitfieldExtract(uint val, int off, int size) {{\n" " // This built-in function is only support in OpenGL 4.0+ and ES 3.1+\n"
" // This built-in function is only support in OpenGL 4.0+ and ES 3.1+\n" " // Microsoft's HLSL compiler automatically optimises this to a bitfield extract "
" // Microsoft's HLSL compiler automatically optimises this to a bitfield extract " "instruction.\n"
"instruction.\n" " uint mask = uint((1 << size) - 1);\n"
" uint mask = uint((1 << size) - 1);\n" " return uint(val >> off) & mask;\n"
" return uint(val >> off) & mask;\n" "}}\n\n");
"}}\n\n");
} }
} }
@ -34,61 +33,60 @@ void WriteLightingFunction(ShaderCode& out)
// ============================================== // ==============================================
// Lighting channel calculation helper // Lighting channel calculation helper
// ============================================== // ==============================================
out.WriteFmt("int4 CalculateLighting(uint index, uint attnfunc, uint diffusefunc, float3 pos, " out.Write("int4 CalculateLighting(uint index, uint attnfunc, uint diffusefunc, float3 pos, "
"float3 normal) {{\n" "float3 normal) {{\n"
" float3 ldir, h, cosAttn, distAttn;\n" " float3 ldir, h, cosAttn, distAttn;\n"
" float dist, dist2, attn;\n" " float dist, dist2, attn;\n"
"\n" "\n"
" switch (attnfunc) {{\n"); " switch (attnfunc) {{\n");
out.WriteFmt(" case {}u: // LIGNTATTN_NONE\n", LIGHTATTN_NONE); out.Write(" case {}u: // LIGNTATTN_NONE\n", LIGHTATTN_NONE);
out.WriteFmt(" case {}u: // LIGHTATTN_DIR\n", LIGHTATTN_DIR); out.Write(" case {}u: // LIGHTATTN_DIR\n", LIGHTATTN_DIR);
out.WriteFmt(" ldir = normalize(" I_LIGHTS "[index].pos.xyz - pos.xyz);\n" out.Write(" ldir = normalize(" I_LIGHTS "[index].pos.xyz - pos.xyz);\n"
" attn = 1.0;\n" " attn = 1.0;\n"
" if (length(ldir) == 0.0)\n" " if (length(ldir) == 0.0)\n"
" ldir = normal;\n" " ldir = normal;\n"
" break;\n\n"); " break;\n\n");
out.WriteFmt(" case {}u: // LIGHTATTN_SPEC\n", LIGHTATTN_SPEC); out.Write(" case {}u: // LIGHTATTN_SPEC\n", LIGHTATTN_SPEC);
out.WriteFmt(" ldir = normalize(" I_LIGHTS "[index].pos.xyz - pos.xyz);\n" out.Write(" ldir = normalize(" I_LIGHTS "[index].pos.xyz - pos.xyz);\n"
" attn = (dot(normal, ldir) >= 0.0) ? max(0.0, dot(normal, " I_LIGHTS " attn = (dot(normal, ldir) >= 0.0) ? max(0.0, dot(normal, " I_LIGHTS
"[index].dir.xyz)) : 0.0;\n" "[index].dir.xyz)) : 0.0;\n"
" cosAttn = " I_LIGHTS "[index].cosatt.xyz;\n"); " cosAttn = " I_LIGHTS "[index].cosatt.xyz;\n");
out.WriteFmt(" if (diffusefunc == {}u) // LIGHTDIF_NONE\n", LIGHTDIF_NONE); out.Write(" if (diffusefunc == {}u) // LIGHTDIF_NONE\n", LIGHTDIF_NONE);
out.WriteFmt(" distAttn = " I_LIGHTS "[index].distatt.xyz;\n" out.Write(" distAttn = " I_LIGHTS "[index].distatt.xyz;\n"
" else\n" " else\n"
" distAttn = normalize(" I_LIGHTS "[index].distatt.xyz);\n" " distAttn = normalize(" I_LIGHTS "[index].distatt.xyz);\n"
" attn = max(0.0, dot(cosAttn, float3(1.0, attn, attn*attn))) / dot(distAttn, " " attn = max(0.0, dot(cosAttn, float3(1.0, attn, attn*attn))) / dot(distAttn, "
"float3(1.0, attn, attn*attn));\n" "float3(1.0, attn, attn*attn));\n"
" break;\n\n"); " break;\n\n");
out.WriteFmt(" case {}u: // LIGHTATTN_SPOT\n", LIGHTATTN_SPOT); out.Write(" case {}u: // LIGHTATTN_SPOT\n", LIGHTATTN_SPOT);
out.WriteFmt(" ldir = " I_LIGHTS "[index].pos.xyz - pos.xyz;\n" out.Write(" ldir = " I_LIGHTS "[index].pos.xyz - pos.xyz;\n"
" dist2 = dot(ldir, ldir);\n" " dist2 = dot(ldir, ldir);\n"
" dist = sqrt(dist2);\n" " dist = sqrt(dist2);\n"
" ldir = ldir / dist;\n" " ldir = ldir / dist;\n"
" attn = max(0.0, dot(ldir, " I_LIGHTS "[index].dir.xyz));\n" " attn = max(0.0, dot(ldir, " I_LIGHTS "[index].dir.xyz));\n"
" attn = max(0.0, " I_LIGHTS "[index].cosatt.x + " I_LIGHTS " attn = max(0.0, " I_LIGHTS "[index].cosatt.x + " I_LIGHTS
"[index].cosatt.y * attn + " I_LIGHTS "[index].cosatt.y * attn + " I_LIGHTS "[index].cosatt.z * attn * attn) / dot(" I_LIGHTS
"[index].cosatt.z * attn * attn) / dot(" I_LIGHTS "[index].distatt.xyz, float3(1.0, dist, dist2));\n"
"[index].distatt.xyz, float3(1.0, dist, dist2));\n" " break;\n\n");
" break;\n\n"); out.Write(" default:\n"
out.WriteFmt(" default:\n" " attn = 1.0;\n"
" attn = 1.0;\n" " ldir = normal;\n"
" ldir = normal;\n" " break;\n"
" break;\n" " }}\n"
" }}\n" "\n"
"\n" " switch (diffusefunc) {{\n");
" switch (diffusefunc) {{\n"); out.Write(" case {}u: // LIGHTDIF_NONE\n", LIGHTDIF_NONE);
out.WriteFmt(" case {}u: // LIGHTDIF_NONE\n", LIGHTDIF_NONE); out.Write(" return int4(round(attn * float4(" I_LIGHTS "[index].color)));\n\n");
out.WriteFmt(" return int4(round(attn * float4(" I_LIGHTS "[index].color)));\n\n"); out.Write(" case {}u: // LIGHTDIF_SIGN\n", LIGHTDIF_SIGN);
out.WriteFmt(" case {}u: // LIGHTDIF_SIGN\n", LIGHTDIF_SIGN); out.Write(" return int4(round(attn * dot(ldir, normal) * float4(" I_LIGHTS
out.WriteFmt(" return int4(round(attn * dot(ldir, normal) * float4(" I_LIGHTS "[index].color)));\n\n");
"[index].color)));\n\n"); out.Write(" case {}u: // LIGHTDIF_CLAMP\n", LIGHTDIF_CLAMP);
out.WriteFmt(" case {}u: // LIGHTDIF_CLAMP\n", LIGHTDIF_CLAMP); out.Write(" return int4(round(attn * max(0.0, dot(ldir, normal)) * float4(" I_LIGHTS
out.WriteFmt(" return int4(round(attn * max(0.0, dot(ldir, normal)) * float4(" I_LIGHTS "[index].color)));\n\n");
"[index].color)));\n\n"); out.Write(" default:\n"
out.WriteFmt(" default:\n" " return int4(0, 0, 0, 0);\n"
" return int4(0, 0, 0, 0);\n" " }}\n"
" }}\n" "}}\n\n");
"}}\n\n");
} }
void WriteVertexLighting(ShaderCode& out, APIType api_type, std::string_view world_pos_var, void WriteVertexLighting(ShaderCode& out, APIType api_type, std::string_view world_pos_var,
@ -96,105 +94,102 @@ void WriteVertexLighting(ShaderCode& out, APIType api_type, std::string_view wor
std::string_view in_color_1_var, std::string_view out_color_0_var, std::string_view in_color_1_var, std::string_view out_color_0_var,
std::string_view out_color_1_var) std::string_view out_color_1_var)
{ {
out.WriteFmt("// Lighting\n"); out.Write("// Lighting\n");
out.WriteFmt("{}for (uint chan = 0u; chan < {}u; chan++) {{\n", out.Write("{}for (uint chan = 0u; chan < {}u; chan++) {{\n",
api_type == APIType::D3D ? "[loop] " : "", NUM_XF_COLOR_CHANNELS); api_type == APIType::D3D ? "[loop] " : "", NUM_XF_COLOR_CHANNELS);
out.WriteFmt(" uint colorreg = xfmem_color(chan);\n" out.Write(" uint colorreg = xfmem_color(chan);\n"
" uint alphareg = xfmem_alpha(chan);\n" " uint alphareg = xfmem_alpha(chan);\n"
" int4 mat = " I_MATERIALS "[chan + 2u]; \n" " int4 mat = " I_MATERIALS "[chan + 2u]; \n"
" int4 lacc = int4(255, 255, 255, 255);\n" " int4 lacc = int4(255, 255, 255, 255);\n"
"\n"); "\n");
out.WriteFmt(" if ({} != 0u) {{\n", BitfieldExtract("colorreg", LitChannel().matsource)); out.Write(" if ({} != 0u) {{\n", BitfieldExtract("colorreg", LitChannel().matsource));
out.WriteFmt(" if ((components & ({}u << chan)) != 0u) // VB_HAS_COL0\n", VB_HAS_COL0); out.Write(" if ((components & ({}u << chan)) != 0u) // VB_HAS_COL0\n", VB_HAS_COL0);
out.WriteFmt(" mat.xyz = int3(round(((chan == 0u) ? {}.xyz : {}.xyz) * 255.0));\n", out.Write(" mat.xyz = int3(round(((chan == 0u) ? {}.xyz : {}.xyz) * 255.0));\n",
in_color_0_var, in_color_1_var); in_color_0_var, in_color_1_var);
out.WriteFmt(" else if ((components & {}u) != 0u) // VB_HAS_COLO0\n", VB_HAS_COL0); out.Write(" else if ((components & {}u) != 0u) // VB_HAS_COLO0\n", VB_HAS_COL0);
out.WriteFmt(" mat.xyz = int3(round({}.xyz * 255.0));\n", in_color_0_var); out.Write(" mat.xyz = int3(round({}.xyz * 255.0));\n", in_color_0_var);
out.WriteFmt(" else\n" out.Write(" else\n"
" mat.xyz = int3(255, 255, 255);\n" " mat.xyz = int3(255, 255, 255);\n"
" }}\n" " }}\n"
"\n"); "\n");
out.WriteFmt(" if ({} != 0u) {{\n", BitfieldExtract("alphareg", LitChannel().matsource)); out.Write(" if ({} != 0u) {{\n", BitfieldExtract("alphareg", LitChannel().matsource));
out.WriteFmt(" if ((components & ({}u << chan)) != 0u) // VB_HAS_COL0\n", VB_HAS_COL0); out.Write(" if ((components & ({}u << chan)) != 0u) // VB_HAS_COL0\n", VB_HAS_COL0);
out.WriteFmt(" mat.w = int(round(((chan == 0u) ? {}.w : {}.w) * 255.0));\n", in_color_0_var, out.Write(" mat.w = int(round(((chan == 0u) ? {}.w : {}.w) * 255.0));\n", in_color_0_var,
in_color_1_var); in_color_1_var);
out.WriteFmt(" else if ((components & {}u) != 0u) // VB_HAS_COLO0\n", VB_HAS_COL0); out.Write(" else if ((components & {}u) != 0u) // VB_HAS_COLO0\n", VB_HAS_COL0);
out.WriteFmt(" mat.w = int(round({}.w * 255.0));\n", in_color_0_var); out.Write(" mat.w = int(round({}.w * 255.0));\n", in_color_0_var);
out.WriteFmt(" else\n" out.Write(" else\n"
" mat.w = 255;\n" " mat.w = 255;\n"
" }} else {{\n" " }} else {{\n"
" mat.w = " I_MATERIALS " [chan + 2u].w;\n" " mat.w = " I_MATERIALS " [chan + 2u].w;\n"
" }}\n" " }}\n"
"\n"); "\n");
out.WriteFmt(" if ({} != 0u) {{\n", BitfieldExtract("colorreg", LitChannel().enablelighting)); out.Write(" if ({} != 0u) {{\n", BitfieldExtract("colorreg", LitChannel().enablelighting));
out.WriteFmt(" if ({} != 0u) {{\n", BitfieldExtract("colorreg", LitChannel().ambsource)); out.Write(" if ({} != 0u) {{\n", BitfieldExtract("colorreg", LitChannel().ambsource));
out.WriteFmt(" if ((components & ({}u << chan)) != 0u) // VB_HAS_COL0\n", VB_HAS_COL0); out.Write(" if ((components & ({}u << chan)) != 0u) // VB_HAS_COL0\n", VB_HAS_COL0);
out.WriteFmt(" lacc.xyz = int3(round(((chan == 0u) ? {}.xyz : {}.xyz) * 255.0));\n", out.Write(" lacc.xyz = int3(round(((chan == 0u) ? {}.xyz : {}.xyz) * 255.0));\n",
in_color_0_var, in_color_1_var); in_color_0_var, in_color_1_var);
out.WriteFmt(" else if ((components & {}u) != 0u) // VB_HAS_COLO0\n", VB_HAS_COL0); out.Write(" else if ((components & {}u) != 0u) // VB_HAS_COLO0\n", VB_HAS_COL0);
out.WriteFmt(" lacc.xyz = int3(round({}.xyz * 255.0));\n", in_color_0_var); out.Write(" lacc.xyz = int3(round({}.xyz * 255.0));\n", in_color_0_var);
out.WriteFmt(" else\n" out.Write(" else\n"
" lacc.xyz = int3(255, 255, 255);\n" " lacc.xyz = int3(255, 255, 255);\n"
" }} else {{\n" " }} else {{\n"
" lacc.xyz = " I_MATERIALS " [chan].xyz;\n" " lacc.xyz = " I_MATERIALS " [chan].xyz;\n"
" }}\n" " }}\n"
"\n"); "\n");
out.WriteFmt(" uint light_mask = {} | ({} << 4u);\n", out.Write(" uint light_mask = {} | ({} << 4u);\n",
BitfieldExtract("colorreg", LitChannel().lightMask0_3), BitfieldExtract("colorreg", LitChannel().lightMask0_3),
BitfieldExtract("colorreg", LitChannel().lightMask4_7)); BitfieldExtract("colorreg", LitChannel().lightMask4_7));
out.WriteFmt(" uint attnfunc = {};\n", BitfieldExtract("colorreg", LitChannel().attnfunc)); out.Write(" uint attnfunc = {};\n", BitfieldExtract("colorreg", LitChannel().attnfunc));
out.WriteFmt(" uint diffusefunc = {};\n", out.Write(" uint diffusefunc = {};\n", BitfieldExtract("colorreg", LitChannel().diffusefunc));
BitfieldExtract("colorreg", LitChannel().diffusefunc)); out.Write(
out.WriteFmt(
" for (uint light_index = 0u; light_index < 8u; light_index++) {{\n" " for (uint light_index = 0u; light_index < 8u; light_index++) {{\n"
" if ((light_mask & (1u << light_index)) != 0u)\n" " if ((light_mask & (1u << light_index)) != 0u)\n"
" lacc.xyz += CalculateLighting(light_index, attnfunc, diffusefunc, {}, {}).xyz;\n", " lacc.xyz += CalculateLighting(light_index, attnfunc, diffusefunc, {}, {}).xyz;\n",
world_pos_var, normal_var); world_pos_var, normal_var);
out.WriteFmt(" }}\n" out.Write(" }}\n"
" }}\n" " }}\n"
"\n"); "\n");
out.WriteFmt(" if ({} != 0u) {{\n", BitfieldExtract("alphareg", LitChannel().enablelighting)); out.Write(" if ({} != 0u) {{\n", BitfieldExtract("alphareg", LitChannel().enablelighting));
out.WriteFmt(" if ({} != 0u) {{\n", BitfieldExtract("alphareg", LitChannel().ambsource)); out.Write(" if ({} != 0u) {{\n", BitfieldExtract("alphareg", LitChannel().ambsource));
out.WriteFmt(" if ((components & ({}u << chan)) != 0u) // VB_HAS_COL0\n", VB_HAS_COL0); out.Write(" if ((components & ({}u << chan)) != 0u) // VB_HAS_COL0\n", VB_HAS_COL0);
out.WriteFmt(" lacc.w = int(round(((chan == 0u) ? {}.w : {}.w) * 255.0));\n", out.Write(" lacc.w = int(round(((chan == 0u) ? {}.w : {}.w) * 255.0));\n", in_color_0_var,
in_color_0_var, in_color_1_var); in_color_1_var);
out.WriteFmt(" else if ((components & {}u) != 0u) // VB_HAS_COLO0\n", VB_HAS_COL0); out.Write(" else if ((components & {}u) != 0u) // VB_HAS_COLO0\n", VB_HAS_COL0);
out.WriteFmt(" lacc.w = int(round({}.w * 255.0));\n", in_color_0_var); out.Write(" lacc.w = int(round({}.w * 255.0));\n", in_color_0_var);
out.WriteFmt(" else\n" out.Write(" else\n"
" lacc.w = 255;\n" " lacc.w = 255;\n"
" }} else {{\n" " }} else {{\n"
" lacc.w = " I_MATERIALS " [chan].w;\n" " lacc.w = " I_MATERIALS " [chan].w;\n"
" }}\n" " }}\n"
"\n"); "\n");
out.WriteFmt(" uint light_mask = {} | ({} << 4u);\n", out.Write(" uint light_mask = {} | ({} << 4u);\n",
BitfieldExtract("alphareg", LitChannel().lightMask0_3), BitfieldExtract("alphareg", LitChannel().lightMask0_3),
BitfieldExtract("alphareg", LitChannel().lightMask4_7)); BitfieldExtract("alphareg", LitChannel().lightMask4_7));
out.WriteFmt(" uint attnfunc = {};\n", BitfieldExtract("alphareg", LitChannel().attnfunc)); out.Write(" uint attnfunc = {};\n", BitfieldExtract("alphareg", LitChannel().attnfunc));
out.WriteFmt(" uint diffusefunc = {};\n", out.Write(" uint diffusefunc = {};\n", BitfieldExtract("alphareg", LitChannel().diffusefunc));
BitfieldExtract("alphareg", LitChannel().diffusefunc)); out.Write(" for (uint light_index = 0u; light_index < 8u; light_index++) {{\n\n"
out.WriteFmt( " if ((light_mask & (1u << light_index)) != 0u)\n\n"
" for (uint light_index = 0u; light_index < 8u; light_index++) {{\n\n" " lacc.w += CalculateLighting(light_index, attnfunc, diffusefunc, {}, {}).w;\n",
" if ((light_mask & (1u << light_index)) != 0u)\n\n" world_pos_var, normal_var);
" lacc.w += CalculateLighting(light_index, attnfunc, diffusefunc, {}, {}).w;\n", out.Write(" }}\n"
world_pos_var, normal_var); " }}\n"
out.WriteFmt(" }}\n" "\n");
" }}\n"
"\n");
out.WriteFmt(" lacc = clamp(lacc, 0, 255);\n" out.Write(" lacc = clamp(lacc, 0, 255);\n"
"\n" "\n"
" // Hopefully GPUs that can support dynamic indexing will optimize this.\n" " // Hopefully GPUs that can support dynamic indexing will optimize this.\n"
" float4 lit_color = float4((mat * (lacc + (lacc >> 7))) >> 8) / 255.0;\n" " float4 lit_color = float4((mat * (lacc + (lacc >> 7))) >> 8) / 255.0;\n"
" switch (chan) {{\n" " switch (chan) {{\n"
" case 0u: {} = lit_color; break;\n", " case 0u: {} = lit_color; break;\n",
out_color_0_var); out_color_0_var);
out.WriteFmt(" case 1u: {} = lit_color; break;\n", out_color_1_var); out.Write(" case 1u: {} = lit_color; break;\n", out_color_1_var);
out.WriteFmt(" }}\n" out.Write(" }}\n"
"}}\n" "}}\n"
"\n"); "\n");
} }
} // namespace UberShader } // namespace UberShader

File diff suppressed because it is too large Load Diff

View File

@ -35,145 +35,145 @@ ShaderCode GenVertexShader(APIType api_type, const ShaderHostConfig& host_config
const u32 num_texgen = uid_data->num_texgens; const u32 num_texgen = uid_data->num_texgens;
ShaderCode out; ShaderCode out;
out.WriteFmt("// Vertex UberShader\n\n"); out.Write("// Vertex UberShader\n\n");
out.WriteFmt("{}", s_lighting_struct); out.Write("{}", s_lighting_struct);
// uniforms // uniforms
if (api_type == APIType::OpenGL || api_type == APIType::Vulkan) if (api_type == APIType::OpenGL || api_type == APIType::Vulkan)
out.WriteFmt("UBO_BINDING(std140, 2) uniform VSBlock {{\n"); out.Write("UBO_BINDING(std140, 2) uniform VSBlock {{\n");
else else
out.WriteFmt("cbuffer VSBlock {{\n"); out.Write("cbuffer VSBlock {{\n");
out.WriteFmt("{}", s_shader_uniforms); out.Write("{}", s_shader_uniforms);
out.WriteFmt("}};\n"); out.Write("}};\n");
out.WriteFmt("struct VS_OUTPUT {{\n"); out.Write("struct VS_OUTPUT {{\n");
GenerateVSOutputMembers(out, api_type, num_texgen, host_config, ""); GenerateVSOutputMembers(out, api_type, num_texgen, host_config, "");
out.WriteFmt("}};\n\n"); out.Write("}};\n\n");
WriteUberShaderCommonHeader(out, api_type, host_config); WriteUberShaderCommonHeader(out, api_type, host_config);
WriteLightingFunction(out); WriteLightingFunction(out);
if (api_type == APIType::OpenGL || api_type == APIType::Vulkan) if (api_type == APIType::OpenGL || api_type == APIType::Vulkan)
{ {
out.WriteFmt("ATTRIBUTE_LOCATION({}) in float4 rawpos;\n", SHADER_POSITION_ATTRIB); out.Write("ATTRIBUTE_LOCATION({}) in float4 rawpos;\n", SHADER_POSITION_ATTRIB);
out.WriteFmt("ATTRIBUTE_LOCATION({}) in uint4 posmtx;\n", SHADER_POSMTX_ATTRIB); out.Write("ATTRIBUTE_LOCATION({}) in uint4 posmtx;\n", SHADER_POSMTX_ATTRIB);
out.WriteFmt("ATTRIBUTE_LOCATION({}) in float3 rawnorm0;\n", SHADER_NORM0_ATTRIB); out.Write("ATTRIBUTE_LOCATION({}) in float3 rawnorm0;\n", SHADER_NORM0_ATTRIB);
out.WriteFmt("ATTRIBUTE_LOCATION({}) in float3 rawnorm1;\n", SHADER_NORM1_ATTRIB); out.Write("ATTRIBUTE_LOCATION({}) in float3 rawnorm1;\n", SHADER_NORM1_ATTRIB);
out.WriteFmt("ATTRIBUTE_LOCATION({}) in float3 rawnorm2;\n", SHADER_NORM2_ATTRIB); out.Write("ATTRIBUTE_LOCATION({}) in float3 rawnorm2;\n", SHADER_NORM2_ATTRIB);
out.WriteFmt("ATTRIBUTE_LOCATION({}) in float4 rawcolor0;\n", SHADER_COLOR0_ATTRIB); out.Write("ATTRIBUTE_LOCATION({}) in float4 rawcolor0;\n", SHADER_COLOR0_ATTRIB);
out.WriteFmt("ATTRIBUTE_LOCATION({}) in float4 rawcolor1;\n", SHADER_COLOR1_ATTRIB); out.Write("ATTRIBUTE_LOCATION({}) in float4 rawcolor1;\n", SHADER_COLOR1_ATTRIB);
for (int i = 0; i < 8; ++i) for (int i = 0; i < 8; ++i)
out.WriteFmt("ATTRIBUTE_LOCATION({}) in float3 rawtex{};\n", SHADER_TEXTURE0_ATTRIB + i, i); out.Write("ATTRIBUTE_LOCATION({}) in float3 rawtex{};\n", SHADER_TEXTURE0_ATTRIB + i, i);
if (host_config.backend_geometry_shaders) if (host_config.backend_geometry_shaders)
{ {
out.WriteFmt("VARYING_LOCATION(0) out VertexData {{\n"); out.Write("VARYING_LOCATION(0) out VertexData {{\n");
GenerateVSOutputMembers(out, api_type, num_texgen, host_config, GenerateVSOutputMembers(out, api_type, num_texgen, host_config,
GetInterpolationQualifier(msaa, ssaa, true, false)); GetInterpolationQualifier(msaa, ssaa, true, false));
out.WriteFmt("}} vs;\n"); out.Write("}} vs;\n");
} }
else else
{ {
// Let's set up attributes // Let's set up attributes
u32 counter = 0; u32 counter = 0;
out.WriteFmt("VARYING_LOCATION({}) {} out float4 colors_0;\n", counter++, out.Write("VARYING_LOCATION({}) {} out float4 colors_0;\n", counter++,
GetInterpolationQualifier(msaa, ssaa)); GetInterpolationQualifier(msaa, ssaa));
out.WriteFmt("VARYING_LOCATION({}) {} out float4 colors_1;\n", counter++, out.Write("VARYING_LOCATION({}) {} out float4 colors_1;\n", counter++,
GetInterpolationQualifier(msaa, ssaa)); GetInterpolationQualifier(msaa, ssaa));
for (u32 i = 0; i < num_texgen; ++i) for (u32 i = 0; i < num_texgen; ++i)
{ {
out.WriteFmt("VARYING_LOCATION({}) {} out float3 tex{};\n", counter++, out.Write("VARYING_LOCATION({}) {} out float3 tex{};\n", counter++,
GetInterpolationQualifier(msaa, ssaa), i); GetInterpolationQualifier(msaa, ssaa), i);
} }
if (!host_config.fast_depth_calc) if (!host_config.fast_depth_calc)
{ {
out.WriteFmt("VARYING_LOCATION({}) {} out float4 clipPos;\n", counter++, out.Write("VARYING_LOCATION({}) {} out float4 clipPos;\n", counter++,
GetInterpolationQualifier(msaa, ssaa)); GetInterpolationQualifier(msaa, ssaa));
} }
if (per_pixel_lighting) if (per_pixel_lighting)
{ {
out.WriteFmt("VARYING_LOCATION({}) {} out float3 Normal;\n", counter++, out.Write("VARYING_LOCATION({}) {} out float3 Normal;\n", counter++,
GetInterpolationQualifier(msaa, ssaa)); GetInterpolationQualifier(msaa, ssaa));
out.WriteFmt("VARYING_LOCATION({}) {} out float3 WorldPos;\n", counter++, out.Write("VARYING_LOCATION({}) {} out float3 WorldPos;\n", counter++,
GetInterpolationQualifier(msaa, ssaa)); GetInterpolationQualifier(msaa, ssaa));
} }
} }
out.WriteFmt("void main()\n{{\n"); out.Write("void main()\n{{\n");
} }
else // D3D else // D3D
{ {
out.WriteFmt("VS_OUTPUT main(\n"); out.Write("VS_OUTPUT main(\n");
// inputs // inputs
out.WriteFmt(" float3 rawnorm0 : NORMAL0,\n" out.Write(" float3 rawnorm0 : NORMAL0,\n"
" float3 rawnorm1 : NORMAL1,\n" " float3 rawnorm1 : NORMAL1,\n"
" float3 rawnorm2 : NORMAL2,\n" " float3 rawnorm2 : NORMAL2,\n"
" float4 rawcolor0 : COLOR0,\n" " float4 rawcolor0 : COLOR0,\n"
" float4 rawcolor1 : COLOR1,\n"); " float4 rawcolor1 : COLOR1,\n");
for (int i = 0; i < 8; ++i) for (int i = 0; i < 8; ++i)
out.WriteFmt(" float3 rawtex{} : TEXCOORD{},\n", i, i); out.Write(" float3 rawtex{} : TEXCOORD{},\n", i, i);
out.WriteFmt(" uint posmtx : BLENDINDICES,\n"); out.Write(" uint posmtx : BLENDINDICES,\n");
out.WriteFmt(" float4 rawpos : POSITION) {{\n"); out.Write(" float4 rawpos : POSITION) {{\n");
} }
out.WriteFmt("VS_OUTPUT o;\n" out.Write("VS_OUTPUT o;\n"
"\n"); "\n");
// Transforms // Transforms
out.WriteFmt("// Position matrix\n" out.Write("// Position matrix\n"
"float4 P0;\n" "float4 P0;\n"
"float4 P1;\n" "float4 P1;\n"
"float4 P2;\n" "float4 P2;\n"
"\n" "\n"
"// Normal matrix\n" "// Normal matrix\n"
"float3 N0;\n" "float3 N0;\n"
"float3 N1;\n" "float3 N1;\n"
"float3 N2;\n" "float3 N2;\n"
"\n" "\n"
"if ((components & {}u) != 0u) {{// VB_HAS_POSMTXIDX\n", "if ((components & {}u) != 0u) {{// VB_HAS_POSMTXIDX\n",
VB_HAS_POSMTXIDX); VB_HAS_POSMTXIDX);
out.WriteFmt(" // Vertex format has a per-vertex matrix\n" out.Write(" // Vertex format has a per-vertex matrix\n"
" int posidx = int(posmtx.r);\n" " int posidx = int(posmtx.r);\n"
" P0 = " I_TRANSFORMMATRICES "[posidx];\n" " P0 = " I_TRANSFORMMATRICES "[posidx];\n"
" P1 = " I_TRANSFORMMATRICES "[posidx+1];\n" " P1 = " I_TRANSFORMMATRICES "[posidx+1];\n"
" P2 = " I_TRANSFORMMATRICES "[posidx+2];\n" " P2 = " I_TRANSFORMMATRICES "[posidx+2];\n"
"\n" "\n"
" int normidx = posidx >= 32 ? (posidx - 32) : posidx;\n" " int normidx = posidx >= 32 ? (posidx - 32) : posidx;\n"
" N0 = " I_NORMALMATRICES "[normidx].xyz;\n" " N0 = " I_NORMALMATRICES "[normidx].xyz;\n"
" N1 = " I_NORMALMATRICES "[normidx+1].xyz;\n" " N1 = " I_NORMALMATRICES "[normidx+1].xyz;\n"
" N2 = " I_NORMALMATRICES "[normidx+2].xyz;\n" " N2 = " I_NORMALMATRICES "[normidx+2].xyz;\n"
"}} else {{\n" "}} else {{\n"
" // One shared matrix\n" " // One shared matrix\n"
" P0 = " I_POSNORMALMATRIX "[0];\n" " P0 = " I_POSNORMALMATRIX "[0];\n"
" P1 = " I_POSNORMALMATRIX "[1];\n" " P1 = " I_POSNORMALMATRIX "[1];\n"
" P2 = " I_POSNORMALMATRIX "[2];\n" " P2 = " I_POSNORMALMATRIX "[2];\n"
" N0 = " I_POSNORMALMATRIX "[3].xyz;\n" " N0 = " I_POSNORMALMATRIX "[3].xyz;\n"
" N1 = " I_POSNORMALMATRIX "[4].xyz;\n" " N1 = " I_POSNORMALMATRIX "[4].xyz;\n"
" N2 = " I_POSNORMALMATRIX "[5].xyz;\n" " N2 = " I_POSNORMALMATRIX "[5].xyz;\n"
"}}\n" "}}\n"
"\n" "\n"
"float4 pos = float4(dot(P0, rawpos), dot(P1, rawpos), dot(P2, rawpos), 1.0);\n" "float4 pos = float4(dot(P0, rawpos), dot(P1, rawpos), dot(P2, rawpos), 1.0);\n"
"o.pos = float4(dot(" I_PROJECTION "[0], pos), dot(" I_PROJECTION "o.pos = float4(dot(" I_PROJECTION "[0], pos), dot(" I_PROJECTION
"[1], pos), dot(" I_PROJECTION "[2], pos), dot(" I_PROJECTION "[3], pos));\n" "[1], pos), dot(" I_PROJECTION "[2], pos), dot(" I_PROJECTION "[3], pos));\n"
"\n" "\n"
"// Only the first normal gets normalized (TODO: why?)\n" "// Only the first normal gets normalized (TODO: why?)\n"
"float3 _norm0 = float3(0.0, 0.0, 0.0);\n" "float3 _norm0 = float3(0.0, 0.0, 0.0);\n"
"if ((components & {}u) != 0u) // VB_HAS_NRM0\n", "if ((components & {}u) != 0u) // VB_HAS_NRM0\n",
VB_HAS_NRM0); VB_HAS_NRM0);
out.WriteFmt( out.Write(
" _norm0 = normalize(float3(dot(N0, rawnorm0), dot(N1, rawnorm0), dot(N2, rawnorm0)));\n" " _norm0 = normalize(float3(dot(N0, rawnorm0), dot(N1, rawnorm0), dot(N2, rawnorm0)));\n"
"\n" "\n"
"float3 _norm1 = float3(0.0, 0.0, 0.0);\n" "float3 _norm1 = float3(0.0, 0.0, 0.0);\n"
"if ((components & {}u) != 0u) // VB_HAS_NRM1\n", "if ((components & {}u) != 0u) // VB_HAS_NRM1\n",
VB_HAS_NRM1); VB_HAS_NRM1);
out.WriteFmt(" _norm1 = float3(dot(N0, rawnorm1), dot(N1, rawnorm1), dot(N2, rawnorm1));\n" out.Write(" _norm1 = float3(dot(N0, rawnorm1), dot(N1, rawnorm1), dot(N2, rawnorm1));\n"
"\n" "\n"
"float3 _norm2 = float3(0.0, 0.0, 0.0);\n" "float3 _norm2 = float3(0.0, 0.0, 0.0);\n"
"if ((components & {}u) != 0u) // VB_HAS_NRM2\n", "if ((components & {}u) != 0u) // VB_HAS_NRM2\n",
VB_HAS_NRM2); VB_HAS_NRM2);
out.WriteFmt(" _norm2 = float3(dot(N0, rawnorm2), dot(N1, rawnorm2), dot(N2, rawnorm2));\n" out.Write(" _norm2 = float3(dot(N0, rawnorm2), dot(N1, rawnorm2), dot(N2, rawnorm2));\n"
"\n"); "\n");
// Hardware Lighting // Hardware Lighting
WriteVertexLighting(out, api_type, "pos.xyz", "_norm0", "rawcolor0", "rawcolor1", "o.colors_0", WriteVertexLighting(out, api_type, "pos.xyz", "_norm0", "rawcolor0", "rawcolor1", "o.colors_0",
@ -183,37 +183,37 @@ ShaderCode GenVertexShader(APIType api_type, const ShaderHostConfig& host_config
if (num_texgen > 0) if (num_texgen > 0)
GenVertexShaderTexGens(api_type, num_texgen, out); GenVertexShaderTexGens(api_type, num_texgen, out);
out.WriteFmt("if (xfmem_numColorChans == 0u) {{\n" out.Write("if (xfmem_numColorChans == 0u) {{\n"
" if ((components & {}u) != 0u)\n" " if ((components & {}u) != 0u)\n"
" o.colors_0 = rawcolor0;\n" " o.colors_0 = rawcolor0;\n"
" else\n" " else\n"
" o.colors_1 = float4(1.0, 1.0, 1.0, 1.0);\n" " o.colors_1 = float4(1.0, 1.0, 1.0, 1.0);\n"
"}}\n", "}}\n",
VB_HAS_COL0); VB_HAS_COL0);
out.WriteFmt("if (xfmem_numColorChans < 2u) {{\n" out.Write("if (xfmem_numColorChans < 2u) {{\n"
" if ((components & {}u) != 0u)\n" " if ((components & {}u) != 0u)\n"
" o.colors_0 = rawcolor1;\n" " o.colors_0 = rawcolor1;\n"
" else\n" " else\n"
" o.colors_1 = float4(1.0, 1.0, 1.0, 1.0);\n" " o.colors_1 = float4(1.0, 1.0, 1.0, 1.0);\n"
"}}\n", "}}\n",
VB_HAS_COL1); VB_HAS_COL1);
if (!host_config.fast_depth_calc) if (!host_config.fast_depth_calc)
{ {
// clipPos/w needs to be done in pixel shader, not here // clipPos/w needs to be done in pixel shader, not here
out.WriteFmt("o.clipPos = o.pos;\n"); out.Write("o.clipPos = o.pos;\n");
} }
if (per_pixel_lighting) if (per_pixel_lighting)
{ {
out.WriteFmt("o.Normal = _norm0;\n" out.Write("o.Normal = _norm0;\n"
"o.WorldPos = pos.xyz;\n"); "o.WorldPos = pos.xyz;\n");
out.WriteFmt("if ((components & {}u) != 0u) // VB_HAS_COL0\n" out.Write("if ((components & {}u) != 0u) // VB_HAS_COL0\n"
" o.colors_0 = rawcolor0;\n", " o.colors_0 = rawcolor0;\n",
VB_HAS_COL0); VB_HAS_COL0);
out.WriteFmt("if ((components & {}u) != 0u) // VB_HAS_COL1\n" out.Write("if ((components & {}u) != 0u) // VB_HAS_COL1\n"
" o.colors_1 = rawcolor1;\n", " o.colors_1 = rawcolor1;\n",
VB_HAS_COL1); VB_HAS_COL1);
} }
// If we can disable the incorrect depth clipping planes using depth clamping, then we can do // If we can disable the incorrect depth clipping planes using depth clamping, then we can do
@ -225,13 +225,13 @@ ShaderCode GenVertexShader(APIType api_type, const ShaderHostConfig& host_config
// own clipping. We want to clip so that -w <= z <= 0, which matches the console -1..0 range. // own clipping. We want to clip so that -w <= z <= 0, which matches the console -1..0 range.
// We adjust our depth value for clipping purposes to match the perspective projection in the // We adjust our depth value for clipping purposes to match the perspective projection in the
// software backend, which is a hack to fix Sonic Adventure and Unleashed games. // software backend, which is a hack to fix Sonic Adventure and Unleashed games.
out.WriteFmt("float clipDepth = o.pos.z * (1.0 - 1e-7);\n" out.Write("float clipDepth = o.pos.z * (1.0 - 1e-7);\n"
"float clipDist0 = clipDepth + o.pos.w;\n" // Near: z < -w "float clipDist0 = clipDepth + o.pos.w;\n" // Near: z < -w
"float clipDist1 = -clipDepth;\n"); // Far: z > 0 "float clipDist1 = -clipDepth;\n"); // Far: z > 0
if (host_config.backend_geometry_shaders) if (host_config.backend_geometry_shaders)
{ {
out.WriteFmt("o.clipDist0 = clipDist0;\n" out.Write("o.clipDist0 = clipDist0;\n"
"o.clipDist1 = clipDist1;\n"); "o.clipDist1 = clipDist1;\n");
} }
} }
@ -246,20 +246,20 @@ ShaderCode GenVertexShader(APIType api_type, const ShaderHostConfig& host_config
// divide, because some games will use a depth range larger than what is allowed by the // divide, because some games will use a depth range larger than what is allowed by the
// graphics API. These large depth ranges will still be clipped to the 0..1 range, so these // graphics API. These large depth ranges will still be clipped to the 0..1 range, so these
// games effectively add a depth bias to the values written to the depth buffer. // games effectively add a depth bias to the values written to the depth buffer.
out.WriteFmt("o.pos.z = o.pos.w * " I_PIXELCENTERCORRECTION ".w - " out.Write("o.pos.z = o.pos.w * " I_PIXELCENTERCORRECTION ".w - "
"o.pos.z * " I_PIXELCENTERCORRECTION ".z;\n"); "o.pos.z * " I_PIXELCENTERCORRECTION ".z;\n");
if (!host_config.backend_clip_control) if (!host_config.backend_clip_control)
{ {
// If the graphics API doesn't support a depth range of 0..1, then we need to map z to // If the graphics API doesn't support a depth range of 0..1, then we need to map z to
// the -1..1 range. Unfortunately we have to use a substraction, which is a lossy floating-point // the -1..1 range. Unfortunately we have to use a substraction, which is a lossy floating-point
// operation that can introduce a round-trip error. // operation that can introduce a round-trip error.
out.WriteFmt("o.pos.z = o.pos.z * 2.0 - o.pos.w;\n"); out.Write("o.pos.z = o.pos.z * 2.0 - o.pos.w;\n");
} }
// Correct for negative viewports by mirroring all vertices. We need to negate the height here, // Correct for negative viewports by mirroring all vertices. We need to negate the height here,
// since the viewport height is already negated by the render backend. // since the viewport height is already negated by the render backend.
out.WriteFmt("o.pos.xy *= sign(" I_PIXELCENTERCORRECTION ".xy * float2(1.0, -1.0));\n"); out.Write("o.pos.xy *= sign(" I_PIXELCENTERCORRECTION ".xy * float2(1.0, -1.0));\n");
// The console GPU places the pixel center at 7/12 in screen space unless // The console GPU places the pixel center at 7/12 in screen space unless
// antialiasing is enabled, while D3D and OpenGL place it at 0.5. This results // antialiasing is enabled, while D3D and OpenGL place it at 0.5. This results
@ -267,7 +267,7 @@ ShaderCode GenVertexShader(APIType api_type, const ShaderHostConfig& host_config
// which in turn can be critical if it happens for clear quads. // which in turn can be critical if it happens for clear quads.
// Hence, we compensate for this pixel center difference so that primitives // Hence, we compensate for this pixel center difference so that primitives
// get rasterized correctly. // get rasterized correctly.
out.WriteFmt("o.pos.xy = o.pos.xy - o.pos.w * " I_PIXELCENTERCORRECTION ".xy;\n"); out.Write("o.pos.xy = o.pos.xy - o.pos.w * " I_PIXELCENTERCORRECTION ".xy;\n");
if (vertex_rounding) if (vertex_rounding)
{ {
@ -275,18 +275,18 @@ ShaderCode GenVertexShader(APIType api_type, const ShaderHostConfig& host_config
// cause an additional pixel offset. Due to a higher pixel density we need to correct this // cause an additional pixel offset. Due to a higher pixel density we need to correct this
// by converting our clip-space position into the Wii's screen-space. // by converting our clip-space position into the Wii's screen-space.
// Acquire the right pixel and then convert it back. // Acquire the right pixel and then convert it back.
out.WriteFmt("if (o.pos.w == 1.0f)\n" out.Write("if (o.pos.w == 1.0f)\n"
"{{\n"); "{{\n");
out.WriteFmt("\tfloat ss_pixel_x = ((o.pos.x + 1.0f) * (" I_VIEWPORT_SIZE ".x * 0.5f));\n" out.Write("\tfloat ss_pixel_x = ((o.pos.x + 1.0f) * (" I_VIEWPORT_SIZE ".x * 0.5f));\n"
"\tfloat ss_pixel_y = ((o.pos.y + 1.0f) * (" I_VIEWPORT_SIZE ".y * 0.5f));\n"); "\tfloat ss_pixel_y = ((o.pos.y + 1.0f) * (" I_VIEWPORT_SIZE ".y * 0.5f));\n");
out.WriteFmt("\tss_pixel_x = round(ss_pixel_x);\n" out.Write("\tss_pixel_x = round(ss_pixel_x);\n"
"\tss_pixel_y = round(ss_pixel_y);\n"); "\tss_pixel_y = round(ss_pixel_y);\n");
out.WriteFmt("\to.pos.x = ((ss_pixel_x / (" I_VIEWPORT_SIZE ".x * 0.5f)) - 1.0f);\n" out.Write("\to.pos.x = ((ss_pixel_x / (" I_VIEWPORT_SIZE ".x * 0.5f)) - 1.0f);\n"
"\to.pos.y = ((ss_pixel_y / (" I_VIEWPORT_SIZE ".y * 0.5f)) - 1.0f);\n" "\to.pos.y = ((ss_pixel_y / (" I_VIEWPORT_SIZE ".y * 0.5f)) - 1.0f);\n"
"}}\n"); "}}\n");
} }
if (api_type == APIType::OpenGL || api_type == APIType::Vulkan) if (api_type == APIType::OpenGL || api_type == APIType::Vulkan)
@ -300,35 +300,35 @@ ShaderCode GenVertexShader(APIType api_type, const ShaderHostConfig& host_config
// TODO: Pass interface blocks between shader stages even if geometry shaders // TODO: Pass interface blocks between shader stages even if geometry shaders
// are not supported, however that will require at least OpenGL 3.2 support. // are not supported, however that will require at least OpenGL 3.2 support.
for (u32 i = 0; i < num_texgen; ++i) for (u32 i = 0; i < num_texgen; ++i)
out.WriteFmt("tex{}.xyz = o.tex{};\n", i, i); out.Write("tex{}.xyz = o.tex{};\n", i, i);
if (!host_config.fast_depth_calc) if (!host_config.fast_depth_calc)
out.WriteFmt("clipPos = o.clipPos;\n"); out.Write("clipPos = o.clipPos;\n");
if (per_pixel_lighting) if (per_pixel_lighting)
{ {
out.WriteFmt("Normal = o.Normal;\n" out.Write("Normal = o.Normal;\n"
"WorldPos = o.WorldPos;\n"); "WorldPos = o.WorldPos;\n");
} }
out.WriteFmt("colors_0 = o.colors_0;\n" out.Write("colors_0 = o.colors_0;\n"
"colors_1 = o.colors_1;\n"); "colors_1 = o.colors_1;\n");
} }
if (host_config.backend_depth_clamp) if (host_config.backend_depth_clamp)
{ {
out.WriteFmt("gl_ClipDistance[0] = clipDist0;\n" out.Write("gl_ClipDistance[0] = clipDist0;\n"
"gl_ClipDistance[1] = clipDist1;\n"); "gl_ClipDistance[1] = clipDist1;\n");
} }
// Vulkan NDC space has Y pointing down (right-handed NDC space). // Vulkan NDC space has Y pointing down (right-handed NDC space).
if (api_type == APIType::Vulkan) if (api_type == APIType::Vulkan)
out.WriteFmt("gl_Position = float4(o.pos.x, -o.pos.y, o.pos.z, o.pos.w);\n"); out.Write("gl_Position = float4(o.pos.x, -o.pos.y, o.pos.z, o.pos.w);\n");
else else
out.WriteFmt("gl_Position = o.pos;\n"); out.Write("gl_Position = o.pos;\n");
} }
else // D3D else // D3D
{ {
out.WriteFmt("return o;\n"); out.Write("return o;\n");
} }
out.WriteFmt("}}\n"); out.Write("}}\n");
return out; return out;
} }
@ -338,160 +338,158 @@ static void GenVertexShaderTexGens(APIType api_type, u32 num_texgen, ShaderCode&
// The HLSL compiler complains that the output texture coordinates are uninitialized when trying // The HLSL compiler complains that the output texture coordinates are uninitialized when trying
// to dynamically index them. // to dynamically index them.
for (u32 i = 0; i < num_texgen; i++) for (u32 i = 0; i < num_texgen; i++)
out.WriteFmt("o.tex{} = float3(0.0, 0.0, 0.0);\n", i); out.Write("o.tex{} = float3(0.0, 0.0, 0.0);\n", i);
out.WriteFmt("// Texture coordinate generation\n"); out.Write("// Texture coordinate generation\n");
if (num_texgen == 1) if (num_texgen == 1)
{ {
out.WriteFmt("{{ const uint texgen = 0u;\n"); out.Write("{{ const uint texgen = 0u;\n");
} }
else else
{ {
out.WriteFmt("{}for (uint texgen = 0u; texgen < {}u; texgen++) {{\n", out.Write("{}for (uint texgen = 0u; texgen < {}u; texgen++) {{\n",
api_type == APIType::D3D ? "[loop] " : "", num_texgen); api_type == APIType::D3D ? "[loop] " : "", num_texgen);
} }
out.WriteFmt(" // Texcoord transforms\n"); out.Write(" // Texcoord transforms\n");
out.WriteFmt(" float4 coord = float4(0.0, 0.0, 1.0, 1.0);\n" out.Write(" float4 coord = float4(0.0, 0.0, 1.0, 1.0);\n"
" uint texMtxInfo = xfmem_texMtxInfo(texgen);\n"); " uint texMtxInfo = xfmem_texMtxInfo(texgen);\n");
out.WriteFmt(" switch ({}) {{\n", BitfieldExtract("texMtxInfo", TexMtxInfo().sourcerow)); out.Write(" switch ({}) {{\n", BitfieldExtract("texMtxInfo", TexMtxInfo().sourcerow));
out.WriteFmt(" case {}u: // XF_SRCGEOM_INROW\n", XF_SRCGEOM_INROW); out.Write(" case {}u: // XF_SRCGEOM_INROW\n", XF_SRCGEOM_INROW);
out.WriteFmt(" coord.xyz = rawpos.xyz;\n"); out.Write(" coord.xyz = rawpos.xyz;\n");
out.WriteFmt(" break;\n\n"); out.Write(" break;\n\n");
out.WriteFmt(" case {}u: // XF_SRCNORMAL_INROW\n", XF_SRCNORMAL_INROW); out.Write(" case {}u: // XF_SRCNORMAL_INROW\n", XF_SRCNORMAL_INROW);
out.WriteFmt( out.Write(
" coord.xyz = ((components & {}u /* VB_HAS_NRM0 */) != 0u) ? rawnorm0.xyz : coord.xyz;", " coord.xyz = ((components & {}u /* VB_HAS_NRM0 */) != 0u) ? rawnorm0.xyz : coord.xyz;",
VB_HAS_NRM0); VB_HAS_NRM0);
out.WriteFmt(" break;\n\n"); out.Write(" break;\n\n");
out.WriteFmt(" case {}u: // XF_SRCBINORMAL_T_INROW\n", XF_SRCBINORMAL_T_INROW); out.Write(" case {}u: // XF_SRCBINORMAL_T_INROW\n", XF_SRCBINORMAL_T_INROW);
out.WriteFmt( out.Write(
" coord.xyz = ((components & {}u /* VB_HAS_NRM1 */) != 0u) ? rawnorm1.xyz : coord.xyz;", " coord.xyz = ((components & {}u /* VB_HAS_NRM1 */) != 0u) ? rawnorm1.xyz : coord.xyz;",
VB_HAS_NRM1); VB_HAS_NRM1);
out.WriteFmt(" break;\n\n"); out.Write(" break;\n\n");
out.WriteFmt(" case {}u: // XF_SRCBINORMAL_B_INROW\n", XF_SRCBINORMAL_B_INROW); out.Write(" case {}u: // XF_SRCBINORMAL_B_INROW\n", XF_SRCBINORMAL_B_INROW);
out.WriteFmt( out.Write(
" coord.xyz = ((components & {}u /* VB_HAS_NRM2 */) != 0u) ? rawnorm2.xyz : coord.xyz;", " coord.xyz = ((components & {}u /* VB_HAS_NRM2 */) != 0u) ? rawnorm2.xyz : coord.xyz;",
VB_HAS_NRM2); VB_HAS_NRM2);
out.WriteFmt(" break;\n\n"); out.Write(" break;\n\n");
for (u32 i = 0; i < 8; i++) for (u32 i = 0; i < 8; i++)
{ {
out.WriteFmt(" case {}u: // XF_SRCTEX{}_INROW\n", XF_SRCTEX0_INROW + i, i); out.Write(" case {}u: // XF_SRCTEX{}_INROW\n", XF_SRCTEX0_INROW + i, i);
out.WriteFmt( out.Write(
" coord = ((components & {}u /* VB_HAS_UV{} */) != 0u) ? float4(rawtex{}.x, rawtex{}.y, " " coord = ((components & {}u /* VB_HAS_UV{} */) != 0u) ? float4(rawtex{}.x, rawtex{}.y, "
"1.0, 1.0) : coord;\n", "1.0, 1.0) : coord;\n",
VB_HAS_UV0 << i, i, i, i); VB_HAS_UV0 << i, i, i, i);
out.WriteFmt(" break;\n\n"); out.Write(" break;\n\n");
} }
out.WriteFmt(" }}\n" out.Write(" }}\n"
"\n"); "\n");
out.WriteFmt(" // Input form of AB11 sets z element to 1.0\n"); out.Write(" // Input form of AB11 sets z element to 1.0\n");
out.WriteFmt(" if ({} == {}u) // inputform == XF_TEXINPUT_AB11\n", out.Write(" if ({} == {}u) // inputform == XF_TEXINPUT_AB11\n",
BitfieldExtract("texMtxInfo", TexMtxInfo().inputform), XF_TEXINPUT_AB11); BitfieldExtract("texMtxInfo", TexMtxInfo().inputform), XF_TEXINPUT_AB11);
out.WriteFmt(" coord.z = 1.0f;\n" out.Write(" coord.z = 1.0f;\n"
"\n"); "\n");
out.WriteFmt(" // first transformation\n"); out.Write(" // first transformation\n");
out.WriteFmt(" uint texgentype = {};\n", BitfieldExtract("texMtxInfo", TexMtxInfo().texgentype)); out.Write(" uint texgentype = {};\n", BitfieldExtract("texMtxInfo", TexMtxInfo().texgentype));
out.WriteFmt(" float3 output_tex;\n" out.Write(" float3 output_tex;\n"
" switch (texgentype)\n" " switch (texgentype)\n"
" {{\n"); " {{\n");
out.WriteFmt(" case {}u: // XF_TEXGEN_EMBOSS_MAP\n", XF_TEXGEN_EMBOSS_MAP); out.Write(" case {}u: // XF_TEXGEN_EMBOSS_MAP\n", XF_TEXGEN_EMBOSS_MAP);
out.WriteFmt(" {{\n"); out.Write(" {{\n");
out.WriteFmt(" uint light = {};\n", out.Write(" uint light = {};\n",
BitfieldExtract("texMtxInfo", TexMtxInfo().embosslightshift)); BitfieldExtract("texMtxInfo", TexMtxInfo().embosslightshift));
out.WriteFmt(" uint source = {};\n", out.Write(" uint source = {};\n",
BitfieldExtract("texMtxInfo", TexMtxInfo().embosssourceshift)); BitfieldExtract("texMtxInfo", TexMtxInfo().embosssourceshift));
out.WriteFmt(" switch (source) {{\n"); out.Write(" switch (source) {{\n");
for (u32 i = 0; i < num_texgen; i++) for (u32 i = 0; i < num_texgen; i++)
out.WriteFmt(" case {}u: output_tex.xyz = o.tex{}; break;\n", i, i); out.Write(" case {}u: output_tex.xyz = o.tex{}; break;\n", i, i);
out.WriteFmt(" default: output_tex.xyz = float3(0.0, 0.0, 0.0); break;\n" out.Write(" default: output_tex.xyz = float3(0.0, 0.0, 0.0); break;\n"
" }}\n"); " }}\n");
out.WriteFmt(" if ((components & {}u) != 0u) {{ // VB_HAS_NRM1 | VB_HAS_NRM2\n", out.Write(" if ((components & {}u) != 0u) {{ // VB_HAS_NRM1 | VB_HAS_NRM2\n",
VB_HAS_NRM1 | VB_HAS_NRM2); // Should this be VB_HAS_NRM1 | VB_HAS_NRM2 VB_HAS_NRM1 | VB_HAS_NRM2); // Should this be VB_HAS_NRM1 | VB_HAS_NRM2
out.WriteFmt(" float3 ldir = normalize(" I_LIGHTS "[light].pos.xyz - pos.xyz);\n" out.Write(" float3 ldir = normalize(" I_LIGHTS "[light].pos.xyz - pos.xyz);\n"
" output_tex.xyz += float3(dot(ldir, _norm1), dot(ldir, _norm2), 0.0);\n" " output_tex.xyz += float3(dot(ldir, _norm1), dot(ldir, _norm2), 0.0);\n"
" }}\n" " }}\n"
" }}\n" " }}\n"
" break;\n\n"); " break;\n\n");
out.WriteFmt(" case {}u: // XF_TEXGEN_COLOR_STRGBC0\n", XF_TEXGEN_COLOR_STRGBC0); out.Write(" case {}u: // XF_TEXGEN_COLOR_STRGBC0\n", XF_TEXGEN_COLOR_STRGBC0);
out.WriteFmt(" output_tex.xyz = float3(o.colors_0.x, o.colors_0.y, 1.0);\n" out.Write(" output_tex.xyz = float3(o.colors_0.x, o.colors_0.y, 1.0);\n"
" break;\n\n"); " break;\n\n");
out.WriteFmt(" case {}u: // XF_TEXGEN_COLOR_STRGBC1\n", XF_TEXGEN_COLOR_STRGBC1); out.Write(" case {}u: // XF_TEXGEN_COLOR_STRGBC1\n", XF_TEXGEN_COLOR_STRGBC1);
out.WriteFmt(" output_tex.xyz = float3(o.colors_1.x, o.colors_1.y, 1.0);\n" out.Write(" output_tex.xyz = float3(o.colors_1.x, o.colors_1.y, 1.0);\n"
" break;\n\n"); " break;\n\n");
out.WriteFmt(" default: // Also XF_TEXGEN_REGULAR\n" out.Write(" default: // Also XF_TEXGEN_REGULAR\n"
" {{\n"); " {{\n");
out.WriteFmt(" if ((components & ({}u /* VB_HAS_TEXMTXIDX0 */ << texgen)) != 0u) {{\n", out.Write(" if ((components & ({}u /* VB_HAS_TEXMTXIDX0 */ << texgen)) != 0u) {{\n",
VB_HAS_TEXMTXIDX0); VB_HAS_TEXMTXIDX0);
out.WriteFmt( out.Write(" // This is messy, due to dynamic indexing of the input texture coordinates.\n"
" // This is messy, due to dynamic indexing of the input texture coordinates.\n" " // Hopefully the compiler will unroll this whole loop anyway and the switch.\n"
" // Hopefully the compiler will unroll this whole loop anyway and the switch.\n" " int tmp = 0;\n"
" int tmp = 0;\n" " switch (texgen) {{\n");
" switch (texgen) {{\n");
for (u32 i = 0; i < num_texgen; i++) for (u32 i = 0; i < num_texgen; i++)
out.WriteFmt(" case {}u: tmp = int(rawtex{}.z); break;\n", i, i); out.Write(" case {}u: tmp = int(rawtex{}.z); break;\n", i, i);
out.WriteFmt(" }}\n" out.Write(" }}\n"
"\n"); "\n");
out.WriteFmt(" if ({} == {}u) {{\n", out.Write(" if ({} == {}u) {{\n", BitfieldExtract("texMtxInfo", TexMtxInfo().projection),
BitfieldExtract("texMtxInfo", TexMtxInfo().projection), XF_TEXPROJ_STQ); XF_TEXPROJ_STQ);
out.WriteFmt(" output_tex.xyz = float3(dot(coord, " I_TRANSFORMMATRICES "[tmp]),\n" out.Write(" output_tex.xyz = float3(dot(coord, " I_TRANSFORMMATRICES "[tmp]),\n"
" dot(coord, " I_TRANSFORMMATRICES "[tmp + 1]),\n" " dot(coord, " I_TRANSFORMMATRICES "[tmp + 1]),\n"
" dot(coord, " I_TRANSFORMMATRICES "[tmp + 2]));\n" " dot(coord, " I_TRANSFORMMATRICES "[tmp + 2]));\n"
" }} else {{\n" " }} else {{\n"
" output_tex.xyz = float3(dot(coord, " I_TRANSFORMMATRICES "[tmp]),\n" " output_tex.xyz = float3(dot(coord, " I_TRANSFORMMATRICES "[tmp]),\n"
" dot(coord, " I_TRANSFORMMATRICES "[tmp + 1]),\n" " dot(coord, " I_TRANSFORMMATRICES "[tmp + 1]),\n"
" 1.0);\n" " 1.0);\n"
" }}\n" " }}\n"
" }} else {{\n"); " }} else {{\n");
out.WriteFmt(" if ({} == {}u) {{\n", out.Write(" if ({} == {}u) {{\n", BitfieldExtract("texMtxInfo", TexMtxInfo().projection),
BitfieldExtract("texMtxInfo", TexMtxInfo().projection), XF_TEXPROJ_STQ); XF_TEXPROJ_STQ);
out.WriteFmt( out.Write(" output_tex.xyz = float3(dot(coord, " I_TEXMATRICES "[3u * texgen]),\n"
" output_tex.xyz = float3(dot(coord, " I_TEXMATRICES "[3u * texgen]),\n" " dot(coord, " I_TEXMATRICES "[3u * texgen + 1u]),\n"
" dot(coord, " I_TEXMATRICES "[3u * texgen + 1u]),\n" " dot(coord, " I_TEXMATRICES "[3u * texgen + 2u]));\n"
" dot(coord, " I_TEXMATRICES "[3u * texgen + 2u]));\n" " }} else {{\n"
" }} else {{\n" " output_tex.xyz = float3(dot(coord, " I_TEXMATRICES "[3u * texgen]),\n"
" output_tex.xyz = float3(dot(coord, " I_TEXMATRICES "[3u * texgen]),\n" " dot(coord, " I_TEXMATRICES "[3u * texgen + 1u]),\n"
" dot(coord, " I_TEXMATRICES "[3u * texgen + 1u]),\n" " 1.0);\n"
" 1.0);\n" " }}\n"
" }}\n" " }}\n"
" }}\n" " }}\n"
" }}\n" " break;\n\n"
" break;\n\n" " }}\n"
" }}\n" "\n");
"\n");
out.WriteFmt(" if (xfmem_dualTexInfo != 0u) {{\n"); out.Write(" if (xfmem_dualTexInfo != 0u) {{\n");
out.WriteFmt(" uint postMtxInfo = xfmem_postMtxInfo(texgen);"); out.Write(" uint postMtxInfo = xfmem_postMtxInfo(texgen);");
out.WriteFmt(" uint base_index = {};\n", BitfieldExtract("postMtxInfo", PostMtxInfo().index)); out.Write(" uint base_index = {};\n", BitfieldExtract("postMtxInfo", PostMtxInfo().index));
out.WriteFmt(" float4 P0 = " I_POSTTRANSFORMMATRICES "[base_index & 0x3fu];\n" out.Write(" float4 P0 = " I_POSTTRANSFORMMATRICES "[base_index & 0x3fu];\n"
" float4 P1 = " I_POSTTRANSFORMMATRICES "[(base_index + 1u) & 0x3fu];\n" " float4 P1 = " I_POSTTRANSFORMMATRICES "[(base_index + 1u) & 0x3fu];\n"
" float4 P2 = " I_POSTTRANSFORMMATRICES "[(base_index + 2u) & 0x3fu];\n" " float4 P2 = " I_POSTTRANSFORMMATRICES "[(base_index + 2u) & 0x3fu];\n"
"\n"); "\n");
out.WriteFmt(" if ({} != 0u)\n", BitfieldExtract("postMtxInfo", PostMtxInfo().normalize)); out.Write(" if ({} != 0u)\n", BitfieldExtract("postMtxInfo", PostMtxInfo().normalize));
out.WriteFmt(" output_tex.xyz = normalize(output_tex.xyz);\n" out.Write(" output_tex.xyz = normalize(output_tex.xyz);\n"
"\n" "\n"
" // multiply by postmatrix\n" " // multiply by postmatrix\n"
" output_tex.xyz = float3(dot(P0.xyz, output_tex.xyz) + P0.w,\n" " output_tex.xyz = float3(dot(P0.xyz, output_tex.xyz) + P0.w,\n"
" dot(P1.xyz, output_tex.xyz) + P1.w,\n" " dot(P1.xyz, output_tex.xyz) + P1.w,\n"
" dot(P2.xyz, output_tex.xyz) + P2.w);\n" " dot(P2.xyz, output_tex.xyz) + P2.w);\n"
" }}\n\n"); " }}\n\n");
// When q is 0, the GameCube appears to have a special case // When q is 0, the GameCube appears to have a special case
// This can be seen in devkitPro's neheGX Lesson08 example for Wii // This can be seen in devkitPro's neheGX Lesson08 example for Wii
// Makes differences in Rogue Squadron 3 (Hoth sky) and The Last Story (shadow culling) // Makes differences in Rogue Squadron 3 (Hoth sky) and The Last Story (shadow culling)
out.WriteFmt(" if (texgentype == {}u && output_tex.z == 0.0) // XF_TEXGEN_REGULAR\n", out.Write(" if (texgentype == {}u && output_tex.z == 0.0) // XF_TEXGEN_REGULAR\n",
XF_TEXGEN_REGULAR); XF_TEXGEN_REGULAR);
out.WriteFmt( out.Write(
" output_tex.xy = clamp(output_tex.xy / 2.0f, float2(-1.0f,-1.0f), float2(1.0f,1.0f));\n" " output_tex.xy = clamp(output_tex.xy / 2.0f, float2(-1.0f,-1.0f), float2(1.0f,1.0f));\n"
"\n"); "\n");
out.WriteFmt(" // Hopefully GPUs that can support dynamic indexing will optimize this.\n"); out.Write(" // Hopefully GPUs that can support dynamic indexing will optimize this.\n");
out.WriteFmt(" switch (texgen) {{\n"); out.Write(" switch (texgen) {{\n");
for (u32 i = 0; i < num_texgen; i++) for (u32 i = 0; i < num_texgen; i++)
out.WriteFmt(" case {}u: o.tex{} = output_tex; break;\n", i, i); out.Write(" case {}u: o.tex{} = output_tex; break;\n", i, i);
out.WriteFmt(" }}\n" out.Write(" }}\n"
"}}\n"); "}}\n");
} }
void EnumerateVertexShaderUids(const std::function<void(const VertexShaderUid&)>& callback) void EnumerateVertexShaderUids(const std::function<void(const VertexShaderUid&)>& callback)

View File

@ -83,37 +83,37 @@ ShaderCode GenerateVertexShaderCode(APIType api_type, const ShaderHostConfig& ho
const bool ssaa = host_config.ssaa; const bool ssaa = host_config.ssaa;
const bool vertex_rounding = host_config.vertex_rounding; const bool vertex_rounding = host_config.vertex_rounding;
out.WriteFmt("{}", s_lighting_struct); out.Write("{}", s_lighting_struct);
// uniforms // uniforms
if (api_type == APIType::OpenGL || api_type == APIType::Vulkan) if (api_type == APIType::OpenGL || api_type == APIType::Vulkan)
out.WriteFmt("UBO_BINDING(std140, 2) uniform VSBlock {{\n"); out.Write("UBO_BINDING(std140, 2) uniform VSBlock {{\n");
else else
out.WriteFmt("cbuffer VSBlock {{\n"); out.Write("cbuffer VSBlock {{\n");
out.WriteFmt("{}", s_shader_uniforms); out.Write("{}", s_shader_uniforms);
out.WriteFmt("}};\n"); out.Write("}};\n");
out.WriteFmt("struct VS_OUTPUT {{\n"); out.Write("struct VS_OUTPUT {{\n");
GenerateVSOutputMembers(out, api_type, uid_data->numTexGens, host_config, ""); GenerateVSOutputMembers(out, api_type, uid_data->numTexGens, host_config, "");
out.WriteFmt("}};\n"); out.Write("}};\n");
if (api_type == APIType::OpenGL || api_type == APIType::Vulkan) if (api_type == APIType::OpenGL || api_type == APIType::Vulkan)
{ {
out.WriteFmt("ATTRIBUTE_LOCATION({}) in float4 rawpos;\n", SHADER_POSITION_ATTRIB); out.Write("ATTRIBUTE_LOCATION({}) in float4 rawpos;\n", SHADER_POSITION_ATTRIB);
if ((uid_data->components & VB_HAS_POSMTXIDX) != 0) if ((uid_data->components & VB_HAS_POSMTXIDX) != 0)
out.WriteFmt("ATTRIBUTE_LOCATION({}) in uint4 posmtx;\n", SHADER_POSMTX_ATTRIB); out.Write("ATTRIBUTE_LOCATION({}) in uint4 posmtx;\n", SHADER_POSMTX_ATTRIB);
if ((uid_data->components & VB_HAS_NRM0) != 0) if ((uid_data->components & VB_HAS_NRM0) != 0)
out.WriteFmt("ATTRIBUTE_LOCATION({}) in float3 rawnorm0;\n", SHADER_NORM0_ATTRIB); out.Write("ATTRIBUTE_LOCATION({}) in float3 rawnorm0;\n", SHADER_NORM0_ATTRIB);
if ((uid_data->components & VB_HAS_NRM1) != 0) if ((uid_data->components & VB_HAS_NRM1) != 0)
out.WriteFmt("ATTRIBUTE_LOCATION({}) in float3 rawnorm1;\n", SHADER_NORM1_ATTRIB); out.Write("ATTRIBUTE_LOCATION({}) in float3 rawnorm1;\n", SHADER_NORM1_ATTRIB);
if ((uid_data->components & VB_HAS_NRM2) != 0) if ((uid_data->components & VB_HAS_NRM2) != 0)
out.WriteFmt("ATTRIBUTE_LOCATION({}) in float3 rawnorm2;\n", SHADER_NORM2_ATTRIB); out.Write("ATTRIBUTE_LOCATION({}) in float3 rawnorm2;\n", SHADER_NORM2_ATTRIB);
if ((uid_data->components & VB_HAS_COL0) != 0) if ((uid_data->components & VB_HAS_COL0) != 0)
out.WriteFmt("ATTRIBUTE_LOCATION({}) in float4 rawcolor0;\n", SHADER_COLOR0_ATTRIB); out.Write("ATTRIBUTE_LOCATION({}) in float4 rawcolor0;\n", SHADER_COLOR0_ATTRIB);
if ((uid_data->components & VB_HAS_COL1) != 0) if ((uid_data->components & VB_HAS_COL1) != 0)
out.WriteFmt("ATTRIBUTE_LOCATION({}) in float4 rawcolor1;\n", SHADER_COLOR1_ATTRIB); out.Write("ATTRIBUTE_LOCATION({}) in float4 rawcolor1;\n", SHADER_COLOR1_ATTRIB);
for (u32 i = 0; i < 8; ++i) for (u32 i = 0; i < 8; ++i)
{ {
@ -121,161 +121,161 @@ ShaderCode GenerateVertexShaderCode(APIType api_type, const ShaderHostConfig& ho
if ((uid_data->components & (VB_HAS_UV0 << i)) != 0 || has_texmtx != 0) if ((uid_data->components & (VB_HAS_UV0 << i)) != 0 || has_texmtx != 0)
{ {
out.WriteFmt("ATTRIBUTE_LOCATION({}) in float{} rawtex{};\n", SHADER_TEXTURE0_ATTRIB + i, out.Write("ATTRIBUTE_LOCATION({}) in float{} rawtex{};\n", SHADER_TEXTURE0_ATTRIB + i,
has_texmtx != 0 ? 3 : 2, i); has_texmtx != 0 ? 3 : 2, i);
} }
} }
if (host_config.backend_geometry_shaders) if (host_config.backend_geometry_shaders)
{ {
out.WriteFmt("VARYING_LOCATION(0) out VertexData {{\n"); out.Write("VARYING_LOCATION(0) out VertexData {{\n");
GenerateVSOutputMembers(out, api_type, uid_data->numTexGens, host_config, GenerateVSOutputMembers(out, api_type, uid_data->numTexGens, host_config,
GetInterpolationQualifier(msaa, ssaa, true, false)); GetInterpolationQualifier(msaa, ssaa, true, false));
out.WriteFmt("}} vs;\n"); out.Write("}} vs;\n");
} }
else else
{ {
// Let's set up attributes // Let's set up attributes
u32 counter = 0; u32 counter = 0;
out.WriteFmt("VARYING_LOCATION({}) {} out float4 colors_0;\n", counter++, out.Write("VARYING_LOCATION({}) {} out float4 colors_0;\n", counter++,
GetInterpolationQualifier(msaa, ssaa)); GetInterpolationQualifier(msaa, ssaa));
out.WriteFmt("VARYING_LOCATION({}) {} out float4 colors_1;\n", counter++, out.Write("VARYING_LOCATION({}) {} out float4 colors_1;\n", counter++,
GetInterpolationQualifier(msaa, ssaa)); GetInterpolationQualifier(msaa, ssaa));
for (u32 i = 0; i < uid_data->numTexGens; ++i) for (u32 i = 0; i < uid_data->numTexGens; ++i)
{ {
out.WriteFmt("VARYING_LOCATION({}) {} out float3 tex{};\n", counter++, out.Write("VARYING_LOCATION({}) {} out float3 tex{};\n", counter++,
GetInterpolationQualifier(msaa, ssaa), i); GetInterpolationQualifier(msaa, ssaa), i);
} }
if (!host_config.fast_depth_calc) if (!host_config.fast_depth_calc)
{ {
out.WriteFmt("VARYING_LOCATION({}) {} out float4 clipPos;\n", counter++, out.Write("VARYING_LOCATION({}) {} out float4 clipPos;\n", counter++,
GetInterpolationQualifier(msaa, ssaa)); GetInterpolationQualifier(msaa, ssaa));
} }
if (per_pixel_lighting) if (per_pixel_lighting)
{ {
out.WriteFmt("VARYING_LOCATION({}) {} out float3 Normal;\n", counter++, out.Write("VARYING_LOCATION({}) {} out float3 Normal;\n", counter++,
GetInterpolationQualifier(msaa, ssaa)); GetInterpolationQualifier(msaa, ssaa));
out.WriteFmt("VARYING_LOCATION({}) {} out float3 WorldPos;\n", counter++, out.Write("VARYING_LOCATION({}) {} out float3 WorldPos;\n", counter++,
GetInterpolationQualifier(msaa, ssaa)); GetInterpolationQualifier(msaa, ssaa));
} }
} }
out.WriteFmt("void main()\n{{\n"); out.Write("void main()\n{{\n");
} }
else // D3D else // D3D
{ {
out.WriteFmt("VS_OUTPUT main(\n"); out.Write("VS_OUTPUT main(\n");
// inputs // inputs
if ((uid_data->components & VB_HAS_NRM0) != 0) if ((uid_data->components & VB_HAS_NRM0) != 0)
out.WriteFmt(" float3 rawnorm0 : NORMAL0,\n"); out.Write(" float3 rawnorm0 : NORMAL0,\n");
if ((uid_data->components & VB_HAS_NRM1) != 0) if ((uid_data->components & VB_HAS_NRM1) != 0)
out.WriteFmt(" float3 rawnorm1 : NORMAL1,\n"); out.Write(" float3 rawnorm1 : NORMAL1,\n");
if ((uid_data->components & VB_HAS_NRM2) != 0) if ((uid_data->components & VB_HAS_NRM2) != 0)
out.WriteFmt(" float3 rawnorm2 : NORMAL2,\n"); out.Write(" float3 rawnorm2 : NORMAL2,\n");
if ((uid_data->components & VB_HAS_COL0) != 0) if ((uid_data->components & VB_HAS_COL0) != 0)
out.WriteFmt(" float4 rawcolor0 : COLOR0,\n"); out.Write(" float4 rawcolor0 : COLOR0,\n");
if ((uid_data->components & VB_HAS_COL1) != 0) if ((uid_data->components & VB_HAS_COL1) != 0)
out.WriteFmt(" float4 rawcolor1 : COLOR1,\n"); out.Write(" float4 rawcolor1 : COLOR1,\n");
for (u32 i = 0; i < 8; ++i) for (u32 i = 0; i < 8; ++i)
{ {
const u32 has_texmtx = (uid_data->components & (VB_HAS_TEXMTXIDX0 << i)); const u32 has_texmtx = (uid_data->components & (VB_HAS_TEXMTXIDX0 << i));
if ((uid_data->components & (VB_HAS_UV0 << i)) != 0 || has_texmtx != 0) if ((uid_data->components & (VB_HAS_UV0 << i)) != 0 || has_texmtx != 0)
out.WriteFmt(" float{} rawtex{} : TEXCOORD{},\n", has_texmtx ? 3 : 2, i, i); out.Write(" float{} rawtex{} : TEXCOORD{},\n", has_texmtx ? 3 : 2, i, i);
} }
if ((uid_data->components & VB_HAS_POSMTXIDX) != 0) if ((uid_data->components & VB_HAS_POSMTXIDX) != 0)
out.WriteFmt(" uint4 posmtx : BLENDINDICES,\n"); out.Write(" uint4 posmtx : BLENDINDICES,\n");
out.WriteFmt(" float4 rawpos : POSITION) {{\n"); out.Write(" float4 rawpos : POSITION) {{\n");
} }
out.WriteFmt("VS_OUTPUT o;\n"); out.Write("VS_OUTPUT o;\n");
// transforms // transforms
if ((uid_data->components & VB_HAS_POSMTXIDX) != 0) if ((uid_data->components & VB_HAS_POSMTXIDX) != 0)
{ {
out.WriteFmt("int posidx = int(posmtx.r);\n" out.Write("int posidx = int(posmtx.r);\n"
"float4 pos = float4(dot(" I_TRANSFORMMATRICES "float4 pos = float4(dot(" I_TRANSFORMMATRICES
"[posidx], rawpos), dot(" I_TRANSFORMMATRICES "[posidx], rawpos), dot(" I_TRANSFORMMATRICES
"[posidx+1], rawpos), dot(" I_TRANSFORMMATRICES "[posidx+2], rawpos), 1);\n"); "[posidx+1], rawpos), dot(" I_TRANSFORMMATRICES "[posidx+2], rawpos), 1);\n");
if ((uid_data->components & VB_HAS_NRMALL) != 0) if ((uid_data->components & VB_HAS_NRMALL) != 0)
{ {
out.WriteFmt("int normidx = posidx & 31;\n" out.Write("int normidx = posidx & 31;\n"
"float3 N0 = " I_NORMALMATRICES "[normidx].xyz, N1 = " I_NORMALMATRICES "float3 N0 = " I_NORMALMATRICES "[normidx].xyz, N1 = " I_NORMALMATRICES
"[normidx+1].xyz, N2 = " I_NORMALMATRICES "[normidx+2].xyz;\n"); "[normidx+1].xyz, N2 = " I_NORMALMATRICES "[normidx+2].xyz;\n");
} }
if ((uid_data->components & VB_HAS_NRM0) != 0) if ((uid_data->components & VB_HAS_NRM0) != 0)
{ {
out.WriteFmt("float3 _norm0 = normalize(float3(dot(N0, rawnorm0), dot(N1, rawnorm0), dot(N2, " out.Write("float3 _norm0 = normalize(float3(dot(N0, rawnorm0), dot(N1, rawnorm0), dot(N2, "
"rawnorm0)));\n"); "rawnorm0)));\n");
} }
if ((uid_data->components & VB_HAS_NRM1) != 0) if ((uid_data->components & VB_HAS_NRM1) != 0)
{ {
out.WriteFmt( out.Write(
"float3 _norm1 = float3(dot(N0, rawnorm1), dot(N1, rawnorm1), dot(N2, rawnorm1));\n"); "float3 _norm1 = float3(dot(N0, rawnorm1), dot(N1, rawnorm1), dot(N2, rawnorm1));\n");
} }
if ((uid_data->components & VB_HAS_NRM2) != 0) if ((uid_data->components & VB_HAS_NRM2) != 0)
{ {
out.WriteFmt( out.Write(
"float3 _norm2 = float3(dot(N0, rawnorm2), dot(N1, rawnorm2), dot(N2, rawnorm2));\n"); "float3 _norm2 = float3(dot(N0, rawnorm2), dot(N1, rawnorm2), dot(N2, rawnorm2));\n");
} }
} }
else else
{ {
out.WriteFmt("float4 pos = float4(dot(" I_POSNORMALMATRIX "[0], rawpos), dot(" I_POSNORMALMATRIX out.Write("float4 pos = float4(dot(" I_POSNORMALMATRIX "[0], rawpos), dot(" I_POSNORMALMATRIX
"[1], rawpos), dot(" I_POSNORMALMATRIX "[2], rawpos), 1.0);\n"); "[1], rawpos), dot(" I_POSNORMALMATRIX "[2], rawpos), 1.0);\n");
if ((uid_data->components & VB_HAS_NRM0) != 0) if ((uid_data->components & VB_HAS_NRM0) != 0)
{ {
out.WriteFmt("float3 _norm0 = normalize(float3(dot(" I_POSNORMALMATRIX out.Write("float3 _norm0 = normalize(float3(dot(" I_POSNORMALMATRIX
"[3].xyz, rawnorm0), dot(" I_POSNORMALMATRIX "[3].xyz, rawnorm0), dot(" I_POSNORMALMATRIX
"[4].xyz, rawnorm0), dot(" I_POSNORMALMATRIX "[5].xyz, rawnorm0)));\n"); "[4].xyz, rawnorm0), dot(" I_POSNORMALMATRIX "[5].xyz, rawnorm0)));\n");
} }
if ((uid_data->components & VB_HAS_NRM1) != 0) if ((uid_data->components & VB_HAS_NRM1) != 0)
{ {
out.WriteFmt("float3 _norm1 = float3(dot(" I_POSNORMALMATRIX out.Write("float3 _norm1 = float3(dot(" I_POSNORMALMATRIX
"[3].xyz, rawnorm1), dot(" I_POSNORMALMATRIX "[3].xyz, rawnorm1), dot(" I_POSNORMALMATRIX
"[4].xyz, rawnorm1), dot(" I_POSNORMALMATRIX "[5].xyz, rawnorm1));\n"); "[4].xyz, rawnorm1), dot(" I_POSNORMALMATRIX "[5].xyz, rawnorm1));\n");
} }
if ((uid_data->components & VB_HAS_NRM2) != 0) if ((uid_data->components & VB_HAS_NRM2) != 0)
{ {
out.WriteFmt("float3 _norm2 = float3(dot(" I_POSNORMALMATRIX out.Write("float3 _norm2 = float3(dot(" I_POSNORMALMATRIX
"[3].xyz, rawnorm2), dot(" I_POSNORMALMATRIX "[3].xyz, rawnorm2), dot(" I_POSNORMALMATRIX
"[4].xyz, rawnorm2), dot(" I_POSNORMALMATRIX "[5].xyz, rawnorm2));\n"); "[4].xyz, rawnorm2), dot(" I_POSNORMALMATRIX "[5].xyz, rawnorm2));\n");
} }
} }
if ((uid_data->components & VB_HAS_NRM0) == 0) if ((uid_data->components & VB_HAS_NRM0) == 0)
out.WriteFmt("float3 _norm0 = float3(0.0, 0.0, 0.0);\n"); out.Write("float3 _norm0 = float3(0.0, 0.0, 0.0);\n");
out.WriteFmt("o.pos = float4(dot(" I_PROJECTION "[0], pos), dot(" I_PROJECTION out.Write("o.pos = float4(dot(" I_PROJECTION "[0], pos), dot(" I_PROJECTION
"[1], pos), dot(" I_PROJECTION "[2], pos), dot(" I_PROJECTION "[3], pos));\n"); "[1], pos), dot(" I_PROJECTION "[2], pos), dot(" I_PROJECTION "[3], pos));\n");
out.WriteFmt("int4 lacc;\n" out.Write("int4 lacc;\n"
"float3 ldir, h, cosAttn, distAttn;\n" "float3 ldir, h, cosAttn, distAttn;\n"
"float dist, dist2, attn;\n"); "float dist, dist2, attn;\n");
GenerateLightingShaderCode(out, uid_data->lighting, uid_data->components, "rawcolor", GenerateLightingShaderCode(out, uid_data->lighting, uid_data->components, "rawcolor",
"o.colors_"); "o.colors_");
// transform texcoords // transform texcoords
out.WriteFmt("float4 coord = float4(0.0, 0.0, 1.0, 1.0);\n"); out.Write("float4 coord = float4(0.0, 0.0, 1.0, 1.0);\n");
for (u32 i = 0; i < uid_data->numTexGens; ++i) for (u32 i = 0; i < uid_data->numTexGens; ++i)
{ {
auto& texinfo = uid_data->texMtxInfo[i]; auto& texinfo = uid_data->texMtxInfo[i];
out.WriteFmt("{{\n"); out.Write("{{\n");
out.WriteFmt("coord = float4(0.0, 0.0, 1.0, 1.0);\n"); out.Write("coord = float4(0.0, 0.0, 1.0, 1.0);\n");
switch (texinfo.sourcerow) switch (texinfo.sourcerow)
{ {
case XF_SRCGEOM_INROW: case XF_SRCGEOM_INROW:
out.WriteFmt("coord.xyz = rawpos.xyz;\n"); out.Write("coord.xyz = rawpos.xyz;\n");
break; break;
case XF_SRCNORMAL_INROW: case XF_SRCNORMAL_INROW:
if ((uid_data->components & VB_HAS_NRM0) != 0) if ((uid_data->components & VB_HAS_NRM0) != 0)
{ {
out.WriteFmt("coord.xyz = rawnorm0.xyz;\n"); out.Write("coord.xyz = rawnorm0.xyz;\n");
} }
break; break;
case XF_SRCCOLORS_INROW: case XF_SRCCOLORS_INROW:
@ -285,28 +285,28 @@ ShaderCode GenerateVertexShaderCode(APIType api_type, const ShaderHostConfig& ho
case XF_SRCBINORMAL_T_INROW: case XF_SRCBINORMAL_T_INROW:
if ((uid_data->components & VB_HAS_NRM1) != 0) if ((uid_data->components & VB_HAS_NRM1) != 0)
{ {
out.WriteFmt("coord.xyz = rawnorm1.xyz;\n"); out.Write("coord.xyz = rawnorm1.xyz;\n");
} }
break; break;
case XF_SRCBINORMAL_B_INROW: case XF_SRCBINORMAL_B_INROW:
if ((uid_data->components & VB_HAS_NRM2) != 0) if ((uid_data->components & VB_HAS_NRM2) != 0)
{ {
out.WriteFmt("coord.xyz = rawnorm2.xyz;\n"); out.Write("coord.xyz = rawnorm2.xyz;\n");
} }
break; break;
default: default:
ASSERT(texinfo.sourcerow <= XF_SRCTEX7_INROW); ASSERT(texinfo.sourcerow <= XF_SRCTEX7_INROW);
if ((uid_data->components & (VB_HAS_UV0 << (texinfo.sourcerow - XF_SRCTEX0_INROW))) != 0) if ((uid_data->components & (VB_HAS_UV0 << (texinfo.sourcerow - XF_SRCTEX0_INROW))) != 0)
{ {
out.WriteFmt("coord = float4(rawtex{}.x, rawtex{}.y, 1.0, 1.0);\n", out.Write("coord = float4(rawtex{}.x, rawtex{}.y, 1.0, 1.0);\n",
texinfo.sourcerow - XF_SRCTEX0_INROW, texinfo.sourcerow - XF_SRCTEX0_INROW); texinfo.sourcerow - XF_SRCTEX0_INROW, texinfo.sourcerow - XF_SRCTEX0_INROW);
} }
break; break;
} }
// Input form of AB11 sets z element to 1.0 // Input form of AB11 sets z element to 1.0
if (texinfo.inputform == XF_TEXINPUT_AB11) if (texinfo.inputform == XF_TEXINPUT_AB11)
out.WriteFmt("coord.z = 1.0;\n"); out.Write("coord.z = 1.0;\n");
// first transformation // first transformation
switch (texinfo.texgentype) switch (texinfo.texgentype)
@ -316,9 +316,9 @@ ShaderCode GenerateVertexShaderCode(APIType api_type, const ShaderHostConfig& ho
if ((uid_data->components & (VB_HAS_NRM1 | VB_HAS_NRM2)) != 0) if ((uid_data->components & (VB_HAS_NRM1 | VB_HAS_NRM2)) != 0)
{ {
// transform the light dir into tangent space // transform the light dir into tangent space
out.WriteFmt("ldir = normalize(" LIGHT_POS ".xyz - pos.xyz);\n", out.Write("ldir = normalize(" LIGHT_POS ".xyz - pos.xyz);\n",
LIGHT_POS_PARAMS(texinfo.embosslightshift)); LIGHT_POS_PARAMS(texinfo.embosslightshift));
out.WriteFmt( out.Write(
"o.tex{}.xyz = o.tex{}.xyz + float3(dot(ldir, _norm1), dot(ldir, _norm2), 0.0);\n", i, "o.tex{}.xyz = o.tex{}.xyz + float3(dot(ldir, _norm1), dot(ldir, _norm2), 0.0);\n", i,
texinfo.embosssourceshift); texinfo.embosssourceshift);
} }
@ -327,49 +327,49 @@ ShaderCode GenerateVertexShaderCode(APIType api_type, const ShaderHostConfig& ho
// The following assert was triggered in House of the Dead Overkill and Star Wars Rogue // The following assert was triggered in House of the Dead Overkill and Star Wars Rogue
// Squadron 2 // Squadron 2
// ASSERT(0); // should have normals // ASSERT(0); // should have normals
out.WriteFmt("o.tex{}.xyz = o.tex{}.xyz;\n", i, texinfo.embosssourceshift); out.Write("o.tex{}.xyz = o.tex{}.xyz;\n", i, texinfo.embosssourceshift);
} }
break; break;
case XF_TEXGEN_COLOR_STRGBC0: case XF_TEXGEN_COLOR_STRGBC0:
out.WriteFmt("o.tex{}.xyz = float3(o.colors_0.x, o.colors_0.y, 1);\n", i); out.Write("o.tex{}.xyz = float3(o.colors_0.x, o.colors_0.y, 1);\n", i);
break; break;
case XF_TEXGEN_COLOR_STRGBC1: case XF_TEXGEN_COLOR_STRGBC1:
out.WriteFmt("o.tex{}.xyz = float3(o.colors_1.x, o.colors_1.y, 1);\n", i); out.Write("o.tex{}.xyz = float3(o.colors_1.x, o.colors_1.y, 1);\n", i);
break; break;
case XF_TEXGEN_REGULAR: case XF_TEXGEN_REGULAR:
default: default:
if ((uid_data->components & (VB_HAS_TEXMTXIDX0 << i)) != 0) if ((uid_data->components & (VB_HAS_TEXMTXIDX0 << i)) != 0)
{ {
out.WriteFmt("int tmp = int(rawtex{}.z);\n", i); out.Write("int tmp = int(rawtex{}.z);\n", i);
if (((uid_data->texMtxInfo_n_projection >> i) & 1) == XF_TEXPROJ_STQ) if (((uid_data->texMtxInfo_n_projection >> i) & 1) == XF_TEXPROJ_STQ)
{ {
out.WriteFmt("o.tex{}.xyz = float3(dot(coord, " I_TRANSFORMMATRICES out.Write("o.tex{}.xyz = float3(dot(coord, " I_TRANSFORMMATRICES
"[tmp]), dot(coord, " I_TRANSFORMMATRICES "[tmp]), dot(coord, " I_TRANSFORMMATRICES
"[tmp+1]), dot(coord, " I_TRANSFORMMATRICES "[tmp+2]));\n", "[tmp+1]), dot(coord, " I_TRANSFORMMATRICES "[tmp+2]));\n",
i); i);
} }
else else
{ {
out.WriteFmt("o.tex{}.xyz = float3(dot(coord, " I_TRANSFORMMATRICES out.Write("o.tex{}.xyz = float3(dot(coord, " I_TRANSFORMMATRICES
"[tmp]), dot(coord, " I_TRANSFORMMATRICES "[tmp+1]), 1);\n", "[tmp]), dot(coord, " I_TRANSFORMMATRICES "[tmp+1]), 1);\n",
i); i);
} }
} }
else else
{ {
if (((uid_data->texMtxInfo_n_projection >> i) & 1) == XF_TEXPROJ_STQ) if (((uid_data->texMtxInfo_n_projection >> i) & 1) == XF_TEXPROJ_STQ)
{ {
out.WriteFmt("o.tex{}.xyz = float3(dot(coord, " I_TEXMATRICES out.Write("o.tex{}.xyz = float3(dot(coord, " I_TEXMATRICES
"[{}]), dot(coord, " I_TEXMATRICES "[{}]), dot(coord, " I_TEXMATRICES "[{}]), dot(coord, " I_TEXMATRICES "[{}]), dot(coord, " I_TEXMATRICES
"[{}]));\n", "[{}]));\n",
i, 3 * i, 3 * i + 1, 3 * i + 2); i, 3 * i, 3 * i + 1, 3 * i + 2);
} }
else else
{ {
out.WriteFmt("o.tex{}.xyz = float3(dot(coord, " I_TEXMATRICES out.Write("o.tex{}.xyz = float3(dot(coord, " I_TEXMATRICES
"[{}]), dot(coord, " I_TEXMATRICES "[{}]), 1);\n", "[{}]), dot(coord, " I_TEXMATRICES "[{}]), 1);\n",
i, 3 * i, 3 * i + 1); i, 3 * i, 3 * i + 1);
} }
} }
break; break;
@ -380,16 +380,16 @@ ShaderCode GenerateVertexShaderCode(APIType api_type, const ShaderHostConfig& ho
{ {
auto& postInfo = uid_data->postMtxInfo[i]; auto& postInfo = uid_data->postMtxInfo[i];
out.WriteFmt("float4 P0 = " I_POSTTRANSFORMMATRICES "[{}];\n" out.Write("float4 P0 = " I_POSTTRANSFORMMATRICES "[{}];\n"
"float4 P1 = " I_POSTTRANSFORMMATRICES "[{}];\n" "float4 P1 = " I_POSTTRANSFORMMATRICES "[{}];\n"
"float4 P2 = " I_POSTTRANSFORMMATRICES "[{}];\n", "float4 P2 = " I_POSTTRANSFORMMATRICES "[{}];\n",
postInfo.index & 0x3f, (postInfo.index + 1) & 0x3f, (postInfo.index + 2) & 0x3f); postInfo.index & 0x3f, (postInfo.index + 1) & 0x3f, (postInfo.index + 2) & 0x3f);
if (postInfo.normalize) if (postInfo.normalize)
out.WriteFmt("o.tex{}.xyz = normalize(o.tex{}.xyz);\n", i, i); out.Write("o.tex{}.xyz = normalize(o.tex{}.xyz);\n", i, i);
// multiply by postmatrix // multiply by postmatrix
out.WriteFmt( out.Write(
"o.tex{0}.xyz = float3(dot(P0.xyz, o.tex{0}.xyz) + P0.w, dot(P1.xyz, o.tex{0}.xyz) + " "o.tex{0}.xyz = float3(dot(P0.xyz, o.tex{0}.xyz) + P0.w, dot(P1.xyz, o.tex{0}.xyz) + "
"P1.w, dot(P2.xyz, o.tex{0}.xyz) + P2.w);\n", "P1.w, dot(P2.xyz, o.tex{0}.xyz) + P2.w);\n",
i); i);
@ -401,44 +401,44 @@ ShaderCode GenerateVertexShaderCode(APIType api_type, const ShaderHostConfig& ho
// TODO: check if this only affects XF_TEXGEN_REGULAR // TODO: check if this only affects XF_TEXGEN_REGULAR
if (texinfo.texgentype == XF_TEXGEN_REGULAR) if (texinfo.texgentype == XF_TEXGEN_REGULAR)
{ {
out.WriteFmt( out.Write(
"if(o.tex{0}.z == 0.0f)\n" "if(o.tex{0}.z == 0.0f)\n"
"\to.tex{0}.xy = clamp(o.tex{0}.xy / 2.0f, float2(-1.0f,-1.0f), float2(1.0f,1.0f));\n", "\to.tex{0}.xy = clamp(o.tex{0}.xy / 2.0f, float2(-1.0f,-1.0f), float2(1.0f,1.0f));\n",
i); i);
} }
out.WriteFmt("}}\n"); out.Write("}}\n");
} }
if (uid_data->numColorChans == 0) if (uid_data->numColorChans == 0)
{ {
if ((uid_data->components & VB_HAS_COL0) != 0) if ((uid_data->components & VB_HAS_COL0) != 0)
out.WriteFmt("o.colors_0 = rawcolor0;\n"); out.Write("o.colors_0 = rawcolor0;\n");
else else
out.WriteFmt("o.colors_0 = float4(1.0, 1.0, 1.0, 1.0);\n"); out.Write("o.colors_0 = float4(1.0, 1.0, 1.0, 1.0);\n");
} }
if (uid_data->numColorChans < 2) if (uid_data->numColorChans < 2)
{ {
if ((uid_data->components & VB_HAS_COL1) != 0) if ((uid_data->components & VB_HAS_COL1) != 0)
out.WriteFmt("o.colors_1 = rawcolor1;\n"); out.Write("o.colors_1 = rawcolor1;\n");
else else
out.WriteFmt("o.colors_1 = o.colors_0;\n"); out.Write("o.colors_1 = o.colors_0;\n");
} }
// clipPos/w needs to be done in pixel shader, not here // clipPos/w needs to be done in pixel shader, not here
if (!host_config.fast_depth_calc) if (!host_config.fast_depth_calc)
out.WriteFmt("o.clipPos = o.pos;\n"); out.Write("o.clipPos = o.pos;\n");
if (per_pixel_lighting) if (per_pixel_lighting)
{ {
out.WriteFmt("o.Normal = _norm0;\n" out.Write("o.Normal = _norm0;\n"
"o.WorldPos = pos.xyz;\n"); "o.WorldPos = pos.xyz;\n");
if ((uid_data->components & VB_HAS_COL0) != 0) if ((uid_data->components & VB_HAS_COL0) != 0)
out.WriteFmt("o.colors_0 = rawcolor0;\n"); out.Write("o.colors_0 = rawcolor0;\n");
if ((uid_data->components & VB_HAS_COL1) != 0) if ((uid_data->components & VB_HAS_COL1) != 0)
out.WriteFmt("o.colors_1 = rawcolor1;\n"); out.Write("o.colors_1 = rawcolor1;\n");
} }
// If we can disable the incorrect depth clipping planes using depth clamping, then we can do // If we can disable the incorrect depth clipping planes using depth clamping, then we can do
@ -450,14 +450,14 @@ ShaderCode GenerateVertexShaderCode(APIType api_type, const ShaderHostConfig& ho
// own clipping. We want to clip so that -w <= z <= 0, which matches the console -1..0 range. // own clipping. We want to clip so that -w <= z <= 0, which matches the console -1..0 range.
// We adjust our depth value for clipping purposes to match the perspective projection in the // We adjust our depth value for clipping purposes to match the perspective projection in the
// software backend, which is a hack to fix Sonic Adventure and Unleashed games. // software backend, which is a hack to fix Sonic Adventure and Unleashed games.
out.WriteFmt("float clipDepth = o.pos.z * (1.0 - 1e-7);\n" out.Write("float clipDepth = o.pos.z * (1.0 - 1e-7);\n"
"float clipDist0 = clipDepth + o.pos.w;\n" // Near: z < -w "float clipDist0 = clipDepth + o.pos.w;\n" // Near: z < -w
"float clipDist1 = -clipDepth;\n"); // Far: z > 0 "float clipDist1 = -clipDepth;\n"); // Far: z > 0
if (host_config.backend_geometry_shaders) if (host_config.backend_geometry_shaders)
{ {
out.WriteFmt("o.clipDist0 = clipDist0;\n" out.Write("o.clipDist0 = clipDist0;\n"
"o.clipDist1 = clipDist1;\n"); "o.clipDist1 = clipDist1;\n");
} }
} }
@ -472,20 +472,20 @@ ShaderCode GenerateVertexShaderCode(APIType api_type, const ShaderHostConfig& ho
// divide, because some games will use a depth range larger than what is allowed by the // divide, because some games will use a depth range larger than what is allowed by the
// graphics API. These large depth ranges will still be clipped to the 0..1 range, so these // graphics API. These large depth ranges will still be clipped to the 0..1 range, so these
// games effectively add a depth bias to the values written to the depth buffer. // games effectively add a depth bias to the values written to the depth buffer.
out.WriteFmt("o.pos.z = o.pos.w * " I_PIXELCENTERCORRECTION ".w - " out.Write("o.pos.z = o.pos.w * " I_PIXELCENTERCORRECTION ".w - "
"o.pos.z * " I_PIXELCENTERCORRECTION ".z;\n"); "o.pos.z * " I_PIXELCENTERCORRECTION ".z;\n");
if (!host_config.backend_clip_control) if (!host_config.backend_clip_control)
{ {
// If the graphics API doesn't support a depth range of 0..1, then we need to map z to // If the graphics API doesn't support a depth range of 0..1, then we need to map z to
// the -1..1 range. Unfortunately we have to use a substraction, which is a lossy floating-point // the -1..1 range. Unfortunately we have to use a substraction, which is a lossy floating-point
// operation that can introduce a round-trip error. // operation that can introduce a round-trip error.
out.WriteFmt("o.pos.z = o.pos.z * 2.0 - o.pos.w;\n"); out.Write("o.pos.z = o.pos.z * 2.0 - o.pos.w;\n");
} }
// Correct for negative viewports by mirroring all vertices. We need to negate the height here, // Correct for negative viewports by mirroring all vertices. We need to negate the height here,
// since the viewport height is already negated by the render backend. // since the viewport height is already negated by the render backend.
out.WriteFmt("o.pos.xy *= sign(" I_PIXELCENTERCORRECTION ".xy * float2(1.0, -1.0));\n"); out.Write("o.pos.xy *= sign(" I_PIXELCENTERCORRECTION ".xy * float2(1.0, -1.0));\n");
// The console GPU places the pixel center at 7/12 in screen space unless // The console GPU places the pixel center at 7/12 in screen space unless
// antialiasing is enabled, while D3D and OpenGL place it at 0.5. This results // antialiasing is enabled, while D3D and OpenGL place it at 0.5. This results
@ -493,7 +493,7 @@ ShaderCode GenerateVertexShaderCode(APIType api_type, const ShaderHostConfig& ho
// which in turn can be critical if it happens for clear quads. // which in turn can be critical if it happens for clear quads.
// Hence, we compensate for this pixel center difference so that primitives // Hence, we compensate for this pixel center difference so that primitives
// get rasterized correctly. // get rasterized correctly.
out.WriteFmt("o.pos.xy = o.pos.xy - o.pos.w * " I_PIXELCENTERCORRECTION ".xy;\n"); out.Write("o.pos.xy = o.pos.xy - o.pos.w * " I_PIXELCENTERCORRECTION ".xy;\n");
if (vertex_rounding) if (vertex_rounding)
{ {
@ -504,18 +504,18 @@ ShaderCode GenerateVertexShaderCode(APIType api_type, const ShaderHostConfig& ho
// we need to correct this by converting our // we need to correct this by converting our
// clip-space position into the Wii's screen-space // clip-space position into the Wii's screen-space
// acquire the right pixel and then convert it back // acquire the right pixel and then convert it back
out.WriteFmt("if (o.pos.w == 1.0f)\n" out.Write("if (o.pos.w == 1.0f)\n"
"{{\n" "{{\n"
"\tfloat ss_pixel_x = ((o.pos.x + 1.0f) * (" I_VIEWPORT_SIZE ".x * 0.5f));\n" "\tfloat ss_pixel_x = ((o.pos.x + 1.0f) * (" I_VIEWPORT_SIZE ".x * 0.5f));\n"
"\tfloat ss_pixel_y = ((o.pos.y + 1.0f) * (" I_VIEWPORT_SIZE ".y * 0.5f));\n" "\tfloat ss_pixel_y = ((o.pos.y + 1.0f) * (" I_VIEWPORT_SIZE ".y * 0.5f));\n"
"\tss_pixel_x = round(ss_pixel_x);\n" "\tss_pixel_x = round(ss_pixel_x);\n"
"\tss_pixel_y = round(ss_pixel_y);\n" "\tss_pixel_y = round(ss_pixel_y);\n"
"\to.pos.x = ((ss_pixel_x / (" I_VIEWPORT_SIZE ".x * 0.5f)) - 1.0f);\n" "\to.pos.x = ((ss_pixel_x / (" I_VIEWPORT_SIZE ".x * 0.5f)) - 1.0f);\n"
"\to.pos.y = ((ss_pixel_y / (" I_VIEWPORT_SIZE ".y * 0.5f)) - 1.0f);\n" "\to.pos.y = ((ss_pixel_y / (" I_VIEWPORT_SIZE ".y * 0.5f)) - 1.0f);\n"
"}}\n"); "}}\n");
} }
if (api_type == APIType::OpenGL || api_type == APIType::Vulkan) if (api_type == APIType::OpenGL || api_type == APIType::Vulkan)
@ -529,35 +529,35 @@ ShaderCode GenerateVertexShaderCode(APIType api_type, const ShaderHostConfig& ho
// TODO: Pass interface blocks between shader stages even if geometry shaders // TODO: Pass interface blocks between shader stages even if geometry shaders
// are not supported, however that will require at least OpenGL 3.2 support. // are not supported, however that will require at least OpenGL 3.2 support.
for (u32 i = 0; i < uid_data->numTexGens; ++i) for (u32 i = 0; i < uid_data->numTexGens; ++i)
out.WriteFmt("tex{}.xyz = o.tex{};\n", i, i); out.Write("tex{}.xyz = o.tex{};\n", i, i);
if (!host_config.fast_depth_calc) if (!host_config.fast_depth_calc)
out.WriteFmt("clipPos = o.clipPos;\n"); out.Write("clipPos = o.clipPos;\n");
if (per_pixel_lighting) if (per_pixel_lighting)
{ {
out.WriteFmt("Normal = o.Normal;\n" out.Write("Normal = o.Normal;\n"
"WorldPos = o.WorldPos;\n"); "WorldPos = o.WorldPos;\n");
} }
out.WriteFmt("colors_0 = o.colors_0;\n" out.Write("colors_0 = o.colors_0;\n"
"colors_1 = o.colors_1;\n"); "colors_1 = o.colors_1;\n");
} }
if (host_config.backend_depth_clamp) if (host_config.backend_depth_clamp)
{ {
out.WriteFmt("gl_ClipDistance[0] = clipDist0;\n" out.Write("gl_ClipDistance[0] = clipDist0;\n"
"gl_ClipDistance[1] = clipDist1;\n"); "gl_ClipDistance[1] = clipDist1;\n");
} }
// Vulkan NDC space has Y pointing down (right-handed NDC space). // Vulkan NDC space has Y pointing down (right-handed NDC space).
if (api_type == APIType::Vulkan) if (api_type == APIType::Vulkan)
out.WriteFmt("gl_Position = float4(o.pos.x, -o.pos.y, o.pos.z, o.pos.w);\n"); out.Write("gl_Position = float4(o.pos.x, -o.pos.y, o.pos.z, o.pos.w);\n");
else else
out.WriteFmt("gl_Position = o.pos;\n"); out.Write("gl_Position = o.pos;\n");
} }
else // D3D else // D3D
{ {
out.WriteFmt("return o;\n"); out.Write("return o;\n");
} }
out.WriteFmt("}}\n"); out.Write("}}\n");
return out; return out;
} }