LCOV - code coverage report
Current view: top level - source/renderer/backend/gl - ShaderProgram.cpp (source / functions) Hit Total Coverage
Test: 0 A.D. test coverage report Lines: 1 751 0.1 %
Date: 2023-01-19 00:18:29 Functions: 2 59 3.4 %

          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

Generated by: LCOV version 1.13