diff options
| author | Stenzek <stenzek@gmail.com> | 2019-02-15 11:59:50 +1000 |
|---|---|---|
| committer | Stenzek <stenzek@gmail.com> | 2019-02-19 16:57:54 +1000 |
| commit | f039149198657c1891e1c6462ed30c31ed4b8486 (patch) | |
| tree | b914fd7b0ef9c937e9334a880a49a26a26f17c8f /Source/Core/VideoCommon/FramebufferShaderGen.cpp | |
| parent | 933f3ba008942cba96ed623c25e61d6ea86582fe (diff) | |
Move most backend functionality to VideoCommon
Diffstat (limited to 'Source/Core/VideoCommon/FramebufferShaderGen.cpp')
| -rw-r--r-- | Source/Core/VideoCommon/FramebufferShaderGen.cpp | 464 |
1 files changed, 464 insertions, 0 deletions
diff --git a/Source/Core/VideoCommon/FramebufferShaderGen.cpp b/Source/Core/VideoCommon/FramebufferShaderGen.cpp new file mode 100644 index 0000000000..de7bdf3ee8 --- /dev/null +++ b/Source/Core/VideoCommon/FramebufferShaderGen.cpp @@ -0,0 +1,464 @@ +#include "VideoCommon/FramebufferShaderGen.h" +#include <sstream> +#include "VideoCommon/FramebufferManager.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 UBO : register(b0)\n"; + else + ss << "UBO_BINDING(std140, 1) uniform UBO\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; + } +} + +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"; + 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 = "") +{ + 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 << ", "; + ss << extra_vars << "out " << output_type << " ocol0 : SV_Target)\n"; + } + break; + + case APIType::OpenGL: + case APIType::Vulkan: + { + 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"; + 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"; + ss << " v_tex0 = float3(float((id << 1) & 2), float(id & 2), 0.0f);\n"; + ss << " 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"; + ss << "{\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"; + ss << "};\n"; + ss << "struct GS_OUTPUT\n"; + ss << "{"; + 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"; + ss << " uint slice : SV_RenderTargetArrayIndex;\n"; + ss << "};\n\n"; + ss << "[maxvertexcount(6)]\n"; + ss << "void main(triangle VS_OUTPUT vso[3], inout TriangleStream<GS_OUTPUT> output)\n"; + ss << "{\n"; + ss << " for (uint slice = 0; slice < 2u; slice++)\n"; + ss << " {\n"; + ss << " for (int i = 0; i < 3; i++)\n"; + ss << " {\n"; + ss << " GS_OUTPUT gso;\n"; + ss << " 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"; + ss << " output.Append(gso);\n"; + ss << " }\n"; + ss << " output.RestartStrip();\n"; + ss << " }\n"; + ss << "}\n"; + } + else if (GetAPIType() == APIType::OpenGL || GetAPIType() == APIType::Vulkan) + { + ss << "layout(triangles) in;\n"; + ss << "layout(triangle_strip, max_vertices = 6) out;\n"; + for (u32 i = 0; i < num_tex; i++) + { + ss << "layout(location = " << i << ") in float3 v_tex" << i << "[];\n"; + ss << "layout(location = " << i << ") out float3 out_tex" << i << ";\n"; + } + for (u32 i = 0; i < num_colors; i++) + { + ss << "layout(location = " << (num_tex + i) << ") in float4 v_col" << i << "[];\n"; + ss << "layout(location = " << (num_tex + i) << ") out float4 out_col" << i << ";\n"; + } + ss << "\n"; + ss << "void main()\n"; + ss << "{\n"; + ss << " for (int j = 0; j < 2; j++)\n"; + ss << " {\n"; + ss << " 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 << " out_tex" << i << " = float3(v_tex" << i << "[" << v << "].xy, float(j));\n"; + for (u32 i = 0; i < num_colors; i++) + ss << " out_col" << i << " = v_col" << i << "[" << v << "];\n"; + ss << " EmitVertex();\n\n"; + } + ss << " EndPrimitive();\n"; + ss << " }\n"; + ss << "}\n"; + } + + return ss.str(); +} + +std::string GenerateTextureCopyVertexShader() +{ + std::stringstream ss; + EmitUniformBufferDeclaration(ss); + ss << "{"; + ss << " float2 src_offset;\n"; + ss << " float2 src_size;\n"; + ss << "};\n\n"; + + EmitVertexMainDeclaration(ss, 0, 0, false, 1, 0, + GetAPIType() == APIType::D3D ? "in uint id : SV_VertexID, " : + "#define id gl_VertexID"); + ss << "{\n"; + ss << " v_tex0 = float3(float((id << 1) & 2), float(id & 2), 0.0f);\n"; + ss << " opos = float4(v_tex0.xy * float2(2.0f, -2.0f) + float2(-1.0f, 1.0f), 0.0f, 1.0f);\n"; + ss << " 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"; + ss << " ocol0 = "; + EmitSampleTexture(ss, 0, "v_tex0"); + ss << ";\n"; + ss << "}\n"; + return ss.str(); +} + +std::string GenerateColorPixelShader() +{ + std::stringstream ss; + EmitPixelMainDeclaration(ss, 0, 1); + ss << "{\n"; + ss << " ocol0 = v_col0;\n"; + ss << "}\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"; + ss << " 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"; + ss << " float4 clear_color;\n"; + ss << " float clear_depth;\n"; + ss << "};\n"; + + EmitVertexMainDeclaration(ss, 0, 0, false, 0, 1, + GetAPIType() == APIType::D3D ? "in uint id : SV_VertexID, " : + "#define id gl_VertexID\n"); + ss << "{\n"; + ss << " float2 coord = float2(float((id << 1) & 2), float(id & 2));\n"; + ss << " opos = float4(coord * float2(2.0f, -2.0f) + float2(-1.0f, 1.0f), clear_depth, 1.0f);\n"; + ss << " 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"; + ss << " v_col0 = rawcolor0;\n"; + ss << " 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 ? + "in float4 ipos : SV_Position, in uint isample : SV_SampleIndex, " : + ""); + ss << "{\n"; + ss << " 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"; + ss << " int4 dst6;\n"; + ss << " dst6.r = src8.r >> 2;\n"; + ss << " dst6.g = ((src8.r & 0x3) << 4) | (src8.g >> 4);\n"; + ss << " dst6.b = ((src8.g & 0xF) << 2) | (src8.b >> 6);\n"; + ss << " dst6.a = src8.b & 0x3F;\n"; + ss << " 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"; + ss << " int4 dst8;\n"; + ss << " dst8.r = (src6.r << 2) | (src6.g >> 4);\n"; + ss << " dst8.g = ((src6.g & 0xF) << 4) | (src6.b >> 2);\n"; + ss << " dst8.b = ((src6.b & 0x3) << 6) | src6.a;\n"; + ss << " dst8.a = 255;\n"; + ss << " 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(); +} + +} // namespace FramebufferShaderGen |
