blob: c38aa5a61aa3acafcf52b00dd78af1719a0c9fc9 [file] [log] [blame]
#include "precompiled.h"
//
// Copyright (c) 2002-2013 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.
//
// Program.cpp: Implements the gl::Program class. Implements GL program objects
// and related functionality. [OpenGL ES 2.0.24] section 2.10.3 page 28.
#include "libGLESv2/Program.h"
#include "libGLESv2/ProgramBinary.h"
#include "libGLESv2/ResourceManager.h"
namespace gl
{
const char * const g_fakepath = "C:\\fakepath";
AttributeBindings::AttributeBindings()
{
}
AttributeBindings::~AttributeBindings()
{
}
InfoLog::InfoLog() : mInfoLog(NULL)
{
}
InfoLog::~InfoLog()
{
delete[] mInfoLog;
}
int InfoLog::getLength() const
{
if (!mInfoLog)
{
return 0;
}
else
{
return strlen(mInfoLog) + 1;
}
}
void InfoLog::getLog(GLsizei bufSize, GLsizei *length, char *infoLog)
{
int index = 0;
if (bufSize > 0)
{
if (mInfoLog)
{
index = std::min(bufSize - 1, (int)strlen(mInfoLog));
memcpy(infoLog, mInfoLog, index);
}
infoLog[index] = '\0';
}
if (length)
{
*length = index;
}
}
// append a santized message to the program info log.
// The D3D compiler includes a fake file path in some of the warning or error
// messages, so lets remove all occurrences of this fake file path from the log.
void InfoLog::appendSanitized(const char *message)
{
std::string msg(message);
size_t found;
do
{
found = msg.find(g_fakepath);
if (found != std::string::npos)
{
msg.erase(found, strlen(g_fakepath));
}
}
while (found != std::string::npos);
append("%s", msg.c_str());
}
void InfoLog::append(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 (!mInfoLog)
{
mInfoLog = new char[infoLength + 2];
strcpy(mInfoLog, info);
strcpy(mInfoLog + infoLength, "\n");
}
else
{
size_t logLength = strlen(mInfoLog);
char *newLog = new char[logLength + infoLength + 2];
strcpy(newLog, mInfoLog);
strcpy(newLog + logLength, info);
strcpy(newLog + logLength + infoLength, "\n");
delete[] mInfoLog;
mInfoLog = newLog;
}
}
void InfoLog::reset()
{
if (mInfoLog)
{
delete [] mInfoLog;
mInfoLog = NULL;
}
}
Program::Program(rx::Renderer *renderer, ResourceManager *manager, GLuint handle) : mResourceManager(manager), mHandle(handle)
{
mFragmentShader = NULL;
mVertexShader = NULL;
mProgramBinary.set(NULL);
mDeleteStatus = false;
mLinked = false;
mRefCount = 0;
mRenderer = renderer;
}
Program::~Program()
{
unlink(true);
if (mVertexShader != NULL)
{
mVertexShader->release();
}
if (mFragmentShader != NULL)
{
mFragmentShader->release();
}
}
bool Program::attachShader(Shader *shader)
{
if (shader->getType() == GL_VERTEX_SHADER)
{
if (mVertexShader)
{
return false;
}
mVertexShader = (VertexShader*)shader;
mVertexShader->addRef();
}
else if (shader->getType() == GL_FRAGMENT_SHADER)
{
if (mFragmentShader)
{
return false;
}
mFragmentShader = (FragmentShader*)shader;
mFragmentShader->addRef();
}
else UNREACHABLE();
return true;
}
bool Program::detachShader(Shader *shader)
{
if (shader->getType() == GL_VERTEX_SHADER)
{
if (mVertexShader != shader)
{
return false;
}
mVertexShader->release();
mVertexShader = NULL;
}
else if (shader->getType() == GL_FRAGMENT_SHADER)
{
if (mFragmentShader != shader)
{
return false;
}
mFragmentShader->release();
mFragmentShader = NULL;
}
else UNREACHABLE();
return true;
}
int Program::getAttachedShadersCount() const
{
return (mVertexShader ? 1 : 0) + (mFragmentShader ? 1 : 0);
}
void AttributeBindings::bindAttributeLocation(GLuint index, const char *name)
{
if (index < MAX_VERTEX_ATTRIBS)
{
for (int i = 0; i < MAX_VERTEX_ATTRIBS; i++)
{
mAttributeBinding[i].erase(name);
}
mAttributeBinding[index].insert(name);
}
}
void Program::bindAttributeLocation(GLuint index, const char *name)
{
mAttributeBindings.bindAttributeLocation(index, name);
}
// Links the HLSL 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
bool Program::link()
{
unlink(false);
mInfoLog.reset();
mProgramBinary.set(new ProgramBinary(mRenderer));
mLinked = mProgramBinary->link(mInfoLog, mAttributeBindings, mFragmentShader, mVertexShader);
return mLinked;
}
int AttributeBindings::getAttributeBinding(const std::string &name) const
{
for (int location = 0; location < MAX_VERTEX_ATTRIBS; location++)
{
if (mAttributeBinding[location].find(name) != mAttributeBinding[location].end())
{
return location;
}
}
return -1;
}
// Returns the program object to an unlinked state, before re-linking, or at destruction
void Program::unlink(bool destroy)
{
if (destroy) // Object being destructed
{
if (mFragmentShader)
{
mFragmentShader->release();
mFragmentShader = NULL;
}
if (mVertexShader)
{
mVertexShader->release();
mVertexShader = NULL;
}
}
mProgramBinary.set(NULL);
mLinked = false;
}
bool Program::isLinked()
{
return mLinked;
}
ProgramBinary* Program::getProgramBinary()
{
return mProgramBinary.get();
}
bool Program::setProgramBinary(const void *binary, GLsizei length)
{
unlink(false);
mInfoLog.reset();
mProgramBinary.set(new ProgramBinary(mRenderer));
mLinked = mProgramBinary->load(mInfoLog, binary, length);
if (!mLinked)
{
mProgramBinary.set(NULL);
}
return mLinked;
}
void Program::release()
{
mRefCount--;
if (mRefCount == 0 && mDeleteStatus)
{
mResourceManager->deleteProgram(mHandle);
}
}
void Program::addRef()
{
mRefCount++;
}
unsigned int Program::getRefCount() const
{
return mRefCount;
}
GLint Program::getProgramBinaryLength() const
{
ProgramBinary *programBinary = mProgramBinary.get();
if (programBinary)
{
return programBinary->getLength();
}
else
{
return 0;
}
}
int Program::getInfoLogLength() const
{
return mInfoLog.getLength();
}
void Program::getInfoLog(GLsizei bufSize, GLsizei *length, char *infoLog)
{
return mInfoLog.getLog(bufSize, length, infoLog);
}
void Program::getAttachedShaders(GLsizei maxCount, GLsizei *count, GLuint *shaders)
{
int total = 0;
if (mVertexShader)
{
if (total < maxCount)
{
shaders[total] = mVertexShader->getHandle();
}
total++;
}
if (mFragmentShader)
{
if (total < maxCount)
{
shaders[total] = mFragmentShader->getHandle();
}
total++;
}
if (count)
{
*count = total;
}
}
void Program::getActiveAttribute(GLuint index, GLsizei bufsize, GLsizei *length, GLint *size, GLenum *type, GLchar *name)
{
ProgramBinary *programBinary = getProgramBinary();
if (programBinary)
{
programBinary->getActiveAttribute(index, bufsize, length, size, type, name);
}
else
{
if (bufsize > 0)
{
name[0] = '\0';
}
if (length)
{
*length = 0;
}
*type = GL_NONE;
*size = 1;
}
}
GLint Program::getActiveAttributeCount()
{
ProgramBinary *programBinary = getProgramBinary();
if (programBinary)
{
return programBinary->getActiveAttributeCount();
}
else
{
return 0;
}
}
GLint Program::getActiveAttributeMaxLength()
{
ProgramBinary *programBinary = getProgramBinary();
if (programBinary)
{
return programBinary->getActiveAttributeMaxLength();
}
else
{
return 0;
}
}
void Program::getActiveUniform(GLuint index, GLsizei bufsize, GLsizei *length, GLint *size, GLenum *type, GLchar *name)
{
ProgramBinary *programBinary = getProgramBinary();
if (programBinary)
{
return programBinary->getActiveUniform(index, bufsize, length, size, type, name);
}
else
{
if (bufsize > 0)
{
name[0] = '\0';
}
if (length)
{
*length = 0;
}
*size = 0;
*type = GL_NONE;
}
}
GLint Program::getActiveUniformCount()
{
ProgramBinary *programBinary = getProgramBinary();
if (programBinary)
{
return programBinary->getActiveUniformCount();
}
else
{
return 0;
}
}
GLint Program::getActiveUniformMaxLength()
{
ProgramBinary *programBinary = getProgramBinary();
if (programBinary)
{
return programBinary->getActiveUniformMaxLength();
}
else
{
return 0;
}
}
void Program::flagForDeletion()
{
mDeleteStatus = true;
}
bool Program::isFlaggedForDeletion() const
{
return mDeleteStatus;
}
void Program::validate()
{
mInfoLog.reset();
ProgramBinary *programBinary = getProgramBinary();
if (isLinked() && programBinary)
{
programBinary->validate(mInfoLog);
}
else
{
mInfoLog.append("Program has not been successfully linked.");
}
}
bool Program::isValidated() const
{
ProgramBinary *programBinary = mProgramBinary.get();
if (programBinary)
{
return programBinary->isValidated();
}
else
{
return false;
}
}
}