Edit

kc3-lang/angle/src/libGLESv2/Shader.cpp

Branch :

  • Show log

    Commit

  • Author : zmo@google.com
    Date : 2011-10-03 21:45:23
    Hash : a574f78f
    Message : Add an extension for querying translated HLSL shader source. Review URL: http://codereview.appspot.com/5149046 git-svn-id: https://angleproject.googlecode.com/svn/trunk@780 736b8ea6-26fd-11df-bfd4-992fa37f6226

  • src/libGLESv2/Shader.cpp
  • //
    // 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 = context->getMaximumVertexTextureImageUnits();
                resources.MaxCombinedTextureImageUnits = context->getMaximumCombinedTextureImageUnits();
                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, SH_HLSL_OUTPUT, &resources);
                mVertexCompiler = ShConstructCompiler(SH_VERTEX_SHADER, SH_GLES2_SPEC, SH_HLSL_OUTPUT, &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;
        }
    }
    
    int Shader::getTranslatedSourceLength() const
    {
        if (!mHlsl)
        {
            return 0;
        }
        else
        {
           return strlen(mHlsl) + 1;
        }
    }
    
    void Shader::getSourceImpl(char *source, GLsizei bufSize, GLsizei *length, char *buffer)
    {
        int index = 0;
    
        if (source)
        {
            while (index < bufSize - 1 && index < (int)strlen(source))
            {
                buffer[index] = source[index];
                index++;
            }
        }
    
        if (bufSize)
        {
            buffer[index] = '\0';
        }
    
        if (length)
        {
            *length = index;
        }
    }
    
    void Shader::getSource(GLsizei bufSize, GLsizei *length, char *buffer)
    {
        getSourceImpl(mSource, bufSize, length, buffer);
    }
    
    void Shader::getTranslatedSource(GLsizei bufSize, GLsizei *length, char *buffer)
    {
        getSourceImpl(mHlsl, bufSize, length, buffer);
    }
    
    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;
        }
    
        delete[] mInfoLog;
        mInfoLog = NULL;
    
        int compileOptions = SH_OBJECT_CODE;
        std::string sourcePath;
        if (perfActive())
        {
            sourcePath = getTempPath();
            writeFile(sourcePath.c_str(), mSource, strlen(mSource));
            compileOptions |= SH_LINE_DIRECTIVES;
        }
    
        int result;
        if (sourcePath.empty())
        {
            result = ShCompile(compiler, &mSource, 1, compileOptions);
        }
        else
        {
            const char* sourceStrings[2] =
            {
                sourcePath.c_str(),
                mSource
            };
    
            result = ShCompile(compiler, sourceStrings, 2, compileOptions | SH_SOURCE_PATH);
        }
    
        if (result)
        {
            int objCodeLen = 0;
            ShGetInfo(compiler, SH_OBJECT_CODE_LENGTH, &objCodeLen);
            mHlsl = new char[objCodeLen];
            ShGetObjectCode(compiler, 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);
    }
    }