1/**
2 * Copyright (c) 2006-2023 LOVE Development Team
3 *
4 * This software is provided 'as-is', without any express or implied
5 * warranty. In no event will the authors be held liable for any damages
6 * arising from the use of this software.
7 *
8 * Permission is granted to anyone to use this software for any purpose,
9 * including commercial applications, and to alter it and redistribute it
10 * freely, subject to the following restrictions:
11 *
12 * 1. The origin of this software must not be misrepresented; you must not
13 * claim that you wrote the original software. If you use this software
14 * in a product, an acknowledgment in the product documentation would be
15 * appreciated but is not required.
16 * 2. Altered source versions must be plainly marked as such, and must not be
17 * misrepresented as being the original software.
18 * 3. This notice may not be removed or altered from any source distribution.
19 **/
20
21// LOVE
22#include "common/config.h"
23
24#include "Shader.h"
25#include "Graphics.h"
26
27// C++
28#include <algorithm>
29#include <limits>
30#include <sstream>
31
32namespace love
33{
34namespace graphics
35{
36namespace opengl
37{
38
39Shader::Shader(love::graphics::ShaderStage *vertex, love::graphics::ShaderStage *pixel)
40 : love::graphics::Shader(vertex, pixel)
41 , program(0)
42 , builtinUniforms()
43 , builtinUniformInfo()
44 , builtinAttributes()
45 , canvasWasActive(false)
46 , lastViewport()
47 , lastPointSize(0.0f)
48{
49 // load shader source and create program object
50 loadVolatile();
51}
52
53Shader::~Shader()
54{
55 unloadVolatile();
56
57 for (const auto &p : uniforms)
58 {
59 // Allocated with malloc().
60 if (p.second.data != nullptr)
61 free(p.second.data);
62
63 if (p.second.baseType == UNIFORM_SAMPLER)
64 {
65 for (int i = 0; i < p.second.count; i++)
66 {
67 if (p.second.textures[i] != nullptr)
68 p.second.textures[i]->release();
69 }
70
71 delete[] p.second.textures;
72 }
73 }
74}
75
76void Shader::mapActiveUniforms()
77{
78 // Built-in uniform locations default to -1 (nonexistent.)
79 for (int i = 0; i < int(BUILTIN_MAX_ENUM); i++)
80 {
81 builtinUniforms[i] = -1;
82 builtinUniformInfo[i] = nullptr;
83 }
84
85 GLint activeprogram = 0;
86 glGetIntegerv(GL_CURRENT_PROGRAM, &activeprogram);
87
88 gl.useProgram(program);
89
90 GLint numuniforms;
91 glGetProgramiv(program, GL_ACTIVE_UNIFORMS, &numuniforms);
92
93 GLchar cname[256];
94 const GLint bufsize = (GLint) (sizeof(cname) / sizeof(GLchar));
95
96 std::map<std::string, UniformInfo> olduniforms = uniforms;
97 uniforms.clear();
98
99 for (int uindex = 0; uindex < numuniforms; uindex++)
100 {
101 GLsizei namelen = 0;
102 GLenum gltype = 0;
103 UniformInfo u = {};
104
105 glGetActiveUniform(program, (GLuint) uindex, bufsize, &namelen, &u.count, &gltype, cname);
106
107 u.name = std::string(cname, (size_t) namelen);
108 u.location = glGetUniformLocation(program, u.name.c_str());
109 u.baseType = getUniformBaseType(gltype);
110 u.textureType = getUniformTextureType(gltype);
111 u.isDepthSampler = isDepthTextureType(gltype);
112
113 if (u.baseType == UNIFORM_MATRIX)
114 u.matrix = getMatrixSize(gltype);
115 else
116 u.components = getUniformTypeComponents(gltype);
117
118 // glGetActiveUniform appends "[0]" to the end of array uniform names...
119 if (u.name.length() > 3)
120 {
121 size_t findpos = u.name.find("[0]");
122 if (findpos != std::string::npos && findpos == u.name.length() - 3)
123 u.name.erase(u.name.length() - 3);
124 }
125
126 // If this is a built-in (LOVE-created) uniform, store the location.
127 BuiltinUniform builtin = BUILTIN_MAX_ENUM;
128 if (getConstant(u.name.c_str(), builtin))
129 builtinUniforms[int(builtin)] = u.location;
130
131 if (u.location == -1)
132 continue;
133
134 if (u.baseType == UNIFORM_SAMPLER && builtin != BUILTIN_TEXTURE_MAIN)
135 {
136 TextureUnit unit;
137 unit.type = u.textureType;
138 unit.active = true;
139 unit.texture = gl.getDefaultTexture(u.textureType);
140
141 for (int i = 0; i < u.count; i++)
142 textureUnits.push_back(unit);
143 }
144
145 // Make sure previously set uniform data is preserved, and shader-
146 // initialized values are retrieved.
147 auto oldu = olduniforms.find(u.name);
148 if (oldu != olduniforms.end())
149 {
150 u.data = oldu->second.data;
151 u.dataSize = oldu->second.dataSize;
152 u.textures = oldu->second.textures;
153
154 updateUniform(&u, u.count, true);
155 }
156 else
157 {
158 u.dataSize = 0;
159
160 switch (u.baseType)
161 {
162 case UNIFORM_FLOAT:
163 u.dataSize = sizeof(float) * u.components * u.count;
164 u.data = malloc(u.dataSize);
165 break;
166 case UNIFORM_INT:
167 case UNIFORM_BOOL:
168 case UNIFORM_SAMPLER:
169 u.dataSize = sizeof(int) * u.components * u.count;
170 u.data = malloc(u.dataSize);
171 break;
172 case UNIFORM_UINT:
173 u.dataSize = sizeof(unsigned int) * u.components * u.count;
174 u.data = malloc(u.dataSize);
175 break;
176 case UNIFORM_MATRIX:
177 u.dataSize = sizeof(float) * (u.matrix.rows * u.matrix.columns) * u.count;
178 u.data = malloc(u.dataSize);
179 break;
180 default:
181 break;
182 }
183
184 if (u.dataSize > 0)
185 {
186 memset(u.data, 0, u.dataSize);
187
188 if (u.baseType == UNIFORM_SAMPLER)
189 {
190 int startunit = (int) textureUnits.size() - u.count;
191
192 if (builtin == BUILTIN_TEXTURE_MAIN)
193 startunit = 0;
194
195 for (int i = 0; i < u.count; i++)
196 u.ints[i] = startunit + i;
197
198 glUniform1iv(u.location, u.count, u.ints);
199
200 u.textures = new Texture*[u.count];
201 memset(u.textures, 0, sizeof(Texture *) * u.count);
202 }
203 }
204
205 size_t offset = 0;
206
207 // Store any shader-initialized values in our own memory.
208 for (int i = 0; i < u.count; i++)
209 {
210 GLint location = u.location;
211
212 if (u.count > 1)
213 {
214 std::ostringstream ss;
215 ss << i;
216
217 std::string indexname = u.name + "[" + ss.str() + "]";
218 location = glGetUniformLocation(program, indexname.c_str());
219 }
220
221 if (location == -1)
222 continue;
223
224 switch (u.baseType)
225 {
226 case UNIFORM_FLOAT:
227 glGetUniformfv(program, location, &u.floats[offset]);
228 offset += u.components;
229 break;
230 case UNIFORM_INT:
231 case UNIFORM_BOOL:
232 glGetUniformiv(program, location, &u.ints[offset]);
233 offset += u.components;
234 break;
235 case UNIFORM_UINT:
236 glGetUniformuiv(program, location, &u.uints[offset]);
237 offset += u.components;
238 break;
239 case UNIFORM_MATRIX:
240 glGetUniformfv(program, location, &u.floats[offset]);
241 offset += u.matrix.rows * u.matrix.columns;
242 break;
243 default:
244 break;
245 }
246 }
247 }
248
249 uniforms[u.name] = u;
250
251 if (builtin != BUILTIN_MAX_ENUM)
252 builtinUniformInfo[(int)builtin] = &uniforms[u.name];
253
254 if (u.baseType == UNIFORM_SAMPLER)
255 {
256 // Make sure all stored textures have their Volatiles loaded before
257 // the sendTextures call, since it calls getHandle().
258 for (int i = 0; i < u.count; i++)
259 {
260 if (u.textures[i] == nullptr)
261 continue;
262
263 Volatile *v = dynamic_cast<Volatile *>(u.textures[i]);
264 if (v != nullptr)
265 v->loadVolatile();
266 }
267
268 sendTextures(&u, u.textures, u.count, true);
269 }
270 }
271
272 // Make sure uniforms that existed before but don't exist anymore are
273 // cleaned up. This theoretically shouldn't happen, but...
274 for (const auto &p : olduniforms)
275 {
276 if (uniforms.find(p.first) == uniforms.end())
277 {
278 free(p.second.data);
279
280 if (p.second.baseType != UNIFORM_SAMPLER)
281 continue;
282
283 for (int i = 0; i < p.second.count; i++)
284 {
285 if (p.second.textures[i] != nullptr)
286 p.second.textures[i]->release();
287 }
288
289 delete[] p.second.textures;
290 }
291 }
292
293 gl.useProgram(activeprogram);
294}
295
296bool Shader::loadVolatile()
297{
298 OpenGL::TempDebugGroup debuggroup("Shader load");
299
300 // Recreating the shader program will invalidate uniforms that rely on these.
301 canvasWasActive = false;
302 lastViewport = Rect();
303
304 lastPointSize = -1.0f;
305
306 // Invalidate the cached matrices by setting some elements to NaN.
307 float nan = std::numeric_limits<float>::quiet_NaN();
308 lastProjectionMatrix.setTranslation(nan, nan);
309 lastTransformMatrix.setTranslation(nan, nan);
310
311 // zero out active texture list
312 textureUnits.clear();
313 textureUnits.push_back(TextureUnit());
314
315 for (const auto &stage : stages)
316 {
317 if (stage.get() != nullptr)
318 stage->loadVolatile();
319 }
320
321 program = glCreateProgram();
322
323 if (program == 0)
324 throw love::Exception("Cannot create shader program object.");
325
326 for (const auto &stage : stages)
327 {
328 if (stage.get() != nullptr)
329 glAttachShader(program, (GLuint) stage->getHandle());
330 }
331
332 // Bind generic vertex attribute indices to names in the shader.
333 for (int i = 0; i < int(ATTRIB_MAX_ENUM); i++)
334 {
335 const char *name = nullptr;
336 if (vertex::getConstant((BuiltinVertexAttribute) i, name))
337 glBindAttribLocation(program, i, (const GLchar *) name);
338 }
339
340 glLinkProgram(program);
341
342 GLint status;
343 glGetProgramiv(program, GL_LINK_STATUS, &status);
344
345 if (status == GL_FALSE)
346 {
347 std::string warnings = getProgramWarnings();
348 glDeleteProgram(program);
349 program = 0;
350 throw love::Exception("Cannot link shader program object:\n%s", warnings.c_str());
351 }
352
353 // Get all active uniform variables in this shader from OpenGL.
354 mapActiveUniforms();
355
356 for (int i = 0; i < int(ATTRIB_MAX_ENUM); i++)
357 {
358 const char *name = nullptr;
359 if (vertex::getConstant(BuiltinVertexAttribute(i), name))
360 builtinAttributes[i] = glGetAttribLocation(program, name);
361 else
362 builtinAttributes[i] = -1;
363 }
364
365 if (current == this)
366 {
367 // make sure glUseProgram gets called.
368 current = nullptr;
369 attach();
370 updateBuiltinUniforms();
371 }
372
373 return true;
374}
375
376void Shader::unloadVolatile()
377{
378 if (program != 0)
379 {
380 if (current == this)
381 gl.useProgram(0);
382
383 glDeleteProgram(program);
384 program = 0;
385 }
386
387 // active texture list is probably invalid, clear it
388 textureUnits.clear();
389 textureUnits.push_back(TextureUnit());
390
391 attributes.clear();
392
393 // And the locations of any built-in uniform variables.
394 for (int i = 0; i < int(BUILTIN_MAX_ENUM); i++)
395 builtinUniforms[i] = -1;
396}
397
398std::string Shader::getProgramWarnings() const
399{
400 GLint strsize, nullpos;
401 glGetProgramiv(program, GL_INFO_LOG_LENGTH, &strsize);
402
403 if (strsize == 0)
404 return "";
405
406 char *tempstr = new char[strsize];
407 // be extra sure that the error string will be 0-terminated
408 memset(tempstr, '\0', strsize);
409 glGetProgramInfoLog(program, strsize, &nullpos, tempstr);
410 tempstr[nullpos] = '\0';
411
412 std::string warnings(tempstr);
413 delete[] tempstr;
414
415 return warnings;
416}
417
418std::string Shader::getWarnings() const
419{
420 std::string warnings;
421 const char *stagestr;
422
423 for (const auto &stage : stages)
424 {
425 if (stage.get() == nullptr)
426 continue;
427
428 const std::string &stagewarnings = stage->getWarnings();
429
430 if (ShaderStage::getConstant(stage->getStageType(), stagestr))
431 warnings += std::string(stagestr) + std::string(" shader:\n") + stagewarnings;
432 }
433
434 warnings += getProgramWarnings();
435
436 return warnings;
437}
438
439void Shader::attach()
440{
441 if (current != this)
442 {
443 Graphics::flushStreamDrawsGlobal();
444
445 gl.useProgram(program);
446 current = this;
447 // retain/release happens in Graphics::setShader.
448
449 // Make sure all textures are bound to their respective texture units.
450 for (int i = 0; i < (int) textureUnits.size(); ++i)
451 {
452 const TextureUnit &unit = textureUnits[i];
453 if (unit.active)
454 gl.bindTextureToUnit(unit.type, unit.texture, i, false, false);
455 }
456
457 // send any pending uniforms to the shader program.
458 for (const auto &p : pendingUniformUpdates)
459 updateUniform(p.first, p.second, true);
460
461 pendingUniformUpdates.clear();
462 }
463}
464
465const Shader::UniformInfo *Shader::getUniformInfo(const std::string &name) const
466{
467 const auto it = uniforms.find(name);
468
469 if (it == uniforms.end())
470 return nullptr;
471
472 return &(it->second);
473}
474
475const Shader::UniformInfo *Shader::getUniformInfo(BuiltinUniform builtin) const
476{
477 return builtinUniformInfo[(int)builtin];
478}
479
480void Shader::updateUniform(const UniformInfo *info, int count)
481{
482 updateUniform(info, count, false);
483}
484
485void Shader::updateUniform(const UniformInfo *info, int count, bool internalupdate)
486{
487 if (current != this && !internalupdate)
488 {
489 pendingUniformUpdates.push_back(std::make_pair(info, count));
490 return;
491 }
492
493 if (!internalupdate)
494 flushStreamDraws();
495
496 int location = info->location;
497 UniformType type = info->baseType;
498
499 if (type == UNIFORM_FLOAT)
500 {
501 switch (info->components)
502 {
503 case 1:
504 glUniform1fv(location, count, info->floats);
505 break;
506 case 2:
507 glUniform2fv(location, count, info->floats);
508 break;
509 case 3:
510 glUniform3fv(location, count, info->floats);
511 break;
512 case 4:
513 glUniform4fv(location, count, info->floats);
514 break;
515 }
516 }
517 else if (type == UNIFORM_INT || type == UNIFORM_BOOL || type == UNIFORM_SAMPLER)
518 {
519 switch (info->components)
520 {
521 case 1:
522 glUniform1iv(location, count, info->ints);
523 break;
524 case 2:
525 glUniform2iv(location, count, info->ints);
526 break;
527 case 3:
528 glUniform3iv(location, count, info->ints);
529 break;
530 case 4:
531 glUniform4iv(location, count, info->ints);
532 break;
533 }
534 }
535 else if (type == UNIFORM_UINT)
536 {
537 switch (info->components)
538 {
539 case 1:
540 glUniform1uiv(location, count, info->uints);
541 break;
542 case 2:
543 glUniform2uiv(location, count, info->uints);
544 break;
545 case 3:
546 glUniform3uiv(location, count, info->uints);
547 break;
548 case 4:
549 glUniform4uiv(location, count, info->uints);
550 break;
551 }
552 }
553 else if (type == UNIFORM_MATRIX)
554 {
555 int columns = info->matrix.columns;
556 int rows = info->matrix.rows;
557
558 if (columns == 2 && rows == 2)
559 glUniformMatrix2fv(location, count, GL_FALSE, info->floats);
560 else if (columns == 3 && rows == 3)
561 glUniformMatrix3fv(location, count, GL_FALSE, info->floats);
562 else if (columns == 4 && rows == 4)
563 glUniformMatrix4fv(location, count, GL_FALSE, info->floats);
564 else if (columns == 2 && rows == 3)
565 glUniformMatrix2x3fv(location, count, GL_FALSE, info->floats);
566 else if (columns == 2 && rows == 4)
567 glUniformMatrix2x4fv(location, count, GL_FALSE, info->floats);
568 else if (columns == 3 && rows == 2)
569 glUniformMatrix3x2fv(location, count, GL_FALSE, info->floats);
570 else if (columns == 3 && rows == 4)
571 glUniformMatrix3x4fv(location, count, GL_FALSE, info->floats);
572 else if (columns == 4 && rows == 2)
573 glUniformMatrix4x2fv(location, count, GL_FALSE, info->floats);
574 else if (columns == 4 && rows == 3)
575 glUniformMatrix4x3fv(location, count, GL_FALSE, info->floats);
576 }
577}
578
579void Shader::sendTextures(const UniformInfo *info, Texture **textures, int count)
580{
581 Shader::sendTextures(info, textures, count, false);
582}
583
584void Shader::sendTextures(const UniformInfo *info, Texture **textures, int count, bool internalUpdate)
585{
586 if (info->baseType != UNIFORM_SAMPLER)
587 return;
588
589 bool shaderactive = current == this;
590
591 if (!internalUpdate && shaderactive)
592 flushStreamDraws();
593
594 count = std::min(count, info->count);
595
596 // Bind the textures to the texture units.
597 for (int i = 0; i < count; i++)
598 {
599 Texture *tex = textures[i];
600
601 if (tex != nullptr)
602 {
603 if (!tex->isReadable())
604 {
605 if (internalUpdate)
606 continue;
607 else
608 throw love::Exception("Textures with non-readable formats cannot be sampled from in a shader.");
609 }
610 else if (info->isDepthSampler != tex->getDepthSampleMode().hasValue)
611 {
612 if (internalUpdate)
613 continue;
614 else if (info->isDepthSampler)
615 throw love::Exception("Depth comparison samplers in shaders can only be used with depth textures which have depth comparison set.");
616 else
617 throw love::Exception("Depth textures which have depth comparison set can only be used with depth/shadow samplers in shaders.");
618 }
619 else if (tex->getTextureType() != info->textureType)
620 {
621 if (internalUpdate)
622 continue;
623 else
624 {
625 const char *textypestr = "unknown";
626 const char *shadertextypestr = "unknown";
627 Texture::getConstant(tex->getTextureType(), textypestr);
628 Texture::getConstant(info->textureType, shadertextypestr);
629 throw love::Exception("Texture's type (%s) must match the type of %s (%s).", textypestr, info->name.c_str(), shadertextypestr);
630 }
631 }
632
633 tex->retain();
634 }
635
636 if (info->textures[i] != nullptr)
637 info->textures[i]->release();
638
639 info->textures[i] = tex;
640
641 GLuint gltex = 0;
642 if (textures[i] != nullptr)
643 gltex = (GLuint) tex->getHandle();
644 else
645 gltex = gl.getDefaultTexture(info->textureType);
646
647 int texunit = info->ints[i];
648
649 if (shaderactive)
650 gl.bindTextureToUnit(info->textureType, gltex, texunit, false, false);
651
652 // Store texture id so it can be re-bound to the texture unit later.
653 textureUnits[texunit].texture = gltex;
654 }
655}
656
657void Shader::flushStreamDraws() const
658{
659 if (current == this)
660 Graphics::flushStreamDrawsGlobal();
661}
662
663bool Shader::hasUniform(const std::string &name) const
664{
665 return uniforms.find(name) != uniforms.end();
666}
667
668ptrdiff_t Shader::getHandle() const
669{
670 return program;
671}
672
673int Shader::getVertexAttributeIndex(const std::string &name)
674{
675 auto it = attributes.find(name);
676 if (it != attributes.end())
677 return it->second;
678
679 GLint location = glGetAttribLocation(program, name.c_str());
680
681 attributes[name] = location;
682 return location;
683}
684
685void Shader::setVideoTextures(Texture *ytexture, Texture *cbtexture, Texture *crtexture)
686{
687 const BuiltinUniform builtins[3] = {
688 BUILTIN_TEXTURE_VIDEO_Y,
689 BUILTIN_TEXTURE_VIDEO_CB,
690 BUILTIN_TEXTURE_VIDEO_CR,
691 };
692
693 Texture *textures[3] = {ytexture, cbtexture, crtexture};
694
695 for (int i = 0; i < 3; i++)
696 {
697 const UniformInfo *info = builtinUniformInfo[builtins[i]];
698
699 if (info != nullptr)
700 sendTextures(info, &textures[i], 1, true);
701 }
702}
703
704void Shader::updateScreenParams()
705{
706 Rect view = gl.getViewport();
707
708 auto gfx = Module::getInstance<Graphics>(Module::M_GRAPHICS);
709 bool canvasActive = gfx->isCanvasActive();
710
711 if ((view == lastViewport && canvasWasActive == canvasActive) || current != this)
712 return;
713
714 // In the shader, we do pixcoord.y = gl_FragCoord.y * params.z + params.w.
715 // This lets us flip pixcoord.y when needed, to be consistent (drawing with
716 // no Canvas active makes the y-values for pixel coordinates flipped.)
717 GLfloat params[] = {
718 (GLfloat) view.w, (GLfloat) view.h,
719 0.0f, 0.0f,
720 };
721
722 if (canvasActive)
723 {
724 // No flipping: pixcoord.y = gl_FragCoord.y * 1.0 + 0.0.
725 params[2] = 1.0f;
726 params[3] = 0.0f;
727 }
728 else
729 {
730 // gl_FragCoord.y is flipped when drawing to the screen, so we un-flip:
731 // pixcoord.y = gl_FragCoord.y * -1.0 + height.
732 params[2] = -1.0f;
733 params[3] = (GLfloat) view.h;
734 }
735
736 GLint location = builtinUniforms[BUILTIN_SCREEN_SIZE];
737 if (location >= 0)
738 glUniform4fv(location, 1, params);
739
740 canvasWasActive = canvasActive;
741 lastViewport = view;
742}
743
744void Shader::updatePointSize(float size)
745{
746 if (size == lastPointSize || current != this)
747 return;
748
749 GLint location = builtinUniforms[BUILTIN_POINT_SIZE];
750 if (location >= 0)
751 glUniform1f(location, size);
752
753 lastPointSize = size;
754}
755
756void Shader::updateBuiltinUniforms()
757{
758 if (current != this)
759 return;
760
761 updateScreenParams();
762
763 if (GLAD_ES_VERSION_2_0)
764 updatePointSize(gl.getPointSize());
765
766 auto gfx = Module::getInstance<graphics::Graphics>(Module::M_GRAPHICS);
767
768 const Matrix4 &curproj = gfx->getProjection();
769 const Matrix4 &curxform = gfx->getTransform();
770
771 bool tpmatrixneedsupdate = false;
772
773 // Only upload the matrices if they've changed.
774 if (memcmp(curxform.getElements(), lastTransformMatrix.getElements(), sizeof(float) * 16) != 0)
775 {
776 GLint location = builtinUniforms[BUILTIN_MATRIX_VIEW_FROM_LOCAL];
777 if (location >= 0)
778 glUniformMatrix4fv(location, 1, GL_FALSE, curxform.getElements());
779
780 // Also upload the re-calculated normal matrix, if possible. The normal
781 // matrix is the transpose of the inverse of the rotation portion
782 // (top-left 3x3) of the transform matrix.
783 location = builtinUniforms[BUILTIN_MATRIX_VIEW_NORMAL_FROM_LOCAL];
784 if (location >= 0)
785 {
786 Matrix3 normalmatrix = Matrix3(curxform).transposedInverse();
787 glUniformMatrix3fv(location, 1, GL_FALSE, normalmatrix.getElements());
788 }
789
790 tpmatrixneedsupdate = true;
791 lastTransformMatrix = curxform;
792 }
793
794 if (memcmp(curproj.getElements(), lastProjectionMatrix.getElements(), sizeof(float) * 16) != 0)
795 {
796 GLint location = builtinUniforms[BUILTIN_MATRIX_CLIP_FROM_VIEW];
797 if (location >= 0)
798 glUniformMatrix4fv(location, 1, GL_FALSE, curproj.getElements());
799
800 tpmatrixneedsupdate = true;
801 lastProjectionMatrix = curproj;
802 }
803
804 if (tpmatrixneedsupdate)
805 {
806 GLint location = builtinUniforms[BUILTIN_MATRIX_CLIP_FROM_LOCAL];
807 if (location >= 0)
808 {
809 Matrix4 tp_matrix(curproj, curxform);
810 glUniformMatrix4fv(location, 1, GL_FALSE, tp_matrix.getElements());
811 }
812 }
813}
814
815std::string Shader::getGLSLVersion()
816{
817 const char *tmp = (const char *) glGetString(GL_SHADING_LANGUAGE_VERSION);
818
819 if (tmp == nullptr)
820 return "0.0";
821
822 // the version string always begins with a version number of the format
823 // major_number.minor_number
824 // or
825 // major_number.minor_number.release_number
826 // we can keep release_number, since it does not affect the check below.
827 std::string versionstring(tmp);
828 size_t minorendpos = versionstring.find(' ');
829 return versionstring.substr(0, minorendpos);
830}
831
832bool Shader::isSupported()
833{
834 return GLAD_ES_VERSION_2_0 || (getGLSLVersion() >= "1.2");
835}
836
837int Shader::getUniformTypeComponents(GLenum type) const
838{
839 if (getUniformBaseType(type) == UNIFORM_SAMPLER)
840 return 1;
841
842 switch (type)
843 {
844 case GL_INT:
845 case GL_UNSIGNED_INT:
846 case GL_FLOAT:
847 case GL_BOOL:
848 return 1;
849 case GL_INT_VEC2:
850 case GL_UNSIGNED_INT_VEC2:
851 case GL_FLOAT_VEC2:
852 case GL_FLOAT_MAT2:
853 case GL_BOOL_VEC2:
854 return 2;
855 case GL_INT_VEC3:
856 case GL_UNSIGNED_INT_VEC3:
857 case GL_FLOAT_VEC3:
858 case GL_FLOAT_MAT3:
859 case GL_BOOL_VEC3:
860 return 3;
861 case GL_INT_VEC4:
862 case GL_UNSIGNED_INT_VEC4:
863 case GL_FLOAT_VEC4:
864 case GL_FLOAT_MAT4:
865 case GL_BOOL_VEC4:
866 return 4;
867 default:
868 return 1;
869 }
870}
871
872Shader::MatrixSize Shader::getMatrixSize(GLenum type) const
873{
874 MatrixSize m;
875
876 switch (type)
877 {
878 case GL_FLOAT_MAT2:
879 m.columns = m.rows = 2;
880 break;
881 case GL_FLOAT_MAT3:
882 m.columns = m.rows = 3;
883 break;
884 case GL_FLOAT_MAT4:
885 m.columns = m.rows = 4;
886 break;
887 case GL_FLOAT_MAT2x3:
888 m.columns = 2;
889 m.rows = 3;
890 break;
891 case GL_FLOAT_MAT2x4:
892 m.columns = 2;
893 m.rows = 4;
894 break;
895 case GL_FLOAT_MAT3x2:
896 m.columns = 3;
897 m.rows = 2;
898 break;
899 case GL_FLOAT_MAT3x4:
900 m.columns = 3;
901 m.rows = 4;
902 break;
903 case GL_FLOAT_MAT4x2:
904 m.columns = 4;
905 m.rows = 2;
906 break;
907 case GL_FLOAT_MAT4x3:
908 m.columns = 4;
909 m.rows = 3;
910 break;
911 }
912
913 return m;
914}
915
916Shader::UniformType Shader::getUniformBaseType(GLenum type) const
917{
918 switch (type)
919 {
920 case GL_INT:
921 case GL_INT_VEC2:
922 case GL_INT_VEC3:
923 case GL_INT_VEC4:
924 return UNIFORM_INT;
925 case GL_UNSIGNED_INT:
926 case GL_UNSIGNED_INT_VEC2:
927 case GL_UNSIGNED_INT_VEC3:
928 case GL_UNSIGNED_INT_VEC4:
929 return UNIFORM_UINT;
930 case GL_FLOAT:
931 case GL_FLOAT_VEC2:
932 case GL_FLOAT_VEC3:
933 case GL_FLOAT_VEC4:
934 return UNIFORM_FLOAT;
935 case GL_FLOAT_MAT2:
936 case GL_FLOAT_MAT3:
937 case GL_FLOAT_MAT4:
938 case GL_FLOAT_MAT2x3:
939 case GL_FLOAT_MAT2x4:
940 case GL_FLOAT_MAT3x2:
941 case GL_FLOAT_MAT3x4:
942 case GL_FLOAT_MAT4x2:
943 case GL_FLOAT_MAT4x3:
944 return UNIFORM_MATRIX;
945 case GL_BOOL:
946 case GL_BOOL_VEC2:
947 case GL_BOOL_VEC3:
948 case GL_BOOL_VEC4:
949 return UNIFORM_BOOL;
950 case GL_SAMPLER_1D:
951 case GL_SAMPLER_1D_SHADOW:
952 case GL_SAMPLER_1D_ARRAY:
953 case GL_SAMPLER_1D_ARRAY_SHADOW:
954 case GL_SAMPLER_2D:
955 case GL_SAMPLER_2D_MULTISAMPLE:
956 case GL_SAMPLER_2D_MULTISAMPLE_ARRAY:
957 case GL_SAMPLER_2D_RECT:
958 case GL_SAMPLER_2D_RECT_SHADOW:
959 case GL_SAMPLER_2D_SHADOW:
960 case GL_SAMPLER_2D_ARRAY:
961 case GL_SAMPLER_2D_ARRAY_SHADOW:
962 case GL_SAMPLER_3D:
963 case GL_SAMPLER_CUBE:
964 case GL_SAMPLER_CUBE_SHADOW:
965 case GL_SAMPLER_CUBE_MAP_ARRAY:
966 case GL_SAMPLER_CUBE_MAP_ARRAY_SHADOW:
967 return UNIFORM_SAMPLER;
968 default:
969 return UNIFORM_UNKNOWN;
970 }
971}
972
973TextureType Shader::getUniformTextureType(GLenum type) const
974{
975 switch (type)
976 {
977 case GL_SAMPLER_1D:
978 case GL_SAMPLER_1D_SHADOW:
979 case GL_SAMPLER_1D_ARRAY:
980 case GL_SAMPLER_1D_ARRAY_SHADOW:
981 // 1D-typed textures are not supported.
982 return TEXTURE_MAX_ENUM;
983 case GL_SAMPLER_2D:
984 case GL_SAMPLER_2D_SHADOW:
985 return TEXTURE_2D;
986 case GL_SAMPLER_2D_MULTISAMPLE:
987 case GL_SAMPLER_2D_MULTISAMPLE_ARRAY:
988 // Multisample textures are not supported.
989 return TEXTURE_MAX_ENUM;
990 case GL_SAMPLER_2D_RECT:
991 case GL_SAMPLER_2D_RECT_SHADOW:
992 // Rectangle textures are not supported.
993 return TEXTURE_MAX_ENUM;
994 case GL_SAMPLER_2D_ARRAY:
995 case GL_SAMPLER_2D_ARRAY_SHADOW:
996 return TEXTURE_2D_ARRAY;
997 case GL_SAMPLER_3D:
998 return TEXTURE_VOLUME;
999 case GL_SAMPLER_CUBE:
1000 case GL_SAMPLER_CUBE_SHADOW:
1001 return TEXTURE_CUBE;
1002 case GL_SAMPLER_CUBE_MAP_ARRAY:
1003 case GL_SAMPLER_CUBE_MAP_ARRAY_SHADOW:
1004 // Cubemap array textures are not supported.
1005 return TEXTURE_MAX_ENUM;
1006 default:
1007 return TEXTURE_MAX_ENUM;
1008 }
1009}
1010
1011bool Shader::isDepthTextureType(GLenum type) const
1012{
1013 switch (type)
1014 {
1015 case GL_SAMPLER_1D_SHADOW:
1016 case GL_SAMPLER_1D_ARRAY_SHADOW:
1017 case GL_SAMPLER_2D_SHADOW:
1018 case GL_SAMPLER_2D_ARRAY_SHADOW:
1019 case GL_SAMPLER_CUBE_SHADOW:
1020 case GL_SAMPLER_CUBE_MAP_ARRAY_SHADOW:
1021 return true;
1022 default:
1023 return false;
1024 }
1025}
1026
1027} // opengl
1028} // graphics
1029} // love
1030