| // 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) |
| { |
| device = getDevice(); |
| |
| 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() |
| { |
| 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(location, size, b); break; |
| case GL_BOOL_VEC2: applyUniform2bv(location, size, b); break; |
| case GL_BOOL_VEC3: applyUniform3bv(location, size, b); break; |
| case GL_BOOL_VEC4: applyUniform4bv(location, size, b); break; |
| case GL_FLOAT: applyUniform1fv(location, size, f); break; |
| case GL_FLOAT_VEC2: applyUniform2fv(location, size, f); break; |
| case GL_FLOAT_VEC3: applyUniform3fv(location, size, f); break; |
| case GL_FLOAT_VEC4: applyUniform4fv(location, size, f); break; |
| case GL_FLOAT_MAT2: applyUniformMatrix2fv(location, size, f); break; |
| case GL_FLOAT_MAT2x3: applyUniformMatrix2x3fv(location, size, f); break; |
| case GL_FLOAT_MAT2x4: applyUniformMatrix2x4fv(location, size, f); break; |
| case GL_FLOAT_MAT3x2: applyUniformMatrix3x2fv(location, size, f); break; |
| case GL_FLOAT_MAT3: applyUniformMatrix3fv(location, size, f); break; |
| case GL_FLOAT_MAT3x4: applyUniformMatrix3x4fv(location, size, f); break; |
| case GL_FLOAT_MAT4x2: applyUniformMatrix4x2fv(location, size, f); break; |
| case GL_FLOAT_MAT4x3: applyUniformMatrix4x3fv(location, size, f); break; |
| case GL_FLOAT_MAT4: applyUniformMatrix4fv(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(location, size, i); break; |
| case GL_INT_VEC2: applyUniform2iv(location, size, i); break; |
| case GL_INT_VEC3: applyUniform3iv(location, size, i); break; |
| case GL_INT_VEC4: applyUniform4iv(location, size, i); break; |
| case GL_UNSIGNED_INT: applyUniform1uiv(location, size, ui); break; |
| case GL_UNSIGNED_INT_VEC2: applyUniform2uiv(location, size, ui); break; |
| case GL_UNSIGNED_INT_VEC3: applyUniform3uiv(location, size, ui); break; |
| case GL_UNSIGNED_INT_VEC4: applyUniform4uiv(location, size, ui); break; |
| default: |
| UNREACHABLE(targetUniform->type); |
| } |
| |
| targetUniform->dirty = false; |
| } |
| } |
| } |
| |
| void Program::applyUniformBuffers(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(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(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(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(location, (float*)vector); |
| } |
| |
| bool Program::applyUniform2bv(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(location, (float*)vector); |
| } |
| |
| bool Program::applyUniform3bv(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(location, (float*)vector); |
| } |
| |
| bool Program::applyUniform4bv(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(location, (float*)vector); |
| } |
| |
| bool Program::applyUniform1fv(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(location, (float*)vector); |
| } |
| |
| bool Program::applyUniform2fv(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(location, (float*)vector); |
| } |
| |
| bool Program::applyUniform3fv(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(location, (float*)vector); |
| } |
| |
| bool Program::applyUniform4fv(GLint location, GLsizei count, const GLfloat *v) |
| { |
| return applyUniform(location, (float*)v); |
| } |
| |
| bool Program::applyUniformMatrix2fv(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(location, (float*)matrix); |
| } |
| |
| bool Program::applyUniformMatrix2x3fv(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(location, (float*)matrix); |
| } |
| |
| bool Program::applyUniformMatrix2x4fv(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(location, (float*)matrix); |
| } |
| |
| bool Program::applyUniformMatrix3fv(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(location, (float*)matrix); |
| } |
| |
| bool Program::applyUniformMatrix3x2fv(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(location, (float*)matrix); |
| } |
| |
| bool Program::applyUniformMatrix3x4fv(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(location, (float*)matrix); |
| } |
| |
| bool Program::applyUniformMatrix4fv(GLint location, GLsizei count, const GLfloat *value) |
| { |
| return applyUniform(location, (float*)value); |
| } |
| |
| bool Program::applyUniformMatrix4x2fv(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(location, (float*)matrix); |
| } |
| |
| bool Program::applyUniformMatrix4x3fv(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(location, (float*)matrix); |
| } |
| |
| bool Program::applyUniform1iv(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(location, (float*)vector); |
| } |
| |
| return true; |
| } |
| |
| bool Program::applyUniform2iv(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(location, (float*)vector); |
| } |
| |
| bool Program::applyUniform3iv(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(location, (float*)vector); |
| } |
| |
| bool Program::applyUniform4iv(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(location, (float*)vector); |
| } |
| |
| bool Program::applyUniform1uiv(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(location, (float*)vector); |
| } |
| |
| return true; |
| } |
| |
| bool Program::applyUniform2uiv(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(location, (float*)vector); |
| } |
| |
| bool Program::applyUniform3uiv(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(location, (float*)vector); |
| } |
| |
| bool Program::applyUniform4uiv(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(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() |
| { |
| resetInfoLog(); |
| |
| if(!isLinked()) |
| { |
| appendToInfoLog("Program has not been successfully linked."); |
| validated = false; |
| } |
| else |
| { |
| applyUniforms(); |
| 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; |
| } |
| } |