// 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.
// Shader.cpp: Implements the 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 "Shader.h"
#include "main.h"
#include "utilities.h"
#include <string>
#include <algorithm>
namespace es2
{
bool Shader::compilerInitialized = false;
Shader::Shader(ResourceManager *manager, GLuint handle) : mHandle(handle), mResourceManager(manager)
{
mSource = nullptr;
clear();
mRefCount = 0;
mDeleteStatus = false;
}
Shader::~Shader()
{
delete[] mSource;
}
GLuint Shader::getName() const
{
return mHandle;
}
void Shader::setSource(GLsizei count, const char *const *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';
}
size_t Shader::getInfoLogLength() const
{
if(infoLog.empty())
{
return 0;
}
else
{
return infoLog.size() + 1;
}
}
void Shader::getInfoLog(GLsizei bufSize, GLsizei *length, char *infoLogOut)
{
int index = 0;
if(bufSize > 0)
{
if(!infoLog.empty())
{
index = std::min(bufSize - 1, (GLsizei)infoLog.size());
memcpy(infoLogOut, infoLog.c_str(), index);
}
infoLogOut[index] = '\0';
}
if(length)
{
*length = index;
}
}
size_t 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(bufSize > 0)
{
if(mSource)
{
index = std::min(bufSize - 1, (int)strlen(mSource));
memcpy(source, mSource, index);
}
source[index] = '\0';
}
if(length)
{
*length = index;
}
}
TranslatorASM *Shader::createCompiler(GLenum shaderType)
{
if(!compilerInitialized)
{
InitCompilerGlobals();
compilerInitialized = true;
}
TranslatorASM *assembler = new TranslatorASM(this, shaderType);
ShBuiltInResources resources;
resources.MaxVertexAttribs = MAX_VERTEX_ATTRIBS;
resources.MaxVertexUniformVectors = MAX_VERTEX_UNIFORM_VECTORS;
resources.MaxVaryingVectors = MAX_VARYING_VECTORS;
resources.MaxVertexTextureImageUnits = MAX_VERTEX_TEXTURE_IMAGE_UNITS;
resources.MaxCombinedTextureImageUnits = MAX_COMBINED_TEXTURE_IMAGE_UNITS;
resources.MaxTextureImageUnits = MAX_TEXTURE_IMAGE_UNITS;
resources.MaxFragmentUniformVectors = MAX_FRAGMENT_UNIFORM_VECTORS;
resources.MaxDrawBuffers = MAX_DRAW_BUFFERS;
resources.MaxVertexOutputVectors = MAX_VERTEX_OUTPUT_VECTORS;
resources.MaxFragmentInputVectors = MAX_FRAGMENT_INPUT_VECTORS;
resources.MinProgramTexelOffset = MIN_PROGRAM_TEXEL_OFFSET;
resources.MaxProgramTexelOffset = MAX_PROGRAM_TEXEL_OFFSET;
resources.OES_standard_derivatives = 1;
resources.OES_fragment_precision_high = 1;
resources.OES_EGL_image_external = 1;
resources.EXT_draw_buffers = 1;
resources.ARB_texture_rectangle = 1;
resources.MaxCallStackDepth = 64;
assembler->Init(resources);
return assembler;
}
void Shader::clear()
{
infoLog.clear();
varyings.clear();
activeUniforms.clear();
activeAttributes.clear();
}
void Shader::compile()
{
clear();
createShader();
TranslatorASM *compiler = createCompiler(getType());
// Ensure we don't pass a nullptr source to the compiler
const char *source = "\0";
if(mSource)
{
source = mSource;
}
bool success = compiler->compile(&source, 1, SH_OBJECT_CODE);
if(false)
{
static int serial = 1;
if(false)
{
char buffer[256];
sprintf(buffer, "shader-input-%d-%d.txt", getName(), serial);
FILE *file = fopen(buffer, "wt");
fprintf(file, "%s", mSource);
fclose(file);
}
getShader()->print("shader-output-%d-%d.txt", getName(), serial);
serial++;
}
shaderVersion = compiler->getShaderVersion();
int clientVersion = es2::getContext()->getClientVersion();
if(shaderVersion >= 300 && clientVersion < 3)
{
infoLog = "GLSL ES 3.00 is not supported by OpenGL ES 2.0 contexts";
success = false;
}
if(!success)
{
deleteShader();
infoLog += compiler->getInfoSink().info.c_str();
TRACE("\n%s", infoLog.c_str());
}
delete compiler;
}
bool Shader::isCompiled()
{
return getShader() != 0;
}
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()
{
FreeCompilerGlobals();
compilerInitialized = false;
}
// true if varying x has a higher priority in packing than y
bool Shader::compareVarying(const glsl::Varying &x, const glsl::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(y.type);
}
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(y.type);
}
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(y.type);
}
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(y.type);
}
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(y.type);
}
break;
case GL_FLOAT: return false;
default: UNREACHABLE(x.type);
}
return false;
}
VertexShader::VertexShader(ResourceManager *manager, GLuint handle) : Shader(manager, handle)
{
vertexShader = 0;
}
VertexShader::~VertexShader()
{
delete vertexShader;
}
GLenum VertexShader::getType() const
{
return GL_VERTEX_SHADER;
}
int VertexShader::getSemanticIndex(const std::string &attributeName) const
{
if(!attributeName.empty())
{
for(const auto &attribute : activeAttributes)
{
if(attribute.name == attributeName)
{
return attribute.registerIndex;
}
}
}
return -1;
}
sw::Shader *VertexShader::getShader() const
{
return vertexShader;
}
sw::VertexShader *VertexShader::getVertexShader() const
{
return vertexShader;
}
void VertexShader::createShader()
{
delete vertexShader;
vertexShader = new sw::VertexShader();
}
void VertexShader::deleteShader()
{
delete vertexShader;
vertexShader = nullptr;
}
FragmentShader::FragmentShader(ResourceManager *manager, GLuint handle) : Shader(manager, handle)
{
pixelShader = 0;
}
FragmentShader::~FragmentShader()
{
delete pixelShader;
}
GLenum FragmentShader::getType() const
{
return GL_FRAGMENT_SHADER;
}
sw::Shader *FragmentShader::getShader() const
{
return pixelShader;
}
sw::PixelShader *FragmentShader::getPixelShader() const
{
return pixelShader;
}
void FragmentShader::createShader()
{
delete pixelShader;
pixelShader = new sw::PixelShader();
}
void FragmentShader::deleteShader()
{
delete pixelShader;
pixelShader = nullptr;
}
}