| // |
| // Copyright (c) 2002-2010 The ANGLE Project Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| // |
| |
| // Shader.cpp: Implements the gl::Shader class and its derived classes |
| // VertexShader and FragmentShader. Implements GL shader objects and related |
| // functionality. [OpenGL ES 2.0.24] section 2.10 page 24 and section 3.8 page 84. |
| |
| #include "libGLESv2/Shader.h" |
| |
| #include <string> |
| |
| #include "GLSLANG/Shaderlang.h" |
| #include "libGLESv2/main.h" |
| #include "libGLESv2/utilities.h" |
| |
| namespace gl |
| { |
| void *Shader::mFragmentCompiler = NULL; |
| void *Shader::mVertexCompiler = NULL; |
| |
| Shader::Shader(ResourceManager *manager, GLuint handle) : mHandle(handle), mResourceManager(manager) |
| { |
| mSource = NULL; |
| mHlsl = NULL; |
| mInfoLog = NULL; |
| |
| // Perform a one-time initialization of the shader compiler (or after being destructed by releaseCompiler) |
| if (!mFragmentCompiler) |
| { |
| int result = ShInitialize(); |
| |
| if (result) |
| { |
| ShBuiltInResources resources; |
| ShInitBuiltInResources(&resources); |
| Context *context = getContext(); |
| |
| resources.MaxVertexAttribs = MAX_VERTEX_ATTRIBS; |
| resources.MaxVertexUniformVectors = MAX_VERTEX_UNIFORM_VECTORS; |
| resources.MaxVaryingVectors = context->getMaximumVaryingVectors(); |
| resources.MaxVertexTextureImageUnits = MAX_VERTEX_TEXTURE_IMAGE_UNITS; |
| resources.MaxCombinedTextureImageUnits = MAX_COMBINED_TEXTURE_IMAGE_UNITS; |
| resources.MaxTextureImageUnits = MAX_TEXTURE_IMAGE_UNITS; |
| resources.MaxFragmentUniformVectors = context->getMaximumFragmentUniformVectors(); |
| resources.MaxDrawBuffers = MAX_DRAW_BUFFERS; |
| resources.OES_standard_derivatives = 1; |
| |
| mFragmentCompiler = ShConstructCompiler(SH_FRAGMENT_SHADER, SH_GLES2_SPEC, &resources); |
| mVertexCompiler = ShConstructCompiler(SH_VERTEX_SHADER, SH_GLES2_SPEC, &resources); |
| } |
| } |
| |
| mRefCount = 0; |
| mDeleteStatus = false; |
| } |
| |
| Shader::~Shader() |
| { |
| delete[] mSource; |
| delete[] mHlsl; |
| delete[] mInfoLog; |
| } |
| |
| GLuint Shader::getHandle() const |
| { |
| return mHandle; |
| } |
| |
| void Shader::setSource(GLsizei count, const char **string, const GLint *length) |
| { |
| delete[] mSource; |
| int totalLength = 0; |
| |
| for (int i = 0; i < count; i++) |
| { |
| if (length && length[i] >= 0) |
| { |
| totalLength += length[i]; |
| } |
| else |
| { |
| totalLength += (int)strlen(string[i]); |
| } |
| } |
| |
| mSource = new char[totalLength + 1]; |
| char *code = mSource; |
| |
| for (int i = 0; i < count; i++) |
| { |
| int stringLength; |
| |
| if (length && length[i] >= 0) |
| { |
| stringLength = length[i]; |
| } |
| else |
| { |
| stringLength = (int)strlen(string[i]); |
| } |
| |
| strncpy(code, string[i], stringLength); |
| code += stringLength; |
| } |
| |
| mSource[totalLength] = '\0'; |
| } |
| |
| int Shader::getInfoLogLength() const |
| { |
| if (!mInfoLog) |
| { |
| return 0; |
| } |
| else |
| { |
| return strlen(mInfoLog) + 1; |
| } |
| } |
| |
| void Shader::getInfoLog(GLsizei bufSize, GLsizei *length, char *infoLog) |
| { |
| int index = 0; |
| |
| if (mInfoLog) |
| { |
| while (index < bufSize - 1 && index < (int)strlen(mInfoLog)) |
| { |
| infoLog[index] = mInfoLog[index]; |
| index++; |
| } |
| } |
| |
| if (bufSize) |
| { |
| infoLog[index] = '\0'; |
| } |
| |
| if (length) |
| { |
| *length = index; |
| } |
| } |
| |
| int Shader::getSourceLength() const |
| { |
| if (!mSource) |
| { |
| return 0; |
| } |
| else |
| { |
| return strlen(mSource) + 1; |
| } |
| } |
| |
| void Shader::getSource(GLsizei bufSize, GLsizei *length, char *source) |
| { |
| int index = 0; |
| |
| if (mSource) |
| { |
| while (index < bufSize - 1 && index < (int)strlen(mSource)) |
| { |
| source[index] = mSource[index]; |
| index++; |
| } |
| } |
| |
| if (bufSize) |
| { |
| source[index] = '\0'; |
| } |
| |
| if (length) |
| { |
| *length = index; |
| } |
| } |
| |
| bool Shader::isCompiled() |
| { |
| return mHlsl != NULL; |
| } |
| |
| const char *Shader::getHLSL() |
| { |
| return mHlsl; |
| } |
| |
| void Shader::addRef() |
| { |
| mRefCount++; |
| } |
| |
| void Shader::release() |
| { |
| mRefCount--; |
| |
| if (mRefCount == 0 && mDeleteStatus) |
| { |
| mResourceManager->deleteShader(mHandle); |
| } |
| } |
| |
| unsigned int Shader::getRefCount() const |
| { |
| return mRefCount; |
| } |
| |
| bool Shader::isFlaggedForDeletion() const |
| { |
| return mDeleteStatus; |
| } |
| |
| void Shader::flagForDeletion() |
| { |
| mDeleteStatus = true; |
| } |
| |
| void Shader::releaseCompiler() |
| { |
| ShDestruct(mFragmentCompiler); |
| ShDestruct(mVertexCompiler); |
| |
| mFragmentCompiler = NULL; |
| mVertexCompiler = NULL; |
| |
| ShFinalize(); |
| } |
| |
| void Shader::parseVaryings() |
| { |
| if (mHlsl) |
| { |
| const char *input = strstr(mHlsl, "// Varyings") + 12; |
| |
| while(true) |
| { |
| char varyingType[256]; |
| char varyingName[256]; |
| |
| int matches = sscanf(input, "static %255s %255s", varyingType, varyingName); |
| |
| if (matches != 2) |
| { |
| break; |
| } |
| |
| char *array = strstr(varyingName, "["); |
| int size = 1; |
| |
| if (array) |
| { |
| size = atoi(array + 1); |
| *array = '\0'; |
| } |
| |
| varyings.push_back(Varying(parseType(varyingType), varyingName, size, array != NULL)); |
| |
| input = strstr(input, ";") + 2; |
| } |
| |
| mUsesFragCoord = strstr(mHlsl, "GL_USES_FRAG_COORD") != NULL; |
| mUsesFrontFacing = strstr(mHlsl, "GL_USES_FRONT_FACING") != NULL; |
| mUsesPointSize = strstr(mHlsl, "GL_USES_POINT_SIZE") != NULL; |
| mUsesPointCoord = strstr(mHlsl, "GL_USES_POINT_COORD") != NULL; |
| } |
| } |
| |
| void Shader::compileToHLSL(void *compiler) |
| { |
| if (isCompiled() || !mSource) |
| { |
| return; |
| } |
| |
| TRACE("\n%s", mSource); |
| |
| delete[] mInfoLog; |
| mInfoLog = NULL; |
| |
| int result = ShCompile(compiler, &mSource, 1, SH_OBJECT_CODE); |
| |
| if (result) |
| { |
| int objCodeLen = 0; |
| ShGetInfo(compiler, SH_OBJECT_CODE_LENGTH, &objCodeLen); |
| mHlsl = new char[objCodeLen]; |
| ShGetObjectCode(compiler, mHlsl); |
| |
| TRACE("\n%s", mHlsl); |
| } |
| else |
| { |
| int infoLogLen = 0; |
| ShGetInfo(compiler, SH_INFO_LOG_LENGTH, &infoLogLen); |
| mInfoLog = new char[infoLogLen]; |
| ShGetInfoLog(compiler, mInfoLog); |
| |
| TRACE("\n%s", mInfoLog); |
| } |
| } |
| |
| GLenum Shader::parseType(const std::string &type) |
| { |
| if (type == "float") |
| { |
| return GL_FLOAT; |
| } |
| else if (type == "float2") |
| { |
| return GL_FLOAT_VEC2; |
| } |
| else if (type == "float3") |
| { |
| return GL_FLOAT_VEC3; |
| } |
| else if (type == "float4") |
| { |
| return GL_FLOAT_VEC4; |
| } |
| else if (type == "float2x2") |
| { |
| return GL_FLOAT_MAT2; |
| } |
| else if (type == "float3x3") |
| { |
| return GL_FLOAT_MAT3; |
| } |
| else if (type == "float4x4") |
| { |
| return GL_FLOAT_MAT4; |
| } |
| else UNREACHABLE(); |
| |
| return GL_NONE; |
| } |
| |
| // true if varying x has a higher priority in packing than y |
| bool Shader::compareVarying(const Varying &x, const Varying &y) |
| { |
| if(x.type == y.type) |
| { |
| return x.size > y.size; |
| } |
| |
| switch (x.type) |
| { |
| case GL_FLOAT_MAT4: return true; |
| case GL_FLOAT_MAT2: |
| switch(y.type) |
| { |
| case GL_FLOAT_MAT4: return false; |
| case GL_FLOAT_MAT2: return true; |
| case GL_FLOAT_VEC4: return true; |
| case GL_FLOAT_MAT3: return true; |
| case GL_FLOAT_VEC3: return true; |
| case GL_FLOAT_VEC2: return true; |
| case GL_FLOAT: return true; |
| default: UNREACHABLE(); |
| } |
| break; |
| case GL_FLOAT_VEC4: |
| switch(y.type) |
| { |
| case GL_FLOAT_MAT4: return false; |
| case GL_FLOAT_MAT2: return false; |
| case GL_FLOAT_VEC4: return true; |
| case GL_FLOAT_MAT3: return true; |
| case GL_FLOAT_VEC3: return true; |
| case GL_FLOAT_VEC2: return true; |
| case GL_FLOAT: return true; |
| default: UNREACHABLE(); |
| } |
| break; |
| case GL_FLOAT_MAT3: |
| switch(y.type) |
| { |
| case GL_FLOAT_MAT4: return false; |
| case GL_FLOAT_MAT2: return false; |
| case GL_FLOAT_VEC4: return false; |
| case GL_FLOAT_MAT3: return true; |
| case GL_FLOAT_VEC3: return true; |
| case GL_FLOAT_VEC2: return true; |
| case GL_FLOAT: return true; |
| default: UNREACHABLE(); |
| } |
| break; |
| case GL_FLOAT_VEC3: |
| switch(y.type) |
| { |
| case GL_FLOAT_MAT4: return false; |
| case GL_FLOAT_MAT2: return false; |
| case GL_FLOAT_VEC4: return false; |
| case GL_FLOAT_MAT3: return false; |
| case GL_FLOAT_VEC3: return true; |
| case GL_FLOAT_VEC2: return true; |
| case GL_FLOAT: return true; |
| default: UNREACHABLE(); |
| } |
| break; |
| case GL_FLOAT_VEC2: |
| switch(y.type) |
| { |
| case GL_FLOAT_MAT4: return false; |
| case GL_FLOAT_MAT2: return false; |
| case GL_FLOAT_VEC4: return false; |
| case GL_FLOAT_MAT3: return false; |
| case GL_FLOAT_VEC3: return false; |
| case GL_FLOAT_VEC2: return true; |
| case GL_FLOAT: return true; |
| default: UNREACHABLE(); |
| } |
| break; |
| case GL_FLOAT: return false; |
| default: UNREACHABLE(); |
| } |
| |
| return false; |
| } |
| |
| VertexShader::VertexShader(ResourceManager *manager, GLuint handle) : Shader(manager, handle) |
| { |
| } |
| |
| VertexShader::~VertexShader() |
| { |
| } |
| |
| GLenum VertexShader::getType() |
| { |
| return GL_VERTEX_SHADER; |
| } |
| |
| void VertexShader::compile() |
| { |
| compileToHLSL(mVertexCompiler); |
| parseAttributes(); |
| parseVaryings(); |
| } |
| |
| int VertexShader::getSemanticIndex(const std::string &attributeName) |
| { |
| if (!attributeName.empty()) |
| { |
| int semanticIndex = 0; |
| for (AttributeArray::iterator attribute = mAttributes.begin(); attribute != mAttributes.end(); attribute++) |
| { |
| if (attribute->name == attributeName) |
| { |
| return semanticIndex; |
| } |
| |
| semanticIndex += VariableRowCount(attribute->type); |
| } |
| } |
| |
| return -1; |
| } |
| |
| void VertexShader::parseAttributes() |
| { |
| if (mHlsl) |
| { |
| const char *input = strstr(mHlsl, "// Attributes") + 14; |
| |
| while(true) |
| { |
| char attributeType[256]; |
| char attributeName[256]; |
| |
| int matches = sscanf(input, "static %255s _%255s", attributeType, attributeName); |
| |
| if (matches != 2) |
| { |
| break; |
| } |
| |
| mAttributes.push_back(Attribute(parseType(attributeType), attributeName)); |
| |
| input = strstr(input, ";") + 2; |
| } |
| } |
| } |
| |
| FragmentShader::FragmentShader(ResourceManager *manager, GLuint handle) : Shader(manager, handle) |
| { |
| } |
| |
| FragmentShader::~FragmentShader() |
| { |
| } |
| |
| GLenum FragmentShader::getType() |
| { |
| return GL_FRAGMENT_SHADER; |
| } |
| |
| void FragmentShader::compile() |
| { |
| compileToHLSL(mFragmentCompiler); |
| parseVaryings(); |
| varyings.sort(compareVarying); |
| } |
| } |