Line data Source code
1 : /* Copyright (C) 2023 Wildfire Games.
2 : * This file is part of 0 A.D.
3 : *
4 : * 0 A.D. is free software: you can redistribute it and/or modify
5 : * it under the terms of the GNU General Public License as published by
6 : * the Free Software Foundation, either version 2 of the License, or
7 : * (at your option) any later version.
8 : *
9 : * 0 A.D. is distributed in the hope that it will be useful,
10 : * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 : * GNU General Public License for more details.
13 : *
14 : * You should have received a copy of the GNU General Public License
15 : * along with 0 A.D. If not, see <http://www.gnu.org/licenses/>.
16 : */
17 :
18 : #include "precompiled.h"
19 :
20 : #include "ShaderProgram.h"
21 :
22 : #include "graphics/Color.h"
23 : #include "graphics/PreprocessorWrapper.h"
24 : #include "graphics/ShaderManager.h"
25 : #include "graphics/TextureManager.h"
26 : #include "ps/CLogger.h"
27 : #include "ps/Filesystem.h"
28 : #include "ps/Profile.h"
29 : #include "ps/XML/Xeromyces.h"
30 : #include "renderer/backend/gl/Device.h"
31 : #include "renderer/backend/gl/DeviceCommandContext.h"
32 :
33 : #define USE_SHADER_XML_VALIDATION 1
34 :
35 : #if USE_SHADER_XML_VALIDATION
36 : #include "ps/XML/RelaxNG.h"
37 : #include "ps/XML/XMLWriter.h"
38 : #endif
39 :
40 : #include <algorithm>
41 : #include <map>
42 : #include <unordered_map>
43 :
44 : namespace Renderer
45 : {
46 :
47 : namespace Backend
48 : {
49 :
50 : namespace GL
51 : {
52 :
53 : namespace
54 : {
55 :
56 : struct Binding
57 : {
58 : Binding(int a, int b) : first(a), second(b) { }
59 :
60 : Binding() : first(-1), second(-1) { }
61 :
62 : /**
63 : * Returns whether this uniform attribute is active in the shader.
64 : * If not then there's no point calling Uniform() to set its value.
65 : */
66 : bool Active() const { return first != -1 || second != -1; }
67 :
68 : int first;
69 : int second;
70 : };
71 :
72 0 : int GetStreamMask(const VertexAttributeStream stream)
73 : {
74 0 : return 1 << static_cast<int>(stream);
75 : }
76 :
77 0 : GLint GLSizeFromFormat(const Format format)
78 : {
79 0 : GLint size = 1;
80 0 : if (format == Renderer::Backend::Format::R32_SFLOAT ||
81 : format == Renderer::Backend::Format::R16_SINT)
82 0 : size = 1;
83 0 : else if (
84 0 : format == Renderer::Backend::Format::R8G8_UNORM ||
85 0 : format == Renderer::Backend::Format::R8G8_UINT ||
86 0 : format == Renderer::Backend::Format::R16G16_SINT ||
87 : format == Renderer::Backend::Format::R32G32_SFLOAT)
88 0 : size = 2;
89 0 : else if (format == Renderer::Backend::Format::R32G32B32_SFLOAT)
90 0 : size = 3;
91 0 : else if (
92 0 : format == Renderer::Backend::Format::R32G32B32A32_SFLOAT ||
93 0 : format == Renderer::Backend::Format::R8G8B8A8_UNORM ||
94 : format == Renderer::Backend::Format::R8G8B8A8_UINT)
95 0 : size = 4;
96 : else
97 0 : debug_warn("Unsupported format.");
98 0 : return size;
99 : }
100 :
101 0 : GLenum GLTypeFromFormat(const Format format)
102 : {
103 0 : GLenum type = GL_FLOAT;
104 0 : if (format == Renderer::Backend::Format::R32_SFLOAT ||
105 0 : format == Renderer::Backend::Format::R32G32_SFLOAT ||
106 0 : format == Renderer::Backend::Format::R32G32B32_SFLOAT ||
107 : format == Renderer::Backend::Format::R32G32B32A32_SFLOAT)
108 0 : type = GL_FLOAT;
109 0 : else if (
110 0 : format == Renderer::Backend::Format::R16_SINT ||
111 : format == Renderer::Backend::Format::R16G16_SINT)
112 0 : type = GL_SHORT;
113 0 : else if (
114 0 : format == Renderer::Backend::Format::R8G8_UNORM ||
115 0 : format == Renderer::Backend::Format::R8G8_UINT ||
116 0 : format == Renderer::Backend::Format::R8G8B8A8_UNORM ||
117 : format == Renderer::Backend::Format::R8G8B8A8_UINT)
118 0 : type = GL_UNSIGNED_BYTE;
119 : else
120 0 : debug_warn("Unsupported format.");
121 0 : return type;
122 : }
123 :
124 0 : GLboolean NormalizedFromFormat(const Format format)
125 : {
126 0 : switch (format)
127 : {
128 0 : case Format::R8G8_UNORM: FALLTHROUGH;
129 : case Format::R8G8B8_UNORM: FALLTHROUGH;
130 : case Format::R8G8B8A8_UNORM: FALLTHROUGH;
131 : case Format::R16_UNORM: FALLTHROUGH;
132 : case Format::R16G16_UNORM:
133 0 : return GL_TRUE;
134 0 : default:
135 0 : break;
136 : }
137 0 : return GL_FALSE;
138 : }
139 :
140 0 : int GetAttributeLocationFromStream(
141 : CDevice* device, const VertexAttributeStream stream)
142 : {
143 : // Old mapping makes sense only if we have an old/low-end hardware. Else we
144 : // need to use sequential numbering to fix #3054. We use presence of
145 : // compute shaders as a check that the hardware has universal CUs.
146 0 : if (device->GetCapabilities().computeShaders)
147 : {
148 0 : return static_cast<int>(stream);
149 : }
150 : else
151 : {
152 : // Map known semantics onto the attribute locations documented by NVIDIA:
153 : // https://download.nvidia.com/developer/Papers/2005/OpenGL_2.0/NVIDIA_OpenGL_2.0_Support.pdf
154 : // https://developer.download.nvidia.com/opengl/glsl/glsl_release_notes.pdf
155 0 : switch (stream)
156 : {
157 0 : case VertexAttributeStream::POSITION: return 0;
158 0 : case VertexAttributeStream::NORMAL: return 2;
159 0 : case VertexAttributeStream::COLOR: return 3;
160 0 : case VertexAttributeStream::UV0: return 8;
161 0 : case VertexAttributeStream::UV1: return 9;
162 0 : case VertexAttributeStream::UV2: return 10;
163 0 : case VertexAttributeStream::UV3: return 11;
164 0 : case VertexAttributeStream::UV4: return 12;
165 0 : case VertexAttributeStream::UV5: return 13;
166 0 : case VertexAttributeStream::UV6: return 14;
167 0 : case VertexAttributeStream::UV7: return 15;
168 : }
169 : }
170 :
171 0 : debug_warn("Invalid attribute semantics");
172 0 : return 0;
173 : }
174 :
175 0 : bool PreprocessShaderFile(
176 : bool arb, const CShaderDefines& defines, const VfsPath& path, const char* stage,
177 : CStr& source, std::vector<VfsPath>& fileDependencies)
178 : {
179 0 : CVFSFile file;
180 0 : if (file.Load(g_VFS, path) != PSRETURN_OK)
181 : {
182 0 : LOGERROR("Failed to load shader file: '%s'", path.string8());
183 0 : return false;
184 : }
185 :
186 : CPreprocessorWrapper preprocessor(
187 0 : [arb, &fileDependencies](const CStr& includePath, CStr& out) -> bool
188 0 : {
189 : const VfsPath includeFilePath(
190 0 : (arb ? L"shaders/arb/" : L"shaders/glsl/") + wstring_from_utf8(includePath));
191 : // Add dependencies anyway to reload the shader when the file is
192 : // appeared.
193 0 : fileDependencies.push_back(includeFilePath);
194 0 : CVFSFile includeFile;
195 0 : if (includeFile.Load(g_VFS, includeFilePath) != PSRETURN_OK)
196 : {
197 0 : LOGERROR("Failed to load shader include file: '%s'", includeFilePath.string8());
198 0 : return false;
199 : }
200 0 : out = includeFile.GetAsString();
201 0 : return true;
202 0 : });
203 0 : preprocessor.AddDefines(defines);
204 0 : if (!arb)
205 0 : preprocessor.AddDefine(stage, "1");
206 :
207 : #if CONFIG2_GLES
208 : if (!arb)
209 : {
210 : // GLES defines the macro "GL_ES" in its GLSL preprocessor,
211 : // but since we run our own preprocessor first, we need to explicitly
212 : // define it here
213 : preprocessor.AddDefine("GL_ES", "1");
214 : }
215 : #endif
216 :
217 0 : source = preprocessor.Preprocess(file.GetAsString());
218 :
219 0 : return true;
220 : }
221 :
222 : #if !CONFIG2_GLES
223 0 : std::tuple<GLenum, GLenum, GLint> GetElementTypeAndCountFromString(const CStr& str)
224 : {
225 : #define CASE(MATCH_STRING, TYPE, ELEMENT_TYPE, ELEMENT_COUNT) \
226 : if (str == MATCH_STRING) return {GL_ ## TYPE, GL_ ## ELEMENT_TYPE, ELEMENT_COUNT}
227 :
228 0 : CASE("float", FLOAT, FLOAT, 1);
229 0 : CASE("vec2", FLOAT_VEC2, FLOAT, 2);
230 0 : CASE("vec3", FLOAT_VEC3, FLOAT, 3);
231 0 : CASE("vec4", FLOAT_VEC4, FLOAT, 4);
232 0 : CASE("mat2", FLOAT_MAT2, FLOAT, 4);
233 0 : CASE("mat3", FLOAT_MAT3, FLOAT, 9);
234 0 : CASE("mat4", FLOAT_MAT4, FLOAT, 16);
235 : #if !CONFIG2_GLES // GL ES 2.0 doesn't support non-square matrices.
236 0 : CASE("mat2x3", FLOAT_MAT2x3, FLOAT, 6);
237 0 : CASE("mat2x4", FLOAT_MAT2x4, FLOAT, 8);
238 0 : CASE("mat3x2", FLOAT_MAT3x2, FLOAT, 6);
239 0 : CASE("mat3x4", FLOAT_MAT3x4, FLOAT, 12);
240 0 : CASE("mat4x2", FLOAT_MAT4x2, FLOAT, 8);
241 0 : CASE("mat4x3", FLOAT_MAT4x3, FLOAT, 12);
242 : #endif
243 :
244 : // A somewhat incomplete listing, missing "shadow" and "rect" versions
245 : // which are interpreted as 2D (NB: our shadowmaps may change
246 : // type based on user config).
247 : #if CONFIG2_GLES
248 : if (str == "sampler1D") debug_warn(L"sampler1D not implemented on GLES");
249 : #else
250 0 : CASE("sampler1D", SAMPLER_1D, TEXTURE_1D, 1);
251 : #endif
252 0 : CASE("sampler2D", SAMPLER_2D, TEXTURE_2D, 1);
253 : #if CONFIG2_GLES
254 : if (str == "sampler2DShadow") debug_warn(L"sampler2DShadow not implemented on GLES");
255 : if (str == "sampler3D") debug_warn(L"sampler3D not implemented on GLES");
256 : #else
257 0 : CASE("sampler2DShadow", SAMPLER_2D_SHADOW, TEXTURE_2D, 1);
258 0 : CASE("sampler3D", SAMPLER_3D, TEXTURE_3D, 1);
259 : #endif
260 0 : CASE("samplerCube", SAMPLER_CUBE, TEXTURE_CUBE_MAP, 1);
261 :
262 : #undef CASE
263 0 : return {0, 0, 0};
264 : }
265 : #endif // !CONFIG2_GLES
266 :
267 : } // anonymous namespace
268 :
269 0 : IDevice* CVertexInputLayout::GetDevice()
270 : {
271 0 : return m_Device;
272 : }
273 :
274 : #if !CONFIG2_GLES
275 :
276 : class CShaderProgramARB final : public CShaderProgram
277 : {
278 : public:
279 0 : CShaderProgramARB(
280 : CDevice* device,
281 : const VfsPath& path, const VfsPath& vertexFilePath, const VfsPath& fragmentFilePath,
282 : const CShaderDefines& defines,
283 : const std::map<CStrIntern, std::pair<CStr, int>>& vertexIndices,
284 : const std::map<CStrIntern, std::pair<CStr, int>>& fragmentIndices,
285 : int streamflags)
286 0 : : CShaderProgram(streamflags), m_Device(device)
287 : {
288 0 : glGenProgramsARB(1, &m_VertexProgram);
289 0 : glGenProgramsARB(1, &m_FragmentProgram);
290 :
291 0 : std::vector<VfsPath> newFileDependencies = {path, vertexFilePath, fragmentFilePath};
292 :
293 0 : CStr vertexCode;
294 0 : if (!PreprocessShaderFile(true, defines, vertexFilePath, "STAGE_VERTEX", vertexCode, newFileDependencies))
295 0 : return;
296 0 : CStr fragmentCode;
297 0 : if (!PreprocessShaderFile(true, defines, fragmentFilePath, "STAGE_FRAGMENT", fragmentCode, newFileDependencies))
298 0 : return;
299 :
300 0 : m_FileDependencies = std::move(newFileDependencies);
301 :
302 : // TODO: replace by scoped bind.
303 0 : m_Device->GetActiveCommandContext()->SetGraphicsPipelineState(
304 0 : MakeDefaultGraphicsPipelineStateDesc());
305 :
306 0 : if (!Compile(GL_VERTEX_PROGRAM_ARB, "vertex", m_VertexProgram, vertexFilePath, vertexCode))
307 0 : return;
308 :
309 0 : if (!Compile(GL_FRAGMENT_PROGRAM_ARB, "fragment", m_FragmentProgram, fragmentFilePath, fragmentCode))
310 0 : return;
311 :
312 0 : for (const auto& index : vertexIndices)
313 : {
314 0 : BindingSlot& bindingSlot = GetOrCreateBindingSlot(index.first);
315 0 : bindingSlot.vertexProgramLocation = index.second.second;
316 0 : const auto [type, elementType, elementCount] = GetElementTypeAndCountFromString(index.second.first);
317 0 : bindingSlot.type = type;
318 0 : bindingSlot.elementType = elementType;
319 0 : bindingSlot.elementCount = elementCount;
320 : }
321 :
322 0 : for (const auto& index : fragmentIndices)
323 : {
324 0 : BindingSlot& bindingSlot = GetOrCreateBindingSlot(index.first);
325 0 : bindingSlot.fragmentProgramLocation = index.second.second;
326 0 : const auto [type, elementType, elementCount] = GetElementTypeAndCountFromString(index.second.first);
327 0 : if (bindingSlot.type && type != bindingSlot.type)
328 : {
329 0 : LOGERROR("CShaderProgramARB: vertex and fragment program uniforms with the same name should have the same type.");
330 : }
331 0 : bindingSlot.type = type;
332 0 : bindingSlot.elementType = elementType;
333 0 : bindingSlot.elementCount = elementCount;
334 : }
335 : }
336 :
337 0 : ~CShaderProgramARB() override
338 0 : {
339 0 : glDeleteProgramsARB(1, &m_VertexProgram);
340 0 : glDeleteProgramsARB(1, &m_FragmentProgram);
341 0 : }
342 :
343 0 : bool Compile(GLuint target, const char* targetName, GLuint program, const VfsPath& file, const CStr& code)
344 : {
345 0 : ogl_WarnIfError();
346 :
347 0 : glBindProgramARB(target, program);
348 :
349 0 : ogl_WarnIfError();
350 :
351 0 : glProgramStringARB(target, GL_PROGRAM_FORMAT_ASCII_ARB, (GLsizei)code.length(), code.c_str());
352 :
353 0 : if (ogl_SquelchError(GL_INVALID_OPERATION))
354 : {
355 0 : GLint errPos = 0;
356 0 : glGetIntegerv(GL_PROGRAM_ERROR_POSITION_ARB, &errPos);
357 0 : int errLine = std::count(code.begin(), code.begin() + std::min((int)code.length(), errPos + 1), '\n') + 1;
358 0 : char* errStr = (char*)glGetString(GL_PROGRAM_ERROR_STRING_ARB);
359 0 : LOGERROR("Failed to compile %s program '%s' (line %d):\n%s", targetName, file.string8(), errLine, errStr);
360 0 : return false;
361 : }
362 :
363 0 : glBindProgramARB(target, 0);
364 :
365 0 : ogl_WarnIfError();
366 :
367 0 : return true;
368 : }
369 :
370 0 : void Bind(CShaderProgram* previousShaderProgram) override
371 : {
372 0 : if (previousShaderProgram)
373 0 : previousShaderProgram->Unbind();
374 :
375 0 : glBindProgramARB(GL_VERTEX_PROGRAM_ARB, m_VertexProgram);
376 0 : glBindProgramARB(GL_FRAGMENT_PROGRAM_ARB, m_FragmentProgram);
377 :
378 0 : BindClientStates();
379 0 : }
380 :
381 0 : void Unbind() override
382 : {
383 0 : glBindProgramARB(GL_VERTEX_PROGRAM_ARB, 0);
384 0 : glBindProgramARB(GL_FRAGMENT_PROGRAM_ARB, 0);
385 :
386 0 : UnbindClientStates();
387 0 : }
388 :
389 0 : IDevice* GetDevice() override { return m_Device; }
390 :
391 0 : int32_t GetBindingSlot(const CStrIntern name) const override
392 : {
393 0 : auto it = m_BindingSlotsMapping.find(name);
394 0 : return it == m_BindingSlotsMapping.end() ? -1 : it->second;
395 : }
396 :
397 0 : TextureUnit GetTextureUnit(const int32_t bindingSlot) override
398 : {
399 0 : if (bindingSlot < 0 || bindingSlot >= static_cast<int32_t>(m_BindingSlots.size()))
400 0 : return { 0, 0, 0 };
401 : TextureUnit textureUnit;
402 0 : textureUnit.type = m_BindingSlots[bindingSlot].type;
403 0 : textureUnit.target = m_BindingSlots[bindingSlot].elementType;
404 0 : textureUnit.unit = m_BindingSlots[bindingSlot].fragmentProgramLocation;
405 0 : return textureUnit;
406 : }
407 :
408 0 : void SetUniform(
409 : const int32_t bindingSlot,
410 : const float value) override
411 : {
412 0 : if (bindingSlot < 0 || bindingSlot >= static_cast<int32_t>(m_BindingSlots.size()))
413 0 : return;
414 0 : if (m_BindingSlots[bindingSlot].type != GL_FLOAT)
415 : {
416 0 : LOGERROR("CShaderProgramARB::SetUniform(): Invalid uniform type (expected float)");
417 0 : return;
418 : }
419 0 : SetUniform(m_BindingSlots[bindingSlot], value, 0.0f, 0.0f, 0.0f);
420 : }
421 :
422 0 : void SetUniform(
423 : const int32_t bindingSlot,
424 : const float valueX, const float valueY) override
425 : {
426 0 : if (bindingSlot < 0 || bindingSlot >= static_cast<int32_t>(m_BindingSlots.size()))
427 0 : return;
428 0 : if (m_BindingSlots[bindingSlot].type != GL_FLOAT_VEC2)
429 : {
430 0 : LOGERROR("CShaderProgramARB::SetUniform(): Invalid uniform type (expected vec2)");
431 0 : return;
432 : }
433 0 : SetUniform(m_BindingSlots[bindingSlot], valueX, valueY, 0.0f, 0.0f);
434 : }
435 :
436 0 : void SetUniform(
437 : const int32_t bindingSlot,
438 : const float valueX, const float valueY, const float valueZ) override
439 : {
440 0 : if (bindingSlot < 0 || bindingSlot >= static_cast<int32_t>(m_BindingSlots.size()))
441 0 : return;
442 0 : if (m_BindingSlots[bindingSlot].type != GL_FLOAT_VEC3)
443 : {
444 0 : LOGERROR("CShaderProgramARB::SetUniform(): Invalid uniform type (expected vec3)");
445 0 : return;
446 : }
447 0 : SetUniform(m_BindingSlots[bindingSlot], valueX, valueY, valueZ, 0.0f);
448 : }
449 :
450 0 : void SetUniform(
451 : const int32_t bindingSlot,
452 : const float valueX, const float valueY,
453 : const float valueZ, const float valueW) override
454 : {
455 0 : if (bindingSlot < 0 || bindingSlot >= static_cast<int32_t>(m_BindingSlots.size()))
456 0 : return;
457 0 : if (m_BindingSlots[bindingSlot].type != GL_FLOAT_VEC4)
458 : {
459 0 : LOGERROR("CShaderProgramARB::SetUniform(): Invalid uniform type (expected vec4)");
460 0 : return;
461 : }
462 0 : SetUniform(m_BindingSlots[bindingSlot], valueX, valueY, valueZ, valueW);
463 : }
464 :
465 0 : void SetUniform(
466 : const int32_t bindingSlot, PS::span<const float> values) override
467 : {
468 0 : if (bindingSlot < 0 || bindingSlot >= static_cast<int32_t>(m_BindingSlots.size()))
469 0 : return;
470 0 : if (m_BindingSlots[bindingSlot].elementType != GL_FLOAT)
471 : {
472 0 : LOGERROR("CShaderProgramARB::SetUniform(): Invalid uniform element type (expected float)");
473 0 : return;
474 : }
475 0 : if (m_BindingSlots[bindingSlot].elementCount > static_cast<GLint>(values.size()))
476 : {
477 0 : LOGERROR(
478 : "CShaderProgramARB::SetUniform(): Invalid uniform element count (expected: %zu passed: %zu)",
479 : m_BindingSlots[bindingSlot].elementCount, values.size());
480 0 : return;
481 : }
482 0 : const GLenum type = m_BindingSlots[bindingSlot].type;
483 :
484 0 : if (type == GL_FLOAT)
485 0 : SetUniform(m_BindingSlots[bindingSlot], values[0], 0.0f, 0.0f, 0.0f);
486 0 : else if (type == GL_FLOAT_VEC2)
487 0 : SetUniform(m_BindingSlots[bindingSlot], values[0], values[1], 0.0f, 0.0f);
488 0 : else if (type == GL_FLOAT_VEC3)
489 0 : SetUniform(m_BindingSlots[bindingSlot], values[0], values[1], values[2], 0.0f);
490 0 : else if (type == GL_FLOAT_VEC4)
491 0 : SetUniform(m_BindingSlots[bindingSlot], values[0], values[1], values[2], values[3]);
492 0 : else if (type == GL_FLOAT_MAT4)
493 0 : SetUniformMatrix(m_BindingSlots[bindingSlot], values);
494 : else
495 0 : LOGERROR("CShaderProgramARB::SetUniform(): Invalid uniform type (expected float, vec2, vec3, vec4, mat4)");
496 0 : ogl_WarnIfError();
497 : }
498 :
499 0 : std::vector<VfsPath> GetFileDependencies() const override
500 : {
501 0 : return m_FileDependencies;
502 : }
503 :
504 : private:
505 : struct BindingSlot
506 : {
507 : CStrIntern name;
508 : int vertexProgramLocation;
509 : int fragmentProgramLocation;
510 : GLenum type;
511 : GLenum elementType;
512 : GLint elementCount;
513 : };
514 :
515 0 : BindingSlot& GetOrCreateBindingSlot(const CStrIntern name)
516 : {
517 0 : auto it = m_BindingSlotsMapping.find(name);
518 0 : if (it == m_BindingSlotsMapping.end())
519 : {
520 0 : m_BindingSlotsMapping[name] = m_BindingSlots.size();
521 0 : BindingSlot bindingSlot{};
522 0 : bindingSlot.name = name;
523 0 : bindingSlot.vertexProgramLocation = -1;
524 0 : bindingSlot.fragmentProgramLocation = -1;
525 0 : bindingSlot.elementType = 0;
526 0 : bindingSlot.elementCount = 0;
527 0 : m_BindingSlots.emplace_back(std::move(bindingSlot));
528 0 : return m_BindingSlots.back();
529 : }
530 : else
531 0 : return m_BindingSlots[it->second];
532 : }
533 :
534 0 : void SetUniform(
535 : const BindingSlot& bindingSlot,
536 : const float v0, const float v1, const float v2, const float v3)
537 : {
538 0 : SetUniform(GL_VERTEX_PROGRAM_ARB, bindingSlot.vertexProgramLocation, v0, v1, v2, v3);
539 0 : SetUniform(GL_FRAGMENT_PROGRAM_ARB, bindingSlot.fragmentProgramLocation, v0, v1, v2, v3);
540 0 : }
541 :
542 0 : void SetUniform(
543 : const GLenum target, const int location,
544 : const float v0, const float v1, const float v2, const float v3)
545 : {
546 0 : if (location >= 0)
547 : {
548 0 : glProgramLocalParameter4fARB(
549 : target, static_cast<GLuint>(location), v0, v1, v2, v3);
550 : }
551 0 : }
552 :
553 0 : void SetUniformMatrix(
554 : const BindingSlot& bindingSlot, PS::span<const float> values)
555 : {
556 0 : const size_t mat4ElementCount = 16;
557 0 : ENSURE(values.size() == mat4ElementCount);
558 0 : SetUniformMatrix(GL_VERTEX_PROGRAM_ARB, bindingSlot.vertexProgramLocation, values);
559 0 : SetUniformMatrix(GL_FRAGMENT_PROGRAM_ARB, bindingSlot.fragmentProgramLocation, values);
560 0 : }
561 :
562 0 : void SetUniformMatrix(
563 : const GLenum target, const int location, PS::span<const float> values)
564 : {
565 0 : if (location >= 0)
566 : {
567 0 : glProgramLocalParameter4fARB(
568 0 : target, static_cast<GLuint>(location + 0), values[0], values[4], values[8], values[12]);
569 0 : glProgramLocalParameter4fARB(
570 0 : target, static_cast<GLuint>(location + 1), values[1], values[5], values[9], values[13]);
571 0 : glProgramLocalParameter4fARB(
572 0 : target, static_cast<GLuint>(location + 2), values[2], values[6], values[10], values[14]);
573 0 : glProgramLocalParameter4fARB(
574 0 : target, static_cast<GLuint>(location + 3), values[3], values[7], values[11], values[15]);
575 : }
576 0 : }
577 :
578 : CDevice* m_Device = nullptr;
579 :
580 : std::vector<VfsPath> m_FileDependencies;
581 :
582 : GLuint m_VertexProgram;
583 : GLuint m_FragmentProgram;
584 :
585 : std::vector<BindingSlot> m_BindingSlots;
586 : std::unordered_map<CStrIntern, int32_t> m_BindingSlotsMapping;
587 : };
588 :
589 : #endif // !CONFIG2_GLES
590 :
591 : class CShaderProgramGLSL final : public CShaderProgram
592 : {
593 : public:
594 0 : CShaderProgramGLSL(
595 : CDevice* device, const CStr& name,
596 : const VfsPath& path, const VfsPath& vertexFilePath, const VfsPath& fragmentFilePath,
597 : const CShaderDefines& defines,
598 : const std::map<CStrIntern, int>& vertexAttribs,
599 0 : int streamflags) :
600 : CShaderProgram(streamflags),
601 : m_Device(device), m_Name(name),
602 0 : m_VertexAttribs(vertexAttribs)
603 : {
604 0 : for (std::map<CStrIntern, int>::iterator it = m_VertexAttribs.begin(); it != m_VertexAttribs.end(); ++it)
605 0 : m_ActiveVertexAttributes.emplace_back(it->second);
606 0 : std::sort(m_ActiveVertexAttributes.begin(), m_ActiveVertexAttributes.end());
607 :
608 0 : m_Program = 0;
609 0 : m_VertexShader = glCreateShader(GL_VERTEX_SHADER);
610 0 : m_FragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
611 0 : m_FileDependencies = {path, vertexFilePath, fragmentFilePath};
612 :
613 : #if !CONFIG2_GLES
614 0 : if (m_Device->GetCapabilities().debugLabels)
615 : {
616 0 : glObjectLabel(GL_SHADER, m_VertexShader, -1, vertexFilePath.string8().c_str());
617 0 : glObjectLabel(GL_SHADER, m_FragmentShader, -1, fragmentFilePath.string8().c_str());
618 : }
619 : #endif
620 :
621 0 : std::vector<VfsPath> newFileDependencies = {path, vertexFilePath, fragmentFilePath};
622 :
623 0 : CStr vertexCode;
624 0 : if (!PreprocessShaderFile(false, defines, vertexFilePath, "STAGE_VERTEX", vertexCode, newFileDependencies))
625 0 : return;
626 0 : CStr fragmentCode;
627 0 : if (!PreprocessShaderFile(false, defines, fragmentFilePath, "STAGE_FRAGMENT", fragmentCode, newFileDependencies))
628 0 : return;
629 :
630 0 : m_FileDependencies = std::move(newFileDependencies);
631 :
632 0 : if (vertexCode.empty())
633 : {
634 0 : LOGERROR("Failed to preprocess vertex shader: '%s'", vertexFilePath.string8());
635 0 : return;
636 : }
637 0 : if (fragmentCode.empty())
638 : {
639 0 : LOGERROR("Failed to preprocess fragment shader: '%s'", fragmentFilePath.string8());
640 0 : return;
641 : }
642 :
643 : #if CONFIG2_GLES
644 : // Ugly hack to replace desktop GLSL 1.10/1.20 with GLSL ES 1.00,
645 : // and also to set default float precision for fragment shaders
646 : vertexCode.Replace("#version 110\n", "#version 100\nprecision highp float;\n");
647 : vertexCode.Replace("#version 110\r\n", "#version 100\nprecision highp float;\n");
648 : vertexCode.Replace("#version 120\n", "#version 100\nprecision highp float;\n");
649 : vertexCode.Replace("#version 120\r\n", "#version 100\nprecision highp float;\n");
650 : fragmentCode.Replace("#version 110\n", "#version 100\nprecision highp float;\n");
651 : fragmentCode.Replace("#version 110\r\n", "#version 100\nprecision highp float;\n");
652 : fragmentCode.Replace("#version 120\n", "#version 100\nprecision highp float;\n");
653 : fragmentCode.Replace("#version 120\r\n", "#version 100\nprecision highp float;\n");
654 : #endif
655 :
656 : // TODO: replace by scoped bind.
657 0 : m_Device->GetActiveCommandContext()->SetGraphicsPipelineState(
658 0 : MakeDefaultGraphicsPipelineStateDesc());
659 :
660 0 : if (!Compile(m_VertexShader, vertexFilePath, vertexCode))
661 0 : return;
662 :
663 0 : if (!Compile(m_FragmentShader, fragmentFilePath, fragmentCode))
664 0 : return;
665 :
666 0 : if (!Link(vertexFilePath, fragmentFilePath))
667 0 : return;
668 : }
669 :
670 0 : ~CShaderProgramGLSL() override
671 0 : {
672 0 : if (m_Program)
673 0 : glDeleteProgram(m_Program);
674 :
675 0 : glDeleteShader(m_VertexShader);
676 0 : glDeleteShader(m_FragmentShader);
677 0 : }
678 :
679 0 : bool Compile(GLuint shader, const VfsPath& file, const CStr& code)
680 : {
681 0 : const char* code_string = code.c_str();
682 0 : GLint code_length = code.length();
683 0 : glShaderSource(shader, 1, &code_string, &code_length);
684 :
685 0 : ogl_WarnIfError();
686 :
687 0 : glCompileShader(shader);
688 :
689 0 : GLint ok = 0;
690 0 : glGetShaderiv(shader, GL_COMPILE_STATUS, &ok);
691 :
692 0 : GLint length = 0;
693 0 : glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &length);
694 :
695 : // Apparently sometimes GL_INFO_LOG_LENGTH is incorrectly reported as 0
696 : // (http://code.google.com/p/android/issues/detail?id=9953)
697 0 : if (!ok && length == 0)
698 0 : length = 4096;
699 :
700 0 : if (length > 1)
701 : {
702 0 : char* infolog = new char[length];
703 0 : glGetShaderInfoLog(shader, length, NULL, infolog);
704 :
705 0 : if (ok)
706 0 : LOGMESSAGE("Info when compiling shader '%s':\n%s", file.string8(), infolog);
707 : else
708 0 : LOGERROR("Failed to compile shader '%s':\n%s", file.string8(), infolog);
709 :
710 0 : delete[] infolog;
711 : }
712 :
713 0 : ogl_WarnIfError();
714 :
715 0 : return ok;
716 : }
717 :
718 0 : bool Link(const VfsPath& vertexFilePath, const VfsPath& fragmentFilePath)
719 : {
720 0 : ENSURE(!m_Program);
721 0 : m_Program = glCreateProgram();
722 :
723 : #if !CONFIG2_GLES
724 0 : if (m_Device->GetCapabilities().debugLabels)
725 : {
726 0 : glObjectLabel(GL_PROGRAM, m_Program, -1, m_Name.c_str());
727 : }
728 : #endif
729 :
730 0 : glAttachShader(m_Program, m_VertexShader);
731 0 : ogl_WarnIfError();
732 0 : glAttachShader(m_Program, m_FragmentShader);
733 0 : ogl_WarnIfError();
734 :
735 : // Set up the attribute bindings explicitly, since apparently drivers
736 : // don't always pick the most efficient bindings automatically,
737 : // and also this lets us hardcode indexes into VertexPointer etc
738 0 : for (std::map<CStrIntern, int>::iterator it = m_VertexAttribs.begin(); it != m_VertexAttribs.end(); ++it)
739 0 : glBindAttribLocation(m_Program, it->second, it->first.c_str());
740 :
741 0 : glLinkProgram(m_Program);
742 :
743 0 : GLint ok = 0;
744 0 : glGetProgramiv(m_Program, GL_LINK_STATUS, &ok);
745 :
746 0 : GLint length = 0;
747 0 : glGetProgramiv(m_Program, GL_INFO_LOG_LENGTH, &length);
748 :
749 0 : if (!ok && length == 0)
750 0 : length = 4096;
751 :
752 0 : if (length > 1)
753 : {
754 0 : char* infolog = new char[length];
755 0 : glGetProgramInfoLog(m_Program, length, NULL, infolog);
756 :
757 0 : if (ok)
758 0 : LOGMESSAGE("Info when linking program '%s'+'%s':\n%s", vertexFilePath.string8(), fragmentFilePath.string8(), infolog);
759 : else
760 0 : LOGERROR("Failed to link program '%s'+'%s':\n%s", vertexFilePath.string8(), fragmentFilePath.string8(), infolog);
761 :
762 0 : delete[] infolog;
763 : }
764 :
765 0 : ogl_WarnIfError();
766 :
767 0 : if (!ok)
768 0 : return false;
769 :
770 0 : Bind(nullptr);
771 :
772 0 : ogl_WarnIfError();
773 :
774 : // Reorder sampler units to decrease redundant texture unit changes when
775 : // samplers bound in a different order.
776 : const std::unordered_map<CStrIntern, int> requiredUnits =
777 : {
778 0 : {CStrIntern("baseTex"), 0},
779 0 : {CStrIntern("normTex"), 1},
780 0 : {CStrIntern("specTex"), 2},
781 0 : {CStrIntern("aoTex"), 3},
782 0 : {CStrIntern("shadowTex"), 4},
783 0 : {CStrIntern("losTex"), 5},
784 0 : };
785 :
786 0 : std::vector<uint8_t> occupiedUnits;
787 :
788 0 : GLint numUniforms = 0;
789 0 : glGetProgramiv(m_Program, GL_ACTIVE_UNIFORMS, &numUniforms);
790 0 : ogl_WarnIfError();
791 0 : for (GLint i = 0; i < numUniforms; ++i)
792 : {
793 : // TODO: use GL_ACTIVE_UNIFORM_MAX_LENGTH for the size.
794 0 : char name[256] = {0};
795 0 : GLsizei nameLength = 0;
796 0 : GLint size = 0;
797 0 : GLenum type = 0;
798 0 : glGetActiveUniform(m_Program, i, ARRAY_SIZE(name), &nameLength, &size, &type, name);
799 0 : ogl_WarnIfError();
800 :
801 0 : const GLint location = glGetUniformLocation(m_Program, name);
802 :
803 : // OpenGL specification is a bit vague about a name returned by glGetActiveUniform.
804 : // NVIDIA drivers return uniform name with "[0]", Intel Windows drivers without;
805 0 : while (nameLength > 3 &&
806 0 : name[nameLength - 3] == '[' &&
807 0 : name[nameLength - 2] == '0' &&
808 0 : name[nameLength - 1] == ']')
809 : {
810 0 : nameLength -= 3;
811 : }
812 0 : name[nameLength] = 0;
813 :
814 0 : const CStrIntern nameIntern(name);
815 :
816 0 : m_BindingSlotsMapping[nameIntern] = m_BindingSlots.size();
817 0 : BindingSlot bindingSlot{};
818 0 : bindingSlot.name = nameIntern;
819 0 : bindingSlot.location = location;
820 0 : bindingSlot.size = size;
821 0 : bindingSlot.type = type;
822 0 : bindingSlot.isTexture = false;
823 :
824 : #define CASE(TYPE, ELEMENT_TYPE, ELEMENT_COUNT) \
825 : case GL_ ## TYPE: \
826 : bindingSlot.elementType = GL_ ## ELEMENT_TYPE; \
827 : bindingSlot.elementCount = ELEMENT_COUNT; \
828 : break;
829 :
830 0 : switch (type)
831 : {
832 0 : CASE(FLOAT, FLOAT, 1);
833 0 : CASE(FLOAT_VEC2, FLOAT, 2);
834 0 : CASE(FLOAT_VEC3, FLOAT, 3);
835 0 : CASE(FLOAT_VEC4, FLOAT, 4);
836 0 : CASE(INT, INT, 1);
837 0 : CASE(FLOAT_MAT2, FLOAT, 4);
838 0 : CASE(FLOAT_MAT3, FLOAT, 9);
839 0 : CASE(FLOAT_MAT4, FLOAT, 16);
840 : #if !CONFIG2_GLES // GL ES 2.0 doesn't support non-square matrices.
841 0 : CASE(FLOAT_MAT2x3, FLOAT, 6);
842 0 : CASE(FLOAT_MAT2x4, FLOAT, 8);
843 0 : CASE(FLOAT_MAT3x2, FLOAT, 6);
844 0 : CASE(FLOAT_MAT3x4, FLOAT, 12);
845 0 : CASE(FLOAT_MAT4x2, FLOAT, 8);
846 0 : CASE(FLOAT_MAT4x3, FLOAT, 12);
847 : #endif
848 : }
849 : #undef CASE
850 :
851 : // Assign sampler uniforms to sequential texture units.
852 0 : if (type == GL_SAMPLER_2D
853 0 : || type == GL_SAMPLER_CUBE
854 : #if !CONFIG2_GLES
855 0 : || type == GL_SAMPLER_2D_SHADOW
856 : #endif
857 : )
858 : {
859 0 : const auto it = requiredUnits.find(nameIntern);
860 0 : const int unit = it == requiredUnits.end() ? -1 : it->second;
861 0 : bindingSlot.elementType = (type == GL_SAMPLER_CUBE ? GL_TEXTURE_CUBE_MAP : GL_TEXTURE_2D);
862 0 : bindingSlot.elementCount = unit;
863 0 : bindingSlot.isTexture = true;
864 0 : if (unit != -1)
865 : {
866 0 : if (unit >= static_cast<int>(occupiedUnits.size()))
867 0 : occupiedUnits.resize(unit + 1);
868 0 : occupiedUnits[unit] = true;
869 : }
870 : }
871 :
872 0 : if (bindingSlot.elementType == 0)
873 : {
874 0 : LOGERROR("CShaderProgramGLSL::Link: unsupported uniform type: 0x%04x", static_cast<int>(type));
875 : }
876 :
877 0 : m_BindingSlots.emplace_back(std::move(bindingSlot));
878 : }
879 :
880 0 : for (BindingSlot& bindingSlot : m_BindingSlots)
881 : {
882 0 : if (!bindingSlot.isTexture)
883 0 : continue;
884 0 : if (bindingSlot.elementCount == -1)
885 : {
886 : // We need to find a minimal available unit.
887 0 : int unit = 0;
888 0 : while (unit < static_cast<int>(occupiedUnits.size()) && occupiedUnits[unit])
889 0 : ++unit;
890 0 : if (unit >= static_cast<int>(occupiedUnits.size()))
891 0 : occupiedUnits.resize(unit + 1);
892 0 : occupiedUnits[unit] = true;
893 0 : bindingSlot.elementCount = unit;
894 : }
895 : // Link uniform to unit.
896 0 : glUniform1i(bindingSlot.location, bindingSlot.elementCount);
897 0 : ogl_WarnIfError();
898 : }
899 :
900 : // TODO: verify that we're not using more samplers than is supported
901 :
902 0 : Unbind();
903 :
904 0 : return true;
905 : }
906 :
907 0 : void Bind(CShaderProgram* previousShaderProgram) override
908 : {
909 0 : CShaderProgramGLSL* previousShaderProgramGLSL = nullptr;
910 0 : if (previousShaderProgram)
911 0 : previousShaderProgramGLSL = static_cast<CShaderProgramGLSL*>(previousShaderProgram);
912 0 : ENSURE(this != previousShaderProgramGLSL);
913 :
914 0 : glUseProgram(m_Program);
915 :
916 0 : if (previousShaderProgramGLSL)
917 : {
918 0 : std::vector<int>::iterator itPrevious = previousShaderProgramGLSL->m_ActiveVertexAttributes.begin();
919 0 : std::vector<int>::iterator itNext = m_ActiveVertexAttributes.begin();
920 0 : while (
921 0 : itPrevious != previousShaderProgramGLSL->m_ActiveVertexAttributes.end() ||
922 0 : itNext != m_ActiveVertexAttributes.end())
923 : {
924 0 : if (itPrevious != previousShaderProgramGLSL->m_ActiveVertexAttributes.end() &&
925 0 : itNext != m_ActiveVertexAttributes.end())
926 : {
927 0 : if (*itPrevious == *itNext)
928 : {
929 0 : ++itPrevious;
930 0 : ++itNext;
931 : }
932 0 : else if (*itPrevious < *itNext)
933 : {
934 0 : glDisableVertexAttribArray(*itPrevious);
935 0 : ++itPrevious;
936 : }
937 0 : else if (*itPrevious > *itNext)
938 : {
939 0 : glEnableVertexAttribArray(*itNext);
940 0 : ++itNext;
941 : }
942 : }
943 0 : else if (itPrevious != previousShaderProgramGLSL->m_ActiveVertexAttributes.end())
944 : {
945 0 : glDisableVertexAttribArray(*itPrevious);
946 0 : ++itPrevious;
947 : }
948 0 : else if (itNext != m_ActiveVertexAttributes.end())
949 : {
950 0 : glEnableVertexAttribArray(*itNext);
951 0 : ++itNext;
952 : }
953 : }
954 : }
955 : else
956 : {
957 0 : for (const int index : m_ActiveVertexAttributes)
958 0 : glEnableVertexAttribArray(index);
959 : }
960 :
961 0 : m_ValidStreams = 0;
962 0 : }
963 :
964 0 : void Unbind() override
965 : {
966 0 : glUseProgram(0);
967 :
968 0 : for (const int index : m_ActiveVertexAttributes)
969 0 : glDisableVertexAttribArray(index);
970 0 : }
971 :
972 0 : IDevice* GetDevice() override { return m_Device; }
973 :
974 0 : int32_t GetBindingSlot(const CStrIntern name) const override
975 : {
976 0 : auto it = m_BindingSlotsMapping.find(name);
977 0 : return it == m_BindingSlotsMapping.end() ? -1 : it->second;
978 : }
979 :
980 0 : TextureUnit GetTextureUnit(const int32_t bindingSlot) override
981 : {
982 0 : if (bindingSlot < 0 || bindingSlot >= static_cast<int32_t>(m_BindingSlots.size()))
983 0 : return { 0, 0, 0 };
984 : TextureUnit textureUnit;
985 0 : textureUnit.type = m_BindingSlots[bindingSlot].type;
986 0 : textureUnit.target = m_BindingSlots[bindingSlot].elementType;
987 0 : textureUnit.unit = m_BindingSlots[bindingSlot].elementCount;
988 0 : return textureUnit;
989 : }
990 :
991 0 : void SetUniform(
992 : const int32_t bindingSlot,
993 : const float value) override
994 : {
995 0 : if (bindingSlot < 0 || bindingSlot >= static_cast<int32_t>(m_BindingSlots.size()))
996 0 : return;
997 0 : if (m_BindingSlots[bindingSlot].type != GL_FLOAT ||
998 0 : m_BindingSlots[bindingSlot].size != 1)
999 : {
1000 0 : LOGERROR("CShaderProgramGLSL::SetUniform(): Invalid uniform type (expected float) '%s'", m_BindingSlots[bindingSlot].name.c_str());
1001 0 : return;
1002 : }
1003 0 : glUniform1f(m_BindingSlots[bindingSlot].location, value);
1004 0 : ogl_WarnIfError();
1005 : }
1006 :
1007 0 : void SetUniform(
1008 : const int32_t bindingSlot,
1009 : const float valueX, const float valueY) override
1010 : {
1011 0 : if (bindingSlot < 0 || bindingSlot >= static_cast<int32_t>(m_BindingSlots.size()))
1012 0 : return;
1013 0 : if (m_BindingSlots[bindingSlot].type != GL_FLOAT_VEC2 ||
1014 0 : m_BindingSlots[bindingSlot].size != 1)
1015 : {
1016 0 : LOGERROR("CShaderProgramGLSL::SetUniform(): Invalid uniform type (expected vec2) '%s'", m_BindingSlots[bindingSlot].name.c_str());
1017 0 : return;
1018 : }
1019 0 : glUniform2f(m_BindingSlots[bindingSlot].location, valueX, valueY);
1020 0 : ogl_WarnIfError();
1021 : }
1022 :
1023 0 : void SetUniform(
1024 : const int32_t bindingSlot,
1025 : const float valueX, const float valueY, const float valueZ) override
1026 : {
1027 0 : if (bindingSlot < 0 || bindingSlot >= static_cast<int32_t>(m_BindingSlots.size()))
1028 0 : return;
1029 0 : if (m_BindingSlots[bindingSlot].type != GL_FLOAT_VEC3 ||
1030 0 : m_BindingSlots[bindingSlot].size != 1)
1031 : {
1032 0 : LOGERROR("CShaderProgramGLSL::SetUniform(): Invalid uniform type (expected vec3) '%s'", m_BindingSlots[bindingSlot].name.c_str());
1033 0 : return;
1034 : }
1035 0 : glUniform3f(m_BindingSlots[bindingSlot].location, valueX, valueY, valueZ);
1036 0 : ogl_WarnIfError();
1037 : }
1038 :
1039 0 : void SetUniform(
1040 : const int32_t bindingSlot,
1041 : const float valueX, const float valueY,
1042 : const float valueZ, const float valueW) override
1043 : {
1044 0 : if (bindingSlot < 0 || bindingSlot >= static_cast<int32_t>(m_BindingSlots.size()))
1045 0 : return;
1046 0 : if (m_BindingSlots[bindingSlot].type != GL_FLOAT_VEC4 ||
1047 0 : m_BindingSlots[bindingSlot].size != 1)
1048 : {
1049 0 : LOGERROR("CShaderProgramGLSL::SetUniform(): Invalid uniform type (expected vec4) '%s'", m_BindingSlots[bindingSlot].name.c_str());
1050 0 : return;
1051 : }
1052 0 : glUniform4f(m_BindingSlots[bindingSlot].location, valueX, valueY, valueZ, valueW);
1053 0 : ogl_WarnIfError();
1054 : }
1055 :
1056 0 : void SetUniform(
1057 : const int32_t bindingSlot, PS::span<const float> values) override
1058 : {
1059 0 : if (bindingSlot < 0 || bindingSlot >= static_cast<int32_t>(m_BindingSlots.size()))
1060 0 : return;
1061 0 : if (m_BindingSlots[bindingSlot].elementType != GL_FLOAT)
1062 : {
1063 0 : LOGERROR("CShaderProgramGLSL::SetUniform(): Invalid uniform element type (expected float) '%s'", m_BindingSlots[bindingSlot].name.c_str());
1064 0 : return;
1065 : }
1066 0 : if (m_BindingSlots[bindingSlot].size == 1 && m_BindingSlots[bindingSlot].elementCount > static_cast<GLint>(values.size()))
1067 : {
1068 0 : LOGERROR(
1069 : "CShaderProgramGLSL::SetUniform(): Invalid uniform element count (expected: %zu passed: %zu) '%s'",
1070 : m_BindingSlots[bindingSlot].elementCount, values.size(), m_BindingSlots[bindingSlot].name.c_str());
1071 0 : return;
1072 : }
1073 0 : const GLint location = m_BindingSlots[bindingSlot].location;
1074 0 : const GLenum type = m_BindingSlots[bindingSlot].type;
1075 :
1076 0 : if (type == GL_FLOAT)
1077 0 : glUniform1fv(location, 1, values.data());
1078 0 : else if (type == GL_FLOAT_VEC2)
1079 0 : glUniform2fv(location, 1, values.data());
1080 0 : else if (type == GL_FLOAT_VEC3)
1081 0 : glUniform3fv(location, 1, values.data());
1082 0 : else if (type == GL_FLOAT_VEC4)
1083 0 : glUniform4fv(location, 1, values.data());
1084 0 : else if (type == GL_FLOAT_MAT4)
1085 : {
1086 : // For case of array of matrices we might pass less number of matrices.
1087 : const GLint size = std::min(
1088 0 : m_BindingSlots[bindingSlot].size, static_cast<GLint>(values.size() / 16));
1089 0 : glUniformMatrix4fv(location, size, GL_FALSE, values.data());
1090 : }
1091 : else
1092 0 : LOGERROR("CShaderProgramGLSL::SetUniform(): Invalid uniform type (expected float, vec2, vec3, vec4, mat4) '%s'", m_BindingSlots[bindingSlot].name.c_str());
1093 0 : ogl_WarnIfError();
1094 : }
1095 :
1096 0 : void VertexAttribPointer(
1097 : const VertexAttributeStream stream, const Format format,
1098 : const uint32_t offset, const uint32_t stride,
1099 : const VertexAttributeRate rate, const void* data) override
1100 : {
1101 0 : const int attributeLocation = GetAttributeLocationFromStream(m_Device, stream);
1102 : std::vector<int>::const_iterator it =
1103 0 : std::lower_bound(m_ActiveVertexAttributes.begin(), m_ActiveVertexAttributes.end(), attributeLocation);
1104 0 : if (it == m_ActiveVertexAttributes.end() || *it != attributeLocation)
1105 0 : return;
1106 0 : const GLint size = GLSizeFromFormat(format);
1107 0 : const GLenum type = GLTypeFromFormat(format);
1108 0 : const GLboolean normalized = NormalizedFromFormat(format);
1109 0 : glVertexAttribPointer(
1110 : attributeLocation, size, type, normalized, stride, static_cast<const u8*>(data) + offset);
1111 : #if CONFIG2_GLES
1112 : ENSURE(!m_Device->GetCapabilities().instancing);
1113 : UNUSED2(rate);
1114 : #else
1115 0 : if (rate == VertexAttributeRate::PER_INSTANCE)
1116 0 : ENSURE(m_Device->GetCapabilities().instancing);
1117 0 : if (m_Device->GetCapabilities().instancing)
1118 : {
1119 0 : glVertexAttribDivisorARB(attributeLocation, rate == VertexAttributeRate::PER_INSTANCE ? 1 : 0);
1120 : }
1121 : #endif
1122 0 : m_ValidStreams |= GetStreamMask(stream);
1123 : }
1124 :
1125 0 : std::vector<VfsPath> GetFileDependencies() const override
1126 : {
1127 0 : return m_FileDependencies;
1128 : }
1129 :
1130 : private:
1131 : CDevice* m_Device = nullptr;
1132 :
1133 : CStr m_Name;
1134 : std::vector<VfsPath> m_FileDependencies;
1135 :
1136 : std::map<CStrIntern, int> m_VertexAttribs;
1137 : // Sorted list of active vertex attributes.
1138 : std::vector<int> m_ActiveVertexAttributes;
1139 :
1140 : GLuint m_Program;
1141 : GLuint m_VertexShader, m_FragmentShader;
1142 :
1143 : struct BindingSlot
1144 : {
1145 : CStrIntern name;
1146 : GLint location;
1147 : GLint size;
1148 : GLenum type;
1149 : GLenum elementType;
1150 : GLint elementCount;
1151 : bool isTexture;
1152 : };
1153 : std::vector<BindingSlot> m_BindingSlots;
1154 : std::unordered_map<CStrIntern, int32_t> m_BindingSlotsMapping;
1155 : };
1156 :
1157 0 : CShaderProgram::CShaderProgram(int streamflags)
1158 0 : : m_StreamFlags(streamflags), m_ValidStreams(0)
1159 : {
1160 0 : }
1161 :
1162 : CShaderProgram::~CShaderProgram() = default;
1163 :
1164 : // static
1165 0 : std::unique_ptr<CShaderProgram> CShaderProgram::Create(CDevice* device, const CStr& name, const CShaderDefines& baseDefines)
1166 : {
1167 0 : PROFILE2("loading shader");
1168 0 : PROFILE2_ATTR("name: %s", name.c_str());
1169 :
1170 0 : VfsPath xmlFilename = L"shaders/" + wstring_from_utf8(name) + L".xml";
1171 :
1172 0 : CXeromyces XeroFile;
1173 0 : PSRETURN ret = XeroFile.Load(g_VFS, xmlFilename);
1174 0 : if (ret != PSRETURN_OK)
1175 0 : return nullptr;
1176 :
1177 : #if USE_SHADER_XML_VALIDATION
1178 : {
1179 : // Serialize the XMB data and pass it to the validator
1180 0 : XMLWriter_File shaderFile;
1181 0 : shaderFile.SetPrettyPrint(false);
1182 0 : shaderFile.XMB(XeroFile);
1183 0 : bool ok = CXeromyces::ValidateEncoded("shader", name, shaderFile.GetOutput());
1184 0 : if (!ok)
1185 0 : return nullptr;
1186 : }
1187 : #endif
1188 :
1189 : // Define all the elements and attributes used in the XML file
1190 : #define EL(x) int el_##x = XeroFile.GetElementID(#x)
1191 : #define AT(x) int at_##x = XeroFile.GetAttributeID(#x)
1192 0 : EL(define);
1193 0 : EL(fragment);
1194 0 : EL(stream);
1195 0 : EL(uniform);
1196 0 : EL(vertex);
1197 0 : AT(attribute);
1198 0 : AT(file);
1199 0 : AT(if);
1200 0 : AT(loc);
1201 0 : AT(name);
1202 0 : AT(type);
1203 0 : AT(value);
1204 : #undef AT
1205 : #undef EL
1206 :
1207 0 : CPreprocessorWrapper preprocessor;
1208 0 : preprocessor.AddDefines(baseDefines);
1209 :
1210 0 : XMBElement root = XeroFile.GetRoot();
1211 :
1212 0 : const bool isGLSL = root.GetAttributes().GetNamedItem(at_type) == "glsl";
1213 :
1214 0 : VfsPath vertexFile;
1215 0 : VfsPath fragmentFile;
1216 0 : CShaderDefines defines = baseDefines;
1217 0 : std::map<CStrIntern, std::pair<CStr, int>> vertexUniforms;
1218 0 : std::map<CStrIntern, std::pair<CStr, int>> fragmentUniforms;
1219 0 : std::map<CStrIntern, int> vertexAttribs;
1220 0 : int streamFlags = 0;
1221 :
1222 0 : XERO_ITER_EL(root, child)
1223 : {
1224 0 : if (child.GetNodeName() == el_define)
1225 : {
1226 0 : defines.Add(CStrIntern(child.GetAttributes().GetNamedItem(at_name)), CStrIntern(child.GetAttributes().GetNamedItem(at_value)));
1227 : }
1228 0 : else if (child.GetNodeName() == el_vertex)
1229 : {
1230 0 : vertexFile = L"shaders/" + child.GetAttributes().GetNamedItem(at_file).FromUTF8();
1231 :
1232 0 : XERO_ITER_EL(child, param)
1233 : {
1234 0 : XMBAttributeList attributes = param.GetAttributes();
1235 :
1236 0 : CStr cond = attributes.GetNamedItem(at_if);
1237 0 : if (!cond.empty() && !preprocessor.TestConditional(cond))
1238 0 : continue;
1239 :
1240 0 : if (param.GetNodeName() == el_uniform)
1241 : {
1242 0 : vertexUniforms[CStrIntern(attributes.GetNamedItem(at_name))] =
1243 0 : std::make_pair(attributes.GetNamedItem(at_type), attributes.GetNamedItem(at_loc).ToInt());
1244 : }
1245 0 : else if (param.GetNodeName() == el_stream)
1246 : {
1247 0 : const CStr streamName = attributes.GetNamedItem(at_name);
1248 0 : const CStr attributeName = attributes.GetNamedItem(at_attribute);
1249 0 : if (attributeName.empty() && isGLSL)
1250 0 : LOGERROR("Empty attribute name in vertex shader description '%s'", vertexFile.string8().c_str());
1251 :
1252 0 : VertexAttributeStream stream =
1253 : VertexAttributeStream::UV7;
1254 0 : if (streamName == "pos")
1255 0 : stream = VertexAttributeStream::POSITION;
1256 0 : else if (streamName == "normal")
1257 0 : stream = VertexAttributeStream::NORMAL;
1258 0 : else if (streamName == "color")
1259 0 : stream = VertexAttributeStream::COLOR;
1260 0 : else if (streamName == "uv0")
1261 0 : stream = VertexAttributeStream::UV0;
1262 0 : else if (streamName == "uv1")
1263 0 : stream = VertexAttributeStream::UV1;
1264 0 : else if (streamName == "uv2")
1265 0 : stream = VertexAttributeStream::UV2;
1266 0 : else if (streamName == "uv3")
1267 0 : stream = VertexAttributeStream::UV3;
1268 0 : else if (streamName == "uv4")
1269 0 : stream = VertexAttributeStream::UV4;
1270 0 : else if (streamName == "uv5")
1271 0 : stream = VertexAttributeStream::UV5;
1272 0 : else if (streamName == "uv6")
1273 0 : stream = VertexAttributeStream::UV6;
1274 0 : else if (streamName == "uv7")
1275 0 : stream = VertexAttributeStream::UV7;
1276 : else
1277 0 : LOGERROR("Unknown stream '%s' in vertex shader description '%s'", streamName.c_str(), vertexFile.string8().c_str());
1278 :
1279 0 : if (isGLSL)
1280 : {
1281 0 : const int attributeLocation = GetAttributeLocationFromStream(device, stream);
1282 0 : vertexAttribs[CStrIntern(attributeName)] = attributeLocation;
1283 : }
1284 0 : streamFlags |= GetStreamMask(stream);
1285 : }
1286 : }
1287 : }
1288 0 : else if (child.GetNodeName() == el_fragment)
1289 : {
1290 0 : fragmentFile = L"shaders/" + child.GetAttributes().GetNamedItem(at_file).FromUTF8();
1291 :
1292 0 : XERO_ITER_EL(child, param)
1293 : {
1294 0 : XMBAttributeList attributes = param.GetAttributes();
1295 :
1296 0 : CStr cond = attributes.GetNamedItem(at_if);
1297 0 : if (!cond.empty() && !preprocessor.TestConditional(cond))
1298 0 : continue;
1299 :
1300 0 : if (param.GetNodeName() == el_uniform)
1301 : {
1302 0 : fragmentUniforms[CStrIntern(attributes.GetNamedItem(at_name))] =
1303 0 : std::make_pair(attributes.GetNamedItem(at_type), attributes.GetNamedItem(at_loc).ToInt());
1304 : }
1305 : }
1306 : }
1307 : }
1308 :
1309 0 : if (isGLSL)
1310 : {
1311 0 : return std::make_unique<CShaderProgramGLSL>(
1312 : device, name, xmlFilename, vertexFile, fragmentFile, defines,
1313 0 : vertexAttribs, streamFlags);
1314 : }
1315 : else
1316 : {
1317 : #if CONFIG2_GLES
1318 : LOGERROR("CShaderProgram::Create: '%s'+'%s': ARB shaders not supported on this device",
1319 : vertexFile.string8(), fragmentFile.string8());
1320 : return nullptr;
1321 : #else
1322 0 : return std::make_unique<CShaderProgramARB>(
1323 : device, xmlFilename, vertexFile, fragmentFile, defines,
1324 0 : vertexUniforms, fragmentUniforms, streamFlags);
1325 : #endif
1326 : }
1327 : }
1328 :
1329 : // These should all be overridden by CShaderProgramGLSL, and not used
1330 : // if a non-GLSL shader was loaded instead:
1331 :
1332 : #if CONFIG2_GLES
1333 :
1334 : // These should all be overridden by CShaderProgramGLSL
1335 : // (GLES doesn't support any other types of shader program):
1336 :
1337 : void CShaderProgram::VertexPointer(const Renderer::Backend::Format UNUSED(format), GLsizei UNUSED(stride), const void* UNUSED(pointer))
1338 : {
1339 : debug_warn("CShaderProgram::VertexPointer should be overridden");
1340 : }
1341 : void CShaderProgram::NormalPointer(const Renderer::Backend::Format UNUSED(format), GLsizei UNUSED(stride), const void* UNUSED(pointer))
1342 : {
1343 : debug_warn("CShaderProgram::NormalPointer should be overridden");
1344 : }
1345 : void CShaderProgram::ColorPointer(const Renderer::Backend::Format UNUSED(format), GLsizei UNUSED(stride), const void* UNUSED(pointer))
1346 : {
1347 : debug_warn("CShaderProgram::ColorPointer should be overridden");
1348 : }
1349 : void CShaderProgram::TexCoordPointer(GLenum UNUSED(texture), const Renderer::Backend::Format UNUSED(format), GLsizei UNUSED(stride), const void* UNUSED(pointer))
1350 : {
1351 : debug_warn("CShaderProgram::TexCoordPointer should be overridden");
1352 : }
1353 :
1354 : #else
1355 :
1356 : // These are overridden by CShaderProgramGLSL, but fixed-function and ARB shaders
1357 : // both use the fixed-function vertex attribute pointers so we'll share their
1358 : // definitions here:
1359 :
1360 0 : void CShaderProgram::VertexPointer(const Renderer::Backend::Format format, GLsizei stride, const void* pointer)
1361 : {
1362 0 : const GLint size = GLSizeFromFormat(format);
1363 0 : ENSURE(2 <= size && size <= 4);
1364 0 : const GLenum type = GLTypeFromFormat(format);
1365 0 : glVertexPointer(size, type, stride, pointer);
1366 0 : m_ValidStreams |= GetStreamMask(VertexAttributeStream::POSITION);
1367 0 : }
1368 :
1369 0 : void CShaderProgram::NormalPointer(const Renderer::Backend::Format format, GLsizei stride, const void* pointer)
1370 : {
1371 0 : ENSURE(format == Renderer::Backend::Format::R32G32B32_SFLOAT);
1372 0 : glNormalPointer(GL_FLOAT, stride, pointer);
1373 0 : m_ValidStreams |= GetStreamMask(VertexAttributeStream::NORMAL);
1374 0 : }
1375 :
1376 0 : void CShaderProgram::ColorPointer(const Renderer::Backend::Format format, GLsizei stride, const void* pointer)
1377 : {
1378 0 : const GLint size = GLSizeFromFormat(format);
1379 0 : ENSURE(3 <= size && size <= 4);
1380 0 : const GLenum type = GLTypeFromFormat(format);
1381 0 : glColorPointer(size, type, stride, pointer);
1382 0 : m_ValidStreams |= GetStreamMask(VertexAttributeStream::COLOR);
1383 0 : }
1384 :
1385 0 : void CShaderProgram::TexCoordPointer(GLenum texture, const Renderer::Backend::Format format, GLsizei stride, const void* pointer)
1386 : {
1387 0 : glClientActiveTextureARB(texture);
1388 0 : const GLint size = GLSizeFromFormat(format);
1389 0 : ENSURE(1 <= size && size <= 4);
1390 0 : const GLenum type = GLTypeFromFormat(format);
1391 0 : glTexCoordPointer(size, type, stride, pointer);
1392 0 : glClientActiveTextureARB(GL_TEXTURE0);
1393 0 : m_ValidStreams |= GetStreamMask(VertexAttributeStream::UV0) << (texture - GL_TEXTURE0);
1394 0 : }
1395 :
1396 0 : void CShaderProgram::BindClientStates()
1397 : {
1398 0 : ENSURE(m_StreamFlags == (m_StreamFlags & (
1399 : GetStreamMask(VertexAttributeStream::POSITION) |
1400 : GetStreamMask(VertexAttributeStream::NORMAL) |
1401 : GetStreamMask(VertexAttributeStream::COLOR) |
1402 : GetStreamMask(VertexAttributeStream::UV0) |
1403 : GetStreamMask(VertexAttributeStream::UV1))));
1404 :
1405 : // Enable all the desired client states for non-GLSL rendering
1406 :
1407 0 : if (m_StreamFlags & GetStreamMask(VertexAttributeStream::POSITION))
1408 0 : glEnableClientState(GL_VERTEX_ARRAY);
1409 0 : if (m_StreamFlags & GetStreamMask(VertexAttributeStream::NORMAL))
1410 0 : glEnableClientState(GL_NORMAL_ARRAY);
1411 0 : if (m_StreamFlags & GetStreamMask(VertexAttributeStream::COLOR))
1412 0 : glEnableClientState(GL_COLOR_ARRAY);
1413 :
1414 0 : if (m_StreamFlags & GetStreamMask(VertexAttributeStream::UV0))
1415 : {
1416 0 : glClientActiveTextureARB(GL_TEXTURE0);
1417 0 : glEnableClientState(GL_TEXTURE_COORD_ARRAY);
1418 : }
1419 :
1420 0 : if (m_StreamFlags & GetStreamMask(VertexAttributeStream::UV1))
1421 : {
1422 0 : glClientActiveTextureARB(GL_TEXTURE1);
1423 0 : glEnableClientState(GL_TEXTURE_COORD_ARRAY);
1424 0 : glClientActiveTextureARB(GL_TEXTURE0);
1425 : }
1426 :
1427 : // Rendering code must subsequently call VertexPointer etc for all of the streams
1428 : // that were activated in this function, else AssertPointersBound will complain
1429 : // that some arrays were unspecified
1430 0 : m_ValidStreams = 0;
1431 0 : }
1432 :
1433 0 : void CShaderProgram::UnbindClientStates()
1434 : {
1435 0 : if (m_StreamFlags & GetStreamMask(VertexAttributeStream::POSITION))
1436 0 : glDisableClientState(GL_VERTEX_ARRAY);
1437 0 : if (m_StreamFlags & GetStreamMask(VertexAttributeStream::NORMAL))
1438 0 : glDisableClientState(GL_NORMAL_ARRAY);
1439 0 : if (m_StreamFlags & GetStreamMask(VertexAttributeStream::COLOR))
1440 0 : glDisableClientState(GL_COLOR_ARRAY);
1441 :
1442 0 : if (m_StreamFlags & GetStreamMask(VertexAttributeStream::UV0))
1443 : {
1444 0 : glClientActiveTextureARB(GL_TEXTURE0);
1445 0 : glDisableClientState(GL_TEXTURE_COORD_ARRAY);
1446 : }
1447 :
1448 0 : if (m_StreamFlags & GetStreamMask(VertexAttributeStream::UV1))
1449 : {
1450 0 : glClientActiveTextureARB(GL_TEXTURE1);
1451 0 : glDisableClientState(GL_TEXTURE_COORD_ARRAY);
1452 0 : glClientActiveTextureARB(GL_TEXTURE0);
1453 : }
1454 0 : }
1455 :
1456 : #endif // !CONFIG2_GLES
1457 :
1458 0 : bool CShaderProgram::IsStreamActive(const VertexAttributeStream stream) const
1459 : {
1460 0 : return (m_StreamFlags & GetStreamMask(stream)) != 0;
1461 : }
1462 :
1463 0 : void CShaderProgram::VertexAttribPointer(
1464 : const VertexAttributeStream stream, const Format format,
1465 : const uint32_t offset, const uint32_t stride,
1466 : const VertexAttributeRate rate, const void* data)
1467 : {
1468 0 : ENSURE(rate == VertexAttributeRate::PER_VERTEX);
1469 0 : switch (stream)
1470 : {
1471 0 : case VertexAttributeStream::POSITION:
1472 0 : VertexPointer(format, stride, static_cast<const u8*>(data) + offset);
1473 0 : break;
1474 0 : case VertexAttributeStream::NORMAL:
1475 0 : NormalPointer(format, stride, static_cast<const u8*>(data) + offset);
1476 0 : break;
1477 0 : case VertexAttributeStream::COLOR:
1478 0 : ColorPointer(format, stride, static_cast<const u8*>(data) + offset);
1479 0 : break;
1480 0 : case VertexAttributeStream::UV0: FALLTHROUGH;
1481 : case VertexAttributeStream::UV1: FALLTHROUGH;
1482 : case VertexAttributeStream::UV2: FALLTHROUGH;
1483 : case VertexAttributeStream::UV3: FALLTHROUGH;
1484 : case VertexAttributeStream::UV4: FALLTHROUGH;
1485 : case VertexAttributeStream::UV5: FALLTHROUGH;
1486 : case VertexAttributeStream::UV6: FALLTHROUGH;
1487 : case VertexAttributeStream::UV7:
1488 : {
1489 0 : const int indexOffset = static_cast<int>(stream) - static_cast<int>(VertexAttributeStream::UV0);
1490 0 : TexCoordPointer(GL_TEXTURE0 + indexOffset, format, stride, static_cast<const u8*>(data) + offset);
1491 0 : break;
1492 : }
1493 0 : default:
1494 0 : debug_warn("Unsupported stream");
1495 : };
1496 0 : }
1497 :
1498 0 : void CShaderProgram::AssertPointersBound()
1499 : {
1500 0 : ENSURE((m_StreamFlags & ~m_ValidStreams) == 0);
1501 0 : }
1502 :
1503 : } // namespace GL
1504 :
1505 : } // namespace Backend
1506 :
1507 3 : } // namespace Renderer
|