1 // Copyright 2019 Dolphin Emulator Project
2 // Licensed under GPLv2+
3 // Refer to the license.txt file included.
4 
5 #include "VideoCommon/FramebufferShaderGen.h"
6 
7 #include <sstream>
8 #include <string_view>
9 
10 #include "Common/Logging/Log.h"
11 
12 #include "VideoCommon/FramebufferManager.h"
13 #include "VideoCommon/TextureDecoder.h"
14 #include "VideoCommon/VertexShaderGen.h"
15 #include "VideoCommon/VideoCommon.h"
16 #include "VideoCommon/VideoConfig.h"
17 
18 namespace FramebufferShaderGen
19 {
20 namespace
21 {
GetAPIType()22 APIType GetAPIType()
23 {
24   return g_ActiveConfig.backend_info.api_type;
25 }
26 
EmitUniformBufferDeclaration(std::ostringstream & ss)27 void EmitUniformBufferDeclaration(std::ostringstream& ss)
28 {
29   if (GetAPIType() == APIType::D3D)
30     ss << "cbuffer PSBlock : register(b0)\n";
31   else
32     ss << "UBO_BINDING(std140, 1) uniform PSBlock\n";
33 }
34 
EmitSamplerDeclarations(std::ostringstream & ss,u32 start=0,u32 end=1,bool multisampled=false)35 void EmitSamplerDeclarations(std::ostringstream& ss, u32 start = 0, u32 end = 1,
36                              bool multisampled = false)
37 {
38   switch (GetAPIType())
39   {
40   case APIType::D3D:
41   {
42     for (u32 i = start; i < end; i++)
43     {
44       ss << (multisampled ? "Texture2DMSArray<float4>" : "Texture2DArray<float4>") << " tex" << i
45          << " : register(t" << i << ");\n";
46       ss << "SamplerState"
47          << " samp" << i << " : register(s" << i << ");\n";
48     }
49   }
50   break;
51 
52   case APIType::OpenGL:
53   case APIType::Vulkan:
54   {
55     for (u32 i = start; i < end; i++)
56     {
57       ss << "SAMPLER_BINDING(" << i << ") uniform "
58          << (multisampled ? "sampler2DMSArray" : "sampler2DArray") << " samp" << i << ";\n";
59     }
60   }
61   break;
62   default:
63     break;
64   }
65 }
66 
EmitSampleTexture(std::ostringstream & ss,u32 n,std::string_view coords)67 void EmitSampleTexture(std::ostringstream& ss, u32 n, std::string_view coords)
68 {
69   switch (GetAPIType())
70   {
71   case APIType::D3D:
72     ss << "tex" << n << ".Sample(samp" << n << ", " << coords << ')';
73     break;
74 
75   case APIType::OpenGL:
76   case APIType::Vulkan:
77     ss << "texture(samp" << n << ", " << coords << ')';
78     break;
79 
80   default:
81     break;
82   }
83 }
84 
85 // Emits a texel fetch/load instruction. Assumes that "coords" is a 4-element vector, with z
86 // containing the layer, and w containing the mipmap level.
EmitTextureLoad(std::ostringstream & ss,u32 n,std::string_view coords)87 void EmitTextureLoad(std::ostringstream& ss, u32 n, std::string_view coords)
88 {
89   switch (GetAPIType())
90   {
91   case APIType::D3D:
92     ss << "tex" << n << ".Load(" << coords << ')';
93     break;
94 
95   case APIType::OpenGL:
96   case APIType::Vulkan:
97     ss << "texelFetch(samp" << n << ", (" << coords << ").xyz, (" << coords << ").w)";
98     break;
99 
100   default:
101     break;
102   }
103 }
104 
EmitVertexMainDeclaration(std::ostringstream & ss,u32 num_tex_inputs,u32 num_color_inputs,bool position_input,u32 num_tex_outputs,u32 num_color_outputs,std::string_view extra_inputs={})105 void EmitVertexMainDeclaration(std::ostringstream& ss, u32 num_tex_inputs, u32 num_color_inputs,
106                                bool position_input, u32 num_tex_outputs, u32 num_color_outputs,
107                                std::string_view extra_inputs = {})
108 {
109   switch (GetAPIType())
110   {
111   case APIType::D3D:
112   {
113     ss << "void main(";
114     for (u32 i = 0; i < num_tex_inputs; i++)
115       ss << "in float3 rawtex" << i << " : TEXCOORD" << i << ", ";
116     for (u32 i = 0; i < num_color_inputs; i++)
117       ss << "in float4 rawcolor" << i << " : COLOR" << i << ", ";
118     if (position_input)
119       ss << "in float4 rawpos : POSITION, ";
120     ss << extra_inputs;
121     for (u32 i = 0; i < num_tex_outputs; i++)
122       ss << "out float3 v_tex" << i << " : TEXCOORD" << i << ", ";
123     for (u32 i = 0; i < num_color_outputs; i++)
124       ss << "out float4 v_col" << i << " : COLOR" << i << ", ";
125     ss << "out float4 opos : SV_Position)\n";
126   }
127   break;
128 
129   case APIType::OpenGL:
130   case APIType::Vulkan:
131   {
132     for (u32 i = 0; i < num_tex_inputs; i++)
133     {
134       ss << "ATTRIBUTE_LOCATION(" << (SHADER_TEXTURE0_ATTRIB + i) << ") in float3 rawtex" << i
135          << ";\n";
136     }
137     for (u32 i = 0; i < num_color_inputs; i++)
138     {
139       ss << "ATTRIBUTE_LOCATION(" << (SHADER_COLOR0_ATTRIB + i) << ") in float4 rawcolor" << i
140          << ";\n";
141     }
142     if (position_input)
143       ss << "ATTRIBUTE_LOCATION(" << SHADER_POSITION_ATTRIB << ") in float4 rawpos;\n";
144 
145     if (g_ActiveConfig.backend_info.bSupportsGeometryShaders)
146     {
147       ss << "VARYING_LOCATION(0) out VertexData {\n";
148       for (u32 i = 0; i < num_tex_outputs; i++)
149         ss << "  float3 v_tex" << i << ";\n";
150       for (u32 i = 0; i < num_color_outputs; i++)
151         ss << "  float4 v_col" << i << ";\n";
152       ss << "};\n";
153     }
154     else
155     {
156       for (u32 i = 0; i < num_tex_outputs; i++)
157         ss << "VARYING_LOCATION(" << i << ") out float3 v_tex" << i << ";\n";
158       for (u32 i = 0; i < num_color_outputs; i++)
159         ss << "VARYING_LOCATION(" << (num_tex_inputs + i) << ") out float4 v_col" << i << ";\n";
160     }
161     ss << "#define opos gl_Position\n";
162     ss << extra_inputs << '\n';
163     ss << "void main()\n";
164   }
165   break;
166   default:
167     break;
168   }
169 }
170 
EmitPixelMainDeclaration(std::ostringstream & ss,u32 num_tex_inputs,u32 num_color_inputs,std::string_view output_type="float4",std::string_view extra_vars={},bool emit_frag_coord=false)171 void EmitPixelMainDeclaration(std::ostringstream& ss, u32 num_tex_inputs, u32 num_color_inputs,
172                               std::string_view output_type = "float4",
173                               std::string_view extra_vars = {}, bool emit_frag_coord = false)
174 {
175   switch (GetAPIType())
176   {
177   case APIType::D3D:
178   {
179     ss << "void main(";
180     for (u32 i = 0; i < num_tex_inputs; i++)
181       ss << "in float3 v_tex" << i << " : TEXCOORD" << i << ", ";
182     for (u32 i = 0; i < num_color_inputs; i++)
183       ss << "in float4 v_col" << i << " : COLOR" << i << ", ";
184     if (emit_frag_coord)
185       ss << "in float4 frag_coord : SV_Position, ";
186     ss << extra_vars << "out " << output_type << " ocol0 : SV_Target)\n";
187   }
188   break;
189 
190   case APIType::OpenGL:
191   case APIType::Vulkan:
192   {
193     if (g_ActiveConfig.backend_info.bSupportsGeometryShaders)
194     {
195       ss << "VARYING_LOCATION(0) in VertexData {\n";
196       for (u32 i = 0; i < num_tex_inputs; i++)
197         ss << "  in float3 v_tex" << i << ";\n";
198       for (u32 i = 0; i < num_color_inputs; i++)
199         ss << "  in float4 v_col" << i << ";\n";
200       ss << "};\n";
201     }
202     else
203     {
204       for (u32 i = 0; i < num_tex_inputs; i++)
205         ss << "VARYING_LOCATION(" << i << ") in float3 v_tex" << i << ";\n";
206       for (u32 i = 0; i < num_color_inputs; i++)
207         ss << "VARYING_LOCATION(" << (num_tex_inputs + i) << ") in float4 v_col" << i << ";\n";
208     }
209 
210     ss << "FRAGMENT_OUTPUT_LOCATION(0) out " << output_type << " ocol0;\n";
211     ss << extra_vars << "\n";
212     if (emit_frag_coord)
213       ss << "#define frag_coord gl_FragCoord\n";
214     ss << "void main()\n";
215   }
216   break;
217 
218   default:
219     break;
220   }
221 }
222 }  // Anonymous namespace
223 
GenerateScreenQuadVertexShader()224 std::string GenerateScreenQuadVertexShader()
225 {
226   std::ostringstream ss;
227   EmitVertexMainDeclaration(ss, 0, 0, false, 1, 0,
228                             GetAPIType() == APIType::D3D ? "in uint id : SV_VertexID, " :
229                                                            "#define id gl_VertexID\n");
230   ss << "{\n"
231         "  v_tex0 = float3(float((id << 1) & 2), float(id & 2), 0.0f);\n"
232         "  opos = float4(v_tex0.xy * float2(2.0f, -2.0f) + float2(-1.0f, 1.0f), 0.0f, 1.0f);\n";
233 
234   // NDC space is flipped in Vulkan. We also flip in GL so that (0,0) is in the lower-left.
235   if (GetAPIType() == APIType::Vulkan || GetAPIType() == APIType::OpenGL)
236     ss << "  opos.y = -opos.y;\n";
237 
238   ss << "}\n";
239 
240   return ss.str();
241 }
242 
GeneratePassthroughGeometryShader(u32 num_tex,u32 num_colors)243 std::string GeneratePassthroughGeometryShader(u32 num_tex, u32 num_colors)
244 {
245   std::ostringstream ss;
246   if (GetAPIType() == APIType::D3D)
247   {
248     ss << "struct VS_OUTPUT\n"
249           "{\n";
250     for (u32 i = 0; i < num_tex; i++)
251       ss << "  float3 tex" << i << " : TEXCOORD" << i << ";\n";
252     for (u32 i = 0; i < num_colors; i++)
253       ss << "  float4 color" << i << " : COLOR" << i << ";\n";
254     ss << "  float4 position : SV_Position;\n"
255           "};\n";
256 
257     ss << "struct GS_OUTPUT\n"
258           "{";
259     for (u32 i = 0; i < num_tex; i++)
260       ss << "  float3 tex" << i << " : TEXCOORD" << i << ";\n";
261     for (u32 i = 0; i < num_colors; i++)
262       ss << "  float4 color" << i << " : COLOR" << i << ";\n";
263     ss << "  float4 position : SV_Position;\n"
264           "  uint slice : SV_RenderTargetArrayIndex;\n"
265           "};\n\n";
266 
267     ss << "[maxvertexcount(6)]\n"
268           "void main(triangle VS_OUTPUT vso[3], inout TriangleStream<GS_OUTPUT> output)\n"
269           "{\n"
270           "  for (uint slice = 0; slice < 2u; slice++)\n"
271           "  {\n"
272           "    for (int i = 0; i < 3; i++)\n"
273           "    {\n"
274           "      GS_OUTPUT gso;\n"
275           "      gso.position = vso[i].position;\n";
276     for (u32 i = 0; i < num_tex; i++)
277       ss << "      gso.tex" << i << " = float3(vso[i].tex" << i << ".xy, float(slice));\n";
278     for (u32 i = 0; i < num_colors; i++)
279       ss << "      gso.color" << i << " = vso[i].color" << i << ";\n";
280     ss << "      gso.slice = slice;\n"
281           "      output.Append(gso);\n"
282           "    }\n"
283           "    output.RestartStrip();\n"
284           "  }\n"
285           "}\n";
286   }
287   else if (GetAPIType() == APIType::OpenGL || GetAPIType() == APIType::Vulkan)
288   {
289     ss << "layout(triangles) in;\n"
290           "layout(triangle_strip, max_vertices = 6) out;\n";
291     if (num_tex > 0 || num_colors > 0)
292     {
293       ss << "VARYING_LOCATION(0) in VertexData {\n";
294       for (u32 i = 0; i < num_tex; i++)
295         ss << "  float3 v_tex" << i << ";\n";
296       for (u32 i = 0; i < num_colors; i++)
297         ss << "  float4 v_col" << i << ";\n";
298       ss << "} v_in[];\n";
299 
300       ss << "VARYING_LOCATION(0) out VertexData {\n";
301       for (u32 i = 0; i < num_tex; i++)
302         ss << "  float3 v_tex" << i << ";\n";
303       for (u32 i = 0; i < num_colors; i++)
304         ss << "  float4 v_col" << i << ";\n";
305       ss << "} v_out;\n";
306     }
307     ss << "\n"
308           "void main()\n"
309           "{\n"
310           "  for (int j = 0; j < 2; j++)\n"
311           "  {\n"
312           "    gl_Layer = j;\n";
313 
314     // We have to explicitly unroll this loop otherwise the GL compiler gets cranky.
315     for (u32 v = 0; v < 3; v++)
316     {
317       ss << "    gl_Position = gl_in[" << v << "].gl_Position;\n";
318       for (u32 i = 0; i < num_tex; i++)
319         ss << "    v_out.v_tex" << i << " = float3(v_in[" << v << "].v_tex" << i
320            << ".xy, float(j));\n";
321       for (u32 i = 0; i < num_colors; i++)
322         ss << "    v_out.v_col" << i << " = v_in[" << v << "].v_col" << i << ";\n";
323       ss << "    EmitVertex();\n\n";
324     }
325     ss << "    EndPrimitive();\n"
326           "  }\n"
327           "}\n";
328   }
329 
330   return ss.str();
331 }
332 
GenerateTextureCopyVertexShader()333 std::string GenerateTextureCopyVertexShader()
334 {
335   std::ostringstream ss;
336   EmitUniformBufferDeclaration(ss);
337   ss << "{"
338         "  float2 src_offset;\n"
339         "  float2 src_size;\n"
340         "};\n\n";
341 
342   EmitVertexMainDeclaration(ss, 0, 0, false, 1, 0,
343                             GetAPIType() == APIType::D3D ? "in uint id : SV_VertexID, " :
344                                                            "#define id gl_VertexID");
345   ss << "{\n"
346         "  v_tex0 = float3(float((id << 1) & 2), float(id & 2), 0.0f);\n"
347         "  opos = float4(v_tex0.xy * float2(2.0f, -2.0f) + float2(-1.0f, 1.0f), 0.0f, 1.0f);\n"
348         "  v_tex0 = float3(src_offset + (src_size * v_tex0.xy), 0.0f);\n";
349 
350   // NDC space is flipped in Vulkan. We also flip in GL so that (0,0) is in the lower-left.
351   if (GetAPIType() == APIType::Vulkan || GetAPIType() == APIType::OpenGL)
352     ss << "  opos.y = -opos.y;\n";
353 
354   ss << "}\n";
355 
356   return ss.str();
357 }
358 
GenerateTextureCopyPixelShader()359 std::string GenerateTextureCopyPixelShader()
360 {
361   std::ostringstream ss;
362   EmitSamplerDeclarations(ss, 0, 1, false);
363   EmitPixelMainDeclaration(ss, 1, 0);
364   ss << "{\n"
365         "  ocol0 = ";
366   EmitSampleTexture(ss, 0, "v_tex0");
367   ss << ";\n"
368         "}\n";
369   return ss.str();
370 }
371 
GenerateColorPixelShader()372 std::string GenerateColorPixelShader()
373 {
374   std::ostringstream ss;
375   EmitPixelMainDeclaration(ss, 0, 1);
376   ss << "{\n"
377         "  ocol0 = v_col0;\n"
378         "}\n";
379   return ss.str();
380 }
381 
GenerateResolveDepthPixelShader(u32 samples)382 std::string GenerateResolveDepthPixelShader(u32 samples)
383 {
384   std::ostringstream ss;
385   EmitSamplerDeclarations(ss, 0, 1, true);
386   EmitPixelMainDeclaration(ss, 1, 0, "float",
387                            GetAPIType() == APIType::D3D ? "in float4 ipos : SV_Position, " : "");
388   ss << "{\n"
389         "  int layer = int(v_tex0.z);\n";
390   if (GetAPIType() == APIType::D3D)
391     ss << "  int3 coords = int3(int2(ipos.xy), layer);\n";
392   else
393     ss << "  int3 coords = int3(int2(gl_FragCoord.xy), layer);\n";
394 
395   // Take the minimum of all depth samples.
396   if (GetAPIType() == APIType::D3D)
397     ss << "  ocol0 = tex0.Load(coords, 0).r;\n";
398   else
399     ss << "  ocol0 = texelFetch(samp0, coords, 0).r;\n";
400   ss << "  for (int i = 1; i < " << samples << "; i++)\n";
401   if (GetAPIType() == APIType::D3D)
402     ss << "    ocol0 = min(ocol0, tex0.Load(coords, i).r);\n";
403   else
404     ss << "    ocol0 = min(ocol0, texelFetch(samp0, coords, i).r);\n";
405 
406   ss << "}\n";
407   return ss.str();
408 }
409 
GenerateClearVertexShader()410 std::string GenerateClearVertexShader()
411 {
412   std::ostringstream ss;
413   EmitUniformBufferDeclaration(ss);
414   ss << "{\n"
415         "  float4 clear_color;\n"
416         "  float clear_depth;\n"
417         "};\n";
418 
419   EmitVertexMainDeclaration(ss, 0, 0, false, 0, 1,
420                             GetAPIType() == APIType::D3D ? "in uint id : SV_VertexID, " :
421                                                            "#define id gl_VertexID\n");
422   ss << "{\n"
423         "  float2 coord = float2(float((id << 1) & 2), float(id & 2));\n"
424         "  opos = float4(coord * float2(2.0f, -2.0f) + float2(-1.0f, 1.0f), clear_depth, 1.0f);\n"
425         "  v_col0 = clear_color;\n";
426 
427   // NDC space is flipped in Vulkan
428   if (GetAPIType() == APIType::Vulkan)
429     ss << "  opos.y = -opos.y;\n";
430 
431   ss << "}\n";
432 
433   return ss.str();
434 }
435 
GenerateEFBPokeVertexShader()436 std::string GenerateEFBPokeVertexShader()
437 {
438   std::ostringstream ss;
439   EmitVertexMainDeclaration(ss, 0, 1, true, 0, 1);
440   ss << "{\n"
441         "  v_col0 = rawcolor0;\n"
442         "  opos = float4(rawpos.xyz, 1.0f);\n";
443   if (g_ActiveConfig.backend_info.bSupportsLargePoints)
444     ss << "  gl_PointSize = rawpos.w;\n";
445 
446   // NDC space is flipped in Vulkan.
447   if (GetAPIType() == APIType::Vulkan)
448     ss << "  opos.y = -opos.y;\n";
449 
450   ss << "}\n";
451   return ss.str();
452 }
453 
GenerateFormatConversionShader(EFBReinterpretType convtype,u32 samples)454 std::string GenerateFormatConversionShader(EFBReinterpretType convtype, u32 samples)
455 {
456   std::ostringstream ss;
457   EmitSamplerDeclarations(ss, 0, 1, samples > 1);
458   EmitPixelMainDeclaration(
459       ss, 1, 0, "float4",
460       GetAPIType() == APIType::D3D ?
461           (g_ActiveConfig.bSSAA ?
462                "in float4 ipos : SV_Position, in uint isample : SV_SampleIndex, " :
463                "in float4 ipos : SV_Position, ") :
464           "");
465   ss << "{\n"
466         "  int layer = int(v_tex0.z);\n";
467   if (GetAPIType() == APIType::D3D)
468     ss << "  int3 coords = int3(int2(ipos.xy), layer);\n";
469   else
470     ss << "  int3 coords = int3(int2(gl_FragCoord.xy), layer);\n";
471 
472   if (samples == 1)
473   {
474     // No MSAA at all.
475     if (GetAPIType() == APIType::D3D)
476       ss << "  float4 val = tex0.Load(int4(coords, 0));\n";
477     else
478       ss << "  float4 val = texelFetch(samp0, coords, 0);\n";
479   }
480   else if (g_ActiveConfig.bSSAA)
481   {
482     // Sample shading, shader runs once per sample
483     if (GetAPIType() == APIType::D3D)
484       ss << "  float4 val = tex0.Load(coords, isample);";
485     else
486       ss << "  float4 val = texelFetch(samp0, coords, gl_SampleID);";
487   }
488   else
489   {
490     // MSAA without sample shading, average out all samples.
491     ss << "  float4 val = float4(0.0f, 0.0f, 0.0f, 0.0f);\n";
492     ss << "  for (int i = 0; i < " << samples << "; i++)\n";
493     if (GetAPIType() == APIType::D3D)
494       ss << "    val += tex0.Load(coords, i);\n";
495     else
496       ss << "    val += texelFetch(samp0, coords, i);\n";
497     ss << "  val /= float(" << samples << ");\n";
498   }
499 
500   switch (convtype)
501   {
502   case EFBReinterpretType::RGB8ToRGBA6:
503     ss << "  int4 src8 = int4(round(val * 255.f));\n"
504           "  int4 dst6;\n"
505           "  dst6.r = src8.r >> 2;\n"
506           "  dst6.g = ((src8.r & 0x3) << 4) | (src8.g >> 4);\n"
507           "  dst6.b = ((src8.g & 0xF) << 2) | (src8.b >> 6);\n"
508           "  dst6.a = src8.b & 0x3F;\n"
509           "  ocol0 = float4(dst6) / 63.f;\n";
510     break;
511 
512   case EFBReinterpretType::RGB8ToRGB565:
513     ss << "  ocol0 = val;\n";
514     break;
515 
516   case EFBReinterpretType::RGBA6ToRGB8:
517     ss << "  int4 src6 = int4(round(val * 63.f));\n"
518           "  int4 dst8;\n"
519           "  dst8.r = (src6.r << 2) | (src6.g >> 4);\n"
520           "  dst8.g = ((src6.g & 0xF) << 4) | (src6.b >> 2);\n"
521           "  dst8.b = ((src6.b & 0x3) << 6) | src6.a;\n"
522           "  dst8.a = 255;\n"
523           "  ocol0 = float4(dst8) / 255.f;\n";
524     break;
525 
526   case EFBReinterpretType::RGBA6ToRGB565:
527     ss << "  ocol0 = val;\n";
528     break;
529 
530   case EFBReinterpretType::RGB565ToRGB8:
531     ss << "  ocol0 = val;\n";
532     break;
533 
534   case EFBReinterpretType::RGB565ToRGBA6:
535     //
536     ss << "  ocol0 = val;\n";
537     break;
538   }
539 
540   ss << "}\n";
541   return ss.str();
542 }
543 
GenerateTextureReinterpretShader(TextureFormat from_format,TextureFormat to_format)544 std::string GenerateTextureReinterpretShader(TextureFormat from_format, TextureFormat to_format)
545 {
546   std::ostringstream ss;
547   EmitSamplerDeclarations(ss, 0, 1, false);
548   EmitPixelMainDeclaration(ss, 1, 0, "float4", "", true);
549   ss << "{\n"
550         "  int layer = int(v_tex0.z);\n"
551         "  int4 coords = int4(int2(frag_coord.xy), layer, 0);\n";
552 
553   // Convert to a 32-bit value encompassing all channels, filling the most significant bits with
554   // zeroes.
555   ss << "  uint raw_value;\n";
556   switch (from_format)
557   {
558   case TextureFormat::I8:
559   case TextureFormat::C8:
560   {
561     ss << "  float4 temp_value = ";
562     EmitTextureLoad(ss, 0, "coords");
563     ss << ";\n"
564           "  raw_value = uint(temp_value.r * 255.0);\n";
565   }
566   break;
567 
568   case TextureFormat::IA8:
569   {
570     ss << "  float4 temp_value = ";
571     EmitTextureLoad(ss, 0, "coords");
572     ss << ";\n"
573           "  raw_value = uint(temp_value.r * 255.0) | (uint(temp_value.a * 255.0) << 8);\n";
574   }
575   break;
576 
577   case TextureFormat::I4:
578   {
579     ss << "  float4 temp_value = ";
580     EmitTextureLoad(ss, 0, "coords");
581     ss << ";\n"
582           "  raw_value = uint(temp_value.r * 15.0);\n";
583   }
584   break;
585 
586   case TextureFormat::IA4:
587   {
588     ss << "  float4 temp_value = ";
589     EmitTextureLoad(ss, 0, "coords");
590     ss << ";\n"
591           "  raw_value = uint(temp_value.r * 15.0) | (uint(temp_value.a * 15.0) << 4);\n";
592   }
593   break;
594 
595   case TextureFormat::RGB565:
596   {
597     ss << "  float4 temp_value = ";
598     EmitTextureLoad(ss, 0, "coords");
599     ss << ";\n"
600           "  raw_value = uint(temp_value.b * 31.0) | (uint(temp_value.g * 63.0) << 5) |\n"
601           "              (uint(temp_value.r * 31.0) << 11);\n";
602   }
603   break;
604 
605   case TextureFormat::RGB5A3:
606   {
607     ss << "  float4 temp_value = ";
608     EmitTextureLoad(ss, 0, "coords");
609     ss << ";\n";
610 
611     // 0.8784 = 224 / 255 which is the maximum alpha value that can be represented in 3 bits
612     ss << "  if (temp_value.a > 0.878f) {\n"
613           "    raw_value = (uint(temp_value.b * 31.0)) | (uint(temp_value.g * 31.0) << 5) |\n"
614           "                (uint(temp_value.r * 31.0) << 10) | 0x8000u;\n"
615           "  } else {\n"
616           "     raw_value = (uint(temp_value.b * 15.0)) | (uint(temp_value.g * 15.0) << 4) |\n"
617           "                 (uint(temp_value.r * 15.0) << 8) | (uint(temp_value.a * 7.0) << 12);\n"
618           "  }\n";
619   }
620   break;
621 
622   default:
623     WARN_LOG(VIDEO, "From format %u is not supported", static_cast<u32>(from_format));
624     return "{}\n";
625   }
626 
627   // Now convert it to its new representation.
628   switch (to_format)
629   {
630   case TextureFormat::I8:
631   case TextureFormat::C8:
632   {
633     ss << "  float orgba = float(raw_value & 0xFFu) / 255.0;\n"
634           "  ocol0 = float4(orgba, orgba, orgba, orgba);\n";
635   }
636   break;
637 
638   case TextureFormat::IA8:
639   {
640     ss << "  float orgb = float(raw_value & 0xFFu) / 255.0;\n"
641           "  ocol0 = float4(orgb, orgb, orgb, float((raw_value >> 8) & 0xFFu) / 255.0);\n";
642   }
643   break;
644 
645   case TextureFormat::IA4:
646   {
647     ss << "  float orgb = float(raw_value & 0xFu) / 15.0;\n"
648           "  ocol0 = float4(orgb, orgb, orgb, float((raw_value >> 4) & 0xFu) / 15.0);\n";
649   }
650   break;
651 
652   case TextureFormat::RGB565:
653   {
654     ss << "  ocol0 = float4(float((raw_value >> 10) & 0x1Fu) / 31.0,\n"
655           "                 float((raw_value >> 5) & 0x1Fu) / 31.0,\n"
656           "                 float(raw_value & 0x1Fu) / 31.0, 1.0);\n";
657   }
658   break;
659 
660   case TextureFormat::RGB5A3:
661   {
662     ss << "  if ((raw_value & 0x8000u) != 0u) {\n"
663           "    ocol0 = float4(float((raw_value >> 10) & 0x1Fu) / 31.0,\n"
664           "                   float((raw_value >> 5) & 0x1Fu) / 31.0,\n"
665           "                   float(raw_value & 0x1Fu) / 31.0, 1.0);\n"
666           "  } else {\n"
667           "    ocol0 = float4(float((raw_value >> 8) & 0x0Fu) / 15.0,\n"
668           "                   float((raw_value >> 4) & 0x0Fu) / 15.0,\n"
669           "                   float(raw_value & 0x0Fu) / 15.0,\n"
670           "                   float((raw_value >> 12) & 0x07u) / 7.0);\n"
671           "  }\n";
672   }
673   break;
674   default:
675     WARN_LOG(VIDEO, "To format %u is not supported", static_cast<u32>(to_format));
676     return "{}\n";
677   }
678 
679   ss << "}\n";
680   return ss.str();
681 }
682 
GenerateEFBRestorePixelShader()683 std::string GenerateEFBRestorePixelShader()
684 {
685   std::ostringstream ss;
686   EmitSamplerDeclarations(ss, 0, 2, false);
687   EmitPixelMainDeclaration(ss, 1, 0, "float4",
688                            GetAPIType() == APIType::D3D ? "out float depth : SV_Depth, " : "");
689   ss << "{\n"
690         "  ocol0 = ";
691   EmitSampleTexture(ss, 0, "v_tex0");
692   ss << ";\n";
693   ss << "  " << (GetAPIType() == APIType::D3D ? "depth" : "gl_FragDepth") << " = ";
694   EmitSampleTexture(ss, 1, "v_tex0");
695   ss << ".r;\n"
696         "}\n";
697   return ss.str();
698 }
699 
GenerateImGuiVertexShader()700 std::string GenerateImGuiVertexShader()
701 {
702   std::ostringstream ss;
703 
704   // Uniform buffer contains the viewport size, and we transform in the vertex shader.
705   EmitUniformBufferDeclaration(ss);
706   ss << "{\n"
707         "float2 u_rcp_viewport_size_mul2;\n"
708         "};\n\n";
709 
710   EmitVertexMainDeclaration(ss, 1, 1, true, 1, 1);
711   ss << "{\n"
712         "  v_tex0 = float3(rawtex0.xy, 0.0);\n"
713         "  v_col0 = rawcolor0;\n"
714         "  opos = float4(rawpos.x * u_rcp_viewport_size_mul2.x - 1.0,"
715         "                1.0 - rawpos.y * u_rcp_viewport_size_mul2.y, 0.0, 1.0);\n";
716 
717   // NDC space is flipped in Vulkan.
718   if (GetAPIType() == APIType::Vulkan)
719     ss << "  opos.y = -opos.y;\n";
720 
721   ss << "}\n";
722   return ss.str();
723 }
724 
GenerateImGuiPixelShader()725 std::string GenerateImGuiPixelShader()
726 {
727   std::ostringstream ss;
728   EmitSamplerDeclarations(ss, 0, 1, false);
729   EmitPixelMainDeclaration(ss, 1, 1);
730   ss << "{\n"
731         "  ocol0 = ";
732   EmitSampleTexture(ss, 0, "float3(v_tex0.xy, 0.0)");
733   ss << " * v_col0;\n"
734         "}\n";
735 
736   return ss.str();
737 }
738 
739 }  // namespace FramebufferShaderGen
740