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