@@ -4,12 +4,12 @@
#include " VideoCommon/FramebufferShaderGen.h"
#include < sstream>
#include < string_view>
#include " Common/Logging/Log.h"
#include " VideoCommon/FramebufferManager.h"
#include " VideoCommon/ShaderGenCommon.h"
#include " VideoCommon/TextureDecoder.h"
#include " VideoCommon/VertexShaderGen.h"
#include " VideoCommon/VideoCommon.h"
@@ -24,38 +24,39 @@ APIType GetAPIType()
return g_ActiveConfig.backend_info .api_type ;
}
void EmitUniformBufferDeclaration (std::ostringstream& ss )
void EmitUniformBufferDeclaration (ShaderCode& code )
{
if (GetAPIType () == APIType::D3D)
ss << " cbuffer PSBlock : register(b0)\n " ;
code. WriteFmt ( " cbuffer PSBlock : register(b0)\n " ) ;
else
ss << " UBO_BINDING(std140, 1) uniform PSBlock\n " ;
code. WriteFmt ( " UBO_BINDING(std140, 1) uniform PSBlock\n " ) ;
}
void EmitSamplerDeclarations (std::ostringstream& ss , u32 start = 0 , u32 end = 1 ,
void EmitSamplerDeclarations (ShaderCode& code , u32 start = 0 , u32 end = 1 ,
bool multisampled = false )
{
switch (GetAPIType ())
{
case APIType::D3D:
{
const char * array_type = multisampled ? " Texture2DMSArray<float4>" : " Texture2DArray<float4>" ;
for (u32 i = start; i < end; i++)
{
ss << (multisampled ? " Texture2DMSArray<float4>" : " Texture2DArray<float4>" ) << " tex" << i
<< " : register(t" << i << " );\n " ;
ss << " SamplerState"
<< " samp" << i << " : register(s" << i << " );\n " ;
code.WriteFmt (" {} tex{} : register(t{});\n " , array_type, i, i);
code.WriteFmt (" SamplerState samp{} : register(s{});\n " , i, i);
}
}
break ;
case APIType::OpenGL:
case APIType::Vulkan:
{
const char * array_type = multisampled ? " sampler2DMSArray" : " sampler2DArray" ;
for (u32 i = start; i < end; i++)
{
ss << " SAMPLER_BINDING(" << i << " ) uniform "
<< (multisampled ? " sampler2DMSArray" : " sampler2DArray" ) << " samp" << i << " ;\n " ;
code.WriteFmt (" SAMPLER_BINDING({}) uniform {} samp{};\n " , i, array_type, i);
}
}
break ;
@@ -64,17 +65,17 @@ void EmitSamplerDeclarations(std::ostringstream& ss, u32 start = 0, u32 end = 1,
}
}
void EmitSampleTexture (std::ostringstream& ss , u32 n, std::string_view coords)
void EmitSampleTexture (ShaderCode& code , u32 n, std::string_view coords)
{
switch (GetAPIType ())
{
case APIType::D3D:
ss << " tex" << n << " .Sample(samp" << n << " , " << coords << ' ) ' ;
code. WriteFmt ( " tex{} .Sample(samp{}, {}) " , n, n, coords) ;
break ;
case APIType::OpenGL:
case APIType::Vulkan:
ss << " texture(samp" << n << " , " << coords << ' ) ' ;
code. WriteFmt ( " texture(samp{}, {}) " , n, coords) ;
break ;
default :
@@ -84,45 +85,45 @@ void EmitSampleTexture(std::ostringstream& ss, u32 n, std::string_view coords)
// Emits a texel fetch/load instruction. Assumes that "coords" is a 4-element vector, with z
// containing the layer, and w containing the mipmap level.
void EmitTextureLoad (std::ostringstream& ss , u32 n, std::string_view coords)
void EmitTextureLoad (ShaderCode& code , u32 n, std::string_view coords)
{
switch (GetAPIType ())
{
case APIType::D3D:
ss << " tex" << n << " .Load(" << coords << ' ) ' ;
code. WriteFmt ( " tex{} .Load({}) " , n, coords) ;
break ;
case APIType::OpenGL:
case APIType::Vulkan:
ss << " texelFetch(samp" << n << " , (" << coords << " ).xyz, (" << coords << " ).w) " ;
code. WriteFmt ( " texelFetch(samp{} , ({} ).xyz, ({}).w) " , n, coords, coords) ;
break ;
default :
break ;
}
}
void EmitVertexMainDeclaration (std::ostringstream& ss , u32 num_tex_inputs, u32 num_color_inputs,
void EmitVertexMainDeclaration (ShaderCode& code , u32 num_tex_inputs, u32 num_color_inputs,
bool position_input, u32 num_tex_outputs, u32 num_color_outputs,
std::string_view extra_inputs = {})
{
switch (GetAPIType ())
{
case APIType::D3D:
{
ss << " void main(" ;
code. WriteFmt ( " void main(" ) ;
for (u32 i = 0 ; i < num_tex_inputs; i++)
ss << " in float3 rawtex" << i << " : TEXCOORD" << i << " , " ;
code. WriteFmt ( " in float3 rawtex{} : TEXCOORD{}, " , i, i) ;
for (u32 i = 0 ; i < num_color_inputs; i++)
ss << " in float4 rawcolor" << i << " : COLOR" << i << " , " ;
code. WriteFmt ( " in float4 rawcolor{} : COLOR{}, " , i, i) ;
if (position_input)
ss << " in float4 rawpos : POSITION, " ;
ss << extra_inputs;
code. WriteFmt ( " in float4 rawpos : POSITION, " ) ;
code. WriteFmt ( " {} " , extra_inputs) ;
for (u32 i = 0 ; i < num_tex_outputs; i++)
ss << " out float3 v_tex" << i << " : TEXCOORD" << i << " , " ;
code. WriteFmt ( " out float3 v_tex{} : TEXCOORD{}, " , i, i) ;
for (u32 i = 0 ; i < num_color_outputs; i++)
ss << " out float4 v_col" << i << " : COLOR" << i << " , " ;
ss << " out float4 opos : SV_Position)\n " ;
code. WriteFmt ( " out float4 v_col{} : COLOR{}, " , i, i) ;
code. WriteFmt ( " out float4 opos : SV_Position)\n " ) ;
}
break ;
@@ -131,59 +132,59 @@ void EmitVertexMainDeclaration(std::ostringstream& ss, u32 num_tex_inputs, u32 n
{
for (u32 i = 0 ; i < num_tex_inputs; i++)
{
ss << " ATTRIBUTE_LOCATION( " << ( SHADER_TEXTURE0_ATTRIB + i) << " ) in float3 rawtex " << i
<< " ;\n " ;
const auto attribute = SHADER_TEXTURE0_ATTRIB + i;
code. WriteFmt ( " ATTRIBUTE_LOCATION({}) in float3 rawtex{} ;\n " , attribute, i) ;
}
for (u32 i = 0 ; i < num_color_inputs; i++)
{
ss << " ATTRIBUTE_LOCATION( " << ( SHADER_COLOR0_ATTRIB + i) << " ) in float4 rawcolor " << i
<< " ;\n " ;
const auto attribute = SHADER_COLOR0_ATTRIB + i;
code. WriteFmt ( " ATTRIBUTE_LOCATION({}) in float4 rawcolor{} ;\n " , attribute, i) ;
}
if (position_input)
ss << " ATTRIBUTE_LOCATION(" << SHADER_POSITION_ATTRIB << " ) in float4 rawpos;\n " ;
code. WriteFmt ( " ATTRIBUTE_LOCATION({} ) in float4 rawpos;\n " , SHADER_POSITION_ATTRIB) ;
if (g_ActiveConfig.backend_info .bSupportsGeometryShaders )
{
ss << " VARYING_LOCATION(0) out VertexData {\n " ;
code. WriteFmt ( " VARYING_LOCATION(0) out VertexData {{ \n " ) ;
for (u32 i = 0 ; i < num_tex_outputs; i++)
ss << " float3 v_tex" << i << " ;\n " ;
code. WriteFmt ( " float3 v_tex{} ;\n " , i) ;
for (u32 i = 0 ; i < num_color_outputs; i++)
ss << " float4 v_col" << i << " ;\n " ;
ss << " } ;\n " ;
code. WriteFmt ( " float4 v_col{} ;\n " , i) ;
code. WriteFmt ( " }} ;\n " ) ;
}
else
{
for (u32 i = 0 ; i < num_tex_outputs; i++)
ss << " VARYING_LOCATION(" << i << " ) out float3 v_tex" << i << " ;\n " ;
code. WriteFmt ( " VARYING_LOCATION({} ) out float3 v_tex{} ;\n " , i, i) ;
for (u32 i = 0 ; i < num_color_outputs; i++)
ss << " VARYING_LOCATION(" << (num_tex_inputs + i) << " ) out float4 v_col" << i << " ; \n " ;
code. WriteFmt ( " VARYING_LOCATION({}) out float4 v_col{}; \n " , num_tex_inputs + i, i) ;
}
ss << " #define opos gl_Position\n " ;
ss << extra_inputs << ' \n ' ;
ss << " void main()\n " ;
code. WriteFmt ( " #define opos gl_Position\n " ) ;
code. WriteFmt ( " {} \n " , extra_inputs) ;
code. WriteFmt ( " void main()\n " ) ;
}
break ;
default :
break ;
}
}
void EmitPixelMainDeclaration (std::ostringstream& ss , u32 num_tex_inputs, u32 num_color_inputs,
void EmitPixelMainDeclaration (ShaderCode& code , u32 num_tex_inputs, u32 num_color_inputs,
std::string_view output_type = " float4" ,
std::string_view extra_vars = {}, bool emit_frag_coord = false )
{
switch (GetAPIType ())
{
case APIType::D3D:
{
ss << " void main(" ;
code. WriteFmt ( " void main(" ) ;
for (u32 i = 0 ; i < num_tex_inputs; i++)
ss << " in float3 v_tex" << i << " : TEXCOORD" << i << " , " ;
code. WriteFmt ( " in float3 v_tex{} : TEXCOORD{}, " , i, i) ;
for (u32 i = 0 ; i < num_color_inputs; i++)
ss << " in float4 v_col" << i << " : COLOR" << i << " , " ;
code. WriteFmt ( " in float4 v_col{} : COLOR{}, " , i, i) ;
if (emit_frag_coord)
ss << " in float4 frag_coord : SV_Position, " ;
ss << extra_vars << " out " << output_type << " ocol0 : SV_Target)\n " ;
code. WriteFmt ( " in float4 frag_coord : SV_Position, " ) ;
code. WriteFmt ( " {} out {} ocol0 : SV_Target)\n " , extra_vars, output_type) ;
}
break ;
@@ -192,26 +193,26 @@ void EmitPixelMainDeclaration(std::ostringstream& ss, u32 num_tex_inputs, u32 nu
{
if (g_ActiveConfig.backend_info .bSupportsGeometryShaders )
{
ss << " VARYING_LOCATION(0) in VertexData {\n " ;
code. WriteFmt ( " VARYING_LOCATION(0) in VertexData {{ \n " ) ;
for (u32 i = 0 ; i < num_tex_inputs; i++)
ss << " in float3 v_tex" << i << " ;\n " ;
code. WriteFmt ( " in float3 v_tex{} ;\n " , i) ;
for (u32 i = 0 ; i < num_color_inputs; i++)
ss << " in float4 v_col" << i << " ;\n " ;
ss << " } ;\n " ;
code. WriteFmt ( " in float4 v_col{} ;\n " , i) ;
code. WriteFmt ( " }} ;\n " ) ;
}
else
{
for (u32 i = 0 ; i < num_tex_inputs; i++)
ss << " VARYING_LOCATION(" << i << " ) in float3 v_tex" << i << " ;\n " ;
code. WriteFmt ( " VARYING_LOCATION({} ) in float3 v_tex{} ;\n " , i, i) ;
for (u32 i = 0 ; i < num_color_inputs; i++)
ss << " VARYING_LOCATION(" << (num_tex_inputs + i) << " ) in float4 v_col" << i << " ; \n " ;
code. WriteFmt ( " VARYING_LOCATION({}) in float4 v_col{}; \n " , num_tex_inputs + i, i) ;
}
ss << " FRAGMENT_OUTPUT_LOCATION(0) out " << output_type << " ocol0;\n " ;
ss << extra_vars << " \n " ;
code. WriteFmt ( " FRAGMENT_OUTPUT_LOCATION(0) out {} ocol0;\n " , output_type) ;
code. WriteFmt ( " {} \n " , extra_vars) ;
if (emit_frag_coord)
ss << " #define frag_coord gl_FragCoord\n " ;
ss << " void main()\n " ;
code. WriteFmt ( " #define frag_coord gl_FragCoord\n " ) ;
code. WriteFmt ( " void main()\n " ) ;
}
break ;
@@ -223,399 +224,406 @@ void EmitPixelMainDeclaration(std::ostringstream& ss, u32 num_tex_inputs, u32 nu
std::string GenerateScreenQuadVertexShader ()
{
std::ostringstream ss ;
EmitVertexMainDeclaration (ss , 0 , 0 , false , 1 , 0 ,
ShaderCode code ;
EmitVertexMainDeclaration (code , 0 , 0 , false , 1 , 0 ,
GetAPIType () == APIType::D3D ? " in uint id : SV_VertexID, " :
" #define id gl_VertexID\n " );
ss << " {\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 " ;
code.WriteFmt (
" {{\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 " );
// 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)
ss << " opos.y = -opos.y;\n " ;
code. WriteFmt ( " opos.y = -opos.y;\n " ) ;
ss << " } \n " ;
code. WriteFmt ( " }} \n " ) ;
return ss. str ();
return code. GetBuffer ();
}
std::string GeneratePassthroughGeometryShader (u32 num_tex, u32 num_colors)
{
std::ostringstream ss ;
ShaderCode code ;
if (GetAPIType () == APIType::D3D)
{
ss << " struct VS_OUTPUT\n "
" { \n " ;
code. WriteFmt ( " struct VS_OUTPUT\n "
" {{ \n " ) ;
for (u32 i = 0 ; i < num_tex; i++)
ss << " float3 tex" << i << " : TEXCOORD" << i << " ;\n " ;
code. WriteFmt ( " float3 tex{} : TEXCOORD{} ;\n " , i, i) ;
for (u32 i = 0 ; i < num_colors; i++)
ss << " float4 color" << i << " : COLOR" << i << " ;\n " ;
ss << " float4 position : SV_Position;\n "
" } ;\n " ;
code. WriteFmt ( " float4 color{} : COLOR{} ;\n " , i, i) ;
code. WriteFmt ( " float4 position : SV_Position;\n "
" }} ;\n " ) ;
ss << " struct GS_OUTPUT\n "
" { " ;
code. WriteFmt ( " struct GS_OUTPUT\n "
" {{ " ) ;
for (u32 i = 0 ; i < num_tex; i++)
ss << " float3 tex" << i << " : TEXCOORD" << i << " ;\n " ;
code. WriteFmt ( " float3 tex{} : TEXCOORD{} ;\n " , i, i) ;
for (u32 i = 0 ; i < num_colors; i++)
ss << " float4 color" << i << " : COLOR" << i << " ;\n " ;
ss << " float4 position : SV_Position;\n "
" uint slice : SV_RenderTargetArrayIndex;\n "
" } ;\n\n " ;
ss << " [maxvertexcount(6)]\n "
" void main(triangle VS_OUTPUT vso[3], inout TriangleStream<GS_OUTPUT> output)\n "
" {\n "
" for (uint slice = 0; slice < 2u; slice++)\n "
" {\n "
" for (int i = 0; i < 3; i++)\n "
" {\n "
" GS_OUTPUT gso;\n "
" gso.position = vso[i].position;\n " ;
code. WriteFmt ( " float4 color{} : COLOR{} ;\n " , i, i) ;
code. WriteFmt ( " float4 position : SV_Position;\n "
" uint slice : SV_RenderTargetArrayIndex;\n "
" }} ;\n\n " ) ;
code. WriteFmt ( " [maxvertexcount(6)]\n "
" void main(triangle VS_OUTPUT vso[3], inout TriangleStream<GS_OUTPUT> output)\n "
" { {\n "
" for (uint slice = 0; slice < 2u; slice++)\n "
" { {\n "
" for (int i = 0; i < 3; i++)\n "
" { {\n "
" GS_OUTPUT gso;\n "
" gso.position = vso[i].position;\n " ) ;
for (u32 i = 0 ; i < num_tex; i++)
ss << " gso.tex" << i << " = float3(vso[i].tex" << i << " .xy, float(slice));\n " ;
code. WriteFmt ( " gso.tex{} = float3(vso[i].tex{} .xy, float(slice));\n " , i, i) ;
for (u32 i = 0 ; i < num_colors; i++)
ss << " gso.color" << i << " = vso[i].color" << i << " ;\n " ;
ss << " gso.slice = slice;\n "
" output.Append(gso);\n "
" }\n "
" output.RestartStrip();\n "
" }\n "
" } \n " ;
code. WriteFmt ( " gso.color{} = vso[i].color{} ;\n " , i, i) ;
code. WriteFmt ( " gso.slice = slice;\n "
" output.Append(gso);\n "
" } }\n "
" output.RestartStrip();\n "
" } }\n "
" }} \n " ) ;
}
else if (GetAPIType () == APIType::OpenGL || GetAPIType () == APIType::Vulkan)
{
ss << " layout(triangles) in;\n "
" layout(triangle_strip, max_vertices = 6) out;\n " ;
code.WriteFmt (" layout(triangles) in;\n "
" layout(triangle_strip, max_vertices = 6) out;\n " );
if (num_tex > 0 || num_colors > 0 )
{
ss << " VARYING_LOCATION(0) in VertexData {\n " ;
code. WriteFmt ( " VARYING_LOCATION(0) in VertexData {{ \n " ) ;
for (u32 i = 0 ; i < num_tex; i++)
ss << " float3 v_tex" << i << " ;\n " ;
code. WriteFmt ( " float3 v_tex{} ;\n " , i) ;
for (u32 i = 0 ; i < num_colors; i++)
ss << " float4 v_col" << i << " ;\n " ;
ss << " } v_in[];\n " ;
code. WriteFmt ( " float4 v_col{} ;\n " , i) ;
code. WriteFmt ( " }} v_in[];\n " ) ;
ss << " VARYING_LOCATION(0) out VertexData {\n " ;
code. WriteFmt ( " VARYING_LOCATION(0) out VertexData {{ \n " ) ;
for (u32 i = 0 ; i < num_tex; i++)
ss << " float3 v_tex" << i << " ;\n " ;
code. WriteFmt ( " float3 v_tex{} ;\n " , i) ;
for (u32 i = 0 ; i < num_colors; i++)
ss << " float4 v_col" << i << " ;\n " ;
ss << " } v_out;\n " ;
code. WriteFmt ( " float4 v_col{} ;\n " , i) ;
code. WriteFmt ( " }} v_out;\n " ) ;
}
ss << " \n "
" void main()\n "
" {\n "
" for (int j = 0; j < 2; j++)\n "
" {\n "
" gl_Layer = j;\n " ;
code. WriteFmt ( " \n "
" void main()\n "
" { {\n "
" for (int j = 0; j < 2; j++)\n "
" { {\n "
" gl_Layer = j;\n " ) ;
// We have to explicitly unroll this loop otherwise the GL compiler gets cranky.
for (u32 v = 0 ; v < 3 ; v++)
{
ss << " gl_Position = gl_in[" << v << " ].gl_Position;\n " ;
code. WriteFmt ( " gl_Position = gl_in[{} ].gl_Position;\n " , v) ;
for (u32 i = 0 ; i < num_tex; i++)
ss << " v_out.v_tex" << i << " = float3(v_in[" << v << " ].v_tex" << i
<< " .xy, float(j));\n " ;
{
code.WriteFmt (" v_out.v_tex{} = float3(v_in[{}].v_tex{}.xy, float(j));\n " , i, v, i);
}
for (u32 i = 0 ; i < num_colors; i++)
ss << " v_out.v_col" << i << " = v_in[" << v << " ].v_col" << i << " ; \n " ;
ss << " EmitVertex();\n\n " ;
code. WriteFmt ( " v_out.v_col{} = v_in[{} ].v_col{}; \n " , i, v, i) ;
code. WriteFmt ( " EmitVertex();\n\n " ) ;
}
ss << " EndPrimitive();\n "
" }\n "
" } \n " ;
code. WriteFmt ( " EndPrimitive();\n "
" } }\n "
" }} \n " ) ;
}
return ss. str ();
return code. GetBuffer ();
}
std::string GenerateTextureCopyVertexShader ()
{
std::ostringstream ss ;
EmitUniformBufferDeclaration (ss );
ss << " {"
" float2 src_offset;\n "
" float2 src_size;\n "
" } ;\n\n " ;
EmitVertexMainDeclaration (ss , 0 , 0 , false , 1 , 0 ,
ShaderCode code ;
EmitUniformBufferDeclaration (code );
code. WriteFmt ( " { {"
" float2 src_offset;\n "
" float2 src_size;\n "
" }} ;\n\n " ) ;
EmitVertexMainDeclaration (code , 0 , 0 , false , 1 , 0 ,
GetAPIType () == APIType::D3D ? " in uint id : SV_VertexID, " :
" #define id gl_VertexID" );
ss << " {\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 "
" v_tex0 = float3(src_offset + (src_size * v_tex0.xy), 0.0f);\n " ;
code.WriteFmt (
" {{\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 "
" 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.
if (GetAPIType () == APIType::Vulkan || GetAPIType () == APIType::OpenGL)
ss << " opos.y = -opos.y;\n " ;
code. WriteFmt ( " opos.y = -opos.y;\n " ) ;
ss << " } \n " ;
code. WriteFmt ( " }} \n " ) ;
return ss. str ();
return code. GetBuffer ();
}
std::string GenerateTextureCopyPixelShader ()
{
std::ostringstream ss ;
EmitSamplerDeclarations (ss , 0 , 1 , false );
EmitPixelMainDeclaration (ss , 1 , 0 );
ss << " {\n "
" ocol0 = " ;
EmitSampleTexture (ss , 0 , " v_tex0" );
ss << " ;\n "
" } \n " ;
return ss. str ();
ShaderCode code ;
EmitSamplerDeclarations (code , 0 , 1 , false );
EmitPixelMainDeclaration (code , 1 , 0 );
code. WriteFmt ( " { {\n "
" ocol0 = " ) ;
EmitSampleTexture (code , 0 , " v_tex0" );
code. WriteFmt ( " ;\n "
" }} \n " ) ;
return code. GetBuffer ();
}
std::string GenerateColorPixelShader ()
{
std::ostringstream ss ;
EmitPixelMainDeclaration (ss , 0 , 1 );
ss << " {\n "
" ocol0 = v_col0;\n "
" } \n " ;
return ss. str ();
ShaderCode code ;
EmitPixelMainDeclaration (code , 0 , 1 );
code. WriteFmt ( " { {\n "
" ocol0 = v_col0;\n "
" }} \n " ) ;
return code. GetBuffer ();
}
std::string GenerateResolveDepthPixelShader (u32 samples)
{
std::ostringstream ss ;
EmitSamplerDeclarations (ss , 0 , 1 , true );
EmitPixelMainDeclaration (ss , 1 , 0 , " float" ,
ShaderCode code ;
EmitSamplerDeclarations (code , 0 , 1 , true );
EmitPixelMainDeclaration (code , 1 , 0 , " float" ,
GetAPIType () == APIType::D3D ? " in float4 ipos : SV_Position, " : " " );
ss << " {\n "
" int layer = int(v_tex0.z);\n " ;
code. WriteFmt ( " { {\n "
" int layer = int(v_tex0.z);\n " ) ;
if (GetAPIType () == APIType::D3D)
ss << " int3 coords = int3(int2(ipos.xy), layer);\n " ;
code. WriteFmt ( " int3 coords = int3(int2(ipos.xy), layer);\n " ) ;
else
ss << " int3 coords = int3(int2(gl_FragCoord.xy), layer);\n " ;
code. WriteFmt ( " int3 coords = int3(int2(gl_FragCoord.xy), layer);\n " ) ;
// Take the minimum of all depth samples.
if (GetAPIType () == APIType::D3D)
ss << " ocol0 = tex0.Load(coords, 0).r;\n " ;
code. WriteFmt ( " ocol0 = tex0.Load(coords, 0).r;\n " ) ;
else
ss << " ocol0 = texelFetch(samp0, coords, 0).r;\n " ;
ss << " for (int i = 1; i < " << samples << " ; i++)\n " ;
code. WriteFmt ( " ocol0 = texelFetch(samp0, coords, 0).r;\n " ) ;
code. WriteFmt ( " for (int i = 1; i < {} ; i++)\n " , samples) ;
if (GetAPIType () == APIType::D3D)
ss << " ocol0 = min(ocol0, tex0.Load(coords, i).r);\n " ;
code. WriteFmt ( " ocol0 = min(ocol0, tex0.Load(coords, i).r);\n " ) ;
else
ss << " ocol0 = min(ocol0, texelFetch(samp0, coords, i).r);\n " ;
code. WriteFmt ( " ocol0 = min(ocol0, texelFetch(samp0, coords, i).r);\n " ) ;
ss << " } \n " ;
return ss. str ();
code. WriteFmt ( " }} \n " ) ;
return code. GetBuffer ();
}
std::string GenerateClearVertexShader ()
{
std::ostringstream ss ;
EmitUniformBufferDeclaration (ss );
ss << " {\n "
" float4 clear_color;\n "
" float clear_depth;\n "
" } ;\n " ;
EmitVertexMainDeclaration (ss , 0 , 0 , false , 0 , 1 ,
ShaderCode code ;
EmitUniformBufferDeclaration (code );
code. WriteFmt ( " { {\n "
" float4 clear_color;\n "
" float clear_depth;\n "
" }} ;\n " ) ;
EmitVertexMainDeclaration (code , 0 , 0 , false , 0 , 1 ,
GetAPIType () == APIType::D3D ? " in uint id : SV_VertexID, " :
" #define id gl_VertexID\n " );
ss << " {\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 "
" v_col0 = clear_color;\n " ;
code.WriteFmt (
" {{\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 "
" v_col0 = clear_color;\n " );
// NDC space is flipped in Vulkan
if (GetAPIType () == APIType::Vulkan)
ss << " opos.y = -opos.y;\n " ;
code. WriteFmt ( " opos.y = -opos.y;\n " ) ;
ss << " } \n " ;
code. WriteFmt ( " }} \n " ) ;
return ss. str ();
return code. GetBuffer ();
}
std::string GenerateEFBPokeVertexShader ()
{
std::ostringstream ss ;
EmitVertexMainDeclaration (ss , 0 , 1 , true , 0 , 1 );
ss << " {\n "
" v_col0 = rawcolor0;\n "
" opos = float4(rawpos.xyz, 1.0f);\n " ;
ShaderCode code ;
EmitVertexMainDeclaration (code , 0 , 1 , true , 0 , 1 );
code. WriteFmt ( " { {\n "
" v_col0 = rawcolor0;\n "
" opos = float4(rawpos.xyz, 1.0f);\n " ) ;
if (g_ActiveConfig.backend_info .bSupportsLargePoints )
ss << " gl_PointSize = rawpos.w;\n " ;
code. WriteFmt ( " gl_PointSize = rawpos.w;\n " ) ;
// NDC space is flipped in Vulkan.
if (GetAPIType () == APIType::Vulkan)
ss << " opos.y = -opos.y;\n " ;
code. WriteFmt ( " opos.y = -opos.y;\n " ) ;
ss << " } \n " ;
return ss. str ();
code. WriteFmt ( " }} \n " ) ;
return code. GetBuffer ();
}
std::string GenerateFormatConversionShader (EFBReinterpretType convtype, u32 samples)
{
std::ostringstream ss ;
EmitSamplerDeclarations (ss , 0 , 1 , samples > 1 );
ShaderCode code ;
EmitSamplerDeclarations (code , 0 , 1 , samples > 1 );
EmitPixelMainDeclaration (
ss , 1 , 0 , " float4" ,
code , 1 , 0 , " float4" ,
GetAPIType () == APIType::D3D ?
(g_ActiveConfig.bSSAA ?
" in float4 ipos : SV_Position, in uint isample : SV_SampleIndex, " :
" in float4 ipos : SV_Position, " ) :
" " );
ss << " {\n "
" int layer = int(v_tex0.z);\n " ;
code. WriteFmt ( " { {\n "
" int layer = int(v_tex0.z);\n " ) ;
if (GetAPIType () == APIType::D3D)
ss << " int3 coords = int3(int2(ipos.xy), layer);\n " ;
code. WriteFmt ( " int3 coords = int3(int2(ipos.xy), layer);\n " ) ;
else
ss << " int3 coords = int3(int2(gl_FragCoord.xy), layer);\n " ;
code. WriteFmt ( " int3 coords = int3(int2(gl_FragCoord.xy), layer);\n " ) ;
if (samples == 1 )
{
// No MSAA at all.
if (GetAPIType () == APIType::D3D)
ss << " float4 val = tex0.Load(int4(coords, 0));\n " ;
code. WriteFmt ( " float4 val = tex0.Load(int4(coords, 0));\n " ) ;
else
ss << " float4 val = texelFetch(samp0, coords, 0);\n " ;
code. WriteFmt ( " float4 val = texelFetch(samp0, coords, 0);\n " ) ;
}
else if (g_ActiveConfig.bSSAA )
{
// Sample shading, shader runs once per sample
if (GetAPIType () == APIType::D3D)
ss << " float4 val = tex0.Load(coords, isample);" ;
code. WriteFmt ( " float4 val = tex0.Load(coords, isample);" ) ;
else
ss << " float4 val = texelFetch(samp0, coords, gl_SampleID);" ;
code. WriteFmt ( " float4 val = texelFetch(samp0, coords, gl_SampleID);" ) ;
}
else
{
// MSAA without sample shading, average out all samples.
ss << " float4 val = float4(0.0f, 0.0f, 0.0f, 0.0f);\n " ;
ss << " for (int i = 0; i < " << samples << " ; i++)\n " ;
code. WriteFmt ( " float4 val = float4(0.0f, 0.0f, 0.0f, 0.0f);\n " ) ;
code. WriteFmt ( " for (int i = 0; i < {} ; i++)\n " , samples) ;
if (GetAPIType () == APIType::D3D)
ss << " val += tex0.Load(coords, i);\n " ;
code. WriteFmt ( " val += tex0.Load(coords, i);\n " ) ;
else
ss << " val += texelFetch(samp0, coords, i);\n " ;
ss << " val /= float(" << samples << " );\n " ;
code. WriteFmt ( " val += texelFetch(samp0, coords, i);\n " ) ;
code. WriteFmt ( " val /= float({} );\n " , samples) ;
}
switch (convtype)
{
case EFBReinterpretType::RGB8ToRGBA6:
ss << " int4 src8 = int4(round(val * 255.f));\n "
" int4 dst6;\n "
" dst6.r = src8.r >> 2;\n "
" dst6.g = ((src8.r & 0x3) << 4) | (src8.g >> 4);\n "
" dst6.b = ((src8.g & 0xF) << 2) | (src8.b >> 6);\n "
" dst6.a = src8.b & 0x3F;\n "
" ocol0 = float4(dst6) / 63.f;\n " ;
code. WriteFmt ( " int4 src8 = int4(round(val * 255.f));\n "
" int4 dst6;\n "
" dst6.r = src8.r >> 2;\n "
" dst6.g = ((src8.r & 0x3) << 4) | (src8.g >> 4);\n "
" dst6.b = ((src8.g & 0xF) << 2) | (src8.b >> 6);\n "
" dst6.a = src8.b & 0x3F;\n "
" ocol0 = float4(dst6) / 63.f;\n " ) ;
break ;
case EFBReinterpretType::RGB8ToRGB565:
ss << " ocol0 = val;\n " ;
code. WriteFmt ( " ocol0 = val;\n " ) ;
break ;
case EFBReinterpretType::RGBA6ToRGB8:
ss << " int4 src6 = int4(round(val * 63.f));\n "
" int4 dst8;\n "
" dst8.r = (src6.r << 2) | (src6.g >> 4);\n "
" dst8.g = ((src6.g & 0xF) << 4) | (src6.b >> 2);\n "
" dst8.b = ((src6.b & 0x3) << 6) | src6.a;\n "
" dst8.a = 255;\n "
" ocol0 = float4(dst8) / 255.f;\n " ;
code. WriteFmt ( " int4 src6 = int4(round(val * 63.f));\n "
" int4 dst8;\n "
" dst8.r = (src6.r << 2) | (src6.g >> 4);\n "
" dst8.g = ((src6.g & 0xF) << 4) | (src6.b >> 2);\n "
" dst8.b = ((src6.b & 0x3) << 6) | src6.a;\n "
" dst8.a = 255;\n "
" ocol0 = float4(dst8) / 255.f;\n " ) ;
break ;
case EFBReinterpretType::RGBA6ToRGB565:
ss << " ocol0 = val;\n " ;
code. WriteFmt ( " ocol0 = val;\n " ) ;
break ;
case EFBReinterpretType::RGB565ToRGB8:
ss << " ocol0 = val;\n " ;
code. WriteFmt ( " ocol0 = val;\n " ) ;
break ;
case EFBReinterpretType::RGB565ToRGBA6:
//
ss << " ocol0 = val;\n " ;
code. WriteFmt ( " ocol0 = val;\n " ) ;
break ;
}
ss << " } \n " ;
return ss. str ();
code. WriteFmt ( " }} \n " ) ;
return code. GetBuffer ();
}
std::string GenerateTextureReinterpretShader (TextureFormat from_format, TextureFormat to_format)
{
std::ostringstream ss ;
EmitSamplerDeclarations (ss , 0 , 1 , false );
EmitPixelMainDeclaration (ss , 1 , 0 , " float4" , " " , true );
ss << " {\n "
" int layer = int(v_tex0.z);\n "
" int4 coords = int4(int2(frag_coord.xy), layer, 0);\n " ;
ShaderCode code ;
EmitSamplerDeclarations (code , 0 , 1 , false );
EmitPixelMainDeclaration (code , 1 , 0 , " float4" , " " , true );
code. WriteFmt ( " { {\n "
" int layer = int(v_tex0.z);\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
// zeroes.
ss << " uint raw_value;\n " ;
code. WriteFmt ( " uint raw_value;\n " ) ;
switch (from_format)
{
case TextureFormat::I8:
case TextureFormat::C8:
{
ss << " float4 temp_value = " ;
EmitTextureLoad (ss , 0 , " coords" );
ss << " ;\n "
" raw_value = uint(temp_value.r * 255.0);\n " ;
code. WriteFmt ( " float4 temp_value = " ) ;
EmitTextureLoad (code , 0 , " coords" );
code. WriteFmt ( " ;\n "
" raw_value = uint(temp_value.r * 255.0);\n " ) ;
}
break ;
case TextureFormat::IA8:
{
ss << " float4 temp_value = " ;
EmitTextureLoad (ss, 0 , " coords" );
ss << " ;\n "
" raw_value = uint(temp_value.r * 255.0) | (uint(temp_value.a * 255.0) << 8);\n " ;
code.WriteFmt (" float4 temp_value = " );
EmitTextureLoad (code, 0 , " coords" );
code.WriteFmt (
" ;\n "
" raw_value = uint(temp_value.r * 255.0) | (uint(temp_value.a * 255.0) << 8);\n " );
}
break ;
case TextureFormat::I4:
{
ss << " float4 temp_value = " ;
EmitTextureLoad (ss , 0 , " coords" );
ss << " ;\n "
" raw_value = uint(temp_value.r * 15.0);\n " ;
code. WriteFmt ( " float4 temp_value = " ) ;
EmitTextureLoad (code , 0 , " coords" );
code. WriteFmt ( " ;\n "
" raw_value = uint(temp_value.r * 15.0);\n " ) ;
}
break ;
case TextureFormat::IA4:
{
ss << " float4 temp_value = " ;
EmitTextureLoad (ss , 0 , " coords" );
ss << " ;\n "
" raw_value = uint(temp_value.r * 15.0) | (uint(temp_value.a * 15.0) << 4);\n " ;
code. WriteFmt ( " float4 temp_value = " ) ;
EmitTextureLoad (code , 0 , " coords" );
code. WriteFmt ( " ;\n "
" raw_value = uint(temp_value.r * 15.0) | (uint(temp_value.a * 15.0) << 4);\n " ) ;
}
break ;
case TextureFormat::RGB565:
{
ss << " float4 temp_value = " ;
EmitTextureLoad (ss , 0 , " coords" );
ss << " ;\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 " ;
code. WriteFmt ( " float4 temp_value = " ) ;
EmitTextureLoad (code , 0 , " coords" );
code. WriteFmt ( " ;\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 " ) ;
}
break ;
case TextureFormat::RGB5A3:
{
ss << " float4 temp_value = " ;
EmitTextureLoad (ss , 0 , " coords" );
ss << " ;\n " ;
code. WriteFmt ( " float4 temp_value = " ) ;
EmitTextureLoad (code , 0 , " coords" );
code. WriteFmt ( " ;\n " ) ;
// 0.8784 = 224 / 255 which is the maximum alpha value that can be represented in 3 bits
ss << " if (temp_value.a > 0.878f) {\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 "
" } else {\n "
" raw_value = (uint(temp_value.b * 15.0)) | (uint(temp_value.g * 15.0) << 4) |\n "
" (uint(temp_value.r * 15.0) << 8) | (uint(temp_value.a * 7.0) << 12);\n "
" }\n " ;
code.WriteFmt (
" if (temp_value.a > 0.878f) {{\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 "
" }} else {{\n "
" raw_value = (uint(temp_value.b * 15.0)) | (uint(temp_value.g * 15.0) << 4) |\n "
" (uint(temp_value.r * 15.0) << 8) | (uint(temp_value.a * 7.0) << 12);\n "
" }}\n " );
}
break ;
@@ -630,110 +638,110 @@ std::string GenerateTextureReinterpretShader(TextureFormat from_format, TextureF
case TextureFormat::I8:
case TextureFormat::C8:
{
ss << " float orgba = float(raw_value & 0xFFu) / 255.0;\n "
" ocol0 = float4(orgba, orgba, orgba, orgba);\n " ;
code. WriteFmt ( " float orgba = float(raw_value & 0xFFu) / 255.0;\n "
" ocol0 = float4(orgba, orgba, orgba, orgba);\n " ) ;
}
break ;
case TextureFormat::IA8:
{
ss << " float orgb = float(raw_value & 0xFFu) / 255.0;\n "
" ocol0 = float4(orgb, orgb, orgb, float((raw_value >> 8) & 0xFFu) / 255.0);\n " ;
code. WriteFmt ( " float orgb = float(raw_value & 0xFFu) / 255.0;\n "
" ocol0 = float4(orgb, orgb, orgb, float((raw_value >> 8) & 0xFFu) / 255.0);\n " ) ;
}
break ;
case TextureFormat::IA4:
{
ss << " float orgb = float(raw_value & 0xFu) / 15.0;\n "
" ocol0 = float4(orgb, orgb, orgb, float((raw_value >> 4) & 0xFu) / 15.0);\n " ;
code. WriteFmt ( " float orgb = float(raw_value & 0xFu) / 15.0;\n "
" ocol0 = float4(orgb, orgb, orgb, float((raw_value >> 4) & 0xFu) / 15.0);\n " ) ;
}
break ;
case TextureFormat::RGB565:
{
ss << " ocol0 = float4(float((raw_value >> 10) & 0x1Fu) / 31.0,\n "
" float((raw_value >> 5) & 0x1Fu) / 31.0,\n "
" float(raw_value & 0x1Fu) / 31.0, 1.0);\n " ;
code. WriteFmt ( " ocol0 = float4(float((raw_value >> 10) & 0x1Fu) / 31.0,\n "
" float((raw_value >> 5) & 0x1Fu) / 31.0,\n "
" float(raw_value & 0x1Fu) / 31.0, 1.0);\n " ) ;
}
break ;
case TextureFormat::RGB5A3:
{
ss << " if ((raw_value & 0x8000u) != 0u) {\n "
" ocol0 = float4(float((raw_value >> 10) & 0x1Fu) / 31.0,\n "
" float((raw_value >> 5) & 0x1Fu) / 31.0,\n "
" float(raw_value & 0x1Fu) / 31.0, 1.0);\n "
" } else {\n "
" ocol0 = float4(float((raw_value >> 8) & 0x0Fu) / 15.0,\n "
" float((raw_value >> 4) & 0x0Fu) / 15.0,\n "
" float(raw_value & 0x0Fu) / 15.0,\n "
" float((raw_value >> 12) & 0x07u) / 7.0);\n "
" }\n " ;
code. WriteFmt ( " if ((raw_value & 0x8000u) != 0u) { {\n "
" ocol0 = float4(float((raw_value >> 10) & 0x1Fu) / 31.0,\n "
" float((raw_value >> 5) & 0x1Fu) / 31.0,\n "
" float(raw_value & 0x1Fu) / 31.0, 1.0);\n "
" }} else { {\n "
" ocol0 = float4(float((raw_value >> 8) & 0x0Fu) / 15.0,\n "
" float((raw_value >> 4) & 0x0Fu) / 15.0,\n "
" float(raw_value & 0x0Fu) / 15.0,\n "
" float((raw_value >> 12) & 0x07u) / 7.0);\n "
" }} \n " ) ;
}
break ;
default :
WARN_LOG (VIDEO, " To format %u is not supported" , static_cast <u32>(to_format));
return " {}\n " ;
}
ss << " } \n " ;
return ss. str ();
code. WriteFmt ( " }} \n " ) ;
return code. GetBuffer ();
}
std::string GenerateEFBRestorePixelShader ()
{
std::ostringstream ss ;
EmitSamplerDeclarations (ss , 0 , 2 , false );
EmitPixelMainDeclaration (ss , 1 , 0 , " float4" ,
ShaderCode code ;
EmitSamplerDeclarations (code , 0 , 2 , false );
EmitPixelMainDeclaration (code , 1 , 0 , " float4" ,
GetAPIType () == APIType::D3D ? " out float depth : SV_Depth, " : " " );
ss << " {\n "
" ocol0 = " ;
EmitSampleTexture (ss , 0 , " v_tex0" );
ss << " ;\n " ;
ss << " " << ( GetAPIType () == APIType::D3D ? " depth" : " gl_FragDepth" ) << " = " ;
EmitSampleTexture (ss , 1 , " v_tex0" );
ss << " .r;\n "
" } \n " ;
return ss. str ();
code. WriteFmt ( " { {\n "
" ocol0 = " ) ;
EmitSampleTexture (code , 0 , " v_tex0" );
code. WriteFmt ( " ;\n " ) ;
code. WriteFmt ( " {} = " , GetAPIType () == APIType::D3D ? " depth" : " gl_FragDepth" );
EmitSampleTexture (code , 1 , " v_tex0" );
code. WriteFmt ( " .r;\n "
" }} \n " ) ;
return code. GetBuffer ();
}
std::string GenerateImGuiVertexShader ()
{
std::ostringstream ss ;
ShaderCode code ;
// Uniform buffer contains the viewport size, and we transform in the vertex shader.
EmitUniformBufferDeclaration (ss );
ss << " {\n "
" float2 u_rcp_viewport_size_mul2;\n "
" } ;\n\n " ;
EmitVertexMainDeclaration (ss , 1 , 1 , true , 1 , 1 );
ss << " {\n "
" v_tex0 = float3(rawtex0.xy, 0.0);\n "
" v_col0 = rawcolor0;\n "
" 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 " ;
EmitUniformBufferDeclaration (code );
code. WriteFmt ( " { {\n "
" float2 u_rcp_viewport_size_mul2;\n "
" }} ;\n\n " ) ;
EmitVertexMainDeclaration (code , 1 , 1 , true , 1 , 1 );
code. WriteFmt ( " { {\n "
" v_tex0 = float3(rawtex0.xy, 0.0);\n "
" v_col0 = rawcolor0;\n "
" 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 " ) ;
// NDC space is flipped in Vulkan.
if (GetAPIType () == APIType::Vulkan)
ss << " opos.y = -opos.y;\n " ;
code. WriteFmt ( " opos.y = -opos.y;\n " ) ;
ss << " } \n " ;
return ss. str ();
code. WriteFmt ( " }} \n " ) ;
return code. GetBuffer ();
}
std::string GenerateImGuiPixelShader ()
{
std::ostringstream ss ;
EmitSamplerDeclarations (ss , 0 , 1 , false );
EmitPixelMainDeclaration (ss , 1 , 1 );
ss << " {\n "
" ocol0 = " ;
EmitSampleTexture (ss , 0 , " float3(v_tex0.xy, 0.0)" );
ss << " * v_col0;\n "
" } \n " ;
return ss. str ();
ShaderCode code ;
EmitSamplerDeclarations (code , 0 , 1 , false );
EmitPixelMainDeclaration (code , 1 , 1 );
code. WriteFmt ( " { {\n "
" ocol0 = " ) ;
EmitSampleTexture (code , 0 , " float3(v_tex0.xy, 0.0)" );
code. WriteFmt ( " * v_col0;\n "
" }} \n " ) ;
return code. GetBuffer ();
}
} // namespace FramebufferShaderGen