|  | // Copyright 2016 The SwiftShader Authors. All Rights Reserved. | 
|  | // | 
|  | // Licensed under the Apache License, Version 2.0 (the "License"); | 
|  | // you may not use this file except in compliance with the License. | 
|  | // You may obtain a copy of the License at | 
|  | // | 
|  | //    http://www.apache.org/licenses/LICENSE-2.0 | 
|  | // | 
|  | // Unless required by applicable law or agreed to in writing, software | 
|  | // distributed under the License is distributed on an "AS IS" BASIS, | 
|  | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 
|  | // See the License for the specific language governing permissions and | 
|  | // limitations under the License. | 
|  |  | 
|  | // Program.cpp: Implements the Program class. Implements GL program objects | 
|  | // and related functionality. [OpenGL ES 2.0.24] section 2.10.3 page 28. | 
|  |  | 
|  | #include "Program.h" | 
|  |  | 
|  | #include "main.h" | 
|  | #include "Buffer.h" | 
|  | #include "Shader.h" | 
|  | #include "TransformFeedback.h" | 
|  | #include "utilities.h" | 
|  | #include "common/debug.h" | 
|  | #include "Shader/PixelShader.hpp" | 
|  | #include "Shader/VertexShader.hpp" | 
|  |  | 
|  | #include <algorithm> | 
|  | #include <string> | 
|  | #include <stdlib.h> | 
|  |  | 
|  | namespace es2 | 
|  | { | 
|  | unsigned int Program::currentSerial = 1; | 
|  |  | 
|  | std::string str(int i) | 
|  | { | 
|  | char buffer[20]; | 
|  | sprintf(buffer, "%d", i); | 
|  | return buffer; | 
|  | } | 
|  |  | 
|  | Uniform::BlockInfo::BlockInfo(const glsl::Uniform& uniform, int blockIndex) | 
|  | { | 
|  | if(blockIndex >= 0) | 
|  | { | 
|  | index = blockIndex; | 
|  | offset = uniform.blockInfo.offset; | 
|  | arrayStride = uniform.blockInfo.arrayStride; | 
|  | matrixStride = uniform.blockInfo.matrixStride; | 
|  | isRowMajorMatrix = uniform.blockInfo.isRowMajorMatrix; | 
|  | } | 
|  | else | 
|  | { | 
|  | index = -1; | 
|  | offset = -1; | 
|  | arrayStride = -1; | 
|  | matrixStride = -1; | 
|  | isRowMajorMatrix = false; | 
|  | } | 
|  | } | 
|  |  | 
|  | Uniform::Uniform(GLenum type, GLenum precision, const std::string &name, unsigned int arraySize, | 
|  | const BlockInfo &blockInfo) | 
|  | : type(type), precision(precision), name(name), arraySize(arraySize), blockInfo(blockInfo) | 
|  | { | 
|  | if(blockInfo.index == -1) | 
|  | { | 
|  | size_t bytes = UniformTypeSize(type) * size(); | 
|  | data = new unsigned char[bytes]; | 
|  | memset(data, 0, bytes); | 
|  | } | 
|  | else | 
|  | { | 
|  | data = nullptr; | 
|  | } | 
|  | dirty = true; | 
|  |  | 
|  | psRegisterIndex = -1; | 
|  | vsRegisterIndex = -1; | 
|  | } | 
|  |  | 
|  | Uniform::~Uniform() | 
|  | { | 
|  | delete[] data; | 
|  | } | 
|  |  | 
|  | bool Uniform::isArray() const | 
|  | { | 
|  | return arraySize >= 1; | 
|  | } | 
|  |  | 
|  | int Uniform::size() const | 
|  | { | 
|  | return arraySize > 0 ? arraySize : 1; | 
|  | } | 
|  |  | 
|  | int Uniform::registerCount() const | 
|  | { | 
|  | return size() * VariableRegisterCount(type); | 
|  | } | 
|  |  | 
|  | UniformBlock::UniformBlock(const std::string &name, unsigned int elementIndex, unsigned int dataSize, std::vector<unsigned int> memberUniformIndexes) : | 
|  | name(name), elementIndex(elementIndex), dataSize(dataSize), memberUniformIndexes(memberUniformIndexes), psRegisterIndex(GL_INVALID_INDEX), vsRegisterIndex(GL_INVALID_INDEX) | 
|  | { | 
|  | } | 
|  |  | 
|  | void UniformBlock::setRegisterIndex(GLenum shader, unsigned int registerIndex) | 
|  | { | 
|  | switch(shader) | 
|  | { | 
|  | case GL_VERTEX_SHADER: | 
|  | vsRegisterIndex = registerIndex; | 
|  | break; | 
|  | case GL_FRAGMENT_SHADER: | 
|  | psRegisterIndex = registerIndex; | 
|  | break; | 
|  | default: | 
|  | UNREACHABLE(shader); | 
|  | } | 
|  | } | 
|  |  | 
|  | bool UniformBlock::isArrayElement() const | 
|  | { | 
|  | return elementIndex != GL_INVALID_INDEX; | 
|  | } | 
|  |  | 
|  | bool UniformBlock::isReferencedByVertexShader() const | 
|  | { | 
|  | return vsRegisterIndex != GL_INVALID_INDEX; | 
|  | } | 
|  |  | 
|  | bool UniformBlock::isReferencedByFragmentShader() const | 
|  | { | 
|  | return psRegisterIndex != GL_INVALID_INDEX; | 
|  | } | 
|  |  | 
|  | UniformLocation::UniformLocation(const std::string &name, unsigned int element, unsigned int index) : name(name), element(element), index(index) | 
|  | { | 
|  | } | 
|  |  | 
|  | LinkedVarying::LinkedVarying() | 
|  | { | 
|  | } | 
|  |  | 
|  | LinkedVarying::LinkedVarying(const std::string &name, GLenum type, GLsizei size, int reg, int col) | 
|  | : name(name), type(type), size(size), reg(reg), col(col) | 
|  | { | 
|  | } | 
|  |  | 
|  | Program::Program(ResourceManager *manager, GLuint handle) : serial(issueSerial()), resourceManager(manager), handle(handle) | 
|  | { | 
|  | fragmentShader = 0; | 
|  | vertexShader = 0; | 
|  | pixelBinary = 0; | 
|  | vertexBinary = 0; | 
|  |  | 
|  | transformFeedbackBufferMode = GL_INTERLEAVED_ATTRIBS; | 
|  | totalLinkedVaryingsComponents = 0; | 
|  |  | 
|  | infoLog = 0; | 
|  | validated = false; | 
|  |  | 
|  | resetUniformBlockBindings(); | 
|  | unlink(); | 
|  |  | 
|  | orphaned = false; | 
|  | retrievableBinary = false; | 
|  | referenceCount = 0; | 
|  | } | 
|  |  | 
|  | Program::~Program() | 
|  | { | 
|  | unlink(); | 
|  |  | 
|  | if(vertexShader) | 
|  | { | 
|  | vertexShader->release(); | 
|  | } | 
|  |  | 
|  | if(fragmentShader) | 
|  | { | 
|  | fragmentShader->release(); | 
|  | } | 
|  | } | 
|  |  | 
|  | bool Program::attachShader(Shader *shader) | 
|  | { | 
|  | if(shader->getType() == GL_VERTEX_SHADER) | 
|  | { | 
|  | if(vertexShader) | 
|  | { | 
|  | return false; | 
|  | } | 
|  |  | 
|  | vertexShader = (VertexShader*)shader; | 
|  | vertexShader->addRef(); | 
|  | } | 
|  | else if(shader->getType() == GL_FRAGMENT_SHADER) | 
|  | { | 
|  | if(fragmentShader) | 
|  | { | 
|  | return false; | 
|  | } | 
|  |  | 
|  | fragmentShader = (FragmentShader*)shader; | 
|  | fragmentShader->addRef(); | 
|  | } | 
|  | else UNREACHABLE(shader->getType()); | 
|  |  | 
|  | return true; | 
|  | } | 
|  |  | 
|  | bool Program::detachShader(Shader *shader) | 
|  | { | 
|  | if(shader->getType() == GL_VERTEX_SHADER) | 
|  | { | 
|  | if(vertexShader != shader) | 
|  | { | 
|  | return false; | 
|  | } | 
|  |  | 
|  | vertexShader->release(); | 
|  | vertexShader = 0; | 
|  | } | 
|  | else if(shader->getType() == GL_FRAGMENT_SHADER) | 
|  | { | 
|  | if(fragmentShader != shader) | 
|  | { | 
|  | return false; | 
|  | } | 
|  |  | 
|  | fragmentShader->release(); | 
|  | fragmentShader = 0; | 
|  | } | 
|  | else UNREACHABLE(shader->getType()); | 
|  |  | 
|  | return true; | 
|  | } | 
|  |  | 
|  | int Program::getAttachedShadersCount() const | 
|  | { | 
|  | return (vertexShader ? 1 : 0) + (fragmentShader ? 1 : 0); | 
|  | } | 
|  |  | 
|  | sw::PixelShader *Program::getPixelShader() | 
|  | { | 
|  | return pixelBinary; | 
|  | } | 
|  |  | 
|  | sw::VertexShader *Program::getVertexShader() | 
|  | { | 
|  | return vertexBinary; | 
|  | } | 
|  |  | 
|  | void Program::bindAttributeLocation(GLuint index, const char *name) | 
|  | { | 
|  | if(index < MAX_VERTEX_ATTRIBS) | 
|  | { | 
|  | for(int i = 0; i < MAX_VERTEX_ATTRIBS; i++) | 
|  | { | 
|  | attributeBinding[i].erase(name); | 
|  | } | 
|  |  | 
|  | attributeBinding[index].insert(name); | 
|  | } | 
|  | } | 
|  |  | 
|  | GLint Program::getAttributeLocation(const char *name) | 
|  | { | 
|  | if(name) | 
|  | { | 
|  | for(int index = 0; index < MAX_VERTEX_ATTRIBS; index++) | 
|  | { | 
|  | if(linkedAttribute[index].name == std::string(name)) | 
|  | { | 
|  | return index; | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | int Program::getAttributeStream(int attributeIndex) | 
|  | { | 
|  | ASSERT(attributeIndex >= 0 && attributeIndex < MAX_VERTEX_ATTRIBS); | 
|  |  | 
|  | return attributeStream[attributeIndex]; | 
|  | } | 
|  |  | 
|  | // Returns the index of the texture image unit (0-19) corresponding to a sampler index (0-15 for the pixel shader and 0-3 for the vertex shader) | 
|  | GLint Program::getSamplerMapping(sw::SamplerType type, unsigned int samplerIndex) | 
|  | { | 
|  | GLint logicalTextureUnit = -1; | 
|  |  | 
|  | switch(type) | 
|  | { | 
|  | case sw::SAMPLER_PIXEL: | 
|  | ASSERT(samplerIndex < sizeof(samplersPS) / sizeof(samplersPS[0])); | 
|  |  | 
|  | if(samplersPS[samplerIndex].active) | 
|  | { | 
|  | logicalTextureUnit = samplersPS[samplerIndex].logicalTextureUnit; | 
|  | } | 
|  | break; | 
|  | case sw::SAMPLER_VERTEX: | 
|  | ASSERT(samplerIndex < sizeof(samplersVS) / sizeof(samplersVS[0])); | 
|  |  | 
|  | if(samplersVS[samplerIndex].active) | 
|  | { | 
|  | logicalTextureUnit = samplersVS[samplerIndex].logicalTextureUnit; | 
|  | } | 
|  | break; | 
|  | default: UNREACHABLE(type); | 
|  | } | 
|  |  | 
|  | if(logicalTextureUnit < MAX_COMBINED_TEXTURE_IMAGE_UNITS) | 
|  | { | 
|  | return logicalTextureUnit; | 
|  | } | 
|  |  | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | // Returns the texture type for a given sampler type and index (0-15 for the pixel shader and 0-3 for the vertex shader) | 
|  | TextureType Program::getSamplerTextureType(sw::SamplerType type, unsigned int samplerIndex) | 
|  | { | 
|  | switch(type) | 
|  | { | 
|  | case sw::SAMPLER_PIXEL: | 
|  | ASSERT(samplerIndex < sizeof(samplersPS)/sizeof(samplersPS[0])); | 
|  | ASSERT(samplersPS[samplerIndex].active); | 
|  | return samplersPS[samplerIndex].textureType; | 
|  | case sw::SAMPLER_VERTEX: | 
|  | ASSERT(samplerIndex < sizeof(samplersVS)/sizeof(samplersVS[0])); | 
|  | ASSERT(samplersVS[samplerIndex].active); | 
|  | return samplersVS[samplerIndex].textureType; | 
|  | default: UNREACHABLE(type); | 
|  | } | 
|  |  | 
|  | return TEXTURE_2D; | 
|  | } | 
|  |  | 
|  | GLint Program::getUniformLocation(const std::string &name) const | 
|  | { | 
|  | unsigned int subscript = GL_INVALID_INDEX; | 
|  | std::string baseName = es2::ParseUniformName(name, &subscript); | 
|  |  | 
|  | size_t numUniforms = uniformIndex.size(); | 
|  | for(size_t location = 0; location < numUniforms; location++) | 
|  | { | 
|  | const int index = uniformIndex[location].index; | 
|  | const bool isArray = uniforms[index]->isArray(); | 
|  |  | 
|  | if(uniformIndex[location].name == baseName && | 
|  | ((isArray && uniformIndex[location].element == subscript) || | 
|  | (subscript == GL_INVALID_INDEX))) | 
|  | { | 
|  | return (GLint)location; | 
|  | } | 
|  | } | 
|  |  | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | GLuint Program::getUniformIndex(const std::string &name) const | 
|  | { | 
|  | unsigned int subscript = GL_INVALID_INDEX; | 
|  | std::string baseName = es2::ParseUniformName(name, &subscript); | 
|  |  | 
|  | // The app is not allowed to specify array indices other than 0 for arrays of basic types | 
|  | if(subscript != 0 && subscript != GL_INVALID_INDEX) | 
|  | { | 
|  | return GL_INVALID_INDEX; | 
|  | } | 
|  |  | 
|  | size_t numUniforms = uniforms.size(); | 
|  | for(GLuint index = 0; index < numUniforms; index++) | 
|  | { | 
|  | if(uniforms[index]->name == baseName) | 
|  | { | 
|  | if(uniforms[index]->isArray() || subscript == GL_INVALID_INDEX) | 
|  | { | 
|  | return index; | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | return GL_INVALID_INDEX; | 
|  | } | 
|  |  | 
|  | void Program::getActiveUniformBlockiv(GLuint uniformBlockIndex, GLenum pname, GLint *params) const | 
|  | { | 
|  | ASSERT(uniformBlockIndex < getActiveUniformBlockCount()); | 
|  |  | 
|  | const UniformBlock &uniformBlock = *uniformBlocks[uniformBlockIndex]; | 
|  |  | 
|  | switch(pname) | 
|  | { | 
|  | case GL_UNIFORM_BLOCK_DATA_SIZE: | 
|  | *params = static_cast<GLint>(uniformBlock.dataSize); | 
|  | break; | 
|  | case GL_UNIFORM_BLOCK_NAME_LENGTH: | 
|  | *params = static_cast<GLint>(uniformBlock.name.size() + 1 + (uniformBlock.isArrayElement() ? 3 : 0)); | 
|  | break; | 
|  | case GL_UNIFORM_BLOCK_ACTIVE_UNIFORMS: | 
|  | *params = static_cast<GLint>(uniformBlock.memberUniformIndexes.size()); | 
|  | break; | 
|  | case GL_UNIFORM_BLOCK_ACTIVE_UNIFORM_INDICES: | 
|  | { | 
|  | for(unsigned int blockMemberIndex = 0; blockMemberIndex < uniformBlock.memberUniformIndexes.size(); blockMemberIndex++) | 
|  | { | 
|  | params[blockMemberIndex] = static_cast<GLint>(uniformBlock.memberUniformIndexes[blockMemberIndex]); | 
|  | } | 
|  | } | 
|  | break; | 
|  | case GL_UNIFORM_BLOCK_REFERENCED_BY_VERTEX_SHADER: | 
|  | *params = static_cast<GLint>(uniformBlock.isReferencedByVertexShader()); | 
|  | break; | 
|  | case GL_UNIFORM_BLOCK_REFERENCED_BY_FRAGMENT_SHADER: | 
|  | *params = static_cast<GLint>(uniformBlock.isReferencedByFragmentShader()); | 
|  | break; | 
|  | default: UNREACHABLE(pname); | 
|  | } | 
|  | } | 
|  |  | 
|  | GLuint Program::getUniformBlockIndex(const std::string &name) const | 
|  | { | 
|  | unsigned int subscript = GL_INVALID_INDEX; | 
|  | std::string baseName = es2::ParseUniformName(name, &subscript); | 
|  |  | 
|  | size_t numUniformBlocks = getActiveUniformBlockCount(); | 
|  | for(GLuint blockIndex = 0; blockIndex < numUniformBlocks; blockIndex++) | 
|  | { | 
|  | const UniformBlock &uniformBlock = *uniformBlocks[blockIndex]; | 
|  | if(uniformBlock.name == baseName) | 
|  | { | 
|  | const bool arrayElementZero = (subscript == GL_INVALID_INDEX && uniformBlock.elementIndex == 0); | 
|  | if(subscript == uniformBlock.elementIndex || arrayElementZero) | 
|  | { | 
|  | return blockIndex; | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | return GL_INVALID_INDEX; | 
|  | } | 
|  |  | 
|  | void Program::bindUniformBlock(GLuint uniformBlockIndex, GLuint uniformBlockBinding) | 
|  | { | 
|  | uniformBlockBindings[uniformBlockIndex] = uniformBlockBinding; | 
|  | } | 
|  |  | 
|  | GLuint Program::getUniformBlockBinding(GLuint uniformBlockIndex) const | 
|  | { | 
|  | return uniformBlockBindings[uniformBlockIndex]; | 
|  | } | 
|  |  | 
|  | void Program::resetUniformBlockBindings() | 
|  | { | 
|  | for(unsigned int blockId = 0; blockId < MAX_UNIFORM_BUFFER_BINDINGS; blockId++) | 
|  | { | 
|  | uniformBlockBindings[blockId] = 0; | 
|  | } | 
|  | } | 
|  |  | 
|  | bool Program::setUniformfv(GLint location, GLsizei count, const GLfloat *v, int numElements) | 
|  | { | 
|  | ASSERT(numElements >= 1 && numElements <= 4); | 
|  |  | 
|  | static GLenum floatType[] = { GL_FLOAT, GL_FLOAT_VEC2, GL_FLOAT_VEC3, GL_FLOAT_VEC4 }; | 
|  | static GLenum boolType[] = { GL_BOOL, GL_BOOL_VEC2, GL_BOOL_VEC3, GL_BOOL_VEC4 }; | 
|  |  | 
|  | if(location < 0 || location >= (int)uniformIndex.size()) | 
|  | { | 
|  | return false; | 
|  | } | 
|  |  | 
|  | Uniform *targetUniform = uniforms[uniformIndex[location].index]; | 
|  | targetUniform->dirty = true; | 
|  |  | 
|  | int size = targetUniform->size(); | 
|  |  | 
|  | if(size == 1 && count > 1) | 
|  | { | 
|  | return false;   // Attempting to write an array to a non-array uniform is an INVALID_OPERATION | 
|  | } | 
|  |  | 
|  | count = std::min(size - (int)uniformIndex[location].element, count); | 
|  |  | 
|  | int index = numElements - 1; | 
|  | if(targetUniform->type == floatType[index]) | 
|  | { | 
|  | memcpy(targetUniform->data + uniformIndex[location].element * sizeof(GLfloat)* numElements, | 
|  | v, numElements * sizeof(GLfloat) * count); | 
|  | } | 
|  | else if(targetUniform->type == boolType[index]) | 
|  | { | 
|  | GLboolean *boolParams = (GLboolean*)targetUniform->data + uniformIndex[location].element * numElements; | 
|  |  | 
|  | for(int i = 0; i < count * numElements; i++) | 
|  | { | 
|  | boolParams[i] = (v[i] == 0.0f) ? GL_FALSE : GL_TRUE; | 
|  | } | 
|  | } | 
|  | else | 
|  | { | 
|  | return false; | 
|  | } | 
|  |  | 
|  | return true; | 
|  | } | 
|  |  | 
|  | bool Program::setUniform1fv(GLint location, GLsizei count, const GLfloat* v) | 
|  | { | 
|  | return setUniformfv(location, count, v, 1); | 
|  | } | 
|  |  | 
|  | bool Program::setUniform2fv(GLint location, GLsizei count, const GLfloat *v) | 
|  | { | 
|  | return setUniformfv(location, count, v, 2); | 
|  | } | 
|  |  | 
|  | bool Program::setUniform3fv(GLint location, GLsizei count, const GLfloat *v) | 
|  | { | 
|  | return setUniformfv(location, count, v, 3); | 
|  | } | 
|  |  | 
|  | bool Program::setUniform4fv(GLint location, GLsizei count, const GLfloat *v) | 
|  | { | 
|  | return setUniformfv(location, count, v, 4); | 
|  | } | 
|  |  | 
|  | bool Program::setUniformMatrixfv(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value, GLenum type) | 
|  | { | 
|  | int numElements; | 
|  | switch(type) | 
|  | { | 
|  | case GL_FLOAT_MAT2: | 
|  | numElements = 4; | 
|  | break; | 
|  | case GL_FLOAT_MAT2x3: | 
|  | case GL_FLOAT_MAT3x2: | 
|  | numElements = 6; | 
|  | break; | 
|  | case GL_FLOAT_MAT2x4: | 
|  | case GL_FLOAT_MAT4x2: | 
|  | numElements = 8; | 
|  | break; | 
|  | case GL_FLOAT_MAT3: | 
|  | numElements = 9; | 
|  | break; | 
|  | case GL_FLOAT_MAT3x4: | 
|  | case GL_FLOAT_MAT4x3: | 
|  | numElements = 12; | 
|  | break; | 
|  | case GL_FLOAT_MAT4: | 
|  | numElements = 16; | 
|  | break; | 
|  | default: | 
|  | return false; | 
|  | } | 
|  |  | 
|  | if(location < 0 || location >= (int)uniformIndex.size()) | 
|  | { | 
|  | return false; | 
|  | } | 
|  |  | 
|  | Uniform *targetUniform = uniforms[uniformIndex[location].index]; | 
|  | targetUniform->dirty = true; | 
|  |  | 
|  | if(targetUniform->type != type) | 
|  | { | 
|  | return false; | 
|  | } | 
|  |  | 
|  | int size = targetUniform->size(); | 
|  |  | 
|  | if(size == 1 && count > 1) | 
|  | { | 
|  | return false;   // Attempting to write an array to a non-array uniform is an INVALID_OPERATION | 
|  | } | 
|  |  | 
|  | count = std::min(size - (int)uniformIndex[location].element, count); | 
|  |  | 
|  | GLfloat* dst = reinterpret_cast<GLfloat*>(targetUniform->data + uniformIndex[location].element * sizeof(GLfloat) * numElements); | 
|  |  | 
|  | if(transpose == GL_FALSE) | 
|  | { | 
|  | memcpy(dst, value, numElements * sizeof(GLfloat) * count); | 
|  | } | 
|  | else | 
|  | { | 
|  | const int rowSize = VariableRowCount(type); | 
|  | const int colSize = VariableColumnCount(type); | 
|  | for(int n = 0; n < count; ++n) | 
|  | { | 
|  | for(int i = 0; i < colSize; ++i) | 
|  | { | 
|  | for(int j = 0; j < rowSize; ++j) | 
|  | { | 
|  | dst[i * rowSize + j] = value[j * colSize + i]; | 
|  | } | 
|  | } | 
|  | dst += numElements; | 
|  | value += numElements; | 
|  | } | 
|  | } | 
|  |  | 
|  |  | 
|  | return true; | 
|  | } | 
|  |  | 
|  | bool Program::setUniformMatrix2fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value) | 
|  | { | 
|  | return setUniformMatrixfv(location, count, transpose, value, GL_FLOAT_MAT2); | 
|  | } | 
|  |  | 
|  | bool Program::setUniformMatrix2x3fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value) | 
|  | { | 
|  | return setUniformMatrixfv(location, count, transpose, value, GL_FLOAT_MAT2x3); | 
|  | } | 
|  |  | 
|  | bool Program::setUniformMatrix2x4fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value) | 
|  | { | 
|  | return setUniformMatrixfv(location, count, transpose, value, GL_FLOAT_MAT2x4); | 
|  | } | 
|  |  | 
|  | bool Program::setUniformMatrix3fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value) | 
|  | { | 
|  | return setUniformMatrixfv(location, count, transpose, value, GL_FLOAT_MAT3); | 
|  | } | 
|  |  | 
|  | bool Program::setUniformMatrix3x2fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value) | 
|  | { | 
|  | return setUniformMatrixfv(location, count, transpose, value, GL_FLOAT_MAT3x2); | 
|  | } | 
|  |  | 
|  | bool Program::setUniformMatrix3x4fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value) | 
|  | { | 
|  | return setUniformMatrixfv(location, count, transpose, value, GL_FLOAT_MAT3x4); | 
|  | } | 
|  |  | 
|  | bool Program::setUniformMatrix4fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value) | 
|  | { | 
|  | return setUniformMatrixfv(location, count, transpose, value, GL_FLOAT_MAT4); | 
|  | } | 
|  |  | 
|  | bool Program::setUniformMatrix4x2fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value) | 
|  | { | 
|  | return setUniformMatrixfv(location, count, transpose, value, GL_FLOAT_MAT4x2); | 
|  | } | 
|  |  | 
|  | bool Program::setUniformMatrix4x3fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value) | 
|  | { | 
|  | return setUniformMatrixfv(location, count, transpose, value, GL_FLOAT_MAT4x3); | 
|  | } | 
|  |  | 
|  | bool Program::setUniform1iv(GLint location, GLsizei count, const GLint *v) | 
|  | { | 
|  | if(location < 0 || location >= (int)uniformIndex.size()) | 
|  | { | 
|  | return false; | 
|  | } | 
|  |  | 
|  | Uniform *targetUniform = uniforms[uniformIndex[location].index]; | 
|  | targetUniform->dirty = true; | 
|  |  | 
|  | int size = targetUniform->size(); | 
|  |  | 
|  | if(size == 1 && count > 1) | 
|  | { | 
|  | return false;   // Attempting to write an array to a non-array uniform is an INVALID_OPERATION | 
|  | } | 
|  |  | 
|  | count = std::min(size - (int)uniformIndex[location].element, count); | 
|  |  | 
|  | if(targetUniform->type == GL_INT || targetUniform->type == GL_UNSIGNED_INT || IsSamplerUniform(targetUniform->type)) | 
|  | { | 
|  | memcpy(targetUniform->data + uniformIndex[location].element * sizeof(GLint), | 
|  | v, sizeof(GLint) * count); | 
|  | } | 
|  | else if(targetUniform->type == GL_BOOL) | 
|  | { | 
|  | GLboolean *boolParams = new GLboolean[count]; | 
|  |  | 
|  | for(int i = 0; i < count; i++) | 
|  | { | 
|  | if(v[i] == 0) | 
|  | { | 
|  | boolParams[i] = GL_FALSE; | 
|  | } | 
|  | else | 
|  | { | 
|  | boolParams[i] = GL_TRUE; | 
|  | } | 
|  | } | 
|  |  | 
|  | memcpy(targetUniform->data + uniformIndex[location].element * sizeof(GLboolean), | 
|  | boolParams, sizeof(GLboolean) * count); | 
|  |  | 
|  | delete[] boolParams; | 
|  | } | 
|  | else | 
|  | { | 
|  | return false; | 
|  | } | 
|  |  | 
|  | return true; | 
|  | } | 
|  |  | 
|  | bool Program::setUniformiv(GLint location, GLsizei count, const GLint *v, int numElements) | 
|  | { | 
|  | static GLenum intType[] = { GL_INT, GL_INT_VEC2, GL_INT_VEC3, GL_INT_VEC4 }; | 
|  | static GLenum uintType[] = { GL_UNSIGNED_INT, GL_UNSIGNED_INT_VEC2, GL_UNSIGNED_INT_VEC3, GL_UNSIGNED_INT_VEC4 }; | 
|  | static GLenum boolType[] = { GL_BOOL, GL_BOOL_VEC2, GL_BOOL_VEC3, GL_BOOL_VEC4 }; | 
|  |  | 
|  | if(location < 0 || location >= (int)uniformIndex.size()) | 
|  | { | 
|  | return false; | 
|  | } | 
|  |  | 
|  | Uniform *targetUniform = uniforms[uniformIndex[location].index]; | 
|  | targetUniform->dirty = true; | 
|  |  | 
|  | int size = targetUniform->size(); | 
|  |  | 
|  | if(size == 1 && count > 1) | 
|  | { | 
|  | return false;   // Attempting to write an array to a non-array uniform is an INVALID_OPERATION | 
|  | } | 
|  |  | 
|  | count = std::min(size - (int)uniformIndex[location].element, count); | 
|  |  | 
|  | int index = numElements - 1; | 
|  | if(targetUniform->type == intType[index] || targetUniform->type == uintType[index]) | 
|  | { | 
|  | memcpy(targetUniform->data + uniformIndex[location].element * sizeof(GLint)* numElements, | 
|  | v, numElements * sizeof(GLint)* count); | 
|  | } | 
|  | else if(targetUniform->type == boolType[index]) | 
|  | { | 
|  | GLboolean *boolParams = new GLboolean[count * numElements]; | 
|  |  | 
|  | for(int i = 0; i < count * numElements; i++) | 
|  | { | 
|  | boolParams[i] = (v[i] == 0) ? GL_FALSE : GL_TRUE; | 
|  | } | 
|  |  | 
|  | memcpy(targetUniform->data + uniformIndex[location].element * sizeof(GLboolean)* numElements, | 
|  | boolParams, numElements * sizeof(GLboolean)* count); | 
|  |  | 
|  | delete[] boolParams; | 
|  | } | 
|  | else | 
|  | { | 
|  | return false; | 
|  | } | 
|  |  | 
|  | return true; | 
|  | } | 
|  |  | 
|  | bool Program::setUniform2iv(GLint location, GLsizei count, const GLint *v) | 
|  | { | 
|  | return setUniformiv(location, count, v, 2); | 
|  | } | 
|  |  | 
|  | bool Program::setUniform3iv(GLint location, GLsizei count, const GLint *v) | 
|  | { | 
|  | return setUniformiv(location, count, v, 3); | 
|  | } | 
|  |  | 
|  | bool Program::setUniform4iv(GLint location, GLsizei count, const GLint *v) | 
|  | { | 
|  | return setUniformiv(location, count, v, 4); | 
|  | } | 
|  |  | 
|  | bool Program::setUniform1uiv(GLint location, GLsizei count, const GLuint *v) | 
|  | { | 
|  | if(location < 0 || location >= (int)uniformIndex.size()) | 
|  | { | 
|  | return false; | 
|  | } | 
|  |  | 
|  | Uniform *targetUniform = uniforms[uniformIndex[location].index]; | 
|  | targetUniform->dirty = true; | 
|  |  | 
|  | int size = targetUniform->size(); | 
|  |  | 
|  | if(size == 1 && count > 1) | 
|  | { | 
|  | return false;   // Attempting to write an array to a non-array uniform is an INVALID_OPERATION | 
|  | } | 
|  |  | 
|  | count = std::min(size - (int)uniformIndex[location].element, count); | 
|  |  | 
|  | if(targetUniform->type == GL_INT || targetUniform->type == GL_UNSIGNED_INT || IsSamplerUniform(targetUniform->type)) | 
|  | { | 
|  | memcpy(targetUniform->data + uniformIndex[location].element * sizeof(GLuint), | 
|  | v, sizeof(GLuint)* count); | 
|  | } | 
|  | else if(targetUniform->type == GL_BOOL) | 
|  | { | 
|  | GLboolean *boolParams = new GLboolean[count]; | 
|  |  | 
|  | for(int i = 0; i < count; i++) | 
|  | { | 
|  | if(v[i] == 0) | 
|  | { | 
|  | boolParams[i] = GL_FALSE; | 
|  | } | 
|  | else | 
|  | { | 
|  | boolParams[i] = GL_TRUE; | 
|  | } | 
|  | } | 
|  |  | 
|  | memcpy(targetUniform->data + uniformIndex[location].element * sizeof(GLboolean), | 
|  | boolParams, sizeof(GLboolean)* count); | 
|  |  | 
|  | delete[] boolParams; | 
|  | } | 
|  | else | 
|  | { | 
|  | return false; | 
|  | } | 
|  |  | 
|  | return true; | 
|  | } | 
|  |  | 
|  | bool Program::setUniformuiv(GLint location, GLsizei count, const GLuint *v, int numElements) | 
|  | { | 
|  | static GLenum intType[] = { GL_INT, GL_INT_VEC2, GL_INT_VEC3, GL_INT_VEC4 }; | 
|  | static GLenum uintType[] = { GL_UNSIGNED_INT, GL_UNSIGNED_INT_VEC2, GL_UNSIGNED_INT_VEC3, GL_UNSIGNED_INT_VEC4 }; | 
|  | static GLenum boolType[] = { GL_BOOL, GL_BOOL_VEC2, GL_BOOL_VEC3, GL_BOOL_VEC4 }; | 
|  |  | 
|  | if(location < 0 || location >= (int)uniformIndex.size()) | 
|  | { | 
|  | return false; | 
|  | } | 
|  |  | 
|  | Uniform *targetUniform = uniforms[uniformIndex[location].index]; | 
|  | targetUniform->dirty = true; | 
|  |  | 
|  | int size = targetUniform->size(); | 
|  |  | 
|  | if(size == 1 && count > 1) | 
|  | { | 
|  | return false;   // Attempting to write an array to a non-array uniform is an INVALID_OPERATION | 
|  | } | 
|  |  | 
|  | count = std::min(size - (int)uniformIndex[location].element, count); | 
|  |  | 
|  | int index = numElements - 1; | 
|  | if(targetUniform->type == uintType[index] || targetUniform->type == intType[index]) | 
|  | { | 
|  | memcpy(targetUniform->data + uniformIndex[location].element * sizeof(GLuint)* numElements, | 
|  | v, numElements * sizeof(GLuint)* count); | 
|  | } | 
|  | else if(targetUniform->type == boolType[index]) | 
|  | { | 
|  | GLboolean *boolParams = new GLboolean[count * numElements]; | 
|  |  | 
|  | for(int i = 0; i < count * numElements; i++) | 
|  | { | 
|  | boolParams[i] = (v[i] == 0) ? GL_FALSE : GL_TRUE; | 
|  | } | 
|  |  | 
|  | memcpy(targetUniform->data + uniformIndex[location].element * sizeof(GLboolean)* numElements, | 
|  | boolParams, numElements * sizeof(GLboolean)* count); | 
|  |  | 
|  | delete[] boolParams; | 
|  | } | 
|  | else | 
|  | { | 
|  | return false; | 
|  | } | 
|  |  | 
|  | return true; | 
|  | } | 
|  |  | 
|  | bool Program::setUniform2uiv(GLint location, GLsizei count, const GLuint *v) | 
|  | { | 
|  | return setUniformuiv(location, count, v, 2); | 
|  | } | 
|  |  | 
|  | bool Program::setUniform3uiv(GLint location, GLsizei count, const GLuint *v) | 
|  | { | 
|  | return setUniformuiv(location, count, v, 3); | 
|  | } | 
|  |  | 
|  | bool Program::setUniform4uiv(GLint location, GLsizei count, const GLuint *v) | 
|  | { | 
|  | return setUniformuiv(location, count, v, 4); | 
|  | } | 
|  |  | 
|  | bool Program::getUniformfv(GLint location, GLsizei *bufSize, GLfloat *params) | 
|  | { | 
|  | if(location < 0 || location >= (int)uniformIndex.size()) | 
|  | { | 
|  | return false; | 
|  | } | 
|  |  | 
|  | Uniform *targetUniform = uniforms[uniformIndex[location].index]; | 
|  | unsigned int count = UniformComponentCount(targetUniform->type); | 
|  |  | 
|  | // Sized query - ensure the provided buffer is large enough | 
|  | if(bufSize && static_cast<unsigned int>(*bufSize) < count * sizeof(GLfloat)) | 
|  | { | 
|  | return false; | 
|  | } | 
|  |  | 
|  | switch(UniformComponentType(targetUniform->type)) | 
|  | { | 
|  | case GL_BOOL: | 
|  | { | 
|  | GLboolean *boolParams = (GLboolean*)targetUniform->data + uniformIndex[location].element * count; | 
|  |  | 
|  | for(unsigned int i = 0; i < count; i++) | 
|  | { | 
|  | params[i] = (boolParams[i] == GL_FALSE) ? 0.0f : 1.0f; | 
|  | } | 
|  | } | 
|  | break; | 
|  | case GL_FLOAT: | 
|  | memcpy(params, targetUniform->data + uniformIndex[location].element * count * sizeof(GLfloat), | 
|  | count * sizeof(GLfloat)); | 
|  | break; | 
|  | case GL_INT: | 
|  | { | 
|  | GLint *intParams = (GLint*)targetUniform->data + uniformIndex[location].element * count; | 
|  |  | 
|  | for(unsigned int i = 0; i < count; i++) | 
|  | { | 
|  | params[i] = (float)intParams[i]; | 
|  | } | 
|  | } | 
|  | break; | 
|  | case GL_UNSIGNED_INT: | 
|  | { | 
|  | GLuint *uintParams = (GLuint*)targetUniform->data + uniformIndex[location].element * count; | 
|  |  | 
|  | for(unsigned int i = 0; i < count; i++) | 
|  | { | 
|  | params[i] = (float)uintParams[i]; | 
|  | } | 
|  | } | 
|  | break; | 
|  | default: UNREACHABLE(targetUniform->type); | 
|  | } | 
|  |  | 
|  | return true; | 
|  | } | 
|  |  | 
|  | bool Program::getUniformiv(GLint location, GLsizei *bufSize, GLint *params) | 
|  | { | 
|  | if(location < 0 || location >= (int)uniformIndex.size()) | 
|  | { | 
|  | return false; | 
|  | } | 
|  |  | 
|  | Uniform *targetUniform = uniforms[uniformIndex[location].index]; | 
|  | unsigned int count = UniformComponentCount(targetUniform->type); | 
|  |  | 
|  | // Sized query - ensure the provided buffer is large enough | 
|  | if(bufSize && static_cast<unsigned int>(*bufSize) < count * sizeof(GLint)) | 
|  | { | 
|  | return false; | 
|  | } | 
|  |  | 
|  | switch(UniformComponentType(targetUniform->type)) | 
|  | { | 
|  | case GL_BOOL: | 
|  | { | 
|  | GLboolean *boolParams = targetUniform->data + uniformIndex[location].element * count; | 
|  |  | 
|  | for(unsigned int i = 0; i < count; i++) | 
|  | { | 
|  | params[i] = (GLint)boolParams[i]; | 
|  | } | 
|  | } | 
|  | break; | 
|  | case GL_FLOAT: | 
|  | { | 
|  | GLfloat *floatParams = (GLfloat*)targetUniform->data + uniformIndex[location].element * count; | 
|  |  | 
|  | for(unsigned int i = 0; i < count; i++) | 
|  | { | 
|  | params[i] = (GLint)floatParams[i]; | 
|  | } | 
|  | } | 
|  | break; | 
|  | case GL_INT: | 
|  | case GL_UNSIGNED_INT: | 
|  | memcpy(params, targetUniform->data + uniformIndex[location].element * count * sizeof(GLint), | 
|  | count * sizeof(GLint)); | 
|  | break; | 
|  | default: UNREACHABLE(targetUniform->type); | 
|  | } | 
|  |  | 
|  | return true; | 
|  | } | 
|  |  | 
|  | bool Program::getUniformuiv(GLint location, GLsizei *bufSize, GLuint *params) | 
|  | { | 
|  | if(location < 0 || location >= (int)uniformIndex.size()) | 
|  | { | 
|  | return false; | 
|  | } | 
|  |  | 
|  | Uniform *targetUniform = uniforms[uniformIndex[location].index]; | 
|  | unsigned int count = UniformComponentCount(targetUniform->type); | 
|  |  | 
|  | // Sized query - ensure the provided buffer is large enough | 
|  | if(bufSize && static_cast<unsigned int>(*bufSize) < count * sizeof(GLuint)) | 
|  | { | 
|  | return false; | 
|  | } | 
|  |  | 
|  | switch(UniformComponentType(targetUniform->type)) | 
|  | { | 
|  | case GL_BOOL: | 
|  | { | 
|  | GLboolean *boolParams = targetUniform->data + uniformIndex[location].element * count; | 
|  |  | 
|  | for(unsigned int i = 0; i < count; i++) | 
|  | { | 
|  | params[i] = (GLuint)boolParams[i]; | 
|  | } | 
|  | } | 
|  | break; | 
|  | case GL_FLOAT: | 
|  | { | 
|  | GLfloat *floatParams = (GLfloat*)targetUniform->data + uniformIndex[location].element * count; | 
|  |  | 
|  | for(unsigned int i = 0; i < count; i++) | 
|  | { | 
|  | params[i] = (GLuint)floatParams[i]; | 
|  | } | 
|  | } | 
|  | break; | 
|  | case GL_INT: | 
|  | case GL_UNSIGNED_INT: | 
|  | memcpy(params, targetUniform->data + uniformIndex[location].element * count * sizeof(GLuint), | 
|  | count * sizeof(GLuint)); | 
|  | break; | 
|  | default: UNREACHABLE(targetUniform->type); | 
|  | } | 
|  |  | 
|  | return true; | 
|  | } | 
|  |  | 
|  | void Program::dirtyAllUniforms() | 
|  | { | 
|  | size_t numUniforms = uniforms.size(); | 
|  | for(size_t index = 0; index < numUniforms; index++) | 
|  | { | 
|  | uniforms[index]->dirty = true; | 
|  | } | 
|  | } | 
|  |  | 
|  | // Applies all the uniforms set for this program object to the device | 
|  | void Program::applyUniforms(Device *device) | 
|  | { | 
|  | GLint numUniforms = static_cast<GLint>(uniformIndex.size()); | 
|  | for(GLint location = 0; location < numUniforms; location++) | 
|  | { | 
|  | if(uniformIndex[location].element != 0) | 
|  | { | 
|  | continue; | 
|  | } | 
|  |  | 
|  | Uniform *targetUniform = uniforms[uniformIndex[location].index]; | 
|  |  | 
|  | if(targetUniform->dirty && (targetUniform->blockInfo.index == -1)) | 
|  | { | 
|  | GLsizei size = targetUniform->size(); | 
|  | GLfloat *f = (GLfloat*)targetUniform->data; | 
|  | GLint *i = (GLint*)targetUniform->data; | 
|  | GLuint *ui = (GLuint*)targetUniform->data; | 
|  | GLboolean *b = (GLboolean*)targetUniform->data; | 
|  |  | 
|  | switch(targetUniform->type) | 
|  | { | 
|  | case GL_BOOL:       applyUniform1bv(device, location, size, b);       break; | 
|  | case GL_BOOL_VEC2:  applyUniform2bv(device, location, size, b);       break; | 
|  | case GL_BOOL_VEC3:  applyUniform3bv(device, location, size, b);       break; | 
|  | case GL_BOOL_VEC4:  applyUniform4bv(device, location, size, b);       break; | 
|  | case GL_FLOAT:      applyUniform1fv(device, location, size, f);       break; | 
|  | case GL_FLOAT_VEC2: applyUniform2fv(device, location, size, f);       break; | 
|  | case GL_FLOAT_VEC3: applyUniform3fv(device, location, size, f);       break; | 
|  | case GL_FLOAT_VEC4: applyUniform4fv(device, location, size, f);       break; | 
|  | case GL_FLOAT_MAT2:   applyUniformMatrix2fv(device, location, size, f);   break; | 
|  | case GL_FLOAT_MAT2x3: applyUniformMatrix2x3fv(device, location, size, f); break; | 
|  | case GL_FLOAT_MAT2x4: applyUniformMatrix2x4fv(device, location, size, f); break; | 
|  | case GL_FLOAT_MAT3x2: applyUniformMatrix3x2fv(device, location, size, f); break; | 
|  | case GL_FLOAT_MAT3:   applyUniformMatrix3fv(device, location, size, f);   break; | 
|  | case GL_FLOAT_MAT3x4: applyUniformMatrix3x4fv(device, location, size, f); break; | 
|  | case GL_FLOAT_MAT4x2: applyUniformMatrix4x2fv(device, location, size, f); break; | 
|  | case GL_FLOAT_MAT4x3: applyUniformMatrix4x3fv(device, location, size, f); break; | 
|  | case GL_FLOAT_MAT4:   applyUniformMatrix4fv(device, location, size, f);   break; | 
|  | case GL_SAMPLER_2D: | 
|  | case GL_SAMPLER_CUBE: | 
|  | case GL_SAMPLER_EXTERNAL_OES: | 
|  | case GL_SAMPLER_3D_OES: | 
|  | case GL_SAMPLER_2D_ARRAY: | 
|  | case GL_SAMPLER_2D_SHADOW: | 
|  | case GL_SAMPLER_CUBE_SHADOW: | 
|  | case GL_SAMPLER_2D_ARRAY_SHADOW: | 
|  | case GL_INT_SAMPLER_2D: | 
|  | case GL_UNSIGNED_INT_SAMPLER_2D: | 
|  | case GL_INT_SAMPLER_CUBE: | 
|  | case GL_UNSIGNED_INT_SAMPLER_CUBE: | 
|  | case GL_INT_SAMPLER_3D: | 
|  | case GL_UNSIGNED_INT_SAMPLER_3D: | 
|  | case GL_INT_SAMPLER_2D_ARRAY: | 
|  | case GL_UNSIGNED_INT_SAMPLER_2D_ARRAY: | 
|  | case GL_INT:        applyUniform1iv(device, location, size, i);       break; | 
|  | case GL_INT_VEC2:   applyUniform2iv(device, location, size, i);       break; | 
|  | case GL_INT_VEC3:   applyUniform3iv(device, location, size, i);       break; | 
|  | case GL_INT_VEC4:   applyUniform4iv(device, location, size, i);       break; | 
|  | case GL_UNSIGNED_INT:      applyUniform1uiv(device, location, size, ui); break; | 
|  | case GL_UNSIGNED_INT_VEC2: applyUniform2uiv(device, location, size, ui); break; | 
|  | case GL_UNSIGNED_INT_VEC3: applyUniform3uiv(device, location, size, ui); break; | 
|  | case GL_UNSIGNED_INT_VEC4: applyUniform4uiv(device, location, size, ui); break; | 
|  | default: | 
|  | UNREACHABLE(targetUniform->type); | 
|  | } | 
|  |  | 
|  | targetUniform->dirty = false; | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | void Program::applyUniformBuffers(Device *device, BufferBinding* uniformBuffers) | 
|  | { | 
|  | GLint vertexUniformBuffers[MAX_UNIFORM_BUFFER_BINDINGS]; | 
|  | GLint fragmentUniformBuffers[MAX_UNIFORM_BUFFER_BINDINGS]; | 
|  |  | 
|  | for(unsigned int bufferBindingIndex = 0; bufferBindingIndex < MAX_UNIFORM_BUFFER_BINDINGS; bufferBindingIndex++) | 
|  | { | 
|  | vertexUniformBuffers[bufferBindingIndex] = -1; | 
|  | } | 
|  |  | 
|  | for(unsigned int bufferBindingIndex = 0; bufferBindingIndex < MAX_UNIFORM_BUFFER_BINDINGS; bufferBindingIndex++) | 
|  | { | 
|  | fragmentUniformBuffers[bufferBindingIndex] = -1; | 
|  | } | 
|  |  | 
|  | int vertexUniformBufferIndex = 0; | 
|  | int fragmentUniformBufferIndex = 0; | 
|  | for(unsigned int uniformBlockIndex = 0; uniformBlockIndex < uniformBlocks.size(); uniformBlockIndex++) | 
|  | { | 
|  | UniformBlock &uniformBlock = *uniformBlocks[uniformBlockIndex]; | 
|  |  | 
|  | // Unnecessary to apply an unreferenced standard or shared UBO | 
|  | if(!uniformBlock.isReferencedByVertexShader() && !uniformBlock.isReferencedByFragmentShader()) | 
|  | { | 
|  | continue; | 
|  | } | 
|  |  | 
|  | GLuint blockBinding = uniformBlockBindings[uniformBlockIndex]; | 
|  |  | 
|  | if(uniformBlock.isReferencedByVertexShader()) | 
|  | { | 
|  | vertexUniformBuffers[vertexUniformBufferIndex++] = blockBinding; | 
|  | } | 
|  |  | 
|  | if(uniformBlock.isReferencedByFragmentShader()) | 
|  | { | 
|  | fragmentUniformBuffers[fragmentUniformBufferIndex++] = blockBinding; | 
|  | } | 
|  | } | 
|  |  | 
|  | for(unsigned int bufferBindingIndex = 0; bufferBindingIndex < MAX_UNIFORM_BUFFER_BINDINGS; bufferBindingIndex++) | 
|  | { | 
|  | int index = vertexUniformBuffers[bufferBindingIndex]; | 
|  | Buffer* vsBuffer = (index != -1) ? (Buffer*)uniformBuffers[index].get() : nullptr; | 
|  | device->VertexProcessor::setUniformBuffer(bufferBindingIndex, | 
|  | vsBuffer ? vsBuffer->getResource() : nullptr, (index != -1) ? uniformBuffers[index].getOffset() : 0); | 
|  | index = fragmentUniformBuffers[bufferBindingIndex]; | 
|  | Buffer* psBuffer = (index != -1) ? (Buffer*)uniformBuffers[index].get() : nullptr; | 
|  | device->PixelProcessor::setUniformBuffer(bufferBindingIndex, | 
|  | psBuffer ? psBuffer->getResource() : nullptr, (index != -1) ? uniformBuffers[index].getOffset() : 0); | 
|  | } | 
|  | } | 
|  |  | 
|  | void Program::applyTransformFeedback(Device *device, TransformFeedback* transformFeedback) | 
|  | { | 
|  | // Make sure the flags will fit in a 64 bit unsigned int variable | 
|  | ASSERT(sw::max<int>(MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS, sw::MAX_TRANSFORM_FEEDBACK_INTERLEAVED_COMPONENTS) <= 64); | 
|  |  | 
|  | BufferBinding* transformFeedbackBuffers = (transformFeedback && transformFeedback->isActive() && !transformFeedback->isPaused()) ? transformFeedback->getBuffers() : nullptr; | 
|  |  | 
|  | uint64_t enableTransformFeedback = 0; | 
|  | if(!transformFeedbackBuffers) | 
|  | { | 
|  | for(unsigned int index = 0; index < sw::MAX_TRANSFORM_FEEDBACK_INTERLEAVED_COMPONENTS; ++index) | 
|  | { | 
|  | device->VertexProcessor::setTransformFeedbackBuffer(index, nullptr, 0, 0, 0, 0, 0); | 
|  | } | 
|  | device->VertexProcessor::enableTransformFeedback(enableTransformFeedback); | 
|  | return; | 
|  | } | 
|  |  | 
|  | unsigned int maxVaryings = static_cast<unsigned int>(transformFeedbackLinkedVaryings.size()); | 
|  | switch(transformFeedbackBufferMode) | 
|  | { | 
|  | case GL_SEPARATE_ATTRIBS: | 
|  | { | 
|  | maxVaryings = sw::min(maxVaryings, (unsigned int)MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS); | 
|  | // Attribs go to separate buffers | 
|  | for(unsigned int index = 0; index < maxVaryings; ++index) | 
|  | { | 
|  | int size = transformFeedbackLinkedVaryings[index].size; | 
|  | int rowCount = VariableRowCount(transformFeedbackLinkedVaryings[index].type); | 
|  | int colCount = VariableColumnCount(transformFeedbackLinkedVaryings[index].type); | 
|  | int nbRegs = rowCount > 1 ? colCount * size : size; | 
|  | int nbComponentsPerReg = rowCount > 1 ? rowCount : colCount; | 
|  | int componentStride = rowCount * colCount * size; | 
|  | int baseOffset = transformFeedback->vertexOffset() * componentStride * sizeof(float); | 
|  | device->VertexProcessor::setTransformFeedbackBuffer(index, | 
|  | transformFeedbackBuffers[index].get()->getResource(), | 
|  | transformFeedbackBuffers[index].getOffset() + baseOffset, | 
|  | transformFeedbackLinkedVaryings[index].reg * 4 + transformFeedbackLinkedVaryings[index].col, | 
|  | nbRegs, nbComponentsPerReg, componentStride); | 
|  | enableTransformFeedback |= 1ULL << index; | 
|  | } | 
|  | } | 
|  | break; | 
|  | case GL_INTERLEAVED_ATTRIBS: | 
|  | { | 
|  | // OpenGL ES 3.0.4 spec, section 2.15.2: | 
|  | // In INTERLEAVED_ATTRIBS mode, the values of one or more output variables | 
|  | // written by a vertex shader are written, interleaved, into the buffer object | 
|  | // bound to the first transform feedback binding point (index = 0). | 
|  | sw::Resource* resource = transformFeedbackBuffers[0].get()->getResource(); | 
|  | int componentStride = static_cast<int>(totalLinkedVaryingsComponents); | 
|  | int baseOffset = transformFeedbackBuffers[0].getOffset() + (transformFeedback->vertexOffset() * componentStride * sizeof(float)); | 
|  | maxVaryings = sw::min(maxVaryings, (unsigned int)sw::MAX_TRANSFORM_FEEDBACK_INTERLEAVED_COMPONENTS); | 
|  | int totalComponents = 0; | 
|  | for(unsigned int index = 0; index < maxVaryings; ++index) | 
|  | { | 
|  | int size = transformFeedbackLinkedVaryings[index].size; | 
|  | int rowCount = VariableRowCount(transformFeedbackLinkedVaryings[index].type); | 
|  | int colCount = VariableColumnCount(transformFeedbackLinkedVaryings[index].type); | 
|  | int nbRegs = rowCount > 1 ? colCount * size : size; | 
|  | int nbComponentsPerReg = rowCount > 1 ? rowCount : colCount; | 
|  | device->VertexProcessor::setTransformFeedbackBuffer(index, resource, | 
|  | baseOffset + (totalComponents * sizeof(float)), | 
|  | transformFeedbackLinkedVaryings[index].reg * 4 + transformFeedbackLinkedVaryings[index].col, | 
|  | nbRegs, nbComponentsPerReg, componentStride); | 
|  | totalComponents += rowCount * colCount * size; | 
|  | enableTransformFeedback |= 1ULL << index; | 
|  | } | 
|  | } | 
|  | break; | 
|  | default: | 
|  | UNREACHABLE(transformFeedbackBufferMode); | 
|  | break; | 
|  | } | 
|  |  | 
|  | // Unset all other transform feedback buffers | 
|  | for(unsigned int index = maxVaryings; index < sw::MAX_TRANSFORM_FEEDBACK_INTERLEAVED_COMPONENTS; ++index) | 
|  | { | 
|  | device->VertexProcessor::setTransformFeedbackBuffer(index, nullptr, 0, 0, 0, 0, 0); | 
|  | } | 
|  |  | 
|  | device->VertexProcessor::enableTransformFeedback(enableTransformFeedback); | 
|  | } | 
|  |  | 
|  | bool Program::linkVaryings() | 
|  | { | 
|  | for(glsl::VaryingList::iterator input = fragmentShader->varyings.begin(); input != fragmentShader->varyings.end(); ++input) | 
|  | { | 
|  | bool matched = false; | 
|  |  | 
|  | for(glsl::VaryingList::iterator output = vertexShader->varyings.begin(); output != vertexShader->varyings.end(); ++output) | 
|  | { | 
|  | if(output->name == input->name) | 
|  | { | 
|  | if(output->type != input->type || output->size() != input->size()) | 
|  | { | 
|  | appendToInfoLog("Type of vertex varying %s does not match that of the fragment varying", output->name.c_str()); | 
|  |  | 
|  | return false; | 
|  | } | 
|  |  | 
|  | matched = true; | 
|  | break; | 
|  | } | 
|  | } | 
|  |  | 
|  | if(!matched) | 
|  | { | 
|  | appendToInfoLog("Fragment varying %s does not match any vertex varying", input->name.c_str()); | 
|  |  | 
|  | return false; | 
|  | } | 
|  | } | 
|  |  | 
|  | glsl::VaryingList &psVaryings = fragmentShader->varyings; | 
|  | glsl::VaryingList &vsVaryings = vertexShader->varyings; | 
|  |  | 
|  | for(glsl::VaryingList::iterator output = vsVaryings.begin(); output != vsVaryings.end(); ++output) | 
|  | { | 
|  | bool matched = false; | 
|  |  | 
|  | for(glsl::VaryingList::iterator input = psVaryings.begin(); input != psVaryings.end(); ++input) | 
|  | { | 
|  | if(output->name == input->name) | 
|  | { | 
|  | int in = input->reg; | 
|  | int out = output->reg; | 
|  | int components = VariableRegisterSize(output->type); | 
|  | int registers = VariableRegisterCount(output->type) * output->size(); | 
|  |  | 
|  | ASSERT(in >= 0); | 
|  |  | 
|  | if(in + registers > MAX_VARYING_VECTORS) | 
|  | { | 
|  | appendToInfoLog("Too many varyings"); | 
|  | return false; | 
|  | } | 
|  |  | 
|  | if(out >= 0) | 
|  | { | 
|  | if(out + registers > MAX_VARYING_VECTORS) | 
|  | { | 
|  | appendToInfoLog("Too many varyings"); | 
|  | return false; | 
|  | } | 
|  |  | 
|  | for(int i = 0; i < registers; i++) | 
|  | { | 
|  | vertexBinary->setOutput(out + i, components, sw::Shader::Semantic(sw::Shader::USAGE_COLOR, in + i, pixelBinary->getInput(in + i, 0).flat)); | 
|  | } | 
|  | } | 
|  | else   // Vertex varying is declared but not written to | 
|  | { | 
|  | for(int i = 0; i < registers; i++) | 
|  | { | 
|  | pixelBinary->setInput(in + i, components, sw::Shader::Semantic()); | 
|  | } | 
|  | } | 
|  |  | 
|  | matched = true; | 
|  | break; | 
|  | } | 
|  | } | 
|  |  | 
|  | if(!matched) | 
|  | { | 
|  | // For openGL ES 3.0, we need to still add the vertex shader outputs for unmatched varyings, for transform feedback. | 
|  | for(const std::string &indexedTfVaryingName : transformFeedbackVaryings) | 
|  | { | 
|  | std::string tfVaryingName = es2::ParseUniformName(indexedTfVaryingName, nullptr); | 
|  |  | 
|  | if(tfVaryingName == output->name) | 
|  | { | 
|  | int out = output->reg; | 
|  | int components = VariableRegisterSize(output->type); | 
|  | int registers = VariableRegisterCount(output->type) * output->size(); | 
|  |  | 
|  | if(out >= 0) | 
|  | { | 
|  | if(out + registers > MAX_VARYING_VECTORS) | 
|  | { | 
|  | appendToInfoLog("Too many varyings"); | 
|  | return false; | 
|  | } | 
|  |  | 
|  | for(int i = 0; i < registers; i++) | 
|  | { | 
|  | vertexBinary->setOutput(out + i, components, sw::Shader::Semantic(sw::Shader::USAGE_COLOR)); | 
|  | } | 
|  | } | 
|  | break; | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | return true; | 
|  | } | 
|  |  | 
|  | bool Program::linkTransformFeedback() | 
|  | { | 
|  | size_t totalComponents = 0; | 
|  | totalLinkedVaryingsComponents = 0; | 
|  |  | 
|  | std::set<std::string> uniqueNames; | 
|  |  | 
|  | for(const std::string &indexedTfVaryingName : transformFeedbackVaryings) | 
|  | { | 
|  | unsigned int subscript = GL_INVALID_INDEX; | 
|  | std::string tfVaryingName = es2::ParseUniformName(indexedTfVaryingName, &subscript); | 
|  | bool hasSubscript = (subscript != GL_INVALID_INDEX); | 
|  |  | 
|  | if(tfVaryingName.find('[') != std::string::npos) | 
|  | { | 
|  | appendToInfoLog("Capture of array sub-elements is undefined and not supported."); | 
|  | return false; | 
|  | } | 
|  |  | 
|  | bool found = false; | 
|  | for(const glsl::Varying varying : vertexShader->varyings) | 
|  | { | 
|  | if(tfVaryingName == varying.name) | 
|  | { | 
|  | if(uniqueNames.count(indexedTfVaryingName) > 0) | 
|  | { | 
|  | appendToInfoLog("Two transform feedback varyings specify the same output variable (%s)", indexedTfVaryingName.c_str()); | 
|  | return false; | 
|  | } | 
|  | uniqueNames.insert(indexedTfVaryingName); | 
|  |  | 
|  | if(hasSubscript && ((static_cast<int>(subscript)) >= varying.size())) | 
|  | { | 
|  | appendToInfoLog("Specified transform feedback varying index out of bounds (%s)", indexedTfVaryingName.c_str()); | 
|  | return false; | 
|  | } | 
|  |  | 
|  | int size = hasSubscript ? 1 : varying.size(); | 
|  |  | 
|  | int rowCount = VariableRowCount(varying.type); | 
|  | int colCount = VariableColumnCount(varying.type); | 
|  | int componentCount = rowCount * colCount * size; | 
|  | if(transformFeedbackBufferMode == GL_SEPARATE_ATTRIBS && | 
|  | componentCount > sw::MAX_TRANSFORM_FEEDBACK_SEPARATE_COMPONENTS) | 
|  | { | 
|  | appendToInfoLog("Transform feedback varying's %s components (%d) exceed the maximum separate components (%d).", | 
|  | varying.name.c_str(), componentCount, sw::MAX_TRANSFORM_FEEDBACK_SEPARATE_COMPONENTS); | 
|  | return false; | 
|  | } | 
|  |  | 
|  | totalComponents += componentCount; | 
|  |  | 
|  | int reg = varying.reg; | 
|  | if(hasSubscript) | 
|  | { | 
|  | reg += rowCount > 1 ? colCount * subscript : subscript; | 
|  | } | 
|  | int col = varying.col; | 
|  | if(tfVaryingName == "gl_PointSize") | 
|  | { | 
|  | // Point size is stored in the y element of the vector, not the x element | 
|  | col = 1; // FIXME: varying.col could already contain this information | 
|  | } | 
|  | transformFeedbackLinkedVaryings.push_back(LinkedVarying(varying.name, varying.type, size, reg, col)); | 
|  |  | 
|  | found = true; | 
|  | break; | 
|  | } | 
|  | } | 
|  |  | 
|  | if(!found) | 
|  | { | 
|  | appendToInfoLog("Transform feedback varying %s does not exist in the vertex shader.", tfVaryingName.c_str()); | 
|  | return false; | 
|  | } | 
|  | } | 
|  |  | 
|  | if(transformFeedbackBufferMode == GL_INTERLEAVED_ATTRIBS && | 
|  | totalComponents > sw::MAX_TRANSFORM_FEEDBACK_INTERLEAVED_COMPONENTS) | 
|  | { | 
|  | appendToInfoLog("Transform feedback varying total components (%d) exceed the maximum separate components (%d).", | 
|  | totalComponents, sw::MAX_TRANSFORM_FEEDBACK_INTERLEAVED_COMPONENTS); | 
|  | return false; | 
|  | } | 
|  |  | 
|  | totalLinkedVaryingsComponents = totalComponents; | 
|  |  | 
|  | return true; | 
|  | } | 
|  |  | 
|  | // Links the code of the vertex and pixel shader by matching up their varyings, | 
|  | // compiling them into binaries, determining the attribute mappings, and collecting | 
|  | // a list of uniforms | 
|  | void Program::link() | 
|  | { | 
|  | unlink(); | 
|  |  | 
|  | resetUniformBlockBindings(); | 
|  |  | 
|  | if(!fragmentShader || !fragmentShader->isCompiled()) | 
|  | { | 
|  | return; | 
|  | } | 
|  |  | 
|  | if(!vertexShader || !vertexShader->isCompiled()) | 
|  | { | 
|  | return; | 
|  | } | 
|  |  | 
|  | vertexBinary = new sw::VertexShader(vertexShader->getVertexShader()); | 
|  | pixelBinary = new sw::PixelShader(fragmentShader->getPixelShader()); | 
|  |  | 
|  | if(!linkVaryings()) | 
|  | { | 
|  | return; | 
|  | } | 
|  |  | 
|  | if(!linkAttributes()) | 
|  | { | 
|  | return; | 
|  | } | 
|  |  | 
|  | // Link uniform blocks before uniforms to make it easy to assign block indices to fields | 
|  | if(!linkUniformBlocks(vertexShader, fragmentShader)) | 
|  | { | 
|  | return; | 
|  | } | 
|  |  | 
|  | if(!linkUniforms(fragmentShader)) | 
|  | { | 
|  | return; | 
|  | } | 
|  |  | 
|  | if(!linkUniforms(vertexShader)) | 
|  | { | 
|  | return; | 
|  | } | 
|  |  | 
|  | if(!linkTransformFeedback()) | 
|  | { | 
|  | return; | 
|  | } | 
|  |  | 
|  | linked = true;   // Success | 
|  | } | 
|  |  | 
|  | // Determines the mapping between GL attributes and vertex stream usage indices | 
|  | bool Program::linkAttributes() | 
|  | { | 
|  | unsigned int usedLocations = 0; | 
|  |  | 
|  | // Link attributes that have a binding location | 
|  | for(glsl::ActiveAttributes::iterator attribute = vertexShader->activeAttributes.begin(); attribute != vertexShader->activeAttributes.end(); ++attribute) | 
|  | { | 
|  | int location = getAttributeBinding(*attribute); | 
|  |  | 
|  | if(location != -1)   // Set by glBindAttribLocation | 
|  | { | 
|  | int rows = VariableRegisterCount(attribute->type); | 
|  |  | 
|  | if(rows + location > MAX_VERTEX_ATTRIBS) | 
|  | { | 
|  | appendToInfoLog("Active attribute (%s) at location %d is too big to fit", attribute->name.c_str(), location); | 
|  | return false; | 
|  | } | 
|  |  | 
|  | // In GLSL 3.00, attribute aliasing produces a link error | 
|  | // In GLSL 1.00, attribute aliasing is allowed | 
|  | if(egl::getClientVersion() >= 3) | 
|  | { | 
|  | for(int i = 0; i < rows; i++) | 
|  | { | 
|  | if(!linkedAttribute[location + i].name.empty()) | 
|  | { | 
|  | appendToInfoLog("Attribute '%s' aliases attribute '%s' at location %d", attribute->name.c_str(), linkedAttribute[location].name.c_str(), location); | 
|  | return false; | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | for(int i = 0; i < rows; i++) | 
|  | { | 
|  | linkedAttribute[location + i] = *attribute; | 
|  | usedLocations |= 1 << (location + i); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | // Link attributes that don't have a binding location | 
|  | for(glsl::ActiveAttributes::iterator attribute = vertexShader->activeAttributes.begin(); attribute != vertexShader->activeAttributes.end(); ++attribute) | 
|  | { | 
|  | int location = getAttributeBinding(*attribute); | 
|  |  | 
|  | if(location == -1)   // Not set by glBindAttribLocation | 
|  | { | 
|  | int rows = VariableRegisterCount(attribute->type); | 
|  | int availableIndex = AllocateFirstFreeBits(&usedLocations, rows, MAX_VERTEX_ATTRIBS); | 
|  |  | 
|  | if(availableIndex == -1 || availableIndex + rows > MAX_VERTEX_ATTRIBS) | 
|  | { | 
|  | appendToInfoLog("Too many active attributes (%s)", attribute->name.c_str()); | 
|  | return false;   // Fail to link | 
|  | } | 
|  |  | 
|  | for(int i = 0; i < rows; i++) | 
|  | { | 
|  | linkedAttribute[availableIndex + i] = *attribute; | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | for(int attributeIndex = 0; attributeIndex < MAX_VERTEX_ATTRIBS; ) | 
|  | { | 
|  | int index = vertexShader->getSemanticIndex(linkedAttribute[attributeIndex].name); | 
|  | int rows = std::max(VariableRegisterCount(linkedAttribute[attributeIndex].type), 1); | 
|  |  | 
|  | for(int r = 0; r < rows; r++) | 
|  | { | 
|  | attributeStream[attributeIndex++] = index++; | 
|  | } | 
|  | } | 
|  |  | 
|  | return true; | 
|  | } | 
|  |  | 
|  | int Program::getAttributeBinding(const glsl::Attribute &attribute) | 
|  | { | 
|  | if(attribute.location != -1) | 
|  | { | 
|  | return attribute.location; | 
|  | } | 
|  |  | 
|  | for(int location = 0; location < MAX_VERTEX_ATTRIBS; location++) | 
|  | { | 
|  | if(attributeBinding[location].find(attribute.name.c_str()) != attributeBinding[location].end()) | 
|  | { | 
|  | return location; | 
|  | } | 
|  | } | 
|  |  | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | bool Program::linkUniforms(const Shader *shader) | 
|  | { | 
|  | const glsl::ActiveUniforms &activeUniforms = shader->activeUniforms; | 
|  |  | 
|  | for(unsigned int uniformIndex = 0; uniformIndex < activeUniforms.size(); uniformIndex++) | 
|  | { | 
|  | const glsl::Uniform &uniform = activeUniforms[uniformIndex]; | 
|  |  | 
|  | unsigned int blockIndex = GL_INVALID_INDEX; | 
|  | if(uniform.blockId >= 0) | 
|  | { | 
|  | const glsl::ActiveUniformBlocks &activeUniformBlocks = shader->activeUniformBlocks; | 
|  | ASSERT(static_cast<size_t>(uniform.blockId) < activeUniformBlocks.size()); | 
|  | blockIndex = getUniformBlockIndex(activeUniformBlocks[uniform.blockId].name); | 
|  | ASSERT(blockIndex != GL_INVALID_INDEX); | 
|  | } | 
|  | if(!defineUniform(shader->getType(), uniform.type, uniform.precision, uniform.name, uniform.arraySize, uniform.registerIndex, Uniform::BlockInfo(uniform, blockIndex))) | 
|  | { | 
|  | return false; | 
|  | } | 
|  | } | 
|  |  | 
|  | return true; | 
|  | } | 
|  |  | 
|  | bool Program::defineUniform(GLenum shader, GLenum type, GLenum precision, const std::string &name, unsigned int arraySize, int registerIndex, const Uniform::BlockInfo& blockInfo) | 
|  | { | 
|  | if(IsSamplerUniform(type)) | 
|  | { | 
|  | int index = registerIndex; | 
|  |  | 
|  | do | 
|  | { | 
|  | if(shader == GL_VERTEX_SHADER) | 
|  | { | 
|  | if(index < MAX_VERTEX_TEXTURE_IMAGE_UNITS) | 
|  | { | 
|  | samplersVS[index].active = true; | 
|  |  | 
|  | switch(type) | 
|  | { | 
|  | default:                      UNREACHABLE(type); | 
|  | case GL_INT_SAMPLER_2D: | 
|  | case GL_UNSIGNED_INT_SAMPLER_2D: | 
|  | case GL_SAMPLER_2D_SHADOW: | 
|  | case GL_SAMPLER_2D:           samplersVS[index].textureType = TEXTURE_2D;       break; | 
|  | case GL_INT_SAMPLER_CUBE: | 
|  | case GL_UNSIGNED_INT_SAMPLER_CUBE: | 
|  | case GL_SAMPLER_CUBE_SHADOW: | 
|  | case GL_SAMPLER_CUBE:         samplersVS[index].textureType = TEXTURE_CUBE;     break; | 
|  | case GL_INT_SAMPLER_3D: | 
|  | case GL_UNSIGNED_INT_SAMPLER_3D: | 
|  | case GL_SAMPLER_3D_OES:       samplersVS[index].textureType = TEXTURE_3D;       break; | 
|  | case GL_SAMPLER_EXTERNAL_OES: samplersVS[index].textureType = TEXTURE_EXTERNAL; break; | 
|  | case GL_INT_SAMPLER_2D_ARRAY: | 
|  | case GL_UNSIGNED_INT_SAMPLER_2D_ARRAY: | 
|  | case GL_SAMPLER_2D_ARRAY_SHADOW: | 
|  | case GL_SAMPLER_2D_ARRAY:     samplersVS[index].textureType = TEXTURE_2D_ARRAY; break; | 
|  | } | 
|  |  | 
|  | samplersVS[index].logicalTextureUnit = 0; | 
|  | } | 
|  | else | 
|  | { | 
|  | appendToInfoLog("Vertex shader sampler count exceeds MAX_VERTEX_TEXTURE_IMAGE_UNITS (%d).", MAX_VERTEX_TEXTURE_IMAGE_UNITS); | 
|  | return false; | 
|  | } | 
|  | } | 
|  | else if(shader == GL_FRAGMENT_SHADER) | 
|  | { | 
|  | if(index < MAX_TEXTURE_IMAGE_UNITS) | 
|  | { | 
|  | samplersPS[index].active = true; | 
|  |  | 
|  | switch(type) | 
|  | { | 
|  | default:                      UNREACHABLE(type); | 
|  | case GL_INT_SAMPLER_2D: | 
|  | case GL_UNSIGNED_INT_SAMPLER_2D: | 
|  | case GL_SAMPLER_2D_SHADOW: | 
|  | case GL_SAMPLER_2D:           samplersPS[index].textureType = TEXTURE_2D;       break; | 
|  | case GL_INT_SAMPLER_CUBE: | 
|  | case GL_UNSIGNED_INT_SAMPLER_CUBE: | 
|  | case GL_SAMPLER_CUBE_SHADOW: | 
|  | case GL_SAMPLER_CUBE:         samplersPS[index].textureType = TEXTURE_CUBE;     break; | 
|  | case GL_INT_SAMPLER_3D: | 
|  | case GL_UNSIGNED_INT_SAMPLER_3D: | 
|  | case GL_SAMPLER_3D_OES:       samplersPS[index].textureType = TEXTURE_3D;       break; | 
|  | case GL_SAMPLER_EXTERNAL_OES: samplersPS[index].textureType = TEXTURE_EXTERNAL; break; | 
|  | case GL_INT_SAMPLER_2D_ARRAY: | 
|  | case GL_UNSIGNED_INT_SAMPLER_2D_ARRAY: | 
|  | case GL_SAMPLER_2D_ARRAY_SHADOW: | 
|  | case GL_SAMPLER_2D_ARRAY:     samplersPS[index].textureType = TEXTURE_2D_ARRAY; break; | 
|  | } | 
|  |  | 
|  | samplersPS[index].logicalTextureUnit = 0; | 
|  | } | 
|  | else | 
|  | { | 
|  | appendToInfoLog("Pixel shader sampler count exceeds MAX_TEXTURE_IMAGE_UNITS (%d).", MAX_TEXTURE_IMAGE_UNITS); | 
|  | return false; | 
|  | } | 
|  | } | 
|  | else UNREACHABLE(shader); | 
|  |  | 
|  | index++; | 
|  | } | 
|  | while(index < registerIndex + static_cast<int>(arraySize)); | 
|  | } | 
|  |  | 
|  | Uniform *uniform = 0; | 
|  | GLint location = getUniformLocation(name); | 
|  |  | 
|  | if(location >= 0)   // Previously defined, types must match | 
|  | { | 
|  | uniform = uniforms[uniformIndex[location].index]; | 
|  |  | 
|  | if(uniform->type != type) | 
|  | { | 
|  | appendToInfoLog("Types for uniform %s do not match between the vertex and fragment shader", uniform->name.c_str()); | 
|  | return false; | 
|  | } | 
|  |  | 
|  | if(uniform->precision != precision) | 
|  | { | 
|  | appendToInfoLog("Precisions for uniform %s do not match between the vertex and fragment shader", uniform->name.c_str()); | 
|  | return false; | 
|  | } | 
|  | } | 
|  | else | 
|  | { | 
|  | uniform = new Uniform(type, precision, name, arraySize, blockInfo); | 
|  | } | 
|  |  | 
|  | if(!uniform) | 
|  | { | 
|  | return false; | 
|  | } | 
|  |  | 
|  | if(shader == GL_VERTEX_SHADER) | 
|  | { | 
|  | uniform->vsRegisterIndex = registerIndex; | 
|  | } | 
|  | else if(shader == GL_FRAGMENT_SHADER) | 
|  | { | 
|  | uniform->psRegisterIndex = registerIndex; | 
|  | } | 
|  | else UNREACHABLE(shader); | 
|  |  | 
|  | if(location == -1)   // Not previously defined | 
|  | { | 
|  | uniforms.push_back(uniform); | 
|  | unsigned int index = static_cast<unsigned int>(uniforms.size() - 1); | 
|  |  | 
|  | for(int i = 0; i < uniform->size(); i++) | 
|  | { | 
|  | uniformIndex.push_back(UniformLocation(name, i, index)); | 
|  | } | 
|  | } | 
|  |  | 
|  | if(shader == GL_VERTEX_SHADER) | 
|  | { | 
|  | if(registerIndex + uniform->registerCount() > MAX_VERTEX_UNIFORM_VECTORS) | 
|  | { | 
|  | appendToInfoLog("Vertex shader active uniforms exceed GL_MAX_VERTEX_UNIFORM_VECTORS (%d)", MAX_VERTEX_UNIFORM_VECTORS); | 
|  | return false; | 
|  | } | 
|  | } | 
|  | else if(shader == GL_FRAGMENT_SHADER) | 
|  | { | 
|  | if(registerIndex + uniform->registerCount() > MAX_FRAGMENT_UNIFORM_VECTORS) | 
|  | { | 
|  | appendToInfoLog("Fragment shader active uniforms exceed GL_MAX_FRAGMENT_UNIFORM_VECTORS (%d)", MAX_FRAGMENT_UNIFORM_VECTORS); | 
|  | return false; | 
|  | } | 
|  | } | 
|  | else UNREACHABLE(shader); | 
|  |  | 
|  | return true; | 
|  | } | 
|  |  | 
|  | bool Program::areMatchingUniformBlocks(const glsl::UniformBlock &block1, const glsl::UniformBlock &block2, const Shader *shader1, const Shader *shader2) | 
|  | { | 
|  | // validate blocks for the same member types | 
|  | if(block1.fields.size() != block2.fields.size()) | 
|  | { | 
|  | return false; | 
|  | } | 
|  | if(block1.arraySize != block2.arraySize) | 
|  | { | 
|  | return false; | 
|  | } | 
|  | if(block1.layout != block2.layout || block1.isRowMajorLayout != block2.isRowMajorLayout) | 
|  | { | 
|  | return false; | 
|  | } | 
|  | const size_t numBlockMembers = block1.fields.size(); | 
|  | for(size_t blockMemberIndex = 0; blockMemberIndex < numBlockMembers; blockMemberIndex++) | 
|  | { | 
|  | const glsl::Uniform& member1 = shader1->activeUniforms[block1.fields[blockMemberIndex]]; | 
|  | const glsl::Uniform& member2 = shader2->activeUniforms[block2.fields[blockMemberIndex]]; | 
|  | if(member1.name != member2.name || | 
|  | member1.arraySize != member2.arraySize || | 
|  | member1.precision != member2.precision || | 
|  | member1.type != member2.type) | 
|  | { | 
|  | return false; | 
|  | } | 
|  | } | 
|  | return true; | 
|  | } | 
|  |  | 
|  | bool Program::linkUniformBlocks(const Shader *vertexShader, const Shader *fragmentShader) | 
|  | { | 
|  | const glsl::ActiveUniformBlocks &vertexUniformBlocks = vertexShader->activeUniformBlocks; | 
|  | const glsl::ActiveUniformBlocks &fragmentUniformBlocks = fragmentShader->activeUniformBlocks; | 
|  | // Check that interface blocks defined in the vertex and fragment shaders are identical | 
|  | typedef std::map<std::string, const glsl::UniformBlock*> UniformBlockMap; | 
|  | UniformBlockMap linkedUniformBlocks; | 
|  | for(unsigned int blockIndex = 0; blockIndex < vertexUniformBlocks.size(); blockIndex++) | 
|  | { | 
|  | const glsl::UniformBlock &vertexUniformBlock = vertexUniformBlocks[blockIndex]; | 
|  | linkedUniformBlocks[vertexUniformBlock.name] = &vertexUniformBlock; | 
|  | } | 
|  | for(unsigned int blockIndex = 0; blockIndex < fragmentUniformBlocks.size(); blockIndex++) | 
|  | { | 
|  | const glsl::UniformBlock &fragmentUniformBlock = fragmentUniformBlocks[blockIndex]; | 
|  | UniformBlockMap::const_iterator entry = linkedUniformBlocks.find(fragmentUniformBlock.name); | 
|  | if(entry != linkedUniformBlocks.end()) | 
|  | { | 
|  | const glsl::UniformBlock &vertexUniformBlock = *entry->second; | 
|  | if(!areMatchingUniformBlocks(vertexUniformBlock, fragmentUniformBlock, vertexShader, fragmentShader)) | 
|  | { | 
|  | return false; | 
|  | } | 
|  | } | 
|  | } | 
|  | for(unsigned int blockIndex = 0; blockIndex < vertexUniformBlocks.size(); blockIndex++) | 
|  | { | 
|  | const glsl::UniformBlock &uniformBlock = vertexUniformBlocks[blockIndex]; | 
|  | if(!defineUniformBlock(vertexShader, uniformBlock)) | 
|  | { | 
|  | return false; | 
|  | } | 
|  | } | 
|  | for(unsigned int blockIndex = 0; blockIndex < fragmentUniformBlocks.size(); blockIndex++) | 
|  | { | 
|  | const glsl::UniformBlock &uniformBlock = fragmentUniformBlocks[blockIndex]; | 
|  | if(!defineUniformBlock(fragmentShader, uniformBlock)) | 
|  | { | 
|  | return false; | 
|  | } | 
|  | } | 
|  | return true; | 
|  | } | 
|  |  | 
|  | bool Program::defineUniformBlock(const Shader *shader, const glsl::UniformBlock &block) | 
|  | { | 
|  | GLuint blockIndex = getUniformBlockIndex(block.name); | 
|  |  | 
|  | if(blockIndex == GL_INVALID_INDEX) | 
|  | { | 
|  | const std::vector<int>& fields = block.fields; | 
|  | std::vector<unsigned int> memberUniformIndexes; | 
|  | for(size_t i = 0; i < fields.size(); ++i) | 
|  | { | 
|  | memberUniformIndexes.push_back(fields[i]); | 
|  | } | 
|  |  | 
|  | if(block.arraySize > 0) | 
|  | { | 
|  | int regIndex = block.registerIndex; | 
|  | int regInc = block.dataSize / (glsl::BlockLayoutEncoder::BytesPerComponent * glsl::BlockLayoutEncoder::ComponentsPerRegister); | 
|  | for(unsigned int i = 0; i < block.arraySize; ++i, regIndex += regInc) | 
|  | { | 
|  | uniformBlocks.push_back(new UniformBlock(block.name, i, block.dataSize, memberUniformIndexes)); | 
|  | uniformBlocks[uniformBlocks.size() - 1]->setRegisterIndex(shader->getType(), regIndex); | 
|  | } | 
|  | } | 
|  | else | 
|  | { | 
|  | uniformBlocks.push_back(new UniformBlock(block.name, GL_INVALID_INDEX, block.dataSize, memberUniformIndexes)); | 
|  | uniformBlocks[uniformBlocks.size() - 1]->setRegisterIndex(shader->getType(), block.registerIndex); | 
|  | } | 
|  | } | 
|  | else | 
|  | { | 
|  | int regIndex = block.registerIndex; | 
|  | int regInc = block.dataSize / (glsl::BlockLayoutEncoder::BytesPerComponent * glsl::BlockLayoutEncoder::ComponentsPerRegister); | 
|  | int nbBlocks = (block.arraySize > 0) ? block.arraySize : 1; | 
|  | for(int i = 0; i < nbBlocks; ++i, regIndex += regInc) | 
|  | { | 
|  | uniformBlocks[blockIndex + i]->setRegisterIndex(shader->getType(), regIndex); | 
|  | } | 
|  | } | 
|  |  | 
|  | return true; | 
|  | } | 
|  |  | 
|  | bool Program::applyUniform(Device *device, GLint location, float* data) | 
|  | { | 
|  | Uniform *targetUniform = uniforms[uniformIndex[location].index]; | 
|  |  | 
|  | if(targetUniform->psRegisterIndex != -1) | 
|  | { | 
|  | device->setPixelShaderConstantF(targetUniform->psRegisterIndex, data, targetUniform->registerCount()); | 
|  | } | 
|  |  | 
|  | if(targetUniform->vsRegisterIndex != -1) | 
|  | { | 
|  | device->setVertexShaderConstantF(targetUniform->vsRegisterIndex, data, targetUniform->registerCount()); | 
|  | } | 
|  |  | 
|  | return true; | 
|  | } | 
|  |  | 
|  | bool Program::applyUniform1bv(Device *device, GLint location, GLsizei count, const GLboolean *v) | 
|  | { | 
|  | int vector[MAX_UNIFORM_VECTORS][4]; | 
|  |  | 
|  | for(int i = 0; i < count; i++) | 
|  | { | 
|  | vector[i][0] = (v[0] == GL_FALSE ? 0x00000000 : 0xFFFFFFFF); | 
|  | vector[i][1] = 0; | 
|  | vector[i][2] = 0; | 
|  | vector[i][3] = 0; | 
|  |  | 
|  | v += 1; | 
|  | } | 
|  |  | 
|  | return applyUniform(device, location, (float*)vector); | 
|  | } | 
|  |  | 
|  | bool Program::applyUniform2bv(Device *device, GLint location, GLsizei count, const GLboolean *v) | 
|  | { | 
|  | int vector[MAX_UNIFORM_VECTORS][4]; | 
|  |  | 
|  | for(int i = 0; i < count; i++) | 
|  | { | 
|  | vector[i][0] = (v[0] == GL_FALSE ? 0x00000000 : 0xFFFFFFFF); | 
|  | vector[i][1] = (v[1] == GL_FALSE ? 0x00000000 : 0xFFFFFFFF); | 
|  | vector[i][2] = 0; | 
|  | vector[i][3] = 0; | 
|  |  | 
|  | v += 2; | 
|  | } | 
|  |  | 
|  | return applyUniform(device, location, (float*)vector); | 
|  | } | 
|  |  | 
|  | bool Program::applyUniform3bv(Device *device, GLint location, GLsizei count, const GLboolean *v) | 
|  | { | 
|  | int vector[MAX_UNIFORM_VECTORS][4]; | 
|  |  | 
|  | for(int i = 0; i < count; i++) | 
|  | { | 
|  | vector[i][0] = (v[0] == GL_FALSE ? 0x00000000 : 0xFFFFFFFF); | 
|  | vector[i][1] = (v[1] == GL_FALSE ? 0x00000000 : 0xFFFFFFFF); | 
|  | vector[i][2] = (v[2] == GL_FALSE ? 0x00000000 : 0xFFFFFFFF); | 
|  | vector[i][3] = 0; | 
|  |  | 
|  | v += 3; | 
|  | } | 
|  |  | 
|  | return applyUniform(device, location, (float*)vector); | 
|  | } | 
|  |  | 
|  | bool Program::applyUniform4bv(Device *device, GLint location, GLsizei count, const GLboolean *v) | 
|  | { | 
|  | int vector[MAX_UNIFORM_VECTORS][4]; | 
|  |  | 
|  | for(int i = 0; i < count; i++) | 
|  | { | 
|  | vector[i][0] = (v[0] == GL_FALSE ? 0x00000000 : 0xFFFFFFFF); | 
|  | vector[i][1] = (v[1] == GL_FALSE ? 0x00000000 : 0xFFFFFFFF); | 
|  | vector[i][2] = (v[2] == GL_FALSE ? 0x00000000 : 0xFFFFFFFF); | 
|  | vector[i][3] = (v[3] == GL_FALSE ? 0x00000000 : 0xFFFFFFFF); | 
|  |  | 
|  | v += 4; | 
|  | } | 
|  |  | 
|  | return applyUniform(device, location, (float*)vector); | 
|  | } | 
|  |  | 
|  | bool Program::applyUniform1fv(Device *device, GLint location, GLsizei count, const GLfloat *v) | 
|  | { | 
|  | float vector[MAX_UNIFORM_VECTORS][4]; | 
|  |  | 
|  | for(int i = 0; i < count; i++) | 
|  | { | 
|  | vector[i][0] = v[0]; | 
|  | vector[i][1] = 0; | 
|  | vector[i][2] = 0; | 
|  | vector[i][3] = 0; | 
|  |  | 
|  | v += 1; | 
|  | } | 
|  |  | 
|  | return applyUniform(device, location, (float*)vector); | 
|  | } | 
|  |  | 
|  | bool Program::applyUniform2fv(Device *device, GLint location, GLsizei count, const GLfloat *v) | 
|  | { | 
|  | float vector[MAX_UNIFORM_VECTORS][4]; | 
|  |  | 
|  | for(int i = 0; i < count; i++) | 
|  | { | 
|  | vector[i][0] = v[0]; | 
|  | vector[i][1] = v[1]; | 
|  | vector[i][2] = 0; | 
|  | vector[i][3] = 0; | 
|  |  | 
|  | v += 2; | 
|  | } | 
|  |  | 
|  | return applyUniform(device, location, (float*)vector); | 
|  | } | 
|  |  | 
|  | bool Program::applyUniform3fv(Device *device, GLint location, GLsizei count, const GLfloat *v) | 
|  | { | 
|  | float vector[MAX_UNIFORM_VECTORS][4]; | 
|  |  | 
|  | for(int i = 0; i < count; i++) | 
|  | { | 
|  | vector[i][0] = v[0]; | 
|  | vector[i][1] = v[1]; | 
|  | vector[i][2] = v[2]; | 
|  | vector[i][3] = 0; | 
|  |  | 
|  | v += 3; | 
|  | } | 
|  |  | 
|  | return applyUniform(device, location, (float*)vector); | 
|  | } | 
|  |  | 
|  | bool Program::applyUniform4fv(Device *device, GLint location, GLsizei count, const GLfloat *v) | 
|  | { | 
|  | return applyUniform(device, location, (float*)v); | 
|  | } | 
|  |  | 
|  | bool Program::applyUniformMatrix2fv(Device *device, GLint location, GLsizei count, const GLfloat *value) | 
|  | { | 
|  | float matrix[(MAX_UNIFORM_VECTORS + 1) / 2][2][4]; | 
|  |  | 
|  | for(int i = 0; i < count; i++) | 
|  | { | 
|  | matrix[i][0][0] = value[0];	matrix[i][0][1] = value[1];	matrix[i][0][2] = 0; matrix[i][0][3] = 0; | 
|  | matrix[i][1][0] = value[2];	matrix[i][1][1] = value[3];	matrix[i][1][2] = 0; matrix[i][1][3] = 0; | 
|  |  | 
|  | value += 4; | 
|  | } | 
|  |  | 
|  | return applyUniform(device, location, (float*)matrix); | 
|  | } | 
|  |  | 
|  | bool Program::applyUniformMatrix2x3fv(Device *device, GLint location, GLsizei count, const GLfloat *value) | 
|  | { | 
|  | float matrix[(MAX_UNIFORM_VECTORS + 1) / 2][2][4]; | 
|  |  | 
|  | for(int i = 0; i < count; i++) | 
|  | { | 
|  | matrix[i][0][0] = value[0];	matrix[i][0][1] = value[1];	matrix[i][0][2] = value[2]; matrix[i][0][3] = 0; | 
|  | matrix[i][1][0] = value[3];	matrix[i][1][1] = value[4];	matrix[i][1][2] = value[5]; matrix[i][1][3] = 0; | 
|  |  | 
|  | value += 6; | 
|  | } | 
|  |  | 
|  | return applyUniform(device, location, (float*)matrix); | 
|  | } | 
|  |  | 
|  | bool Program::applyUniformMatrix2x4fv(Device *device, GLint location, GLsizei count, const GLfloat *value) | 
|  | { | 
|  | float matrix[(MAX_UNIFORM_VECTORS + 1) / 2][2][4]; | 
|  |  | 
|  | for(int i = 0; i < count; i++) | 
|  | { | 
|  | matrix[i][0][0] = value[0];	matrix[i][0][1] = value[1];	matrix[i][0][2] = value[2]; matrix[i][0][3] = value[3]; | 
|  | matrix[i][1][0] = value[4];	matrix[i][1][1] = value[5];	matrix[i][1][2] = value[6]; matrix[i][1][3] = value[7]; | 
|  |  | 
|  | value += 8; | 
|  | } | 
|  |  | 
|  | return applyUniform(device, location, (float*)matrix); | 
|  | } | 
|  |  | 
|  | bool Program::applyUniformMatrix3fv(Device *device, GLint location, GLsizei count, const GLfloat *value) | 
|  | { | 
|  | float matrix[(MAX_UNIFORM_VECTORS + 2) / 3][3][4]; | 
|  |  | 
|  | for(int i = 0; i < count; i++) | 
|  | { | 
|  | matrix[i][0][0] = value[0];	matrix[i][0][1] = value[1];	matrix[i][0][2] = value[2];	matrix[i][0][3] = 0; | 
|  | matrix[i][1][0] = value[3];	matrix[i][1][1] = value[4];	matrix[i][1][2] = value[5];	matrix[i][1][3] = 0; | 
|  | matrix[i][2][0] = value[6];	matrix[i][2][1] = value[7];	matrix[i][2][2] = value[8];	matrix[i][2][3] = 0; | 
|  |  | 
|  | value += 9; | 
|  | } | 
|  |  | 
|  | return applyUniform(device, location, (float*)matrix); | 
|  | } | 
|  |  | 
|  | bool Program::applyUniformMatrix3x2fv(Device *device, GLint location, GLsizei count, const GLfloat *value) | 
|  | { | 
|  | float matrix[(MAX_UNIFORM_VECTORS + 2) / 3][3][4]; | 
|  |  | 
|  | for(int i = 0; i < count; i++) | 
|  | { | 
|  | matrix[i][0][0] = value[0];	matrix[i][0][1] = value[1];	matrix[i][0][2] = 0; matrix[i][0][3] = 0; | 
|  | matrix[i][1][0] = value[2];	matrix[i][1][1] = value[3];	matrix[i][1][2] = 0; matrix[i][1][3] = 0; | 
|  | matrix[i][2][0] = value[4];	matrix[i][2][1] = value[5];	matrix[i][2][2] = 0; matrix[i][2][3] = 0; | 
|  |  | 
|  | value += 6; | 
|  | } | 
|  |  | 
|  | return applyUniform(device, location, (float*)matrix); | 
|  | } | 
|  |  | 
|  | bool Program::applyUniformMatrix3x4fv(Device *device, GLint location, GLsizei count, const GLfloat *value) | 
|  | { | 
|  | float matrix[(MAX_UNIFORM_VECTORS + 2) / 3][3][4]; | 
|  |  | 
|  | for(int i = 0; i < count; i++) | 
|  | { | 
|  | matrix[i][0][0] = value[0];	matrix[i][0][1] = value[1];	matrix[i][0][2] = value[2]; 	matrix[i][0][3] = value[3]; | 
|  | matrix[i][1][0] = value[4];	matrix[i][1][1] = value[5];	matrix[i][1][2] = value[6]; 	matrix[i][1][3] = value[7]; | 
|  | matrix[i][2][0] = value[8];	matrix[i][2][1] = value[9];	matrix[i][2][2] = value[10];	matrix[i][2][3] = value[11]; | 
|  |  | 
|  | value += 12; | 
|  | } | 
|  |  | 
|  | return applyUniform(device, location, (float*)matrix); | 
|  | } | 
|  |  | 
|  | bool Program::applyUniformMatrix4fv(Device *device, GLint location, GLsizei count, const GLfloat *value) | 
|  | { | 
|  | return applyUniform(device, location, (float*)value); | 
|  | } | 
|  |  | 
|  | bool Program::applyUniformMatrix4x2fv(Device *device, GLint location, GLsizei count, const GLfloat *value) | 
|  | { | 
|  | float matrix[(MAX_UNIFORM_VECTORS + 3) / 4][4][4]; | 
|  |  | 
|  | for(int i = 0; i < count; i++) | 
|  | { | 
|  | matrix[i][0][0] = value[0];	matrix[i][0][1] = value[1];	matrix[i][0][2] = 0; matrix[i][0][3] = 0; | 
|  | matrix[i][1][0] = value[2];	matrix[i][1][1] = value[3];	matrix[i][1][2] = 0; matrix[i][1][3] = 0; | 
|  | matrix[i][2][0] = value[4];	matrix[i][2][1] = value[5];	matrix[i][2][2] = 0; matrix[i][2][3] = 0; | 
|  | matrix[i][3][0] = value[6];	matrix[i][3][1] = value[7];	matrix[i][3][2] = 0; matrix[i][3][3] = 0; | 
|  |  | 
|  | value += 8; | 
|  | } | 
|  |  | 
|  | return applyUniform(device, location, (float*)matrix); | 
|  | } | 
|  |  | 
|  | bool Program::applyUniformMatrix4x3fv(Device *device, GLint location, GLsizei count, const GLfloat *value) | 
|  | { | 
|  | float matrix[(MAX_UNIFORM_VECTORS + 3) / 4][4][4]; | 
|  |  | 
|  | for(int i = 0; i < count; i++) | 
|  | { | 
|  | matrix[i][0][0] = value[0];	matrix[i][0][1] = value[1];  matrix[i][0][2] = value[2];  matrix[i][0][3] = 0; | 
|  | matrix[i][1][0] = value[3];	matrix[i][1][1] = value[4];  matrix[i][1][2] = value[5];  matrix[i][1][3] = 0; | 
|  | matrix[i][2][0] = value[6];	matrix[i][2][1] = value[7];  matrix[i][2][2] = value[8];  matrix[i][2][3] = 0; | 
|  | matrix[i][3][0] = value[9];	matrix[i][3][1] = value[10]; matrix[i][3][2] = value[11]; matrix[i][3][3] = 0; | 
|  |  | 
|  | value += 12; | 
|  | } | 
|  |  | 
|  | return applyUniform(device, location, (float*)matrix); | 
|  | } | 
|  |  | 
|  | bool Program::applyUniform1iv(Device *device, GLint location, GLsizei count, const GLint *v) | 
|  | { | 
|  | GLint vector[MAX_UNIFORM_VECTORS][4]; | 
|  |  | 
|  | for(int i = 0; i < count; i++) | 
|  | { | 
|  | vector[i][0] = v[i]; | 
|  | vector[i][1] = 0; | 
|  | vector[i][2] = 0; | 
|  | vector[i][3] = 0; | 
|  | } | 
|  |  | 
|  | Uniform *targetUniform = uniforms[uniformIndex[location].index]; | 
|  | if(IsSamplerUniform(targetUniform->type)) | 
|  | { | 
|  | if(targetUniform->psRegisterIndex != -1) | 
|  | { | 
|  | for(int i = 0; i < count; i++) | 
|  | { | 
|  | unsigned int samplerIndex = targetUniform->psRegisterIndex + i; | 
|  |  | 
|  | if(samplerIndex < MAX_TEXTURE_IMAGE_UNITS) | 
|  | { | 
|  | ASSERT(samplersPS[samplerIndex].active); | 
|  | samplersPS[samplerIndex].logicalTextureUnit = v[i]; | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | if(targetUniform->vsRegisterIndex != -1) | 
|  | { | 
|  | for(int i = 0; i < count; i++) | 
|  | { | 
|  | unsigned int samplerIndex = targetUniform->vsRegisterIndex + i; | 
|  |  | 
|  | if(samplerIndex < MAX_VERTEX_TEXTURE_IMAGE_UNITS) | 
|  | { | 
|  | ASSERT(samplersVS[samplerIndex].active); | 
|  | samplersVS[samplerIndex].logicalTextureUnit = v[i]; | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  | else | 
|  | { | 
|  | return applyUniform(device, location, (float*)vector); | 
|  | } | 
|  |  | 
|  | return true; | 
|  | } | 
|  |  | 
|  | bool Program::applyUniform2iv(Device *device, GLint location, GLsizei count, const GLint *v) | 
|  | { | 
|  | GLint vector[MAX_UNIFORM_VECTORS][4]; | 
|  |  | 
|  | for(int i = 0; i < count; i++) | 
|  | { | 
|  | vector[i][0] = v[0]; | 
|  | vector[i][1] = v[1]; | 
|  | vector[i][2] = 0; | 
|  | vector[i][3] = 0; | 
|  |  | 
|  | v += 2; | 
|  | } | 
|  |  | 
|  | return applyUniform(device, location, (float*)vector); | 
|  | } | 
|  |  | 
|  | bool Program::applyUniform3iv(Device *device, GLint location, GLsizei count, const GLint *v) | 
|  | { | 
|  | GLint vector[MAX_UNIFORM_VECTORS][4]; | 
|  |  | 
|  | for(int i = 0; i < count; i++) | 
|  | { | 
|  | vector[i][0] = v[0]; | 
|  | vector[i][1] = v[1]; | 
|  | vector[i][2] = v[2]; | 
|  | vector[i][3] = 0; | 
|  |  | 
|  | v += 3; | 
|  | } | 
|  |  | 
|  | return applyUniform(device, location, (float*)vector); | 
|  | } | 
|  |  | 
|  | bool Program::applyUniform4iv(Device *device, GLint location, GLsizei count, const GLint *v) | 
|  | { | 
|  | GLint vector[MAX_UNIFORM_VECTORS][4]; | 
|  |  | 
|  | for(int i = 0; i < count; i++) | 
|  | { | 
|  | vector[i][0] = v[0]; | 
|  | vector[i][1] = v[1]; | 
|  | vector[i][2] = v[2]; | 
|  | vector[i][3] = v[3]; | 
|  |  | 
|  | v += 4; | 
|  | } | 
|  |  | 
|  | return applyUniform(device, location, (float*)vector); | 
|  | } | 
|  |  | 
|  | bool Program::applyUniform1uiv(Device *device, GLint location, GLsizei count, const GLuint *v) | 
|  | { | 
|  | GLuint vector[MAX_UNIFORM_VECTORS][4]; | 
|  |  | 
|  | for(int i = 0; i < count; i++) | 
|  | { | 
|  | vector[i][0] = v[i]; | 
|  | vector[i][1] = 0; | 
|  | vector[i][2] = 0; | 
|  | vector[i][3] = 0; | 
|  | } | 
|  |  | 
|  | Uniform *targetUniform = uniforms[uniformIndex[location].index]; | 
|  | if(IsSamplerUniform(targetUniform->type)) | 
|  | { | 
|  | if(targetUniform->psRegisterIndex != -1) | 
|  | { | 
|  | for(int i = 0; i < count; i++) | 
|  | { | 
|  | unsigned int samplerIndex = targetUniform->psRegisterIndex + i; | 
|  |  | 
|  | if(samplerIndex < MAX_TEXTURE_IMAGE_UNITS) | 
|  | { | 
|  | ASSERT(samplersPS[samplerIndex].active); | 
|  | samplersPS[samplerIndex].logicalTextureUnit = v[i]; | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | if(targetUniform->vsRegisterIndex != -1) | 
|  | { | 
|  | for(int i = 0; i < count; i++) | 
|  | { | 
|  | unsigned int samplerIndex = targetUniform->vsRegisterIndex + i; | 
|  |  | 
|  | if(samplerIndex < MAX_VERTEX_TEXTURE_IMAGE_UNITS) | 
|  | { | 
|  | ASSERT(samplersVS[samplerIndex].active); | 
|  | samplersVS[samplerIndex].logicalTextureUnit = v[i]; | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  | else | 
|  | { | 
|  | return applyUniform(device, location, (float*)vector); | 
|  | } | 
|  |  | 
|  | return true; | 
|  | } | 
|  |  | 
|  | bool Program::applyUniform2uiv(Device *device, GLint location, GLsizei count, const GLuint *v) | 
|  | { | 
|  | GLuint vector[MAX_UNIFORM_VECTORS][4]; | 
|  |  | 
|  | for(int i = 0; i < count; i++) | 
|  | { | 
|  | vector[i][0] = v[0]; | 
|  | vector[i][1] = v[1]; | 
|  | vector[i][2] = 0; | 
|  | vector[i][3] = 0; | 
|  |  | 
|  | v += 2; | 
|  | } | 
|  |  | 
|  | return applyUniform(device, location, (float*)vector); | 
|  | } | 
|  |  | 
|  | bool Program::applyUniform3uiv(Device *device, GLint location, GLsizei count, const GLuint *v) | 
|  | { | 
|  | GLuint vector[MAX_UNIFORM_VECTORS][4]; | 
|  |  | 
|  | for(int i = 0; i < count; i++) | 
|  | { | 
|  | vector[i][0] = v[0]; | 
|  | vector[i][1] = v[1]; | 
|  | vector[i][2] = v[2]; | 
|  | vector[i][3] = 0; | 
|  |  | 
|  | v += 3; | 
|  | } | 
|  |  | 
|  | return applyUniform(device, location, (float*)vector); | 
|  | } | 
|  |  | 
|  | bool Program::applyUniform4uiv(Device *device, GLint location, GLsizei count, const GLuint *v) | 
|  | { | 
|  | GLuint vector[MAX_UNIFORM_VECTORS][4]; | 
|  |  | 
|  | for(int i = 0; i < count; i++) | 
|  | { | 
|  | vector[i][0] = v[0]; | 
|  | vector[i][1] = v[1]; | 
|  | vector[i][2] = v[2]; | 
|  | vector[i][3] = v[3]; | 
|  |  | 
|  | v += 4; | 
|  | } | 
|  |  | 
|  | return applyUniform(device, location, (float*)vector); | 
|  | } | 
|  |  | 
|  | void Program::appendToInfoLog(const char *format, ...) | 
|  | { | 
|  | if(!format) | 
|  | { | 
|  | return; | 
|  | } | 
|  |  | 
|  | char info[1024]; | 
|  |  | 
|  | va_list vararg; | 
|  | va_start(vararg, format); | 
|  | vsnprintf(info, sizeof(info), format, vararg); | 
|  | va_end(vararg); | 
|  |  | 
|  | size_t infoLength = strlen(info); | 
|  |  | 
|  | if(!infoLog) | 
|  | { | 
|  | infoLog = new char[infoLength + 2]; | 
|  | strcpy(infoLog, info); | 
|  | strcpy(infoLog + infoLength, "\n"); | 
|  | } | 
|  | else | 
|  | { | 
|  | size_t logLength = strlen(infoLog); | 
|  | char *newLog = new char[logLength + infoLength + 2]; | 
|  | strcpy(newLog, infoLog); | 
|  | strcpy(newLog + logLength, info); | 
|  | strcpy(newLog + logLength + infoLength, "\n"); | 
|  |  | 
|  | delete[] infoLog; | 
|  | infoLog = newLog; | 
|  | } | 
|  | } | 
|  |  | 
|  | void Program::resetInfoLog() | 
|  | { | 
|  | if(infoLog) | 
|  | { | 
|  | delete[] infoLog; | 
|  | infoLog = 0; | 
|  | } | 
|  | } | 
|  |  | 
|  | // Returns the program object to an unlinked state, before re-linking, or at destruction | 
|  | void Program::unlink() | 
|  | { | 
|  | delete vertexBinary; | 
|  | vertexBinary = 0; | 
|  | delete pixelBinary; | 
|  | pixelBinary = 0; | 
|  |  | 
|  | for(int index = 0; index < MAX_VERTEX_ATTRIBS; index++) | 
|  | { | 
|  | linkedAttribute[index].name.clear(); | 
|  | attributeStream[index] = -1; | 
|  | } | 
|  |  | 
|  | for(int index = 0; index < MAX_TEXTURE_IMAGE_UNITS; index++) | 
|  | { | 
|  | samplersPS[index].active = false; | 
|  | } | 
|  |  | 
|  | for(int index = 0; index < MAX_VERTEX_TEXTURE_IMAGE_UNITS; index++) | 
|  | { | 
|  | samplersVS[index].active = false; | 
|  | } | 
|  |  | 
|  | while(!uniforms.empty()) | 
|  | { | 
|  | delete uniforms.back(); | 
|  | uniforms.pop_back(); | 
|  | } | 
|  |  | 
|  | while(!uniformBlocks.empty()) | 
|  | { | 
|  | delete uniformBlocks.back(); | 
|  | uniformBlocks.pop_back(); | 
|  | } | 
|  |  | 
|  | uniformIndex.clear(); | 
|  | transformFeedbackLinkedVaryings.clear(); | 
|  |  | 
|  | delete[] infoLog; | 
|  | infoLog = 0; | 
|  |  | 
|  | linked = false; | 
|  | } | 
|  |  | 
|  | bool Program::isLinked() const | 
|  | { | 
|  | return linked; | 
|  | } | 
|  |  | 
|  | bool Program::isValidated() const | 
|  | { | 
|  | return validated; | 
|  | } | 
|  |  | 
|  | GLint Program::getBinaryLength() const | 
|  | { | 
|  | UNIMPLEMENTED(); | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | void Program::release() | 
|  | { | 
|  | referenceCount--; | 
|  |  | 
|  | if(referenceCount == 0 && orphaned) | 
|  | { | 
|  | resourceManager->deleteProgram(handle); | 
|  | } | 
|  | } | 
|  |  | 
|  | void Program::addRef() | 
|  | { | 
|  | referenceCount++; | 
|  | } | 
|  |  | 
|  | unsigned int Program::getRefCount() const | 
|  | { | 
|  | return referenceCount; | 
|  | } | 
|  |  | 
|  | unsigned int Program::getSerial() const | 
|  | { | 
|  | return serial; | 
|  | } | 
|  |  | 
|  | unsigned int Program::issueSerial() | 
|  | { | 
|  | return currentSerial++; | 
|  | } | 
|  |  | 
|  | size_t Program::getInfoLogLength() const | 
|  | { | 
|  | if(!infoLog) | 
|  | { | 
|  | return 0; | 
|  | } | 
|  | else | 
|  | { | 
|  | return strlen(infoLog) + 1; | 
|  | } | 
|  | } | 
|  |  | 
|  | void Program::getInfoLog(GLsizei bufSize, GLsizei *length, char *buffer) | 
|  | { | 
|  | int index = 0; | 
|  |  | 
|  | if(bufSize > 0) | 
|  | { | 
|  | if(infoLog) | 
|  | { | 
|  | index = std::min(bufSize - 1, (int)strlen(infoLog)); | 
|  | memcpy(buffer, infoLog, index); | 
|  | } | 
|  |  | 
|  | buffer[index] = '\0'; | 
|  | } | 
|  |  | 
|  | if(length) | 
|  | { | 
|  | *length = index; | 
|  | } | 
|  | } | 
|  |  | 
|  | void Program::getAttachedShaders(GLsizei maxCount, GLsizei *count, GLuint *shaders) | 
|  | { | 
|  | int total = 0; | 
|  |  | 
|  | if(vertexShader && (total < maxCount)) | 
|  | { | 
|  | shaders[total++] = vertexShader->getName(); | 
|  | } | 
|  |  | 
|  | if(fragmentShader && (total < maxCount)) | 
|  | { | 
|  | shaders[total++] = fragmentShader->getName(); | 
|  | } | 
|  |  | 
|  | if(count) | 
|  | { | 
|  | *count = total; | 
|  | } | 
|  | } | 
|  |  | 
|  | void Program::getActiveAttribute(GLuint index, GLsizei bufsize, GLsizei *length, GLint *size, GLenum *type, GLchar *name) const | 
|  | { | 
|  | // Skip over inactive attributes | 
|  | unsigned int activeAttribute = 0; | 
|  | unsigned int attribute; | 
|  | for(attribute = 0; attribute < MAX_VERTEX_ATTRIBS; attribute++) | 
|  | { | 
|  | if(linkedAttribute[attribute].name.empty()) | 
|  | { | 
|  | continue; | 
|  | } | 
|  |  | 
|  | if(activeAttribute == index) | 
|  | { | 
|  | break; | 
|  | } | 
|  |  | 
|  | activeAttribute++; | 
|  | } | 
|  |  | 
|  | if(bufsize > 0) | 
|  | { | 
|  | const char *string = linkedAttribute[attribute].name.c_str(); | 
|  |  | 
|  | strncpy(name, string, bufsize); | 
|  | name[bufsize - 1] = '\0'; | 
|  |  | 
|  | if(length) | 
|  | { | 
|  | *length = static_cast<GLsizei>(strlen(name)); | 
|  | } | 
|  | } | 
|  |  | 
|  | *size = 1;   // Always a single 'type' instance | 
|  |  | 
|  | *type = linkedAttribute[attribute].type; | 
|  | } | 
|  |  | 
|  | size_t Program::getActiveAttributeCount() const | 
|  | { | 
|  | int count = 0; | 
|  |  | 
|  | for(int attributeIndex = 0; attributeIndex < MAX_VERTEX_ATTRIBS; attributeIndex++) | 
|  | { | 
|  | if(!linkedAttribute[attributeIndex].name.empty()) | 
|  | { | 
|  | count++; | 
|  | } | 
|  | } | 
|  |  | 
|  | return count; | 
|  | } | 
|  |  | 
|  | GLint Program::getActiveAttributeMaxLength() const | 
|  | { | 
|  | int maxLength = 0; | 
|  |  | 
|  | for(int attributeIndex = 0; attributeIndex < MAX_VERTEX_ATTRIBS; attributeIndex++) | 
|  | { | 
|  | if(!linkedAttribute[attributeIndex].name.empty()) | 
|  | { | 
|  | maxLength = std::max((int)(linkedAttribute[attributeIndex].name.length() + 1), maxLength); | 
|  | } | 
|  | } | 
|  |  | 
|  | return maxLength; | 
|  | } | 
|  |  | 
|  | void Program::getActiveUniform(GLuint index, GLsizei bufsize, GLsizei *length, GLint *size, GLenum *type, GLchar *name) const | 
|  | { | 
|  | if(bufsize > 0) | 
|  | { | 
|  | std::string string = uniforms[index]->name; | 
|  |  | 
|  | if(uniforms[index]->isArray()) | 
|  | { | 
|  | string += "[0]"; | 
|  | } | 
|  |  | 
|  | strncpy(name, string.c_str(), bufsize); | 
|  | name[bufsize - 1] = '\0'; | 
|  |  | 
|  | if(length) | 
|  | { | 
|  | *length = static_cast<GLsizei>(strlen(name)); | 
|  | } | 
|  | } | 
|  |  | 
|  | *size = uniforms[index]->size(); | 
|  |  | 
|  | *type = uniforms[index]->type; | 
|  | } | 
|  |  | 
|  | size_t Program::getActiveUniformCount() const | 
|  | { | 
|  | return uniforms.size(); | 
|  | } | 
|  |  | 
|  | GLint Program::getActiveUniformMaxLength() const | 
|  | { | 
|  | int maxLength = 0; | 
|  |  | 
|  | size_t numUniforms = uniforms.size(); | 
|  | for(size_t uniformIndex = 0; uniformIndex < numUniforms; uniformIndex++) | 
|  | { | 
|  | if(!uniforms[uniformIndex]->name.empty()) | 
|  | { | 
|  | int length = (int)(uniforms[uniformIndex]->name.length() + 1); | 
|  | if(uniforms[uniformIndex]->isArray()) | 
|  | { | 
|  | length += 3;  // Counting in "[0]". | 
|  | } | 
|  | maxLength = std::max(length, maxLength); | 
|  | } | 
|  | } | 
|  |  | 
|  | return maxLength; | 
|  | } | 
|  |  | 
|  | GLint Program::getActiveUniformi(GLuint index, GLenum pname) const | 
|  | { | 
|  | const Uniform& uniform = *uniforms[index]; | 
|  | switch(pname) | 
|  | { | 
|  | case GL_UNIFORM_TYPE:         return static_cast<GLint>(uniform.type); | 
|  | case GL_UNIFORM_SIZE:         return static_cast<GLint>(uniform.size()); | 
|  | case GL_UNIFORM_NAME_LENGTH:  return static_cast<GLint>(uniform.name.size() + 1 + (uniform.isArray() ? 3 : 0)); | 
|  | case GL_UNIFORM_BLOCK_INDEX:  return uniform.blockInfo.index; | 
|  | case GL_UNIFORM_OFFSET:       return uniform.blockInfo.offset; | 
|  | case GL_UNIFORM_ARRAY_STRIDE: return uniform.blockInfo.arrayStride; | 
|  | case GL_UNIFORM_MATRIX_STRIDE: return uniform.blockInfo.matrixStride; | 
|  | case GL_UNIFORM_IS_ROW_MAJOR: return static_cast<GLint>(uniform.blockInfo.isRowMajorMatrix); | 
|  | default: | 
|  | UNREACHABLE(pname); | 
|  | break; | 
|  | } | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | void Program::getActiveUniformBlockName(GLuint index, GLsizei bufSize, GLsizei *length, GLchar *name) const | 
|  | { | 
|  | ASSERT(index < getActiveUniformBlockCount()); | 
|  |  | 
|  | const UniformBlock &uniformBlock = *uniformBlocks[index]; | 
|  |  | 
|  | if(bufSize > 0) | 
|  | { | 
|  | std::string string = uniformBlock.name; | 
|  |  | 
|  | if(uniformBlock.isArrayElement()) | 
|  | { | 
|  | std::ostringstream elementIndex; | 
|  | elementIndex << uniformBlock.elementIndex; | 
|  | string += "[" + elementIndex.str()  + "]"; | 
|  | } | 
|  |  | 
|  | strncpy(name, string.c_str(), bufSize); | 
|  | name[bufSize - 1] = '\0'; | 
|  |  | 
|  | if(length) | 
|  | { | 
|  | *length = static_cast<GLsizei>(strlen(name)); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | size_t Program::getActiveUniformBlockCount() const | 
|  | { | 
|  | return uniformBlocks.size(); | 
|  | } | 
|  |  | 
|  | GLint Program::getActiveUniformBlockMaxLength() const | 
|  | { | 
|  | GLint maxLength = 0; | 
|  |  | 
|  | if(isLinked()) | 
|  | { | 
|  | size_t numUniformBlocks = getActiveUniformBlockCount(); | 
|  | for(size_t uniformBlockIndex = 0; uniformBlockIndex < numUniformBlocks; uniformBlockIndex++) | 
|  | { | 
|  | const UniformBlock &uniformBlock = *uniformBlocks[uniformBlockIndex]; | 
|  | if(!uniformBlock.name.empty()) | 
|  | { | 
|  | GLint length = static_cast<GLint>(uniformBlock.name.length() + 1); | 
|  |  | 
|  | // Counting in "[0]". | 
|  | const GLint arrayLength = (uniformBlock.isArrayElement() ? 3 : 0); | 
|  |  | 
|  | maxLength = std::max(length + arrayLength, maxLength); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | return maxLength; | 
|  | } | 
|  |  | 
|  | void Program::setTransformFeedbackVaryings(GLsizei count, const GLchar *const *varyings, GLenum bufferMode) | 
|  | { | 
|  | transformFeedbackVaryings.resize(count); | 
|  | for(GLsizei i = 0; i < count; i++) | 
|  | { | 
|  | transformFeedbackVaryings[i] = varyings[i]; | 
|  | } | 
|  |  | 
|  | transformFeedbackBufferMode = bufferMode; | 
|  | } | 
|  |  | 
|  | void Program::getTransformFeedbackVarying(GLuint index, GLsizei bufSize, GLsizei *length, GLsizei *size, GLenum *type, GLchar *name) const | 
|  | { | 
|  | if(linked) | 
|  | { | 
|  | ASSERT(index < transformFeedbackLinkedVaryings.size()); | 
|  | const LinkedVarying &varying = transformFeedbackLinkedVaryings[index]; | 
|  | GLsizei lastNameIdx = std::min(bufSize - 1, static_cast<GLsizei>(varying.name.length())); | 
|  | if(length) | 
|  | { | 
|  | *length = lastNameIdx; | 
|  | } | 
|  | if(size) | 
|  | { | 
|  | *size = varying.size; | 
|  | } | 
|  | if(type) | 
|  | { | 
|  | *type = varying.type; | 
|  | } | 
|  | if(name) | 
|  | { | 
|  | memcpy(name, varying.name.c_str(), lastNameIdx); | 
|  | name[lastNameIdx] = '\0'; | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | GLsizei Program::getTransformFeedbackVaryingCount() const | 
|  | { | 
|  | if(linked) | 
|  | { | 
|  | return static_cast<GLsizei>(transformFeedbackLinkedVaryings.size()); | 
|  | } | 
|  | else | 
|  | { | 
|  | return 0; | 
|  | } | 
|  | } | 
|  |  | 
|  | GLsizei Program::getTransformFeedbackVaryingMaxLength() const | 
|  | { | 
|  | if(linked) | 
|  | { | 
|  | GLsizei maxSize = 0; | 
|  | for(size_t i = 0; i < transformFeedbackLinkedVaryings.size(); i++) | 
|  | { | 
|  | const LinkedVarying &varying = transformFeedbackLinkedVaryings[i]; | 
|  | maxSize = std::max(maxSize, static_cast<GLsizei>(varying.name.length() + 1)); | 
|  | } | 
|  |  | 
|  | return maxSize; | 
|  | } | 
|  | else | 
|  | { | 
|  | return 0; | 
|  | } | 
|  | } | 
|  |  | 
|  | GLenum Program::getTransformFeedbackBufferMode() const | 
|  | { | 
|  | return transformFeedbackBufferMode; | 
|  | } | 
|  |  | 
|  | void Program::flagForDeletion() | 
|  | { | 
|  | orphaned = true; | 
|  | } | 
|  |  | 
|  | bool Program::isFlaggedForDeletion() const | 
|  | { | 
|  | return orphaned; | 
|  | } | 
|  |  | 
|  | void Program::validate(Device* device) | 
|  | { | 
|  | resetInfoLog(); | 
|  |  | 
|  | if(!isLinked()) | 
|  | { | 
|  | appendToInfoLog("Program has not been successfully linked."); | 
|  | validated = false; | 
|  | } | 
|  | else | 
|  | { | 
|  | applyUniforms(device); | 
|  | if(!validateSamplers(true)) | 
|  | { | 
|  | validated = false; | 
|  | } | 
|  | else | 
|  | { | 
|  | validated = true; | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | bool Program::validateSamplers(bool logErrors) | 
|  | { | 
|  | // if any two active samplers in a program are of different types, but refer to the same | 
|  | // texture image unit, and this is the current program, then ValidateProgram will fail, and | 
|  | // DrawArrays and DrawElements will issue the INVALID_OPERATION error. | 
|  |  | 
|  | TextureType textureUnitType[MAX_COMBINED_TEXTURE_IMAGE_UNITS]; | 
|  |  | 
|  | for(unsigned int i = 0; i < MAX_COMBINED_TEXTURE_IMAGE_UNITS; i++) | 
|  | { | 
|  | textureUnitType[i] = TEXTURE_UNKNOWN; | 
|  | } | 
|  |  | 
|  | for(unsigned int i = 0; i < MAX_TEXTURE_IMAGE_UNITS; i++) | 
|  | { | 
|  | if(samplersPS[i].active) | 
|  | { | 
|  | unsigned int unit = samplersPS[i].logicalTextureUnit; | 
|  |  | 
|  | if(unit >= MAX_COMBINED_TEXTURE_IMAGE_UNITS) | 
|  | { | 
|  | if(logErrors) | 
|  | { | 
|  | appendToInfoLog("Sampler uniform (%d) exceeds MAX_COMBINED_TEXTURE_IMAGE_UNITS (%d)", unit, MAX_COMBINED_TEXTURE_IMAGE_UNITS); | 
|  | } | 
|  |  | 
|  | return false; | 
|  | } | 
|  |  | 
|  | if(textureUnitType[unit] != TEXTURE_UNKNOWN) | 
|  | { | 
|  | if(samplersPS[i].textureType != textureUnitType[unit]) | 
|  | { | 
|  | if(logErrors) | 
|  | { | 
|  | appendToInfoLog("Samplers of conflicting types refer to the same texture image unit (%d).", unit); | 
|  | } | 
|  |  | 
|  | return false; | 
|  | } | 
|  | } | 
|  | else | 
|  | { | 
|  | textureUnitType[unit] = samplersPS[i].textureType; | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | for(unsigned int i = 0; i < MAX_VERTEX_TEXTURE_IMAGE_UNITS; i++) | 
|  | { | 
|  | if(samplersVS[i].active) | 
|  | { | 
|  | unsigned int unit = samplersVS[i].logicalTextureUnit; | 
|  |  | 
|  | if(unit >= MAX_COMBINED_TEXTURE_IMAGE_UNITS) | 
|  | { | 
|  | if(logErrors) | 
|  | { | 
|  | appendToInfoLog("Sampler uniform (%d) exceeds MAX_COMBINED_TEXTURE_IMAGE_UNITS (%d)", unit, MAX_COMBINED_TEXTURE_IMAGE_UNITS); | 
|  | } | 
|  |  | 
|  | return false; | 
|  | } | 
|  |  | 
|  | if(textureUnitType[unit] != TEXTURE_UNKNOWN) | 
|  | { | 
|  | if(samplersVS[i].textureType != textureUnitType[unit]) | 
|  | { | 
|  | if(logErrors) | 
|  | { | 
|  | appendToInfoLog("Samplers of conflicting types refer to the same texture image unit (%d).", unit); | 
|  | } | 
|  |  | 
|  | return false; | 
|  | } | 
|  | } | 
|  | else | 
|  | { | 
|  | textureUnitType[unit] = samplersVS[i].textureType; | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | return true; | 
|  | } | 
|  | } |