Removes custom vertex attribute locations for GLSL. Refs rP11489.
[0ad.git] / source / graphics / ShaderProgram.cpp
blobf0e62ce9f8b4789b18adf7b81a4fc3e2e870b8f1
1 /* Copyright (C) 2022 Wildfire Games.
2 * This file is part of 0 A.D.
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.
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.
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/>.
18 #include "precompiled.h"
20 #include "ShaderProgram.h"
22 #include "graphics/Color.h"
23 #include "graphics/PreprocessorWrapper.h"
24 #include "graphics/ShaderManager.h"
25 #include "graphics/TextureManager.h"
26 #include "lib/timer.h"
27 #include "maths/Matrix3D.h"
28 #include "maths/Vector3D.h"
29 #include "ps/CLogger.h"
30 #include "ps/Filesystem.h"
31 #include "ps/Profile.h"
32 #include "ps/XML/Xeromyces.h"
33 #include "renderer/backend/gl/DeviceCommandContext.h"
34 #include "renderer/Renderer.h"
36 #include <algorithm>
38 namespace
41 GLint GLSizeFromFormat(const Renderer::Backend::Format format)
43 GLint size = 1;
44 if (format == Renderer::Backend::Format::R32_SFLOAT ||
45 format == Renderer::Backend::Format::R16_SINT)
46 size = 1;
47 else if (
48 format == Renderer::Backend::Format::R8G8_UNORM ||
49 format == Renderer::Backend::Format::R8G8_UINT ||
50 format == Renderer::Backend::Format::R16G16_SINT ||
51 format == Renderer::Backend::Format::R32G32_SFLOAT)
52 size = 2;
53 else if (format == Renderer::Backend::Format::R32G32B32_SFLOAT)
54 size = 3;
55 else if (
56 format == Renderer::Backend::Format::R32G32B32A32_SFLOAT ||
57 format == Renderer::Backend::Format::R8G8B8A8_UNORM ||
58 format == Renderer::Backend::Format::R8G8B8A8_UINT)
59 size = 4;
60 else
61 debug_warn("Unsupported format.");
62 return size;
65 GLenum GLTypeFromFormat(const Renderer::Backend::Format format)
67 GLenum type = GL_FLOAT;
68 if (format == Renderer::Backend::Format::R32_SFLOAT ||
69 format == Renderer::Backend::Format::R32G32_SFLOAT ||
70 format == Renderer::Backend::Format::R32G32B32_SFLOAT ||
71 format == Renderer::Backend::Format::R32G32B32A32_SFLOAT)
72 type = GL_FLOAT;
73 else if (
74 format == Renderer::Backend::Format::R16_SINT ||
75 format == Renderer::Backend::Format::R16G16_SINT)
76 type = GL_SHORT;
77 else if (
78 format == Renderer::Backend::Format::R8G8_UNORM ||
79 format == Renderer::Backend::Format::R8G8_UINT ||
80 format == Renderer::Backend::Format::R8G8B8A8_UNORM ||
81 format == Renderer::Backend::Format::R8G8B8A8_UINT)
82 type = GL_UNSIGNED_BYTE;
83 else
84 debug_warn("Unsupported format.");
85 return type;
88 GLenum ParseAttribSemantics(const CStr& str)
90 // Map known semantics onto the attribute locations documented by NVIDIA
91 if (str == "gl_Vertex") return 0;
92 if (str == "gl_Normal") return 2;
93 if (str == "gl_Color") return 3;
94 if (str == "gl_SecondaryColor") return 4;
95 if (str == "gl_FogCoord") return 5;
96 if (str == "gl_MultiTexCoord0") return 8;
97 if (str == "gl_MultiTexCoord1") return 9;
98 if (str == "gl_MultiTexCoord2") return 10;
99 if (str == "gl_MultiTexCoord3") return 11;
100 if (str == "gl_MultiTexCoord4") return 12;
101 if (str == "gl_MultiTexCoord5") return 13;
102 if (str == "gl_MultiTexCoord6") return 14;
103 if (str == "gl_MultiTexCoord7") return 15;
105 debug_warn("Invalid attribute semantics");
106 return 0;
109 } // anonymous namespace
111 #if !CONFIG2_GLES
113 class CShaderProgramARB : public CShaderProgram
115 public:
116 CShaderProgramARB(const VfsPath& vertexFile, const VfsPath& fragmentFile,
117 const CShaderDefines& defines,
118 const std::map<CStrIntern, int>& vertexIndexes, const std::map<CStrIntern, frag_index_pair_t>& fragmentIndexes,
119 int streamflags) :
120 CShaderProgram(streamflags),
121 m_VertexFile(vertexFile), m_FragmentFile(fragmentFile),
122 m_Defines(defines),
123 m_VertexIndexes(vertexIndexes), m_FragmentIndexes(fragmentIndexes)
125 glGenProgramsARB(1, &m_VertexProgram);
126 glGenProgramsARB(1, &m_FragmentProgram);
129 ~CShaderProgramARB()
131 Unload();
133 glDeleteProgramsARB(1, &m_VertexProgram);
134 glDeleteProgramsARB(1, &m_FragmentProgram);
137 bool Compile(GLuint target, const char* targetName, GLuint program, const VfsPath& file, const CStr& code)
139 ogl_WarnIfError();
141 glBindProgramARB(target, program);
143 ogl_WarnIfError();
145 glProgramStringARB(target, GL_PROGRAM_FORMAT_ASCII_ARB, (GLsizei)code.length(), code.c_str());
147 if (ogl_SquelchError(GL_INVALID_OPERATION))
149 GLint errPos = 0;
150 glGetIntegerv(GL_PROGRAM_ERROR_POSITION_ARB, &errPos);
151 int errLine = std::count(code.begin(), code.begin() + std::min((int)code.length(), errPos + 1), '\n') + 1;
152 char* errStr = (char*)glGetString(GL_PROGRAM_ERROR_STRING_ARB);
153 LOGERROR("Failed to compile %s program '%s' (line %d):\n%s", targetName, file.string8(), errLine, errStr);
154 return false;
157 glBindProgramARB(target, 0);
159 ogl_WarnIfError();
161 return true;
164 void Reload() override
166 Unload();
168 CVFSFile vertexFile;
169 if (vertexFile.Load(g_VFS, m_VertexFile) != PSRETURN_OK)
170 return;
172 CVFSFile fragmentFile;
173 if (fragmentFile.Load(g_VFS, m_FragmentFile) != PSRETURN_OK)
174 return;
176 CPreprocessorWrapper preprocessor;
177 preprocessor.AddDefines(m_Defines);
179 CStr vertexCode = preprocessor.Preprocess(vertexFile.GetAsString());
180 CStr fragmentCode = preprocessor.Preprocess(fragmentFile.GetAsString());
182 if (!Compile(GL_VERTEX_PROGRAM_ARB, "vertex", m_VertexProgram, m_VertexFile, vertexCode))
183 return;
185 if (!Compile(GL_FRAGMENT_PROGRAM_ARB, "fragment", m_FragmentProgram, m_FragmentFile, fragmentCode))
186 return;
189 void Unload()
193 void Bind() override
195 glBindProgramARB(GL_VERTEX_PROGRAM_ARB, m_VertexProgram);
196 glBindProgramARB(GL_FRAGMENT_PROGRAM_ARB, m_FragmentProgram);
198 BindClientStates();
201 void Unbind() override
203 glBindProgramARB(GL_VERTEX_PROGRAM_ARB, 0);
204 glBindProgramARB(GL_FRAGMENT_PROGRAM_ARB, 0);
206 UnbindClientStates();
208 // TODO: should unbind textures, probably
211 int GetUniformVertexIndex(CStrIntern id)
213 std::map<CStrIntern, int>::iterator it = m_VertexIndexes.find(id);
214 if (it == m_VertexIndexes.end())
215 return -1;
216 return it->second;
219 frag_index_pair_t GetUniformFragmentIndex(CStrIntern id)
221 std::map<CStrIntern, frag_index_pair_t>::iterator it = m_FragmentIndexes.find(id);
222 if (it == m_FragmentIndexes.end())
223 return std::make_pair(-1, 0);
224 return it->second;
227 Binding GetTextureBinding(texture_id_t id) override
229 frag_index_pair_t fPair = GetUniformFragmentIndex(id);
230 int index = fPair.first;
231 if (index == -1)
232 return Binding();
233 else
234 return Binding((int)fPair.second, index);
237 void BindTexture(texture_id_t id, GLuint tex) override
239 frag_index_pair_t fPair = GetUniformFragmentIndex(id);
240 int index = fPair.first;
241 if (index != -1)
243 g_Renderer.GetDeviceCommandContext()->BindTexture(index, fPair.second, tex);
247 void BindTexture(Binding id, GLuint tex) override
249 int index = id.second;
250 if (index != -1)
252 g_Renderer.GetDeviceCommandContext()->BindTexture(index, id.first, tex);
256 Binding GetUniformBinding(uniform_id_t id) override
258 return Binding(GetUniformVertexIndex(id), GetUniformFragmentIndex(id).first);
261 void Uniform(Binding id, float v0, float v1, float v2, float v3) override
263 if (id.first != -1)
264 glProgramLocalParameter4fARB(GL_VERTEX_PROGRAM_ARB, (GLuint)id.first, v0, v1, v2, v3);
266 if (id.second != -1)
267 glProgramLocalParameter4fARB(GL_FRAGMENT_PROGRAM_ARB, (GLuint)id.second, v0, v1, v2, v3);
270 void Uniform(Binding id, const CMatrix3D& v) override
272 if (id.first != -1)
274 glProgramLocalParameter4fARB(GL_VERTEX_PROGRAM_ARB, (GLuint)id.first+0, v._11, v._12, v._13, v._14);
275 glProgramLocalParameter4fARB(GL_VERTEX_PROGRAM_ARB, (GLuint)id.first+1, v._21, v._22, v._23, v._24);
276 glProgramLocalParameter4fARB(GL_VERTEX_PROGRAM_ARB, (GLuint)id.first+2, v._31, v._32, v._33, v._34);
277 glProgramLocalParameter4fARB(GL_VERTEX_PROGRAM_ARB, (GLuint)id.first+3, v._41, v._42, v._43, v._44);
280 if (id.second != -1)
282 glProgramLocalParameter4fARB(GL_FRAGMENT_PROGRAM_ARB, (GLuint)id.second+0, v._11, v._12, v._13, v._14);
283 glProgramLocalParameter4fARB(GL_FRAGMENT_PROGRAM_ARB, (GLuint)id.second+1, v._21, v._22, v._23, v._24);
284 glProgramLocalParameter4fARB(GL_FRAGMENT_PROGRAM_ARB, (GLuint)id.second+2, v._31, v._32, v._33, v._34);
285 glProgramLocalParameter4fARB(GL_FRAGMENT_PROGRAM_ARB, (GLuint)id.second+3, v._41, v._42, v._43, v._44);
289 void Uniform(Binding id, size_t count, const CMatrix3D* v) override
291 ENSURE(count == 1);
292 Uniform(id, v[0]);
295 void Uniform(Binding id, size_t count, const float* v) override
297 ENSURE(count == 4);
298 Uniform(id, v[0], v[1], v[2], v[3]);
301 std::vector<VfsPath> GetFileDependencies() const override
303 return {m_VertexFile, m_FragmentFile};
306 private:
307 VfsPath m_VertexFile;
308 VfsPath m_FragmentFile;
309 CShaderDefines m_Defines;
311 GLuint m_VertexProgram;
312 GLuint m_FragmentProgram;
314 std::map<CStrIntern, int> m_VertexIndexes;
316 // pair contains <index, gltype>
317 std::map<CStrIntern, frag_index_pair_t> m_FragmentIndexes;
320 #endif // #if !CONFIG2_GLES
322 //////////////////////////////////////////////////////////////////////////
324 TIMER_ADD_CLIENT(tc_ShaderGLSLCompile);
325 TIMER_ADD_CLIENT(tc_ShaderGLSLLink);
327 class CShaderProgramGLSL : public CShaderProgram
329 public:
330 CShaderProgramGLSL(const VfsPath& vertexFile, const VfsPath& fragmentFile,
331 const CShaderDefines& defines,
332 const std::map<CStrIntern, int>& vertexAttribs,
333 int streamflags) :
334 CShaderProgram(streamflags),
335 m_VertexFile(vertexFile), m_FragmentFile(fragmentFile),
336 m_Defines(defines),
337 m_VertexAttribs(vertexAttribs)
339 m_Program = 0;
340 m_VertexShader = glCreateShader(GL_VERTEX_SHADER);
341 m_FragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
342 m_FileDependencies = {m_VertexFile, m_FragmentFile};
345 ~CShaderProgramGLSL()
347 Unload();
349 glDeleteShader(m_VertexShader);
350 glDeleteShader(m_FragmentShader);
353 bool Compile(GLhandleARB shader, const VfsPath& file, const CStr& code)
355 TIMER_ACCRUE(tc_ShaderGLSLCompile);
357 ogl_WarnIfError();
359 const char* code_string = code.c_str();
360 GLint code_length = code.length();
361 glShaderSource(shader, 1, &code_string, &code_length);
363 glCompileShader(shader);
365 GLint ok = 0;
366 glGetShaderiv(shader, GL_COMPILE_STATUS, &ok);
368 GLint length = 0;
369 glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &length);
371 // Apparently sometimes GL_INFO_LOG_LENGTH is incorrectly reported as 0
372 // (http://code.google.com/p/android/issues/detail?id=9953)
373 if (!ok && length == 0)
374 length = 4096;
376 if (length > 1)
378 char* infolog = new char[length];
379 glGetShaderInfoLog(shader, length, NULL, infolog);
381 if (ok)
382 LOGMESSAGE("Info when compiling shader '%s':\n%s", file.string8(), infolog);
383 else
384 LOGERROR("Failed to compile shader '%s':\n%s", file.string8(), infolog);
386 delete[] infolog;
389 ogl_WarnIfError();
391 return (ok ? true : false);
394 bool Link()
396 TIMER_ACCRUE(tc_ShaderGLSLLink);
398 ENSURE(!m_Program);
399 m_Program = glCreateProgram();
401 glAttachShader(m_Program, m_VertexShader);
402 ogl_WarnIfError();
403 glAttachShader(m_Program, m_FragmentShader);
404 ogl_WarnIfError();
406 // Set up the attribute bindings explicitly, since apparently drivers
407 // don't always pick the most efficient bindings automatically,
408 // and also this lets us hardcode indexes into VertexPointer etc
409 for (std::map<CStrIntern, int>::iterator it = m_VertexAttribs.begin(); it != m_VertexAttribs.end(); ++it)
410 glBindAttribLocation(m_Program, it->second, it->first.c_str());
412 glLinkProgram(m_Program);
414 GLint ok = 0;
415 glGetProgramiv(m_Program, GL_LINK_STATUS, &ok);
417 GLint length = 0;
418 glGetProgramiv(m_Program, GL_INFO_LOG_LENGTH, &length);
420 if (!ok && length == 0)
421 length = 4096;
423 if (length > 1)
425 char* infolog = new char[length];
426 glGetProgramInfoLog(m_Program, length, NULL, infolog);
428 if (ok)
429 LOGMESSAGE("Info when linking program '%s'+'%s':\n%s", m_VertexFile.string8(), m_FragmentFile.string8(), infolog);
430 else
431 LOGERROR("Failed to link program '%s'+'%s':\n%s", m_VertexFile.string8(), m_FragmentFile.string8(), infolog);
433 delete[] infolog;
436 ogl_WarnIfError();
438 if (!ok)
439 return false;
441 m_Uniforms.clear();
442 m_Samplers.clear();
444 Bind();
446 ogl_WarnIfError();
448 GLint numUniforms = 0;
449 glGetProgramiv(m_Program, GL_ACTIVE_UNIFORMS, &numUniforms);
450 ogl_WarnIfError();
451 for (GLint i = 0; i < numUniforms; ++i)
453 char name[256] = {0};
454 GLsizei nameLength = 0;
455 GLint size = 0;
456 GLenum type = 0;
457 glGetActiveUniform(m_Program, i, ARRAY_SIZE(name), &nameLength, &size, &type, name);
458 ogl_WarnIfError();
460 GLint loc = glGetUniformLocation(m_Program, name);
462 CStrIntern nameIntern(name);
463 m_Uniforms[nameIntern] = std::make_pair(loc, type);
465 // Assign sampler uniforms to sequential texture units
466 if (type == GL_SAMPLER_2D
467 || type == GL_SAMPLER_CUBE
468 #if !CONFIG2_GLES
469 || type == GL_SAMPLER_2D_SHADOW
470 #endif
473 int unit = (int)m_Samplers.size();
474 m_Samplers[nameIntern].first = (type == GL_SAMPLER_CUBE ? GL_TEXTURE_CUBE_MAP : GL_TEXTURE_2D);
475 m_Samplers[nameIntern].second = unit;
476 glUniform1i(loc, unit); // link uniform to unit
477 ogl_WarnIfError();
481 // TODO: verify that we're not using more samplers than is supported
483 Unbind();
485 ogl_WarnIfError();
487 return true;
490 void Reload() override
492 Unload();
494 CVFSFile vertexFile;
495 if (vertexFile.Load(g_VFS, m_VertexFile) != PSRETURN_OK)
496 return;
498 CVFSFile fragmentFile;
499 if (fragmentFile.Load(g_VFS, m_FragmentFile) != PSRETURN_OK)
500 return;
502 std::vector<VfsPath> newFileDependencies = {m_VertexFile, m_FragmentFile};
504 CPreprocessorWrapper preprocessor([&newFileDependencies](const CStr& includePath, CStr& out) -> bool {
505 const VfsPath includeFilePath(L"shaders/glsl/" + wstring_from_utf8(includePath));
506 // Add dependencies anyway to reload the shader when the file is
507 // appeared.
508 newFileDependencies.push_back(includeFilePath);
509 CVFSFile includeFile;
510 if (includeFile.Load(g_VFS, includeFilePath) != PSRETURN_OK)
511 return false;
512 out = includeFile.GetAsString();
513 return true;
515 preprocessor.AddDefines(m_Defines);
517 #if CONFIG2_GLES
518 // GLES defines the macro "GL_ES" in its GLSL preprocessor,
519 // but since we run our own preprocessor first, we need to explicitly
520 // define it here
521 preprocessor.AddDefine("GL_ES", "1");
522 #endif
524 CStr vertexCode = preprocessor.Preprocess(vertexFile.GetAsString());
525 CStr fragmentCode = preprocessor.Preprocess(fragmentFile.GetAsString());
527 m_FileDependencies = std::move(newFileDependencies);
529 if (vertexCode.empty())
530 LOGERROR("Failed to preprocess vertex shader: '%s'", m_VertexFile.string8());
531 if (fragmentCode.empty())
532 LOGERROR("Failed to preprocess fragment shader: '%s'", m_FragmentFile.string8());
534 #if CONFIG2_GLES
535 // Ugly hack to replace desktop GLSL 1.10/1.20 with GLSL ES 1.00,
536 // and also to set default float precision for fragment shaders
537 vertexCode.Replace("#version 110\n", "#version 100\n");
538 vertexCode.Replace("#version 110\r\n", "#version 100\n");
539 vertexCode.Replace("#version 120\n", "#version 100\n");
540 vertexCode.Replace("#version 120\r\n", "#version 100\n");
541 fragmentCode.Replace("#version 110\n", "#version 100\nprecision mediump float;\n");
542 fragmentCode.Replace("#version 110\r\n", "#version 100\nprecision mediump float;\n");
543 fragmentCode.Replace("#version 120\n", "#version 100\nprecision mediump float;\n");
544 fragmentCode.Replace("#version 120\r\n", "#version 100\nprecision mediump float;\n");
545 #endif
547 if (!Compile(m_VertexShader, m_VertexFile, vertexCode))
548 return;
550 if (!Compile(m_FragmentShader, m_FragmentFile, fragmentCode))
551 return;
553 if (!Link())
554 return;
557 void Unload()
559 if (m_Program)
560 glDeleteProgram(m_Program);
561 m_Program = 0;
563 // The shader objects can be reused and don't need to be deleted here
566 void Bind() override
568 glUseProgram(m_Program);
570 for (std::map<CStrIntern, int>::iterator it = m_VertexAttribs.begin(); it != m_VertexAttribs.end(); ++it)
571 glEnableVertexAttribArray(it->second);
574 void Unbind() override
576 glUseProgram(0);
578 for (std::map<CStrIntern, int>::iterator it = m_VertexAttribs.begin(); it != m_VertexAttribs.end(); ++it)
579 glDisableVertexAttribArray(it->second);
581 // TODO: should unbind textures, probably
584 Binding GetTextureBinding(texture_id_t id) override
586 std::map<CStrIntern, std::pair<GLenum, int>>::iterator it = m_Samplers.find(CStrIntern(id));
587 if (it == m_Samplers.end())
588 return Binding();
589 else
590 return Binding((int)it->second.first, it->second.second);
593 void BindTexture(texture_id_t id, GLuint tex) override
595 std::map<CStrIntern, std::pair<GLenum, int>>::iterator it = m_Samplers.find(CStrIntern(id));
596 if (it == m_Samplers.end())
597 return;
599 g_Renderer.GetDeviceCommandContext()->BindTexture(it->second.second, it->second.first, tex);
602 void BindTexture(Binding id, GLuint tex) override
604 if (id.second == -1)
605 return;
607 g_Renderer.GetDeviceCommandContext()->BindTexture(id.second, id.first, tex);
610 Binding GetUniformBinding(uniform_id_t id) override
612 std::map<CStrIntern, std::pair<int, GLenum>>::iterator it = m_Uniforms.find(id);
613 if (it == m_Uniforms.end())
614 return Binding();
615 else
616 return Binding(it->second.first, (int)it->second.second);
619 void Uniform(Binding id, float v0, float v1, float v2, float v3) override
621 if (id.first != -1)
623 if (id.second == GL_FLOAT)
624 glUniform1f(id.first, v0);
625 else if (id.second == GL_FLOAT_VEC2)
626 glUniform2f(id.first, v0, v1);
627 else if (id.second == GL_FLOAT_VEC3)
628 glUniform3f(id.first, v0, v1, v2);
629 else if (id.second == GL_FLOAT_VEC4)
630 glUniform4f(id.first, v0, v1, v2, v3);
631 else
632 LOGERROR("CShaderProgramGLSL::Uniform(): Invalid uniform type (expected float, vec2, vec3, vec4)");
636 void Uniform(Binding id, const CMatrix3D& v) override
638 if (id.first != -1)
640 if (id.second == GL_FLOAT_MAT4)
641 glUniformMatrix4fv(id.first, 1, GL_FALSE, &v._11);
642 else
643 LOGERROR("CShaderProgramGLSL::Uniform(): Invalid uniform type (expected mat4)");
647 void Uniform(Binding id, size_t count, const CMatrix3D* v) override
649 if (id.first != -1)
651 if (id.second == GL_FLOAT_MAT4)
652 glUniformMatrix4fv(id.first, count, GL_FALSE, &v->_11);
653 else
654 LOGERROR("CShaderProgramGLSL::Uniform(): Invalid uniform type (expected mat4)");
658 void Uniform(Binding id, size_t count, const float* v) override
660 if (id.first != -1)
662 if (id.second == GL_FLOAT)
663 glUniform1fv(id.first, count, v);
664 else
665 LOGERROR("CShaderProgramGLSL::Uniform(): Invalid uniform type (expected float)");
669 // Map the various fixed-function Pointer functions onto generic vertex attributes
670 // (matching the attribute indexes from ShaderManager's ParseAttribSemantics):
672 void VertexPointer(const Renderer::Backend::Format format, GLsizei stride, const void* pointer) override
674 const GLint size = GLSizeFromFormat(format);
675 const GLenum type = GLTypeFromFormat(format);
676 glVertexAttribPointer(0, size, type, GL_FALSE, stride, pointer);
677 m_ValidStreams |= STREAM_POS;
680 void NormalPointer(const Renderer::Backend::Format format, GLsizei stride, const void* pointer) override
682 const GLint size = GLSizeFromFormat(format);
683 const GLenum type = GLTypeFromFormat(format);
684 glVertexAttribPointer(2, size, type, (type == GL_FLOAT ? GL_FALSE : GL_TRUE), stride, pointer);
685 m_ValidStreams |= STREAM_NORMAL;
688 void ColorPointer(const Renderer::Backend::Format format, GLsizei stride, const void* pointer) override
690 const GLint size = GLSizeFromFormat(format);
691 const GLenum type = GLTypeFromFormat(format);
692 glVertexAttribPointer(3, size, type, (type == GL_FLOAT ? GL_FALSE : GL_TRUE), stride, pointer);
693 m_ValidStreams |= STREAM_COLOR;
696 void TexCoordPointer(GLenum texture, const Renderer::Backend::Format format, GLsizei stride, const void* pointer) override
698 const GLint size = GLSizeFromFormat(format);
699 const GLenum type = GLTypeFromFormat(format);
700 glVertexAttribPointer(8 + texture - GL_TEXTURE0, size, type, GL_FALSE, stride, pointer);
701 m_ValidStreams |= STREAM_UV0 << (texture - GL_TEXTURE0);
704 void VertexAttribPointer(attrib_id_t id, const Renderer::Backend::Format format, GLboolean normalized, GLsizei stride, const void* pointer) override
706 std::map<CStrIntern, int>::iterator it = m_VertexAttribs.find(id);
707 if (it != m_VertexAttribs.end())
709 const GLint size = GLSizeFromFormat(format);
710 const GLenum type = GLTypeFromFormat(format);
711 glVertexAttribPointer(it->second, size, type, normalized, stride, pointer);
715 std::vector<VfsPath> GetFileDependencies() const override
717 return m_FileDependencies;
720 private:
721 VfsPath m_VertexFile;
722 VfsPath m_FragmentFile;
723 std::vector<VfsPath> m_FileDependencies;
724 CShaderDefines m_Defines;
725 std::map<CStrIntern, int> m_VertexAttribs;
727 GLhandleARB m_Program;
728 GLhandleARB m_VertexShader;
729 GLhandleARB m_FragmentShader;
731 std::map<CStrIntern, std::pair<int, GLenum>> m_Uniforms;
732 std::map<CStrIntern, std::pair<GLenum, int>> m_Samplers; // texture target & unit chosen for each uniform sampler
735 //////////////////////////////////////////////////////////////////////////
737 CShaderProgram::CShaderProgram(int streamflags)
738 : m_StreamFlags(streamflags), m_ValidStreams(0)
742 // static
743 CShaderProgramPtr CShaderProgram::Create(const char* name, const CShaderDefines& baseDefines)
745 PROFILE2("loading shader");
746 PROFILE2_ATTR("name: %s", name);
748 VfsPath xmlFilename = L"shaders/" + wstring_from_utf8(name) + L".xml";
750 CXeromyces XeroFile;
751 PSRETURN ret = XeroFile.Load(g_VFS, xmlFilename);
752 if (ret != PSRETURN_OK)
753 return nullptr;
755 #if USE_SHADER_XML_VALIDATION
757 TIMER_ACCRUE(tc_ShaderValidation);
759 // Serialize the XMB data and pass it to the validator
760 XMLWriter_File shaderFile;
761 shaderFile.SetPrettyPrint(false);
762 shaderFile.XMB(XeroFile);
763 bool ok = CXeromyces::ValidateEncoded("shader", name, shaderFile.GetOutput());
764 if (!ok)
765 return nullptr;
767 #endif
769 // Define all the elements and attributes used in the XML file
770 #define EL(x) int el_##x = XeroFile.GetElementID(#x)
771 #define AT(x) int at_##x = XeroFile.GetAttributeID(#x)
772 EL(attrib);
773 EL(define);
774 EL(fragment);
775 EL(stream);
776 EL(uniform);
777 EL(vertex);
778 AT(file);
779 AT(if);
780 AT(loc);
781 AT(name);
782 AT(semantics);
783 AT(type);
784 AT(value);
785 #undef AT
786 #undef EL
788 CPreprocessorWrapper preprocessor;
789 preprocessor.AddDefines(baseDefines);
791 XMBElement root = XeroFile.GetRoot();
793 VfsPath vertexFile;
794 VfsPath fragmentFile;
795 CShaderDefines defines = baseDefines;
796 std::map<CStrIntern, int> vertexUniforms;
797 std::map<CStrIntern, CShaderProgram::frag_index_pair_t> fragmentUniforms;
798 std::map<CStrIntern, int> vertexAttribs;
799 int streamFlags = 0;
801 XERO_ITER_EL(root, Child)
803 if (Child.GetNodeName() == el_define)
805 defines.Add(CStrIntern(Child.GetAttributes().GetNamedItem(at_name)), CStrIntern(Child.GetAttributes().GetNamedItem(at_value)));
807 else if (Child.GetNodeName() == el_vertex)
809 vertexFile = L"shaders/" + Child.GetAttributes().GetNamedItem(at_file).FromUTF8();
811 XERO_ITER_EL(Child, Param)
813 XMBAttributeList Attrs = Param.GetAttributes();
815 CStr cond = Attrs.GetNamedItem(at_if);
816 if (!cond.empty() && !preprocessor.TestConditional(cond))
817 continue;
819 if (Param.GetNodeName() == el_uniform)
821 vertexUniforms[CStrIntern(Attrs.GetNamedItem(at_name))] = Attrs.GetNamedItem(at_loc).ToInt();
823 else if (Param.GetNodeName() == el_stream)
825 CStr StreamName = Attrs.GetNamedItem(at_name);
826 if (StreamName == "pos")
827 streamFlags |= STREAM_POS;
828 else if (StreamName == "normal")
829 streamFlags |= STREAM_NORMAL;
830 else if (StreamName == "color")
831 streamFlags |= STREAM_COLOR;
832 else if (StreamName == "uv0")
833 streamFlags |= STREAM_UV0;
834 else if (StreamName == "uv1")
835 streamFlags |= STREAM_UV1;
836 else if (StreamName == "uv2")
837 streamFlags |= STREAM_UV2;
838 else if (StreamName == "uv3")
839 streamFlags |= STREAM_UV3;
841 else if (Param.GetNodeName() == el_attrib)
843 int attribLoc = ParseAttribSemantics(Attrs.GetNamedItem(at_semantics));
844 vertexAttribs[CStrIntern(Attrs.GetNamedItem(at_name))] = attribLoc;
848 else if (Child.GetNodeName() == el_fragment)
850 fragmentFile = L"shaders/" + Child.GetAttributes().GetNamedItem(at_file).FromUTF8();
852 XERO_ITER_EL(Child, Param)
854 XMBAttributeList Attrs = Param.GetAttributes();
856 CStr cond = Attrs.GetNamedItem(at_if);
857 if (!cond.empty() && !preprocessor.TestConditional(cond))
858 continue;
860 if (Param.GetNodeName() == el_uniform)
862 // A somewhat incomplete listing, missing "shadow" and "rect" versions
863 // which are interpreted as 2D (NB: our shadowmaps may change
864 // type based on user config).
865 GLenum type = GL_TEXTURE_2D;
866 CStr t = Attrs.GetNamedItem(at_type);
867 if (t == "sampler1D")
868 #if CONFIG2_GLES
869 debug_warn(L"sampler1D not implemented on GLES");
870 #else
871 type = GL_TEXTURE_1D;
872 #endif
873 else if (t == "sampler2D")
874 type = GL_TEXTURE_2D;
875 else if (t == "sampler3D")
876 #if CONFIG2_GLES
877 debug_warn(L"sampler3D not implemented on GLES");
878 #else
879 type = GL_TEXTURE_3D;
880 #endif
881 else if (t == "samplerCube")
882 type = GL_TEXTURE_CUBE_MAP;
884 fragmentUniforms[CStrIntern(Attrs.GetNamedItem(at_name))] =
885 std::make_pair(Attrs.GetNamedItem(at_loc).ToInt(), type);
891 if (root.GetAttributes().GetNamedItem(at_type) == "glsl")
892 return CShaderProgram::ConstructGLSL(vertexFile, fragmentFile, defines, vertexAttribs, streamFlags);
893 else
894 return CShaderProgram::ConstructARB(vertexFile, fragmentFile, defines, vertexUniforms, fragmentUniforms, streamFlags);
897 #if CONFIG2_GLES
898 // static
899 CShaderProgramPtr CShaderProgram::ConstructARB(const VfsPath& vertexFile, const VfsPath& fragmentFile,
900 const CShaderDefines& UNUSED(defines),
901 const std::map<CStrIntern, int>& UNUSED(vertexIndexes), const std::map<CStrIntern, frag_index_pair_t>& UNUSED(fragmentIndexes),
902 int UNUSED(streamflags))
904 LOGERROR("CShaderProgram::ConstructARB: '%s'+'%s': ARB shaders not supported on this device",
905 vertexFile.string8(), fragmentFile.string8());
906 return nullptr;
908 #else
909 // static
910 CShaderProgramPtr CShaderProgram::ConstructARB(const VfsPath& vertexFile, const VfsPath& fragmentFile,
911 const CShaderDefines& defines,
912 const std::map<CStrIntern, int>& vertexIndexes, const std::map<CStrIntern, frag_index_pair_t>& fragmentIndexes,
913 int streamflags)
915 return std::make_shared<CShaderProgramARB>(vertexFile, fragmentFile, defines, vertexIndexes, fragmentIndexes, streamflags);
917 #endif
919 // static
920 CShaderProgramPtr CShaderProgram::ConstructGLSL(const VfsPath& vertexFile, const VfsPath& fragmentFile,
921 const CShaderDefines& defines,
922 const std::map<CStrIntern, int>& vertexAttribs,
923 int streamflags)
925 return std::make_shared<CShaderProgramGLSL>(vertexFile, fragmentFile, defines, vertexAttribs, streamflags);
928 int CShaderProgram::GetStreamFlags() const
930 return m_StreamFlags;
933 void CShaderProgram::BindTexture(texture_id_t id, const Renderer::Backend::GL::CTexture* tex)
935 BindTexture(id, tex->GetHandle());
938 void CShaderProgram::BindTexture(Binding id, const Renderer::Backend::GL::CTexture* tex)
940 BindTexture(id, tex->GetHandle());
943 void CShaderProgram::Uniform(Binding id, int v)
945 Uniform(id, (float)v, (float)v, (float)v, (float)v);
948 void CShaderProgram::Uniform(Binding id, float v)
950 Uniform(id, v, v, v, v);
953 void CShaderProgram::Uniform(Binding id, float v0, float v1)
955 Uniform(id, v0, v1, 0.0f, 0.0f);
958 void CShaderProgram::Uniform(Binding id, const CVector3D& v)
960 Uniform(id, v.X, v.Y, v.Z, 0.0f);
963 void CShaderProgram::Uniform(Binding id, const CColor& v)
965 Uniform(id, v.r, v.g, v.b, v.a);
968 void CShaderProgram::Uniform(uniform_id_t id, int v)
970 Uniform(GetUniformBinding(id), (float)v, (float)v, (float)v, (float)v);
973 void CShaderProgram::Uniform(uniform_id_t id, float v)
975 Uniform(GetUniformBinding(id), v, v, v, v);
978 void CShaderProgram::Uniform(uniform_id_t id, float v0, float v1)
980 Uniform(GetUniformBinding(id), v0, v1, 0.0f, 0.0f);
983 void CShaderProgram::Uniform(uniform_id_t id, const CVector3D& v)
985 Uniform(GetUniformBinding(id), v.X, v.Y, v.Z, 0.0f);
988 void CShaderProgram::Uniform(uniform_id_t id, const CColor& v)
990 Uniform(GetUniformBinding(id), v.r, v.g, v.b, v.a);
993 void CShaderProgram::Uniform(uniform_id_t id, float v0, float v1, float v2, float v3)
995 Uniform(GetUniformBinding(id), v0, v1, v2, v3);
998 void CShaderProgram::Uniform(uniform_id_t id, const CMatrix3D& v)
1000 Uniform(GetUniformBinding(id), v);
1003 void CShaderProgram::Uniform(uniform_id_t id, size_t count, const CMatrix3D* v)
1005 Uniform(GetUniformBinding(id), count, v);
1008 void CShaderProgram::Uniform(uniform_id_t id, size_t count, const float* v)
1010 Uniform(GetUniformBinding(id), count, v);
1014 // These should all be overridden by CShaderProgramGLSL, and not used
1015 // if a non-GLSL shader was loaded instead:
1017 void CShaderProgram::VertexAttribPointer(attrib_id_t UNUSED(id), const Renderer::Backend::Format UNUSED(format),
1018 GLboolean UNUSED(normalized), GLsizei UNUSED(stride), const void* UNUSED(pointer))
1020 debug_warn("Shader type doesn't support VertexAttribPointer");
1023 #if CONFIG2_GLES
1025 // These should all be overridden by CShaderProgramGLSL
1026 // (GLES doesn't support any other types of shader program):
1028 void CShaderProgram::VertexPointer(const Renderer::Backend::Format UNUSED(format), GLsizei UNUSED(stride), const void* UNUSED(pointer))
1030 debug_warn("CShaderProgram::VertexPointer should be overridden");
1032 void CShaderProgram::NormalPointer(const Renderer::Backend::Format UNUSED(format), GLsizei UNUSED(stride), const void* UNUSED(pointer))
1034 debug_warn("CShaderProgram::NormalPointer should be overridden");
1036 void CShaderProgram::ColorPointer(const Renderer::Backend::Format UNUSED(format), GLsizei UNUSED(stride), const void* UNUSED(pointer))
1038 debug_warn("CShaderProgram::ColorPointer should be overridden");
1040 void CShaderProgram::TexCoordPointer(GLenum UNUSED(texture), const Renderer::Backend::Format UNUSED(format), GLsizei UNUSED(stride), const void* UNUSED(pointer))
1042 debug_warn("CShaderProgram::TexCoordPointer should be overridden");
1045 #else
1047 // These are overridden by CShaderProgramGLSL, but fixed-function and ARB shaders
1048 // both use the fixed-function vertex attribute pointers so we'll share their
1049 // definitions here:
1051 void CShaderProgram::VertexPointer(const Renderer::Backend::Format format, GLsizei stride, const void* pointer)
1053 const GLint size = GLSizeFromFormat(format);
1054 ENSURE(2 <= size && size <= 4);
1055 const GLenum type = GLTypeFromFormat(format);
1056 glVertexPointer(size, type, stride, pointer);
1057 m_ValidStreams |= STREAM_POS;
1060 void CShaderProgram::NormalPointer(const Renderer::Backend::Format format, GLsizei stride, const void* pointer)
1062 ENSURE(format == Renderer::Backend::Format::R32G32B32_SFLOAT);
1063 glNormalPointer(GL_FLOAT, stride, pointer);
1064 m_ValidStreams |= STREAM_NORMAL;
1067 void CShaderProgram::ColorPointer(const Renderer::Backend::Format format, GLsizei stride, const void* pointer)
1069 const GLint size = GLSizeFromFormat(format);
1070 ENSURE(3 <= size && size <= 4);
1071 const GLenum type = GLTypeFromFormat(format);
1072 glColorPointer(size, type, stride, pointer);
1073 m_ValidStreams |= STREAM_COLOR;
1076 void CShaderProgram::TexCoordPointer(GLenum texture, const Renderer::Backend::Format format, GLsizei stride, const void* pointer)
1078 glClientActiveTextureARB(texture);
1079 const GLint size = GLSizeFromFormat(format);
1080 ENSURE(1 <= size && size <= 4);
1081 const GLenum type = GLTypeFromFormat(format);
1082 glTexCoordPointer(size, type, stride, pointer);
1083 glClientActiveTextureARB(GL_TEXTURE0);
1084 m_ValidStreams |= STREAM_UV0 << (texture - GL_TEXTURE0);
1087 void CShaderProgram::BindClientStates()
1089 ENSURE(m_StreamFlags == (m_StreamFlags & (STREAM_POS|STREAM_NORMAL|STREAM_COLOR|STREAM_UV0|STREAM_UV1)));
1091 // Enable all the desired client states for non-GLSL rendering
1093 if (m_StreamFlags & STREAM_POS) glEnableClientState(GL_VERTEX_ARRAY);
1094 if (m_StreamFlags & STREAM_NORMAL) glEnableClientState(GL_NORMAL_ARRAY);
1095 if (m_StreamFlags & STREAM_COLOR) glEnableClientState(GL_COLOR_ARRAY);
1097 if (m_StreamFlags & STREAM_UV0)
1099 glClientActiveTextureARB(GL_TEXTURE0);
1100 glEnableClientState(GL_TEXTURE_COORD_ARRAY);
1103 if (m_StreamFlags & STREAM_UV1)
1105 glClientActiveTextureARB(GL_TEXTURE1);
1106 glEnableClientState(GL_TEXTURE_COORD_ARRAY);
1107 glClientActiveTextureARB(GL_TEXTURE0);
1110 // Rendering code must subsequently call VertexPointer etc for all of the streams
1111 // that were activated in this function, else AssertPointersBound will complain
1112 // that some arrays were unspecified
1113 m_ValidStreams = 0;
1116 void CShaderProgram::UnbindClientStates()
1118 if (m_StreamFlags & STREAM_POS) glDisableClientState(GL_VERTEX_ARRAY);
1119 if (m_StreamFlags & STREAM_NORMAL) glDisableClientState(GL_NORMAL_ARRAY);
1120 if (m_StreamFlags & STREAM_COLOR) glDisableClientState(GL_COLOR_ARRAY);
1122 if (m_StreamFlags & STREAM_UV0)
1124 glClientActiveTextureARB(GL_TEXTURE0);
1125 glDisableClientState(GL_TEXTURE_COORD_ARRAY);
1128 if (m_StreamFlags & STREAM_UV1)
1130 glClientActiveTextureARB(GL_TEXTURE1);
1131 glDisableClientState(GL_TEXTURE_COORD_ARRAY);
1132 glClientActiveTextureARB(GL_TEXTURE0);
1136 #endif // !CONFIG2_GLES
1138 void CShaderProgram::AssertPointersBound()
1140 ENSURE((m_StreamFlags & ~m_ValidStreams) == 0);