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"
41 GLint
GLSizeFromFormat(const Renderer::Backend::Format format
)
44 if (format
== Renderer::Backend::Format::R32_SFLOAT
||
45 format
== Renderer::Backend::Format::R16_SINT
)
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
)
53 else if (format
== Renderer::Backend::Format::R32G32B32_SFLOAT
)
56 format
== Renderer::Backend::Format::R32G32B32A32_SFLOAT
||
57 format
== Renderer::Backend::Format::R8G8B8A8_UNORM
||
58 format
== Renderer::Backend::Format::R8G8B8A8_UINT
)
61 debug_warn("Unsupported format.");
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
)
74 format
== Renderer::Backend::Format::R16_SINT
||
75 format
== Renderer::Backend::Format::R16G16_SINT
)
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
;
84 debug_warn("Unsupported format.");
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");
109 } // anonymous namespace
113 class CShaderProgramARB
: public CShaderProgram
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
,
120 CShaderProgram(streamflags
),
121 m_VertexFile(vertexFile
), m_FragmentFile(fragmentFile
),
123 m_VertexIndexes(vertexIndexes
), m_FragmentIndexes(fragmentIndexes
)
125 glGenProgramsARB(1, &m_VertexProgram
);
126 glGenProgramsARB(1, &m_FragmentProgram
);
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
)
141 glBindProgramARB(target
, program
);
145 glProgramStringARB(target
, GL_PROGRAM_FORMAT_ASCII_ARB
, (GLsizei
)code
.length(), code
.c_str());
147 if (ogl_SquelchError(GL_INVALID_OPERATION
))
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
);
157 glBindProgramARB(target
, 0);
164 void Reload() override
169 if (vertexFile
.Load(g_VFS
, m_VertexFile
) != PSRETURN_OK
)
172 CVFSFile fragmentFile
;
173 if (fragmentFile
.Load(g_VFS
, m_FragmentFile
) != PSRETURN_OK
)
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
))
185 if (!Compile(GL_FRAGMENT_PROGRAM_ARB
, "fragment", m_FragmentProgram
, m_FragmentFile
, fragmentCode
))
195 glBindProgramARB(GL_VERTEX_PROGRAM_ARB
, m_VertexProgram
);
196 glBindProgramARB(GL_FRAGMENT_PROGRAM_ARB
, m_FragmentProgram
);
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())
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);
227 Binding
GetTextureBinding(texture_id_t id
) override
229 frag_index_pair_t fPair
= GetUniformFragmentIndex(id
);
230 int index
= fPair
.first
;
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
;
243 g_Renderer
.GetDeviceCommandContext()->BindTexture(index
, fPair
.second
, tex
);
247 void BindTexture(Binding id
, GLuint tex
) override
249 int index
= id
.second
;
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
264 glProgramLocalParameter4fARB(GL_VERTEX_PROGRAM_ARB
, (GLuint
)id
.first
, v0
, v1
, v2
, v3
);
267 glProgramLocalParameter4fARB(GL_FRAGMENT_PROGRAM_ARB
, (GLuint
)id
.second
, v0
, v1
, v2
, v3
);
270 void Uniform(Binding id
, const CMatrix3D
& v
) override
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
);
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
295 void Uniform(Binding id
, size_t count
, const float* v
) override
298 Uniform(id
, v
[0], v
[1], v
[2], v
[3]);
301 std::vector
<VfsPath
> GetFileDependencies() const override
303 return {m_VertexFile
, m_FragmentFile
};
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
330 CShaderProgramGLSL(const VfsPath
& vertexFile
, const VfsPath
& fragmentFile
,
331 const CShaderDefines
& defines
,
332 const std::map
<CStrIntern
, int>& vertexAttribs
,
334 CShaderProgram(streamflags
),
335 m_VertexFile(vertexFile
), m_FragmentFile(fragmentFile
),
337 m_VertexAttribs(vertexAttribs
)
340 m_VertexShader
= glCreateShader(GL_VERTEX_SHADER
);
341 m_FragmentShader
= glCreateShader(GL_FRAGMENT_SHADER
);
342 m_FileDependencies
= {m_VertexFile
, m_FragmentFile
};
345 ~CShaderProgramGLSL()
349 glDeleteShader(m_VertexShader
);
350 glDeleteShader(m_FragmentShader
);
353 bool Compile(GLhandleARB shader
, const VfsPath
& file
, const CStr
& code
)
355 TIMER_ACCRUE(tc_ShaderGLSLCompile
);
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
);
366 glGetShaderiv(shader
, GL_COMPILE_STATUS
, &ok
);
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)
378 char* infolog
= new char[length
];
379 glGetShaderInfoLog(shader
, length
, NULL
, infolog
);
382 LOGMESSAGE("Info when compiling shader '%s':\n%s", file
.string8(), infolog
);
384 LOGERROR("Failed to compile shader '%s':\n%s", file
.string8(), infolog
);
391 return (ok
? true : false);
396 TIMER_ACCRUE(tc_ShaderGLSLLink
);
399 m_Program
= glCreateProgram();
401 glAttachShader(m_Program
, m_VertexShader
);
403 glAttachShader(m_Program
, m_FragmentShader
);
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
);
415 glGetProgramiv(m_Program
, GL_LINK_STATUS
, &ok
);
418 glGetProgramiv(m_Program
, GL_INFO_LOG_LENGTH
, &length
);
420 if (!ok
&& length
== 0)
425 char* infolog
= new char[length
];
426 glGetProgramInfoLog(m_Program
, length
, NULL
, infolog
);
429 LOGMESSAGE("Info when linking program '%s'+'%s':\n%s", m_VertexFile
.string8(), m_FragmentFile
.string8(), infolog
);
431 LOGERROR("Failed to link program '%s'+'%s':\n%s", m_VertexFile
.string8(), m_FragmentFile
.string8(), infolog
);
448 GLint numUniforms
= 0;
449 glGetProgramiv(m_Program
, GL_ACTIVE_UNIFORMS
, &numUniforms
);
451 for (GLint i
= 0; i
< numUniforms
; ++i
)
453 char name
[256] = {0};
454 GLsizei nameLength
= 0;
457 glGetActiveUniform(m_Program
, i
, ARRAY_SIZE(name
), &nameLength
, &size
, &type
, name
);
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
469 || type
== GL_SAMPLER_2D_SHADOW
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
481 // TODO: verify that we're not using more samplers than is supported
490 void Reload() override
495 if (vertexFile
.Load(g_VFS
, m_VertexFile
) != PSRETURN_OK
)
498 CVFSFile fragmentFile
;
499 if (fragmentFile
.Load(g_VFS
, m_FragmentFile
) != PSRETURN_OK
)
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
508 newFileDependencies
.push_back(includeFilePath
);
509 CVFSFile includeFile
;
510 if (includeFile
.Load(g_VFS
, includeFilePath
) != PSRETURN_OK
)
512 out
= includeFile
.GetAsString();
515 preprocessor
.AddDefines(m_Defines
);
518 // GLES defines the macro "GL_ES" in its GLSL preprocessor,
519 // but since we run our own preprocessor first, we need to explicitly
521 preprocessor
.AddDefine("GL_ES", "1");
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());
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");
547 if (!Compile(m_VertexShader
, m_VertexFile
, vertexCode
))
550 if (!Compile(m_FragmentShader
, m_FragmentFile
, fragmentCode
))
560 glDeleteProgram(m_Program
);
563 // The shader objects can be reused and don't need to be deleted here
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
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())
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())
599 g_Renderer
.GetDeviceCommandContext()->BindTexture(it
->second
.second
, it
->second
.first
, tex
);
602 void BindTexture(Binding id
, GLuint tex
) override
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())
616 return Binding(it
->second
.first
, (int)it
->second
.second
);
619 void Uniform(Binding id
, float v0
, float v1
, float v2
, float v3
) override
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
);
632 LOGERROR("CShaderProgramGLSL::Uniform(): Invalid uniform type (expected float, vec2, vec3, vec4)");
636 void Uniform(Binding id
, const CMatrix3D
& v
) override
640 if (id
.second
== GL_FLOAT_MAT4
)
641 glUniformMatrix4fv(id
.first
, 1, GL_FALSE
, &v
._11
);
643 LOGERROR("CShaderProgramGLSL::Uniform(): Invalid uniform type (expected mat4)");
647 void Uniform(Binding id
, size_t count
, const CMatrix3D
* v
) override
651 if (id
.second
== GL_FLOAT_MAT4
)
652 glUniformMatrix4fv(id
.first
, count
, GL_FALSE
, &v
->_11
);
654 LOGERROR("CShaderProgramGLSL::Uniform(): Invalid uniform type (expected mat4)");
658 void Uniform(Binding id
, size_t count
, const float* v
) override
662 if (id
.second
== GL_FLOAT
)
663 glUniform1fv(id
.first
, count
, v
);
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
;
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)
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";
751 PSRETURN ret
= XeroFile
.Load(g_VFS
, xmlFilename
);
752 if (ret
!= PSRETURN_OK
)
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());
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)
788 CPreprocessorWrapper preprocessor
;
789 preprocessor
.AddDefines(baseDefines
);
791 XMBElement root
= XeroFile
.GetRoot();
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
;
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
))
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
))
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")
869 debug_warn(L
"sampler1D not implemented on GLES");
871 type
= GL_TEXTURE_1D
;
873 else if (t
== "sampler2D")
874 type
= GL_TEXTURE_2D
;
875 else if (t
== "sampler3D")
877 debug_warn(L
"sampler3D not implemented on GLES");
879 type
= GL_TEXTURE_3D
;
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
);
894 return CShaderProgram::ConstructARB(vertexFile
, fragmentFile
, defines
, vertexUniforms
, fragmentUniforms
, streamFlags
);
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());
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
,
915 return std::make_shared
<CShaderProgramARB
>(vertexFile
, fragmentFile
, defines
, vertexIndexes
, fragmentIndexes
, streamflags
);
920 CShaderProgramPtr
CShaderProgram::ConstructGLSL(const VfsPath
& vertexFile
, const VfsPath
& fragmentFile
,
921 const CShaderDefines
& defines
,
922 const std::map
<CStrIntern
, int>& vertexAttribs
,
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");
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");
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
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);