michael@0: #include "precompiled.h" michael@0: // michael@0: // Copyright (c) 2002-2013 The ANGLE Project Authors. All rights reserved. michael@0: // Use of this source code is governed by a BSD-style license that can be michael@0: // found in the LICENSE file. michael@0: // michael@0: michael@0: // Shader.cpp: Implements the gl::Shader class and its derived classes michael@0: // VertexShader and FragmentShader. Implements GL shader objects and related michael@0: // functionality. [OpenGL ES 2.0.24] section 2.10 page 24 and section 3.8 page 84. michael@0: michael@0: #include "libGLESv2/Shader.h" michael@0: michael@0: #include michael@0: michael@0: #include "GLSLANG/ShaderLang.h" michael@0: #include "libGLESv2/utilities.h" michael@0: #include "libGLESv2/renderer/Renderer.h" michael@0: #include "libGLESv2/Constants.h" michael@0: #include "libGLESv2/ResourceManager.h" michael@0: michael@0: namespace gl michael@0: { michael@0: void *Shader::mFragmentCompiler = NULL; michael@0: void *Shader::mVertexCompiler = NULL; michael@0: michael@0: Shader::Shader(ResourceManager *manager, const rx::Renderer *renderer, GLuint handle) michael@0: : mHandle(handle), mRenderer(renderer), mResourceManager(manager) michael@0: { michael@0: mSource = NULL; michael@0: mHlsl = NULL; michael@0: mInfoLog = NULL; michael@0: michael@0: uncompile(); michael@0: initializeCompiler(); michael@0: michael@0: mRefCount = 0; michael@0: mDeleteStatus = false; michael@0: } michael@0: michael@0: Shader::~Shader() michael@0: { michael@0: delete[] mSource; michael@0: delete[] mHlsl; michael@0: delete[] mInfoLog; michael@0: } michael@0: michael@0: GLuint Shader::getHandle() const michael@0: { michael@0: return mHandle; michael@0: } michael@0: michael@0: void Shader::setSource(GLsizei count, const char **string, const GLint *length) michael@0: { michael@0: delete[] mSource; michael@0: int totalLength = 0; michael@0: michael@0: for (int i = 0; i < count; i++) michael@0: { michael@0: if (length && length[i] >= 0) michael@0: { michael@0: totalLength += length[i]; michael@0: } michael@0: else michael@0: { michael@0: totalLength += (int)strlen(string[i]); michael@0: } michael@0: } michael@0: michael@0: mSource = new char[totalLength + 1]; michael@0: char *code = mSource; michael@0: michael@0: for (int i = 0; i < count; i++) michael@0: { michael@0: int stringLength; michael@0: michael@0: if (length && length[i] >= 0) michael@0: { michael@0: stringLength = length[i]; michael@0: } michael@0: else michael@0: { michael@0: stringLength = (int)strlen(string[i]); michael@0: } michael@0: michael@0: strncpy(code, string[i], stringLength); michael@0: code += stringLength; michael@0: } michael@0: michael@0: mSource[totalLength] = '\0'; michael@0: } michael@0: michael@0: int Shader::getInfoLogLength() const michael@0: { michael@0: if (!mInfoLog) michael@0: { michael@0: return 0; michael@0: } michael@0: else michael@0: { michael@0: return strlen(mInfoLog) + 1; michael@0: } michael@0: } michael@0: michael@0: void Shader::getInfoLog(GLsizei bufSize, GLsizei *length, char *infoLog) michael@0: { michael@0: int index = 0; michael@0: michael@0: if (bufSize > 0) michael@0: { michael@0: if (mInfoLog) michael@0: { michael@0: index = std::min(bufSize - 1, (int)strlen(mInfoLog)); michael@0: memcpy(infoLog, mInfoLog, index); michael@0: } michael@0: michael@0: infoLog[index] = '\0'; michael@0: } michael@0: michael@0: if (length) michael@0: { michael@0: *length = index; michael@0: } michael@0: } michael@0: michael@0: int Shader::getSourceLength() const michael@0: { michael@0: if (!mSource) michael@0: { michael@0: return 0; michael@0: } michael@0: else michael@0: { michael@0: return strlen(mSource) + 1; michael@0: } michael@0: } michael@0: michael@0: int Shader::getTranslatedSourceLength() const michael@0: { michael@0: if (!mHlsl) michael@0: { michael@0: return 0; michael@0: } michael@0: else michael@0: { michael@0: return strlen(mHlsl) + 1; michael@0: } michael@0: } michael@0: michael@0: void Shader::getSourceImpl(char *source, GLsizei bufSize, GLsizei *length, char *buffer) michael@0: { michael@0: int index = 0; michael@0: michael@0: if (bufSize > 0) michael@0: { michael@0: if (source) michael@0: { michael@0: index = std::min(bufSize - 1, (int)strlen(source)); michael@0: memcpy(buffer, source, index); michael@0: } michael@0: michael@0: buffer[index] = '\0'; michael@0: } michael@0: michael@0: if (length) michael@0: { michael@0: *length = index; michael@0: } michael@0: } michael@0: michael@0: void Shader::getSource(GLsizei bufSize, GLsizei *length, char *buffer) michael@0: { michael@0: getSourceImpl(mSource, bufSize, length, buffer); michael@0: } michael@0: michael@0: void Shader::getTranslatedSource(GLsizei bufSize, GLsizei *length, char *buffer) michael@0: { michael@0: getSourceImpl(mHlsl, bufSize, length, buffer); michael@0: } michael@0: michael@0: const sh::ActiveUniforms &Shader::getUniforms() michael@0: { michael@0: return mActiveUniforms; michael@0: } michael@0: michael@0: bool Shader::isCompiled() michael@0: { michael@0: return mHlsl != NULL; michael@0: } michael@0: michael@0: const char *Shader::getHLSL() michael@0: { michael@0: return mHlsl; michael@0: } michael@0: michael@0: void Shader::addRef() michael@0: { michael@0: mRefCount++; michael@0: } michael@0: michael@0: void Shader::release() michael@0: { michael@0: mRefCount--; michael@0: michael@0: if (mRefCount == 0 && mDeleteStatus) michael@0: { michael@0: mResourceManager->deleteShader(mHandle); michael@0: } michael@0: } michael@0: michael@0: unsigned int Shader::getRefCount() const michael@0: { michael@0: return mRefCount; michael@0: } michael@0: michael@0: bool Shader::isFlaggedForDeletion() const michael@0: { michael@0: return mDeleteStatus; michael@0: } michael@0: michael@0: void Shader::flagForDeletion() michael@0: { michael@0: mDeleteStatus = true; michael@0: } michael@0: michael@0: // Perform a one-time initialization of the shader compiler (or after being destructed by releaseCompiler) michael@0: void Shader::initializeCompiler() michael@0: { michael@0: if (!mFragmentCompiler) michael@0: { michael@0: int result = ShInitialize(); michael@0: michael@0: if (result) michael@0: { michael@0: ShShaderOutput hlslVersion = (mRenderer->getMajorShaderModel() >= 4) ? SH_HLSL11_OUTPUT : SH_HLSL9_OUTPUT; michael@0: michael@0: ShBuiltInResources resources; michael@0: ShInitBuiltInResources(&resources); michael@0: michael@0: resources.MaxVertexAttribs = MAX_VERTEX_ATTRIBS; michael@0: resources.MaxVertexUniformVectors = mRenderer->getMaxVertexUniformVectors(); michael@0: resources.MaxVaryingVectors = mRenderer->getMaxVaryingVectors(); michael@0: resources.MaxVertexTextureImageUnits = mRenderer->getMaxVertexTextureImageUnits(); michael@0: resources.MaxCombinedTextureImageUnits = mRenderer->getMaxCombinedTextureImageUnits(); michael@0: resources.MaxTextureImageUnits = MAX_TEXTURE_IMAGE_UNITS; michael@0: resources.MaxFragmentUniformVectors = mRenderer->getMaxFragmentUniformVectors(); michael@0: resources.MaxDrawBuffers = mRenderer->getMaxRenderTargets(); michael@0: resources.OES_standard_derivatives = mRenderer->getDerivativeInstructionSupport(); michael@0: resources.EXT_draw_buffers = mRenderer->getMaxRenderTargets() > 1; michael@0: // resources.OES_EGL_image_external = mRenderer->getShareHandleSupport() ? 1 : 0; // TODO: commented out until the extension is actually supported. michael@0: resources.FragmentPrecisionHigh = 1; // Shader Model 2+ always supports FP24 (s16e7) which corresponds to highp michael@0: resources.EXT_frag_depth = 1; // Shader Model 2+ always supports explicit depth output michael@0: michael@0: mFragmentCompiler = ShConstructCompiler(SH_FRAGMENT_SHADER, SH_GLES2_SPEC, hlslVersion, &resources); michael@0: mVertexCompiler = ShConstructCompiler(SH_VERTEX_SHADER, SH_GLES2_SPEC, hlslVersion, &resources); michael@0: } michael@0: } michael@0: } michael@0: michael@0: void Shader::releaseCompiler() michael@0: { michael@0: ShDestruct(mFragmentCompiler); michael@0: ShDestruct(mVertexCompiler); michael@0: michael@0: mFragmentCompiler = NULL; michael@0: mVertexCompiler = NULL; michael@0: michael@0: ShFinalize(); michael@0: } michael@0: michael@0: void Shader::parseVaryings() michael@0: { michael@0: if (mHlsl) michael@0: { michael@0: const char *input = strstr(mHlsl, "// Varyings") + 12; michael@0: michael@0: while(true) michael@0: { michael@0: char varyingType[256]; michael@0: char varyingName[256]; michael@0: michael@0: int matches = sscanf(input, "static %255s %255s", varyingType, varyingName); michael@0: michael@0: if (matches != 2) michael@0: { michael@0: break; michael@0: } michael@0: michael@0: char *array = strstr(varyingName, "["); michael@0: int size = 1; michael@0: michael@0: if (array) michael@0: { michael@0: size = atoi(array + 1); michael@0: *array = '\0'; michael@0: } michael@0: michael@0: mVaryings.push_back(Varying(parseType(varyingType), varyingName, size, array != NULL)); michael@0: michael@0: input = strstr(input, ";") + 2; michael@0: } michael@0: michael@0: mUsesMultipleRenderTargets = strstr(mHlsl, "GL_USES_MRT") != NULL; michael@0: mUsesFragColor = strstr(mHlsl, "GL_USES_FRAG_COLOR") != NULL; michael@0: mUsesFragData = strstr(mHlsl, "GL_USES_FRAG_DATA") != NULL; michael@0: mUsesFragCoord = strstr(mHlsl, "GL_USES_FRAG_COORD") != NULL; michael@0: mUsesFrontFacing = strstr(mHlsl, "GL_USES_FRONT_FACING") != NULL; michael@0: mUsesPointSize = strstr(mHlsl, "GL_USES_POINT_SIZE") != NULL; michael@0: mUsesPointCoord = strstr(mHlsl, "GL_USES_POINT_COORD") != NULL; michael@0: mUsesDepthRange = strstr(mHlsl, "GL_USES_DEPTH_RANGE") != NULL; michael@0: mUsesFragDepth = strstr(mHlsl, "GL_USES_FRAG_DEPTH") != NULL; michael@0: } michael@0: } michael@0: michael@0: void Shader::resetVaryingsRegisterAssignment() michael@0: { michael@0: for (VaryingList::iterator var = mVaryings.begin(); var != mVaryings.end(); var++) michael@0: { michael@0: var->reg = -1; michael@0: var->col = -1; michael@0: } michael@0: } michael@0: michael@0: // initialize/clean up previous state michael@0: void Shader::uncompile() michael@0: { michael@0: // set by compileToHLSL michael@0: delete[] mHlsl; michael@0: mHlsl = NULL; michael@0: delete[] mInfoLog; michael@0: mInfoLog = NULL; michael@0: michael@0: // set by parseVaryings michael@0: mVaryings.clear(); michael@0: michael@0: mUsesMultipleRenderTargets = false; michael@0: mUsesFragColor = false; michael@0: mUsesFragData = false; michael@0: mUsesFragCoord = false; michael@0: mUsesFrontFacing = false; michael@0: mUsesPointSize = false; michael@0: mUsesPointCoord = false; michael@0: mUsesDepthRange = false; michael@0: mUsesFragDepth = false; michael@0: michael@0: mActiveUniforms.clear(); michael@0: } michael@0: michael@0: void Shader::compileToHLSL(void *compiler) michael@0: { michael@0: // ensure we don't pass a NULL source to the compiler michael@0: const char *source = "\0"; michael@0: if (mSource) michael@0: { michael@0: source = mSource; michael@0: } michael@0: michael@0: // ensure the compiler is loaded michael@0: initializeCompiler(); michael@0: michael@0: int compileOptions = SH_OBJECT_CODE; michael@0: std::string sourcePath; michael@0: if (perfActive()) michael@0: { michael@0: sourcePath = getTempPath(); michael@0: writeFile(sourcePath.c_str(), source, strlen(source)); michael@0: compileOptions |= SH_LINE_DIRECTIVES; michael@0: } michael@0: michael@0: int result; michael@0: if (sourcePath.empty()) michael@0: { michael@0: result = ShCompile(compiler, &source, 1, compileOptions); michael@0: } michael@0: else michael@0: { michael@0: const char* sourceStrings[2] = michael@0: { michael@0: sourcePath.c_str(), michael@0: source michael@0: }; michael@0: michael@0: result = ShCompile(compiler, sourceStrings, 2, compileOptions | SH_SOURCE_PATH); michael@0: } michael@0: michael@0: if (result) michael@0: { michael@0: size_t objCodeLen = 0; michael@0: ShGetInfo(compiler, SH_OBJECT_CODE_LENGTH, &objCodeLen); michael@0: mHlsl = new char[objCodeLen]; michael@0: ShGetObjectCode(compiler, mHlsl); michael@0: michael@0: void *activeUniforms; michael@0: ShGetInfoPointer(compiler, SH_ACTIVE_UNIFORMS_ARRAY, &activeUniforms); michael@0: mActiveUniforms = *(sh::ActiveUniforms*)activeUniforms; michael@0: } michael@0: else michael@0: { michael@0: size_t infoLogLen = 0; michael@0: ShGetInfo(compiler, SH_INFO_LOG_LENGTH, &infoLogLen); michael@0: mInfoLog = new char[infoLogLen]; michael@0: ShGetInfoLog(compiler, mInfoLog); michael@0: michael@0: TRACE("\n%s", mInfoLog); michael@0: } michael@0: } michael@0: michael@0: GLenum Shader::parseType(const std::string &type) michael@0: { michael@0: if (type == "float") michael@0: { michael@0: return GL_FLOAT; michael@0: } michael@0: else if (type == "float2") michael@0: { michael@0: return GL_FLOAT_VEC2; michael@0: } michael@0: else if (type == "float3") michael@0: { michael@0: return GL_FLOAT_VEC3; michael@0: } michael@0: else if (type == "float4") michael@0: { michael@0: return GL_FLOAT_VEC4; michael@0: } michael@0: else if (type == "float2x2") michael@0: { michael@0: return GL_FLOAT_MAT2; michael@0: } michael@0: else if (type == "float3x3") michael@0: { michael@0: return GL_FLOAT_MAT3; michael@0: } michael@0: else if (type == "float4x4") michael@0: { michael@0: return GL_FLOAT_MAT4; michael@0: } michael@0: else UNREACHABLE(); michael@0: michael@0: return GL_NONE; michael@0: } michael@0: michael@0: // true if varying x has a higher priority in packing than y michael@0: bool Shader::compareVarying(const Varying &x, const Varying &y) michael@0: { michael@0: if(x.type == y.type) michael@0: { michael@0: return x.size > y.size; michael@0: } michael@0: michael@0: switch (x.type) michael@0: { michael@0: case GL_FLOAT_MAT4: return true; michael@0: case GL_FLOAT_MAT2: michael@0: switch(y.type) michael@0: { michael@0: case GL_FLOAT_MAT4: return false; michael@0: case GL_FLOAT_MAT2: return true; michael@0: case GL_FLOAT_VEC4: return true; michael@0: case GL_FLOAT_MAT3: return true; michael@0: case GL_FLOAT_VEC3: return true; michael@0: case GL_FLOAT_VEC2: return true; michael@0: case GL_FLOAT: return true; michael@0: default: UNREACHABLE(); michael@0: } michael@0: break; michael@0: case GL_FLOAT_VEC4: michael@0: switch(y.type) michael@0: { michael@0: case GL_FLOAT_MAT4: return false; michael@0: case GL_FLOAT_MAT2: return false; michael@0: case GL_FLOAT_VEC4: return true; michael@0: case GL_FLOAT_MAT3: return true; michael@0: case GL_FLOAT_VEC3: return true; michael@0: case GL_FLOAT_VEC2: return true; michael@0: case GL_FLOAT: return true; michael@0: default: UNREACHABLE(); michael@0: } michael@0: break; michael@0: case GL_FLOAT_MAT3: michael@0: switch(y.type) michael@0: { michael@0: case GL_FLOAT_MAT4: return false; michael@0: case GL_FLOAT_MAT2: return false; michael@0: case GL_FLOAT_VEC4: return false; michael@0: case GL_FLOAT_MAT3: return true; michael@0: case GL_FLOAT_VEC3: return true; michael@0: case GL_FLOAT_VEC2: return true; michael@0: case GL_FLOAT: return true; michael@0: default: UNREACHABLE(); michael@0: } michael@0: break; michael@0: case GL_FLOAT_VEC3: michael@0: switch(y.type) michael@0: { michael@0: case GL_FLOAT_MAT4: return false; michael@0: case GL_FLOAT_MAT2: return false; michael@0: case GL_FLOAT_VEC4: return false; michael@0: case GL_FLOAT_MAT3: return false; michael@0: case GL_FLOAT_VEC3: return true; michael@0: case GL_FLOAT_VEC2: return true; michael@0: case GL_FLOAT: return true; michael@0: default: UNREACHABLE(); michael@0: } michael@0: break; michael@0: case GL_FLOAT_VEC2: michael@0: switch(y.type) michael@0: { michael@0: case GL_FLOAT_MAT4: return false; michael@0: case GL_FLOAT_MAT2: return false; michael@0: case GL_FLOAT_VEC4: return false; michael@0: case GL_FLOAT_MAT3: return false; michael@0: case GL_FLOAT_VEC3: return false; michael@0: case GL_FLOAT_VEC2: return true; michael@0: case GL_FLOAT: return true; michael@0: default: UNREACHABLE(); michael@0: } michael@0: break; michael@0: case GL_FLOAT: return false; michael@0: default: UNREACHABLE(); michael@0: } michael@0: michael@0: return false; michael@0: } michael@0: michael@0: VertexShader::VertexShader(ResourceManager *manager, const rx::Renderer *renderer, GLuint handle) michael@0: : Shader(manager, renderer, handle) michael@0: { michael@0: } michael@0: michael@0: VertexShader::~VertexShader() michael@0: { michael@0: } michael@0: michael@0: GLenum VertexShader::getType() michael@0: { michael@0: return GL_VERTEX_SHADER; michael@0: } michael@0: michael@0: void VertexShader::uncompile() michael@0: { michael@0: Shader::uncompile(); michael@0: michael@0: // set by ParseAttributes michael@0: mAttributes.clear(); michael@0: } michael@0: michael@0: void VertexShader::compile() michael@0: { michael@0: uncompile(); michael@0: michael@0: compileToHLSL(mVertexCompiler); michael@0: parseAttributes(); michael@0: parseVaryings(); michael@0: } michael@0: michael@0: int VertexShader::getSemanticIndex(const std::string &attributeName) michael@0: { michael@0: if (!attributeName.empty()) michael@0: { michael@0: int semanticIndex = 0; michael@0: for (AttributeArray::iterator attribute = mAttributes.begin(); attribute != mAttributes.end(); attribute++) michael@0: { michael@0: if (attribute->name == attributeName) michael@0: { michael@0: return semanticIndex; michael@0: } michael@0: michael@0: semanticIndex += VariableRowCount(attribute->type); michael@0: } michael@0: } michael@0: michael@0: return -1; michael@0: } michael@0: michael@0: void VertexShader::parseAttributes() michael@0: { michael@0: const char *hlsl = getHLSL(); michael@0: if (hlsl) michael@0: { michael@0: const char *input = strstr(hlsl, "// Attributes") + 14; michael@0: michael@0: while(true) michael@0: { michael@0: char attributeType[256]; michael@0: char attributeName[256]; michael@0: michael@0: int matches = sscanf(input, "static %255s _%255s", attributeType, attributeName); michael@0: michael@0: if (matches != 2) michael@0: { michael@0: break; michael@0: } michael@0: michael@0: mAttributes.push_back(Attribute(parseType(attributeType), attributeName)); michael@0: michael@0: input = strstr(input, ";") + 2; michael@0: } michael@0: } michael@0: } michael@0: michael@0: FragmentShader::FragmentShader(ResourceManager *manager, const rx::Renderer *renderer, GLuint handle) michael@0: : Shader(manager, renderer, handle) michael@0: { michael@0: } michael@0: michael@0: FragmentShader::~FragmentShader() michael@0: { michael@0: } michael@0: michael@0: GLenum FragmentShader::getType() michael@0: { michael@0: return GL_FRAGMENT_SHADER; michael@0: } michael@0: michael@0: void FragmentShader::compile() michael@0: { michael@0: uncompile(); michael@0: michael@0: compileToHLSL(mFragmentCompiler); michael@0: parseVaryings(); michael@0: mVaryings.sort(compareVarying); michael@0: } michael@0: }