mirror of
https://github.com/dolphin-emu/dolphin.git
synced 2025-04-25 06:44:59 +00:00
No behavioral change. This is intended to make the transition to fmt less noisy in subsequent changes by combining insertions of multiple string literals into one where applicable.
710 lines
21 KiB
C++
710 lines
21 KiB
C++
#include "VideoCommon/FramebufferShaderGen.h"
|
|
#include <sstream>
|
|
#include "VideoCommon/FramebufferManager.h"
|
|
#include "VideoCommon/TextureDecoder.h"
|
|
#include "VideoCommon/VertexShaderGen.h"
|
|
|
|
namespace FramebufferShaderGen
|
|
{
|
|
static APIType GetAPIType()
|
|
{
|
|
return g_ActiveConfig.backend_info.api_type;
|
|
}
|
|
|
|
static void EmitUniformBufferDeclaration(std::stringstream& ss)
|
|
{
|
|
if (GetAPIType() == APIType::D3D)
|
|
ss << "cbuffer PSBlock : register(b0)\n";
|
|
else
|
|
ss << "UBO_BINDING(std140, 1) uniform PSBlock\n";
|
|
}
|
|
|
|
static void EmitSamplerDeclarations(std::stringstream& ss, u32 start = 0, u32 end = 1,
|
|
bool multisampled = false)
|
|
{
|
|
switch (GetAPIType())
|
|
{
|
|
case APIType::D3D:
|
|
{
|
|
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";
|
|
}
|
|
}
|
|
break;
|
|
|
|
case APIType::OpenGL:
|
|
case APIType::Vulkan:
|
|
{
|
|
for (u32 i = start; i < end; i++)
|
|
{
|
|
ss << "SAMPLER_BINDING(" << i << ") uniform "
|
|
<< (multisampled ? "sampler2DMSArray" : "sampler2DArray") << " samp" << i << ";\n";
|
|
}
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void EmitSampleTexture(std::stringstream& ss, u32 n, const char* coords)
|
|
{
|
|
switch (GetAPIType())
|
|
{
|
|
case APIType::D3D:
|
|
ss << "tex" << n << ".Sample(samp" << n << ", " << coords << ')';
|
|
break;
|
|
|
|
case APIType::OpenGL:
|
|
case APIType::Vulkan:
|
|
ss << "texture(samp" << n << ", " << coords << ')';
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
// 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.
|
|
static void EmitTextureLoad(std::stringstream& ss, u32 n, const char* coords)
|
|
{
|
|
switch (GetAPIType())
|
|
{
|
|
case APIType::D3D:
|
|
ss << "tex" << n << ".Load(" << coords << ')';
|
|
break;
|
|
|
|
case APIType::OpenGL:
|
|
case APIType::Vulkan:
|
|
ss << "texelFetch(samp" << n << ", (" << coords << ").xyz, (" << coords << ").w)";
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void EmitVertexMainDeclaration(std::stringstream& ss, u32 num_tex_inputs,
|
|
u32 num_color_inputs, bool position_input,
|
|
u32 num_tex_outputs, u32 num_color_outputs,
|
|
const char* extra_inputs = "")
|
|
{
|
|
switch (GetAPIType())
|
|
{
|
|
case APIType::D3D:
|
|
{
|
|
ss << "void main(";
|
|
for (u32 i = 0; i < num_tex_inputs; i++)
|
|
ss << "in float3 rawtex" << i << " : TEXCOORD" << i << ", ";
|
|
for (u32 i = 0; i < num_color_inputs; i++)
|
|
ss << "in float4 rawcolor" << i << " : COLOR" << i << ", ";
|
|
if (position_input)
|
|
ss << "in float4 rawpos : POSITION, ";
|
|
ss << extra_inputs;
|
|
for (u32 i = 0; i < num_tex_outputs; i++)
|
|
ss << "out float3 v_tex" << i << " : TEXCOORD" << 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";
|
|
}
|
|
break;
|
|
|
|
case APIType::OpenGL:
|
|
case APIType::Vulkan:
|
|
{
|
|
for (u32 i = 0; i < num_tex_inputs; i++)
|
|
{
|
|
ss << "ATTRIBUTE_LOCATION(" << (SHADER_TEXTURE0_ATTRIB + i) << ") in float3 rawtex" << i
|
|
<< ";\n";
|
|
}
|
|
for (u32 i = 0; i < num_color_inputs; i++)
|
|
{
|
|
ss << "ATTRIBUTE_LOCATION(" << (SHADER_COLOR0_ATTRIB + i) << ") in float4 rawcolor" << i
|
|
<< ";\n";
|
|
}
|
|
if (position_input)
|
|
ss << "ATTRIBUTE_LOCATION(" << SHADER_POSITION_ATTRIB << ") in float4 rawpos;\n";
|
|
|
|
if (g_ActiveConfig.backend_info.bSupportsGeometryShaders)
|
|
{
|
|
ss << "VARYING_LOCATION(0) out VertexData {\n";
|
|
for (u32 i = 0; i < num_tex_outputs; i++)
|
|
ss << " float3 v_tex" << i << ";\n";
|
|
for (u32 i = 0; i < num_color_outputs; i++)
|
|
ss << " float4 v_col" << i << ";\n";
|
|
ss << "};\n";
|
|
}
|
|
else
|
|
{
|
|
for (u32 i = 0; i < num_tex_outputs; i++)
|
|
ss << "VARYING_LOCATION(" << i << ") out float3 v_tex" << i << ";\n";
|
|
for (u32 i = 0; i < num_color_outputs; i++)
|
|
ss << "VARYING_LOCATION(" << (num_tex_inputs + i) << ") out float4 v_col" << i << ";\n";
|
|
}
|
|
ss << "#define opos gl_Position\n";
|
|
ss << extra_inputs << '\n';
|
|
ss << "void main()\n";
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void EmitPixelMainDeclaration(std::stringstream& ss, u32 num_tex_inputs,
|
|
u32 num_color_inputs, const char* output_type = "float4",
|
|
const char* extra_vars = "", bool emit_frag_coord = false)
|
|
{
|
|
switch (GetAPIType())
|
|
{
|
|
case APIType::D3D:
|
|
{
|
|
ss << "void main(";
|
|
for (u32 i = 0; i < num_tex_inputs; i++)
|
|
ss << "in float3 v_tex" << i << " : TEXCOORD" << i << ", ";
|
|
for (u32 i = 0; i < num_color_inputs; i++)
|
|
ss << "in float4 v_col" << i << " : COLOR" << i << ", ";
|
|
if (emit_frag_coord)
|
|
ss << "in float4 frag_coord : SV_Position, ";
|
|
ss << extra_vars << "out " << output_type << " ocol0 : SV_Target)\n";
|
|
}
|
|
break;
|
|
|
|
case APIType::OpenGL:
|
|
case APIType::Vulkan:
|
|
{
|
|
if (g_ActiveConfig.backend_info.bSupportsGeometryShaders)
|
|
{
|
|
ss << "VARYING_LOCATION(0) in VertexData {\n";
|
|
for (u32 i = 0; i < num_tex_inputs; i++)
|
|
ss << " in float3 v_tex" << i << ";\n";
|
|
for (u32 i = 0; i < num_color_inputs; i++)
|
|
ss << " in float4 v_col" << i << ";\n";
|
|
ss << "};\n";
|
|
}
|
|
else
|
|
{
|
|
for (u32 i = 0; i < num_tex_inputs; i++)
|
|
ss << "VARYING_LOCATION(" << i << ") in float3 v_tex" << i << ";\n";
|
|
for (u32 i = 0; i < num_color_inputs; i++)
|
|
ss << "VARYING_LOCATION(" << (num_tex_inputs + i) << ") in float4 v_col" << i << ";\n";
|
|
}
|
|
|
|
ss << "FRAGMENT_OUTPUT_LOCATION(0) out " << output_type << " ocol0;\n";
|
|
ss << extra_vars << "\n";
|
|
if (emit_frag_coord)
|
|
ss << "#define frag_coord gl_FragCoord\n";
|
|
ss << "void main()\n";
|
|
}
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
std::string GenerateScreenQuadVertexShader()
|
|
{
|
|
std::stringstream ss;
|
|
EmitVertexMainDeclaration(ss, 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";
|
|
|
|
// 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";
|
|
|
|
ss << "}\n";
|
|
|
|
return ss.str();
|
|
}
|
|
|
|
std::string GeneratePassthroughGeometryShader(u32 num_tex, u32 num_colors)
|
|
{
|
|
std::stringstream ss;
|
|
if (GetAPIType() == APIType::D3D)
|
|
{
|
|
ss << "struct VS_OUTPUT\n"
|
|
"{\n";
|
|
for (u32 i = 0; i < num_tex; i++)
|
|
ss << " float3 tex" << i << " : TEXCOORD" << i << ";\n";
|
|
for (u32 i = 0; i < num_colors; i++)
|
|
ss << " float4 color" << i << " : COLOR" << i << ";\n";
|
|
ss << " float4 position : SV_Position;\n"
|
|
"};\n";
|
|
|
|
ss << "struct GS_OUTPUT\n"
|
|
"{";
|
|
for (u32 i = 0; i < num_tex; i++)
|
|
ss << " float3 tex" << i << " : TEXCOORD" << i << ";\n";
|
|
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";
|
|
for (u32 i = 0; i < num_tex; i++)
|
|
ss << " gso.tex" << i << " = float3(vso[i].tex" << i << ".xy, float(slice));\n";
|
|
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";
|
|
}
|
|
else if (GetAPIType() == APIType::OpenGL || GetAPIType() == APIType::Vulkan)
|
|
{
|
|
ss << "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";
|
|
for (u32 i = 0; i < num_tex; i++)
|
|
ss << " float3 v_tex" << i << ";\n";
|
|
for (u32 i = 0; i < num_colors; i++)
|
|
ss << " float4 v_col" << i << ";\n";
|
|
ss << "} v_in[];\n";
|
|
|
|
ss << "VARYING_LOCATION(0) out VertexData {\n";
|
|
for (u32 i = 0; i < num_tex; i++)
|
|
ss << " float3 v_tex" << i << ";\n";
|
|
for (u32 i = 0; i < num_colors; i++)
|
|
ss << " float4 v_col" << i << ";\n";
|
|
ss << "} v_out;\n";
|
|
}
|
|
ss << "\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";
|
|
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";
|
|
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";
|
|
}
|
|
ss << " EndPrimitive();\n"
|
|
" }\n"
|
|
"}\n";
|
|
}
|
|
|
|
return ss.str();
|
|
}
|
|
|
|
std::string GenerateTextureCopyVertexShader()
|
|
{
|
|
std::stringstream ss;
|
|
EmitUniformBufferDeclaration(ss);
|
|
ss << "{"
|
|
" float2 src_offset;\n"
|
|
" float2 src_size;\n"
|
|
"};\n\n";
|
|
|
|
EmitVertexMainDeclaration(ss, 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";
|
|
|
|
// 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";
|
|
|
|
ss << "}\n";
|
|
|
|
return ss.str();
|
|
}
|
|
|
|
std::string GenerateTextureCopyPixelShader()
|
|
{
|
|
std::stringstream ss;
|
|
EmitSamplerDeclarations(ss, 0, 1, false);
|
|
EmitPixelMainDeclaration(ss, 1, 0);
|
|
ss << "{\n"
|
|
" ocol0 = ";
|
|
EmitSampleTexture(ss, 0, "v_tex0");
|
|
ss << ";\n"
|
|
"}\n";
|
|
return ss.str();
|
|
}
|
|
|
|
std::string GenerateColorPixelShader()
|
|
{
|
|
std::stringstream ss;
|
|
EmitPixelMainDeclaration(ss, 0, 1);
|
|
ss << "{\n"
|
|
" ocol0 = v_col0;\n"
|
|
"}\n";
|
|
return ss.str();
|
|
}
|
|
|
|
std::string GenerateResolveDepthPixelShader(u32 samples)
|
|
{
|
|
std::stringstream ss;
|
|
EmitSamplerDeclarations(ss, 0, 1, true);
|
|
EmitPixelMainDeclaration(ss, 1, 0, "float",
|
|
GetAPIType() == APIType::D3D ? "in float4 ipos : SV_Position, " : "");
|
|
ss << "{\n"
|
|
" int layer = int(v_tex0.z);\n";
|
|
if (GetAPIType() == APIType::D3D)
|
|
ss << " int3 coords = int3(int2(ipos.xy), layer);\n";
|
|
else
|
|
ss << " 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";
|
|
else
|
|
ss << " ocol0 = texelFetch(samp0, coords, 0).r;\n";
|
|
ss << " for (int i = 1; i < " << samples << "; i++)\n";
|
|
if (GetAPIType() == APIType::D3D)
|
|
ss << " ocol0 = min(ocol0, tex0.Load(coords, i).r);\n";
|
|
else
|
|
ss << " ocol0 = min(ocol0, texelFetch(samp0, coords, i).r);\n";
|
|
|
|
ss << "}\n";
|
|
return ss.str();
|
|
}
|
|
|
|
std::string GenerateClearVertexShader()
|
|
{
|
|
std::stringstream ss;
|
|
EmitUniformBufferDeclaration(ss);
|
|
ss << "{\n"
|
|
" float4 clear_color;\n"
|
|
" float clear_depth;\n"
|
|
"};\n";
|
|
|
|
EmitVertexMainDeclaration(ss, 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";
|
|
|
|
// NDC space is flipped in Vulkan
|
|
if (GetAPIType() == APIType::Vulkan)
|
|
ss << " opos.y = -opos.y;\n";
|
|
|
|
ss << "}\n";
|
|
|
|
return ss.str();
|
|
}
|
|
|
|
std::string GenerateEFBPokeVertexShader()
|
|
{
|
|
std::stringstream ss;
|
|
EmitVertexMainDeclaration(ss, 0, 1, true, 0, 1);
|
|
ss << "{\n"
|
|
" v_col0 = rawcolor0;\n"
|
|
" opos = float4(rawpos.xyz, 1.0f);\n";
|
|
if (g_ActiveConfig.backend_info.bSupportsLargePoints)
|
|
ss << " gl_PointSize = rawpos.w;\n";
|
|
|
|
// NDC space is flipped in Vulkan.
|
|
if (GetAPIType() == APIType::Vulkan)
|
|
ss << " opos.y = -opos.y;\n";
|
|
|
|
ss << "}\n";
|
|
return ss.str();
|
|
}
|
|
|
|
std::string GenerateFormatConversionShader(EFBReinterpretType convtype, u32 samples)
|
|
{
|
|
std::stringstream ss;
|
|
EmitSamplerDeclarations(ss, 0, 1, samples > 1);
|
|
EmitPixelMainDeclaration(
|
|
ss, 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";
|
|
if (GetAPIType() == APIType::D3D)
|
|
ss << " int3 coords = int3(int2(ipos.xy), layer);\n";
|
|
else
|
|
ss << " 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";
|
|
else
|
|
ss << " 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);";
|
|
else
|
|
ss << " 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";
|
|
if (GetAPIType() == APIType::D3D)
|
|
ss << " val += tex0.Load(coords, i);\n";
|
|
else
|
|
ss << " val += texelFetch(samp0, coords, i);\n";
|
|
ss << " val /= float(" << samples << ");\n";
|
|
}
|
|
|
|
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";
|
|
break;
|
|
|
|
case EFBReinterpretType::RGB8ToRGB565:
|
|
ss << " 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";
|
|
break;
|
|
|
|
case EFBReinterpretType::RGBA6ToRGB565:
|
|
ss << " ocol0 = val;\n";
|
|
break;
|
|
|
|
case EFBReinterpretType::RGB565ToRGB8:
|
|
ss << " ocol0 = val;\n";
|
|
break;
|
|
|
|
case EFBReinterpretType::RGB565ToRGBA6:
|
|
//
|
|
ss << " ocol0 = val;\n";
|
|
break;
|
|
}
|
|
|
|
ss << "}\n";
|
|
return ss.str();
|
|
}
|
|
|
|
std::string GenerateTextureReinterpretShader(TextureFormat from_format, TextureFormat to_format)
|
|
{
|
|
std::stringstream 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";
|
|
|
|
// Convert to a 32-bit value encompassing all channels, filling the most significant bits with
|
|
// zeroes.
|
|
ss << " 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";
|
|
}
|
|
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";
|
|
}
|
|
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";
|
|
}
|
|
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";
|
|
}
|
|
break;
|
|
|
|
case TextureFormat::RGB5A3:
|
|
{
|
|
ss << " float4 temp_value = ";
|
|
EmitTextureLoad(ss, 0, "coords");
|
|
ss << ";\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";
|
|
}
|
|
break;
|
|
}
|
|
|
|
// Now convert it to its new representation.
|
|
switch (to_format)
|
|
{
|
|
case TextureFormat::I8:
|
|
case TextureFormat::C8:
|
|
{
|
|
ss << " 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";
|
|
}
|
|
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";
|
|
}
|
|
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";
|
|
}
|
|
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";
|
|
}
|
|
break;
|
|
}
|
|
|
|
ss << "}\n";
|
|
return ss.str();
|
|
}
|
|
|
|
std::string GenerateEFBRestorePixelShader()
|
|
{
|
|
std::stringstream ss;
|
|
EmitSamplerDeclarations(ss, 0, 2, false);
|
|
EmitPixelMainDeclaration(ss, 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();
|
|
}
|
|
|
|
std::string GenerateImGuiVertexShader()
|
|
{
|
|
std::stringstream ss;
|
|
|
|
// 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";
|
|
|
|
// NDC space is flipped in Vulkan.
|
|
if (GetAPIType() == APIType::Vulkan)
|
|
ss << " opos.y = -opos.y;\n";
|
|
|
|
ss << "}\n";
|
|
return ss.str();
|
|
}
|
|
|
|
std::string GenerateImGuiPixelShader()
|
|
{
|
|
std::stringstream 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();
|
|
}
|
|
|
|
} // namespace FramebufferShaderGen
|