1/*
2 Simple DirectMedia Layer
3 Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
4
5 This software is provided 'as-is', without any express or implied
6 warranty. In no event will the authors be held liable for any damages
7 arising from the use of this software.
8
9 Permission is granted to anyone to use this software for any purpose,
10 including commercial applications, and to alter it and redistribute it
11 freely, subject to the following restrictions:
12
13 1. The origin of this software must not be misrepresented; you must not
14 claim that you wrote the original software. If you use this software
15 in a product, an acknowledgment in the product documentation would be
16 appreciated but is not required.
17 2. Altered source versions must be plainly marked as such, and must not be
18 misrepresented as being the original software.
19 3. This notice may not be removed or altered from any source distribution.
20*/
21#include "SDL_internal.h"
22
23#ifdef SDL_VIDEO_RENDER_OGL_ES2
24
25#include <SDL3/SDL_opengles2.h>
26#include "SDL_shaders_gles2.h"
27
28/* *INDENT-OFF* */ // clang-format off
29
30/*************************************************************************************************
31 * Vertex/fragment shader source *
32 *************************************************************************************************/
33
34static const char GLES2_Fragment_Include_Best_Texture_Precision[] =
35"#ifdef GL_FRAGMENT_PRECISION_HIGH\n"
36"#define SDL_TEXCOORD_PRECISION highp\n"
37"#else\n"
38"#define SDL_TEXCOORD_PRECISION mediump\n"
39"#endif\n"
40"\n"
41"precision mediump float;\n"
42"\n"
43;
44
45static const char GLES2_Fragment_Include_Medium_Texture_Precision[] =
46"#define SDL_TEXCOORD_PRECISION mediump\n"
47"precision mediump float;\n"
48"\n"
49;
50
51static const char GLES2_Fragment_Include_High_Texture_Precision[] =
52"#define SDL_TEXCOORD_PRECISION highp\n"
53"precision mediump float;\n"
54"\n"
55;
56
57static const char GLES2_Fragment_Include_Undef_Precision[] =
58"#define mediump\n"
59"#define highp\n"
60"#define lowp\n"
61"#define SDL_TEXCOORD_PRECISION\n"
62"\n"
63;
64
65static const char GLES2_Vertex_Default[] =
66"uniform mat4 u_projection;\n"
67"attribute vec2 a_position;\n"
68"attribute vec4 a_color;\n"
69"attribute vec2 a_texCoord;\n"
70"varying vec2 v_texCoord;\n"
71"varying vec4 v_color;\n"
72"\n"
73"void main()\n"
74"{\n"
75" v_texCoord = a_texCoord;\n"
76" gl_Position = u_projection * vec4(a_position, 0.0, 1.0);\n"
77" gl_PointSize = 1.0;\n"
78" v_color = a_color;\n"
79"}\n"
80;
81
82static const char GLES2_Fragment_Solid[] =
83"varying mediump vec4 v_color;\n"
84"\n"
85"void main()\n"
86"{\n"
87" gl_FragColor = v_color;\n"
88"}\n"
89;
90
91#define RGB_SHADER_PROLOGUE \
92"uniform sampler2D u_texture;\n" \
93"varying mediump vec4 v_color;\n" \
94"varying SDL_TEXCOORD_PRECISION vec2 v_texCoord;\n" \
95
96#define RGB_PIXELART_SHADER_PROLOGUE \
97"uniform sampler2D u_texture;\n" \
98"uniform mediump vec4 u_texel_size;\n" \
99"varying mediump vec4 v_color;\n" \
100"varying SDL_TEXCOORD_PRECISION vec2 v_texCoord;\n" \
101
102#ifdef OPENGLES_300 // This is required for fwidth() and textureGrad()
103#define RGB_PIXELART_GETCOLOR \
104" mediump vec2 boxSize = clamp(fwidth(v_texCoord) * u_texel_size.zw, 1e-5, 1.0);\n" \
105" mediump vec2 tx = v_texCoord * u_texel_size.zw - 0.5 * boxSize;\n" \
106" mediump vec2 txOffset = smoothstep(vec2(1.0) - boxSize, vec2(1.0), fract(tx));\n" \
107" mediump vec2 uv = (floor(tx) + 0.5 + txOffset) * u_texel_size.xy;\n" \
108" mediump vec4 color = textureGrad(u_texture, uv, dFdx(v_texCoord), dFdy(v_texCoord));\n" \
109" mediump vec4 color = texture2D(u_texture, uv);\n"
110#else
111#define RGB_PIXELART_GETCOLOR \
112" mediump vec4 color = texture2D(u_texture, v_texCoord);\n"
113#endif
114
115static const char GLES2_Fragment_TextureABGR[] =
116 RGB_SHADER_PROLOGUE
117"\n"
118"void main()\n"
119"{\n"
120" mediump vec4 color = texture2D(u_texture, v_texCoord);\n"
121" gl_FragColor = color;\n"
122" gl_FragColor *= v_color;\n"
123"}\n"
124;
125
126// ARGB to ABGR conversion
127static const char GLES2_Fragment_TextureARGB[] =
128 RGB_SHADER_PROLOGUE
129"\n"
130"void main()\n"
131"{\n"
132" mediump vec4 color = texture2D(u_texture, v_texCoord);\n"
133" gl_FragColor = color;\n"
134" gl_FragColor.r = color.b;\n"
135" gl_FragColor.b = color.r;\n"
136" gl_FragColor *= v_color;\n"
137"}\n"
138;
139
140// RGB to ABGR conversion
141static const char GLES2_Fragment_TextureRGB[] =
142 RGB_SHADER_PROLOGUE
143"\n"
144"void main()\n"
145"{\n"
146" mediump vec4 color = texture2D(u_texture, v_texCoord);\n"
147" gl_FragColor = color;\n"
148" gl_FragColor.r = color.b;\n"
149" gl_FragColor.b = color.r;\n"
150" gl_FragColor.a = 1.0;\n"
151" gl_FragColor *= v_color;\n"
152"}\n"
153;
154
155// BGR to ABGR conversion
156static const char GLES2_Fragment_TextureBGR[] =
157 RGB_SHADER_PROLOGUE
158"\n"
159"void main()\n"
160"{\n"
161" mediump vec4 color = texture2D(u_texture, v_texCoord);\n"
162" gl_FragColor = color;\n"
163" gl_FragColor.a = 1.0;\n"
164" gl_FragColor *= v_color;\n"
165"}\n"
166;
167
168static const char GLES2_Fragment_TextureABGR_PixelArt[] =
169 RGB_PIXELART_SHADER_PROLOGUE
170"\n"
171"void main()\n"
172"{\n"
173 RGB_PIXELART_GETCOLOR
174" gl_FragColor = color;\n"
175" gl_FragColor *= v_color;\n"
176"}\n"
177;
178
179// ARGB to ABGR conversion
180static const char GLES2_Fragment_TextureARGB_PixelArt[] =
181 RGB_PIXELART_SHADER_PROLOGUE
182"\n"
183"void main()\n"
184"{\n"
185 RGB_PIXELART_GETCOLOR
186" gl_FragColor = color;\n"
187" gl_FragColor.r = color.b;\n"
188" gl_FragColor.b = color.r;\n"
189" gl_FragColor *= v_color;\n"
190"}\n"
191;
192
193// RGB to ABGR conversion
194static const char GLES2_Fragment_TextureRGB_PixelArt[] =
195 RGB_PIXELART_SHADER_PROLOGUE
196"\n"
197"void main()\n"
198"{\n"
199 RGB_PIXELART_GETCOLOR
200" gl_FragColor = color;\n"
201" gl_FragColor.r = color.b;\n"
202" gl_FragColor.b = color.r;\n"
203" gl_FragColor.a = 1.0;\n"
204" gl_FragColor *= v_color;\n"
205"}\n"
206;
207
208// BGR to ABGR conversion
209static const char GLES2_Fragment_TextureBGR_PixelArt[] =
210 RGB_PIXELART_SHADER_PROLOGUE
211"\n"
212"void main()\n"
213"{\n"
214 RGB_PIXELART_GETCOLOR
215" gl_FragColor = color;\n"
216" gl_FragColor.a = 1.0;\n"
217" gl_FragColor *= v_color;\n"
218"}\n"
219;
220
221#ifdef SDL_HAVE_YUV
222
223#define YUV_SHADER_PROLOGUE \
224"uniform sampler2D u_texture;\n" \
225"uniform sampler2D u_texture_u;\n" \
226"uniform sampler2D u_texture_v;\n" \
227"uniform vec3 u_offset;\n" \
228"uniform mat3 u_matrix;\n" \
229"varying mediump vec4 v_color;\n" \
230"varying SDL_TEXCOORD_PRECISION vec2 v_texCoord;\n" \
231"\n" \
232
233#define YUV_SHADER_BODY \
234"void main()\n" \
235"{\n" \
236" mediump vec3 yuv;\n" \
237" lowp vec3 rgb;\n" \
238"\n" \
239" // Get the YUV values \n" \
240" yuv.x = texture2D(u_texture, v_texCoord).r;\n" \
241" yuv.y = texture2D(u_texture_u, v_texCoord).r;\n" \
242" yuv.z = texture2D(u_texture_v, v_texCoord).r;\n" \
243"\n" \
244" // Do the color transform \n" \
245" yuv += u_offset;\n" \
246" rgb = yuv * u_matrix;\n" \
247"\n" \
248" // That was easy. :) \n" \
249" gl_FragColor = vec4(rgb, 1);\n" \
250" gl_FragColor *= v_color;\n" \
251"}" \
252
253#define NV12_RA_SHADER_BODY \
254"void main()\n" \
255"{\n" \
256" mediump vec3 yuv;\n" \
257" lowp vec3 rgb;\n" \
258"\n" \
259" // Get the YUV values \n" \
260" yuv.x = texture2D(u_texture, v_texCoord).r;\n" \
261" yuv.yz = texture2D(u_texture_u, v_texCoord).ra;\n" \
262"\n" \
263" // Do the color transform \n" \
264" yuv += u_offset;\n" \
265" rgb = yuv * u_matrix;\n" \
266"\n" \
267" // That was easy. :) \n" \
268" gl_FragColor = vec4(rgb, 1);\n" \
269" gl_FragColor *= v_color;\n" \
270"}" \
271
272#define NV12_RG_SHADER_BODY \
273"void main()\n" \
274"{\n" \
275" mediump vec3 yuv;\n" \
276" lowp vec3 rgb;\n" \
277"\n" \
278" // Get the YUV values \n" \
279" yuv.x = texture2D(u_texture, v_texCoord).r;\n" \
280" yuv.yz = texture2D(u_texture_u, v_texCoord).rg;\n" \
281"\n" \
282" // Do the color transform \n" \
283" yuv += u_offset;\n" \
284" rgb = yuv * u_matrix;\n" \
285"\n" \
286" // That was easy. :) \n" \
287" gl_FragColor = vec4(rgb, 1);\n" \
288" gl_FragColor *= v_color;\n" \
289"}" \
290
291#define NV21_RA_SHADER_BODY \
292"void main()\n" \
293"{\n" \
294" mediump vec3 yuv;\n" \
295" lowp vec3 rgb;\n" \
296"\n" \
297" // Get the YUV values \n" \
298" yuv.x = texture2D(u_texture, v_texCoord).r;\n" \
299" yuv.yz = texture2D(u_texture_u, v_texCoord).ar;\n" \
300"\n" \
301" // Do the color transform \n" \
302" yuv += u_offset;\n" \
303" rgb = yuv * u_matrix;\n" \
304"\n" \
305" // That was easy. :) \n" \
306" gl_FragColor = vec4(rgb, 1);\n" \
307" gl_FragColor *= v_color;\n" \
308"}" \
309
310#define NV21_RG_SHADER_BODY \
311"void main()\n" \
312"{\n" \
313" mediump vec3 yuv;\n" \
314" lowp vec3 rgb;\n" \
315"\n" \
316" // Get the YUV values \n" \
317" yuv.x = texture2D(u_texture, v_texCoord).r;\n" \
318" yuv.yz = texture2D(u_texture_u, v_texCoord).gr;\n" \
319"\n" \
320" // Do the color transform \n" \
321" yuv += u_offset;\n" \
322" rgb = yuv * u_matrix;\n" \
323"\n" \
324" // That was easy. :) \n" \
325" gl_FragColor = vec4(rgb, 1);\n" \
326" gl_FragColor *= v_color;\n" \
327"}" \
328
329// YUV to ABGR conversion
330static const char GLES2_Fragment_TextureYUV[] =
331 YUV_SHADER_PROLOGUE
332 YUV_SHADER_BODY
333;
334
335// NV12 to ABGR conversion
336static const char GLES2_Fragment_TextureNV12_RA[] =
337 YUV_SHADER_PROLOGUE
338 NV12_RA_SHADER_BODY
339;
340static const char GLES2_Fragment_TextureNV12_RG[] =
341 YUV_SHADER_PROLOGUE
342 NV12_RG_SHADER_BODY
343;
344
345// NV21 to ABGR conversion
346static const char GLES2_Fragment_TextureNV21_RA[] =
347 YUV_SHADER_PROLOGUE
348 NV21_RA_SHADER_BODY
349;
350static const char GLES2_Fragment_TextureNV21_RG[] =
351 YUV_SHADER_PROLOGUE
352 NV21_RG_SHADER_BODY
353;
354#endif
355
356// Custom Android video format texture
357static const char GLES2_Fragment_TextureExternalOES_Prologue[] =
358"#extension GL_OES_EGL_image_external : require\n"
359"\n"
360;
361static const char GLES2_Fragment_TextureExternalOES[] =
362"uniform samplerExternalOES u_texture;\n"
363"varying mediump vec4 v_color;\n"
364"varying SDL_TEXCOORD_PRECISION vec2 v_texCoord;\n"
365"\n"
366"void main()\n"
367"{\n"
368" gl_FragColor = texture2D(u_texture, v_texCoord);\n"
369" gl_FragColor *= v_color;\n"
370"}\n"
371;
372
373/* *INDENT-ON* */ // clang-format on
374
375/*************************************************************************************************
376 * Shader selector *
377 *************************************************************************************************/
378
379const char *GLES2_GetShaderPrologue(GLES2_ShaderType type)
380{
381 switch (type) {
382 case GLES2_SHADER_FRAGMENT_TEXTURE_EXTERNAL_OES:
383 return GLES2_Fragment_TextureExternalOES_Prologue;
384 default:
385 return "";
386 }
387}
388
389const char *GLES2_GetShaderInclude(GLES2_ShaderIncludeType type)
390{
391 switch (type) {
392 case GLES2_SHADER_FRAGMENT_INCLUDE_UNDEF_PRECISION:
393 return GLES2_Fragment_Include_Undef_Precision;
394 case GLES2_SHADER_FRAGMENT_INCLUDE_BEST_TEXCOORD_PRECISION:
395 return GLES2_Fragment_Include_Best_Texture_Precision;
396 case GLES2_SHADER_FRAGMENT_INCLUDE_MEDIUM_TEXCOORD_PRECISION:
397 return GLES2_Fragment_Include_Medium_Texture_Precision;
398 case GLES2_SHADER_FRAGMENT_INCLUDE_HIGH_TEXCOORD_PRECISION:
399 return GLES2_Fragment_Include_High_Texture_Precision;
400 default:
401 return "";
402 }
403}
404
405GLES2_ShaderIncludeType GLES2_GetTexCoordPrecisionEnumFromHint(void)
406{
407 const char *texcoord_hint = SDL_GetHint("SDL_RENDER_OPENGLES2_TEXCOORD_PRECISION");
408 GLES2_ShaderIncludeType value = GLES2_SHADER_FRAGMENT_INCLUDE_BEST_TEXCOORD_PRECISION;
409 if (texcoord_hint) {
410 if (SDL_strcmp(texcoord_hint, "undefined") == 0) {
411 return GLES2_SHADER_FRAGMENT_INCLUDE_UNDEF_PRECISION;
412 }
413 if (SDL_strcmp(texcoord_hint, "high") == 0) {
414 return GLES2_SHADER_FRAGMENT_INCLUDE_HIGH_TEXCOORD_PRECISION;
415 }
416 if (SDL_strcmp(texcoord_hint, "medium") == 0) {
417 return GLES2_SHADER_FRAGMENT_INCLUDE_MEDIUM_TEXCOORD_PRECISION;
418 }
419 }
420 return value;
421}
422
423const char *GLES2_GetShader(GLES2_ShaderType type)
424{
425 switch (type) {
426 case GLES2_SHADER_VERTEX_DEFAULT:
427 return GLES2_Vertex_Default;
428 case GLES2_SHADER_FRAGMENT_SOLID:
429 return GLES2_Fragment_Solid;
430 case GLES2_SHADER_FRAGMENT_TEXTURE_ABGR:
431 return GLES2_Fragment_TextureABGR;
432 case GLES2_SHADER_FRAGMENT_TEXTURE_ARGB:
433 return GLES2_Fragment_TextureARGB;
434 case GLES2_SHADER_FRAGMENT_TEXTURE_RGB:
435 return GLES2_Fragment_TextureRGB;
436 case GLES2_SHADER_FRAGMENT_TEXTURE_BGR:
437 return GLES2_Fragment_TextureBGR;
438 case GLES2_SHADER_FRAGMENT_TEXTURE_ABGR_PIXELART:
439 return GLES2_Fragment_TextureABGR_PixelArt;
440 case GLES2_SHADER_FRAGMENT_TEXTURE_ARGB_PIXELART:
441 return GLES2_Fragment_TextureARGB_PixelArt;
442 case GLES2_SHADER_FRAGMENT_TEXTURE_RGB_PIXELART:
443 return GLES2_Fragment_TextureRGB_PixelArt;
444 case GLES2_SHADER_FRAGMENT_TEXTURE_BGR_PIXELART:
445 return GLES2_Fragment_TextureBGR_PixelArt;
446#ifdef SDL_HAVE_YUV
447 case GLES2_SHADER_FRAGMENT_TEXTURE_YUV:
448 return GLES2_Fragment_TextureYUV;
449 case GLES2_SHADER_FRAGMENT_TEXTURE_NV12_RA:
450 return GLES2_Fragment_TextureNV12_RA;
451 case GLES2_SHADER_FRAGMENT_TEXTURE_NV12_RG:
452 return GLES2_Fragment_TextureNV12_RG;
453 case GLES2_SHADER_FRAGMENT_TEXTURE_NV21_RA:
454 return GLES2_Fragment_TextureNV21_RA;
455 case GLES2_SHADER_FRAGMENT_TEXTURE_NV21_RG:
456 return GLES2_Fragment_TextureNV21_RG;
457#endif
458 case GLES2_SHADER_FRAGMENT_TEXTURE_EXTERNAL_OES:
459 return GLES2_Fragment_TextureExternalOES;
460 default:
461 return NULL;
462 }
463}
464
465#endif // SDL_VIDEO_RENDER_OGL_ES2
466