blob: 859658edff8d3d73404fde54ecf8f6c3886e0d6d [file] [log] [blame]
// SwiftShader Software Renderer
//
// Copyright(c) 2005-2013 TransGaming Inc.
//
// All rights reserved. No part of this software may be copied, distributed, transmitted,
// transcribed, stored in a retrieval system, translated into any human or computer
// language by any means, or disclosed to third parties without the explicit written
// agreement of TransGaming Inc. Without such an agreement, no rights or licenses, express
// or implied, including but not limited to any patent rights, are granted to you.
//
// Program.cpp: Implements the Program class. Implements GL program objects
// and related functionality. [OpenGL ES 2.0.24] section 2.10.3 page 28.
#include "Program.h"
#include "main.h"
#include "Shader.h"
#include "utilities.h"
#include "common/debug.h"
#include "Shader/PixelShader.hpp"
#include "Shader/VertexShader.hpp"
#include <string>
#include <stdlib.h>
namespace es2
{
unsigned int Program::currentSerial = 1;
std::string str(int i)
{
char buffer[20];
sprintf(buffer, "%d", i);
return buffer;
}
Uniform::Uniform(GLenum type, GLenum precision, const std::string &name, unsigned int arraySize) : type(type), precision(precision), name(name), arraySize(arraySize)
{
int bytes = UniformTypeSize(type) * size();
data = new unsigned char[bytes];
memset(data, 0, bytes);
dirty = true;
psRegisterIndex = -1;
vsRegisterIndex = -1;
}
Uniform::~Uniform()
{
delete[] data;
}
bool Uniform::isArray() const
{
return arraySize >= 1;
}
int Uniform::size() const
{
return arraySize > 0 ? arraySize : 1;
}
int Uniform::registerCount() const
{
return size() * VariableRowCount(type);
}
UniformLocation::UniformLocation(const std::string &name, unsigned int element, unsigned int index) : name(name), element(element), index(index)
{
}
Program::Program(GLuint handle) : handle(handle), serial(issueSerial())
{
device = getDevice();
fragmentShader = 0;
vertexShader = 0;
pixelBinary = 0;
vertexBinary = 0;
infoLog = 0;
validated = false;
unlink();
orphaned = false;
referenceCount = 0;
}
Program::~Program()
{
unlink();
if(vertexShader)
{
vertexShader->release();
}
if(fragmentShader)
{
fragmentShader->release();
}
}
bool Program::attachShader(Shader *shader)
{
if(shader->getType() == GL_VERTEX_SHADER)
{
if(vertexShader)
{
return false;
}
vertexShader = (VertexShader*)shader;
vertexShader->addRef();
}
else if(shader->getType() == GL_FRAGMENT_SHADER)
{
if(fragmentShader)
{
return false;
}
fragmentShader = (FragmentShader*)shader;
fragmentShader->addRef();
}
else UNREACHABLE();
return true;
}
bool Program::detachShader(Shader *shader)
{
if(shader->getType() == GL_VERTEX_SHADER)
{
if(vertexShader != shader)
{
return false;
}
vertexShader->release();
vertexShader = 0;
}
else if(shader->getType() == GL_FRAGMENT_SHADER)
{
if(fragmentShader != shader)
{
return false;
}
fragmentShader->release();
fragmentShader = 0;
}
else UNREACHABLE();
return true;
}
int Program::getAttachedShadersCount() const
{
return (vertexShader ? 1 : 0) + (fragmentShader ? 1 : 0);
}
sw::PixelShader *Program::getPixelShader()
{
return pixelBinary;
}
sw::VertexShader *Program::getVertexShader()
{
return vertexBinary;
}
void Program::bindAttributeLocation(GLuint index, const char *name)
{
if(index < MAX_VERTEX_ATTRIBS)
{
for(int i = 0; i < MAX_VERTEX_ATTRIBS; i++)
{
attributeBinding[i].erase(name);
}
attributeBinding[index].insert(name);
}
}
GLuint Program::getAttributeLocation(const char *name)
{
if(name)
{
for(int index = 0; index < MAX_VERTEX_ATTRIBS; index++)
{
if(linkedAttribute[index].name == std::string(name))
{
return index;
}
}
}
return -1;
}
int Program::getAttributeStream(int attributeIndex)
{
ASSERT(attributeIndex >= 0 && attributeIndex < MAX_VERTEX_ATTRIBS);
return attributeStream[attributeIndex];
}
// Returns the index of the texture image unit (0-19) corresponding to a sampler index (0-15 for the pixel shader and 0-3 for the vertex shader)
GLint Program::getSamplerMapping(sw::SamplerType type, unsigned int samplerIndex)
{
GLuint logicalTextureUnit = -1;
switch(type)
{
case sw::SAMPLER_PIXEL:
ASSERT(samplerIndex < sizeof(samplersPS) / sizeof(samplersPS[0]));
if(samplersPS[samplerIndex].active)
{
logicalTextureUnit = samplersPS[samplerIndex].logicalTextureUnit;
}
break;
case sw::SAMPLER_VERTEX:
ASSERT(samplerIndex < sizeof(samplersVS) / sizeof(samplersVS[0]));
if(samplersVS[samplerIndex].active)
{
logicalTextureUnit = samplersVS[samplerIndex].logicalTextureUnit;
}
break;
default: UNREACHABLE();
}
if(logicalTextureUnit >= 0 && logicalTextureUnit < MAX_COMBINED_TEXTURE_IMAGE_UNITS)
{
return logicalTextureUnit;
}
return -1;
}
// Returns the texture type for a given sampler type and index (0-15 for the pixel shader and 0-3 for the vertex shader)
TextureType Program::getSamplerTextureType(sw::SamplerType type, unsigned int samplerIndex)
{
switch(type)
{
case sw::SAMPLER_PIXEL:
ASSERT(samplerIndex < sizeof(samplersPS)/sizeof(samplersPS[0]));
ASSERT(samplersPS[samplerIndex].active);
return samplersPS[samplerIndex].textureType;
case sw::SAMPLER_VERTEX:
ASSERT(samplerIndex < sizeof(samplersVS)/sizeof(samplersVS[0]));
ASSERT(samplersVS[samplerIndex].active);
return samplersVS[samplerIndex].textureType;
default: UNREACHABLE();
}
return TEXTURE_2D;
}
GLint Program::getUniformLocation(std::string name)
{
int subscript = 0;
// Strip any trailing array operator and retrieve the subscript
size_t open = name.find_last_of('[');
size_t close = name.find_last_of(']');
if(open != std::string::npos && close == name.length() - 1)
{
subscript = atoi(name.substr(open + 1).c_str());
name.erase(open);
}
unsigned int numUniforms = uniformIndex.size();
for(unsigned int location = 0; location < numUniforms; location++)
{
if(uniformIndex[location].name == name &&
uniformIndex[location].element == subscript)
{
return location;
}
}
return -1;
}
bool Program::setUniform1fv(GLint location, GLsizei count, const GLfloat* v)
{
if(location < 0 || location >= (int)uniformIndex.size())
{
return false;
}
Uniform *targetUniform = uniforms[uniformIndex[location].index];
targetUniform->dirty = true;
int size = targetUniform->size();
if(size == 1 && count > 1)
{
return false; // Attempting to write an array to a non-array uniform is an INVALID_OPERATION
}
count = std::min(size - (int)uniformIndex[location].element, count);
if(targetUniform->type == GL_FLOAT)
{
memcpy(targetUniform->data + uniformIndex[location].element * sizeof(GLfloat),
v, sizeof(GLfloat) * count);
}
else if(targetUniform->type == GL_BOOL)
{
GLboolean *boolParams = (GLboolean*)targetUniform->data + uniformIndex[location].element;
for(int i = 0; i < count; i++)
{
if(v[i] == 0.0f)
{
boolParams[i] = GL_FALSE;
}
else
{
boolParams[i] = GL_TRUE;
}
}
}
else
{
return false;
}
return true;
}
bool Program::setUniform2fv(GLint location, GLsizei count, const GLfloat *v)
{
if(location < 0 || location >= (int)uniformIndex.size())
{
return false;
}
Uniform *targetUniform = uniforms[uniformIndex[location].index];
targetUniform->dirty = true;
int size = targetUniform->size();
if(size == 1 && count > 1)
{
return false; // Attempting to write an array to a non-array uniform is an INVALID_OPERATION
}
count = std::min(size - (int)uniformIndex[location].element, count);
if(targetUniform->type == GL_FLOAT_VEC2)
{
memcpy(targetUniform->data + uniformIndex[location].element * sizeof(GLfloat) * 2,
v, 2 * sizeof(GLfloat) * count);
}
else if(targetUniform->type == GL_BOOL_VEC2)
{
GLboolean *boolParams = (GLboolean*)targetUniform->data + uniformIndex[location].element * 2;
for(int i = 0; i < count * 2; i++)
{
if(v[i] == 0.0f)
{
boolParams[i] = GL_FALSE;
}
else
{
boolParams[i] = GL_TRUE;
}
}
}
else
{
return false;
}
return true;
}
bool Program::setUniform3fv(GLint location, GLsizei count, const GLfloat *v)
{
if(location < 0 || location >= (int)uniformIndex.size())
{
return false;
}
Uniform *targetUniform = uniforms[uniformIndex[location].index];
targetUniform->dirty = true;
int size = targetUniform->size();
if(size == 1 && count > 1)
{
return false; // Attempting to write an array to a non-array uniform is an INVALID_OPERATION
}
count = std::min(size - (int)uniformIndex[location].element, count);
if(targetUniform->type == GL_FLOAT_VEC3)
{
memcpy(targetUniform->data + uniformIndex[location].element * sizeof(GLfloat) * 3,
v, 3 * sizeof(GLfloat) * count);
}
else if(targetUniform->type == GL_BOOL_VEC3)
{
GLboolean *boolParams = (GLboolean*)targetUniform->data + uniformIndex[location].element * 3;
for(int i = 0; i < count * 3; i++)
{
if(v[i] == 0.0f)
{
boolParams[i] = GL_FALSE;
}
else
{
boolParams[i] = GL_TRUE;
}
}
}
else
{
return false;
}
return true;
}
bool Program::setUniform4fv(GLint location, GLsizei count, const GLfloat *v)
{
if(location < 0 || location >= (int)uniformIndex.size())
{
return false;
}
Uniform *targetUniform = uniforms[uniformIndex[location].index];
targetUniform->dirty = true;
int size = targetUniform->size();
if(size == 1 && count > 1)
{
return false; // Attempting to write an array to a non-array uniform is an INVALID_OPERATION
}
count = std::min(size - (int)uniformIndex[location].element, count);
if(targetUniform->type == GL_FLOAT_VEC4)
{
memcpy(targetUniform->data + uniformIndex[location].element * sizeof(GLfloat) * 4,
v, 4 * sizeof(GLfloat) * count);
}
else if(targetUniform->type == GL_BOOL_VEC4)
{
GLboolean *boolParams = (GLboolean*)targetUniform->data + uniformIndex[location].element * 4;
for(int i = 0; i < count * 4; i++)
{
if(v[i] == 0.0f)
{
boolParams[i] = GL_FALSE;
}
else
{
boolParams[i] = GL_TRUE;
}
}
}
else
{
return false;
}
return true;
}
bool Program::setUniformMatrix2fv(GLint location, GLsizei count, const GLfloat *value)
{
if(location < 0 || location >= (int)uniformIndex.size())
{
return false;
}
Uniform *targetUniform = uniforms[uniformIndex[location].index];
targetUniform->dirty = true;
if(targetUniform->type != GL_FLOAT_MAT2)
{
return false;
}
int size = targetUniform->size();
if(size == 1 && count > 1)
{
return false; // Attempting to write an array to a non-array uniform is an INVALID_OPERATION
}
count = std::min(size - (int)uniformIndex[location].element, count);
memcpy(targetUniform->data + uniformIndex[location].element * sizeof(GLfloat) * 4,
value, 4 * sizeof(GLfloat) * count);
return true;
}
bool Program::setUniformMatrix3fv(GLint location, GLsizei count, const GLfloat *value)
{
if(location < 0 || location >= (int)uniformIndex.size())
{
return false;
}
Uniform *targetUniform = uniforms[uniformIndex[location].index];
targetUniform->dirty = true;
if(targetUniform->type != GL_FLOAT_MAT3)
{
return false;
}
int size = targetUniform->size();
if(size == 1 && count > 1)
{
return false; // Attempting to write an array to a non-array uniform is an INVALID_OPERATION
}
count = std::min(size - (int)uniformIndex[location].element, count);
memcpy(targetUniform->data + uniformIndex[location].element * sizeof(GLfloat) * 9,
value, 9 * sizeof(GLfloat) * count);
return true;
}
bool Program::setUniformMatrix4fv(GLint location, GLsizei count, const GLfloat *value)
{
if(location < 0 || location >= (int)uniformIndex.size())
{
return false;
}
Uniform *targetUniform = uniforms[uniformIndex[location].index];
targetUniform->dirty = true;
if(targetUniform->type != GL_FLOAT_MAT4)
{
return false;
}
int size = targetUniform->size();
if(size == 1 && count > 1)
{
return false; // Attempting to write an array to a non-array uniform is an INVALID_OPERATION
}
count = std::min(size - (int)uniformIndex[location].element, count);
memcpy(targetUniform->data + uniformIndex[location].element * sizeof(GLfloat) * 16,
value, 16 * sizeof(GLfloat) * count);
return true;
}
bool Program::setUniform1iv(GLint location, GLsizei count, const GLint *v)
{
if(location < 0 || location >= (int)uniformIndex.size())
{
return false;
}
Uniform *targetUniform = uniforms[uniformIndex[location].index];
targetUniform->dirty = true;
int size = targetUniform->size();
if(size == 1 && count > 1)
{
return false; // Attempting to write an array to a non-array uniform is an INVALID_OPERATION
}
count = std::min(size - (int)uniformIndex[location].element, count);
if(targetUniform->type == GL_INT ||
targetUniform->type == GL_SAMPLER_2D ||
targetUniform->type == GL_SAMPLER_CUBE ||
targetUniform->type == GL_SAMPLER_EXTERNAL_OES)
{
memcpy(targetUniform->data + uniformIndex[location].element * sizeof(GLint),
v, sizeof(GLint) * count);
}
else if(targetUniform->type == GL_BOOL)
{
GLboolean *boolParams = new GLboolean[count];
for(int i = 0; i < count; i++)
{
if(v[i] == 0)
{
boolParams[i] = GL_FALSE;
}
else
{
boolParams[i] = GL_TRUE;
}
}
memcpy(targetUniform->data + uniformIndex[location].element * sizeof(GLboolean),
boolParams, sizeof(GLboolean) * count);
delete[] boolParams;
}
else
{
return false;
}
return true;
}
bool Program::setUniform2iv(GLint location, GLsizei count, const GLint *v)
{
if(location < 0 || location >= (int)uniformIndex.size())
{
return false;
}
Uniform *targetUniform = uniforms[uniformIndex[location].index];
targetUniform->dirty = true;
int size = targetUniform->size();
if(size == 1 && count > 1)
{
return false; // Attempting to write an array to a non-array uniform is an INVALID_OPERATION
}
count = std::min(size - (int)uniformIndex[location].element, count);
if(targetUniform->type == GL_INT_VEC2)
{
memcpy(targetUniform->data + uniformIndex[location].element * sizeof(GLint) * 2,
v, 2 * sizeof(GLint) * count);
}
else if(targetUniform->type == GL_BOOL_VEC2)
{
GLboolean *boolParams = new GLboolean[count * 2];
for(int i = 0; i < count * 2; i++)
{
if(v[i] == 0)
{
boolParams[i] = GL_FALSE;
}
else
{
boolParams[i] = GL_TRUE;
}
}
memcpy(targetUniform->data + uniformIndex[location].element * sizeof(GLboolean) * 2,
boolParams, 2 * sizeof(GLboolean) * count);
delete[] boolParams;
}
else
{
return false;
}
return true;
}
bool Program::setUniform3iv(GLint location, GLsizei count, const GLint *v)
{
if(location < 0 || location >= (int)uniformIndex.size())
{
return false;
}
Uniform *targetUniform = uniforms[uniformIndex[location].index];
targetUniform->dirty = true;
int size = targetUniform->size();
if(size == 1 && count > 1)
{
return false; // Attempting to write an array to a non-array uniform is an INVALID_OPERATION
}
count = std::min(size - (int)uniformIndex[location].element, count);
if(targetUniform->type == GL_INT_VEC3)
{
memcpy(targetUniform->data + uniformIndex[location].element * sizeof(GLint) * 3,
v, 3 * sizeof(GLint) * count);
}
else if(targetUniform->type == GL_BOOL_VEC3)
{
GLboolean *boolParams = new GLboolean[count * 3];
for(int i = 0; i < count * 3; i++)
{
if(v[i] == 0)
{
boolParams[i] = GL_FALSE;
}
else
{
boolParams[i] = GL_TRUE;
}
}
memcpy(targetUniform->data + uniformIndex[location].element * sizeof(GLboolean) * 3,
boolParams, 3 * sizeof(GLboolean) * count);
delete[] boolParams;
}
else
{
return false;
}
return true;
}
bool Program::setUniform4iv(GLint location, GLsizei count, const GLint *v)
{
if(location < 0 || location >= (int)uniformIndex.size())
{
return false;
}
Uniform *targetUniform = uniforms[uniformIndex[location].index];
targetUniform->dirty = true;
int size = targetUniform->size();
if(size == 1 && count > 1)
{
return false; // Attempting to write an array to a non-array uniform is an INVALID_OPERATION
}
count = std::min(size - (int)uniformIndex[location].element, count);
if(targetUniform->type == GL_INT_VEC4)
{
memcpy(targetUniform->data + uniformIndex[location].element * sizeof(GLint) * 4,
v, 4 * sizeof(GLint) * count);
}
else if(targetUniform->type == GL_BOOL_VEC4)
{
GLboolean *boolParams = new GLboolean[count * 4];
for(int i = 0; i < count * 4; i++)
{
if(v[i] == 0)
{
boolParams[i] = GL_FALSE;
}
else
{
boolParams[i] = GL_TRUE;
}
}
memcpy(targetUniform->data + uniformIndex[location].element * sizeof(GLboolean) * 4,
boolParams, 4 * sizeof(GLboolean) * count);
delete[] boolParams;
}
else
{
return false;
}
return true;
}
bool Program::getUniformfv(GLint location, GLsizei *bufSize, GLfloat *params)
{
if(location < 0 || location >= (int)uniformIndex.size())
{
return false;
}
Uniform *targetUniform = uniforms[uniformIndex[location].index];
unsigned int count = UniformComponentCount(targetUniform->type);
// Sized query - ensure the provided buffer is large enough
if(bufSize && *bufSize < count * sizeof(GLfloat))
{
return false;
}
switch (UniformComponentType(targetUniform->type))
{
case GL_BOOL:
{
GLboolean *boolParams = (GLboolean*)targetUniform->data + uniformIndex[location].element * count;
for(unsigned int i = 0; i < count; i++)
{
params[i] = (boolParams[i] == GL_FALSE) ? 0.0f : 1.0f;
}
}
break;
case GL_FLOAT:
memcpy(params, targetUniform->data + uniformIndex[location].element * count * sizeof(GLfloat),
count * sizeof(GLfloat));
break;
case GL_INT:
{
GLint *intParams = (GLint*)targetUniform->data + uniformIndex[location].element * count;
for(unsigned int i = 0; i < count; i++)
{
params[i] = (float)intParams[i];
}
}
break;
default: UNREACHABLE();
}
return true;
}
bool Program::getUniformiv(GLint location, GLsizei *bufSize, GLint *params)
{
if(location < 0 || location >= (int)uniformIndex.size())
{
return false;
}
Uniform *targetUniform = uniforms[uniformIndex[location].index];
unsigned int count = UniformComponentCount(targetUniform->type);
// Sized query - ensure the provided buffer is large enough
if(bufSize && *bufSize < count * sizeof(GLint))
{
return false;
}
switch (UniformComponentType(targetUniform->type))
{
case GL_BOOL:
{
GLboolean *boolParams = targetUniform->data + uniformIndex[location].element * count;
for(unsigned int i = 0; i < count; i++)
{
params[i] = (GLint)boolParams[i];
}
}
break;
case GL_FLOAT:
{
GLfloat *floatParams = (GLfloat*)targetUniform->data + uniformIndex[location].element * count;
for(unsigned int i = 0; i < count; i++)
{
params[i] = (GLint)floatParams[i];
}
}
break;
case GL_INT:
memcpy(params, targetUniform->data + uniformIndex[location].element * count * sizeof(GLint),
count * sizeof(GLint));
break;
default: UNREACHABLE();
}
return true;
}
void Program::dirtyAllUniforms()
{
unsigned int numUniforms = uniforms.size();
for(unsigned int index = 0; index < numUniforms; index++)
{
uniforms[index]->dirty = true;
}
}
// Applies all the uniforms set for this program object to the device
void Program::applyUniforms()
{
unsigned int numUniforms = uniformIndex.size();
for(unsigned int location = 0; location < numUniforms; location++)
{
if(uniformIndex[location].element != 0)
{
continue;
}
Uniform *targetUniform = uniforms[uniformIndex[location].index];
if(targetUniform->dirty)
{
int size = targetUniform->size();
GLfloat *f = (GLfloat*)targetUniform->data;
GLint *i = (GLint*)targetUniform->data;
GLboolean *b = (GLboolean*)targetUniform->data;
switch(targetUniform->type)
{
case GL_BOOL: applyUniform1bv(location, size, b); break;
case GL_BOOL_VEC2: applyUniform2bv(location, size, b); break;
case GL_BOOL_VEC3: applyUniform3bv(location, size, b); break;
case GL_BOOL_VEC4: applyUniform4bv(location, size, b); break;
case GL_FLOAT: applyUniform1fv(location, size, f); break;
case GL_FLOAT_VEC2: applyUniform2fv(location, size, f); break;
case GL_FLOAT_VEC3: applyUniform3fv(location, size, f); break;
case GL_FLOAT_VEC4: applyUniform4fv(location, size, f); break;
case GL_FLOAT_MAT2: applyUniformMatrix2fv(location, size, f); break;
case GL_FLOAT_MAT3: applyUniformMatrix3fv(location, size, f); break;
case GL_FLOAT_MAT4: applyUniformMatrix4fv(location, size, f); break;
case GL_SAMPLER_2D:
case GL_SAMPLER_CUBE:
case GL_SAMPLER_EXTERNAL_OES:
case GL_INT: applyUniform1iv(location, size, i); break;
case GL_INT_VEC2: applyUniform2iv(location, size, i); break;
case GL_INT_VEC3: applyUniform3iv(location, size, i); break;
case GL_INT_VEC4: applyUniform4iv(location, size, i); break;
default:
UNREACHABLE();
}
targetUniform->dirty = false;
}
}
}
// Packs varyings into generic varying registers, using the algorithm from [OpenGL ES Shading Language 1.00 rev. 17] appendix A section 7 page 111
// Returns the number of used varying registers, or -1 if unsuccesful
int Program::packVaryings(const glsl::Varying *packing[][4])
{
for(glsl::VaryingList::iterator varying = fragmentShader->varyings.begin(); varying != fragmentShader->varyings.end(); varying++)
{
int n = VariableRowCount(varying->type) * varying->size();
int m = VariableColumnCount(varying->type);
bool success = false;
if(m == 2 || m == 3 || m == 4)
{
for(int r = 0; r <= MAX_VARYING_VECTORS - n && !success; r++)
{
bool available = true;
for(int y = 0; y < n && available; y++)
{
for(int x = 0; x < m && available; x++)
{
if(packing[r + y][x])
{
available = false;
}
}
}
if(available)
{
varying->reg = r;
varying->col = 0;
for(int y = 0; y < n; y++)
{
for(int x = 0; x < m; x++)
{
packing[r + y][x] = &*varying;
}
}
success = true;
}
}
if(!success && m == 2)
{
for(int r = MAX_VARYING_VECTORS - n; r >= 0 && !success; r--)
{
bool available = true;
for(int y = 0; y < n && available; y++)
{
for(int x = 2; x < 4 && available; x++)
{
if(packing[r + y][x])
{
available = false;
}
}
}
if(available)
{
varying->reg = r;
varying->col = 2;
for(int y = 0; y < n; y++)
{
for(int x = 2; x < 4; x++)
{
packing[r + y][x] = &*varying;
}
}
success = true;
}
}
}
}
else if(m == 1)
{
int space[4] = {0};
for(int y = 0; y < MAX_VARYING_VECTORS; y++)
{
for(int x = 0; x < 4; x++)
{
space[x] += packing[y][x] ? 0 : 1;
}
}
int column = 0;
for(int x = 0; x < 4; x++)
{
if(space[x] >= n && space[x] < space[column])
{
column = x;
}
}
if(space[column] >= n)
{
for(int r = 0; r < MAX_VARYING_VECTORS; r++)
{
if(!packing[r][column])
{
varying->reg = r;
for(int y = r; y < r + n; y++)
{
packing[y][column] = &*varying;
}
break;
}
}
varying->col = column;
success = true;
}
}
else UNREACHABLE();
if(!success)
{
appendToInfoLog("Could not pack varying %s", varying->name.c_str());
return -1;
}
}
// Return the number of used registers
int registers = 0;
for(int r = 0; r < MAX_VARYING_VECTORS; r++)
{
if(packing[r][0] || packing[r][1] || packing[r][2] || packing[r][3])
{
registers++;
}
}
return registers;
}
bool Program::linkVaryings()
{
for(glsl::VaryingList::iterator input = fragmentShader->varyings.begin(); input != fragmentShader->varyings.end(); input++)
{
bool matched = false;
for(glsl::VaryingList::iterator output = vertexShader->varyings.begin(); output != vertexShader->varyings.end(); output++)
{
if(output->name == input->name)
{
if(output->type != input->type || output->size() != input->size())
{
appendToInfoLog("Type of vertex varying %s does not match that of the fragment varying", output->name.c_str());
return false;
}
matched = true;
break;
}
}
if(!matched)
{
appendToInfoLog("Fragment varying %s does not match any vertex varying", input->name.c_str());
return false;
}
}
glsl::VaryingList &psVaryings = fragmentShader->varyings;
glsl::VaryingList &vsVaryings = vertexShader->varyings;
for(glsl::VaryingList::iterator output = vsVaryings.begin(); output != vsVaryings.end(); output++)
{
for(glsl::VaryingList::iterator input = psVaryings.begin(); input != psVaryings.end(); input++)
{
if(output->name == input->name)
{
int in = input->reg;
int out = output->reg;
int components = VariableColumnCount(output->type);
int registers = VariableRowCount(output->type) * output->size();
ASSERT(in >= 0);
if(in + registers > MAX_VARYING_VECTORS)
{
appendToInfoLog("Too many varyings");
return false;
}
if(out >= 0)
{
if(out + registers > MAX_VARYING_VECTORS)
{
appendToInfoLog("Too many varyings");
return false;
}
for(int i = 0; i < registers; i++)
{
if(components >= 1) vertexBinary->output[out + i][0] = sw::Shader::Semantic(sw::Shader::USAGE_COLOR, in + i);
if(components >= 2) vertexBinary->output[out + i][1] = sw::Shader::Semantic(sw::Shader::USAGE_COLOR, in + i);
if(components >= 3) vertexBinary->output[out + i][2] = sw::Shader::Semantic(sw::Shader::USAGE_COLOR, in + i);
if(components >= 4) vertexBinary->output[out + i][3] = sw::Shader::Semantic(sw::Shader::USAGE_COLOR, in + i);
}
}
else // Vertex varying is declared but not written to
{
for(int i = 0; i < registers; i++)
{
if(components >= 1) pixelBinary->semantic[in + i][0] = sw::Shader::Semantic();
if(components >= 2) pixelBinary->semantic[in + i][1] = sw::Shader::Semantic();
if(components >= 3) pixelBinary->semantic[in + i][2] = sw::Shader::Semantic();
if(components >= 4) pixelBinary->semantic[in + i][3] = sw::Shader::Semantic();
}
}
break;
}
}
}
return true;
}
// Links the 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
void Program::link()
{
unlink();
if(!fragmentShader || !fragmentShader->isCompiled())
{
return;
}
if(!vertexShader || !vertexShader->isCompiled())
{
return;
}
vertexBinary = new sw::VertexShader(vertexShader->getVertexShader());
pixelBinary = new sw::PixelShader(fragmentShader->getPixelShader());
if(!linkVaryings())
{
return;
}
if(!linkAttributes())
{
return;
}
if(!linkUniforms(fragmentShader))
{
return;
}
if(!linkUniforms(vertexShader))
{
return;
}
linked = true; // Success
}
// Determines the mapping between GL attributes and vertex stream usage indices
bool Program::linkAttributes()
{
unsigned int usedLocations = 0;
// Link attributes that have a binding location
for(glsl::ActiveAttributes::iterator attribute = vertexShader->activeAttributes.begin(); attribute != vertexShader->activeAttributes.end(); attribute++)
{
int location = getAttributeBinding(attribute->name);
if(location != -1) // Set by glBindAttribLocation
{
if(!linkedAttribute[location].name.empty())
{
// Multiple active attributes bound to the same location; not an error
}
linkedAttribute[location] = *attribute;
int rows = VariableRowCount(attribute->type);
if(rows + location > MAX_VERTEX_ATTRIBS)
{
appendToInfoLog("Active attribute (%s) at location %d is too big to fit", attribute->name.c_str(), location);
return false;
}
for(int i = 0; i < rows; i++)
{
usedLocations |= 1 << (location + i);
}
}
}
// Link attributes that don't have a binding location
for(glsl::ActiveAttributes::iterator attribute = vertexShader->activeAttributes.begin(); attribute != vertexShader->activeAttributes.end(); attribute++)
{
int location = getAttributeBinding(attribute->name);
if(location == -1) // Not set by glBindAttribLocation
{
int rows = VariableRowCount(attribute->type);
int availableIndex = AllocateFirstFreeBits(&usedLocations, rows, MAX_VERTEX_ATTRIBS);
if(availableIndex == -1 || availableIndex + rows > MAX_VERTEX_ATTRIBS)
{
appendToInfoLog("Too many active attributes (%s)", attribute->name.c_str());
return false; // Fail to link
}
linkedAttribute[availableIndex] = *attribute;
}
}
for(int attributeIndex = 0; attributeIndex < MAX_VERTEX_ATTRIBS; )
{
int index = vertexShader->getSemanticIndex(linkedAttribute[attributeIndex].name);
int rows = std::max(VariableRowCount(linkedAttribute[attributeIndex].type), 1);
for(int r = 0; r < rows; r++)
{
attributeStream[attributeIndex++] = index++;
}
}
return true;
}
int Program::getAttributeBinding(const std::string &name)
{
for(int location = 0; location < MAX_VERTEX_ATTRIBS; location++)
{
if(attributeBinding[location].find(name) != attributeBinding[location].end())
{
return location;
}
}
return -1;
}
bool Program::linkUniforms(Shader *shader)
{
const glsl::ActiveUniforms &activeUniforms = shader->activeUniforms;
for(unsigned int uniformIndex = 0; uniformIndex < activeUniforms.size(); uniformIndex++)
{
const glsl::Uniform &uniform = activeUniforms[uniformIndex];
if(!defineUniform(shader->getType(), uniform.type, uniform.precision, uniform.name, uniform.arraySize, uniform.registerIndex))
{
return false;
}
}
return true;
}
bool Program::defineUniform(GLenum shader, GLenum type, GLenum precision, const std::string &name, unsigned int arraySize, int registerIndex)
{
if(type == GL_SAMPLER_2D || type == GL_SAMPLER_CUBE || type == GL_SAMPLER_EXTERNAL_OES)
{
int index = registerIndex;
do
{
if(shader == GL_VERTEX_SHADER)
{
if(index < MAX_VERTEX_TEXTURE_IMAGE_UNITS)
{
samplersVS[index].active = true;
samplersVS[index].textureType = (type == GL_SAMPLER_CUBE) ? TEXTURE_CUBE : TEXTURE_2D;
samplersVS[index].logicalTextureUnit = 0;
}
else
{
appendToInfoLog("Vertex shader sampler count exceeds MAX_VERTEX_TEXTURE_IMAGE_UNITS (%d).", MAX_VERTEX_TEXTURE_IMAGE_UNITS);
return false;
}
}
else if(shader == GL_FRAGMENT_SHADER)
{
if(index < MAX_TEXTURE_IMAGE_UNITS)
{
samplersPS[index].active = true;
samplersPS[index].textureType = (type == GL_SAMPLER_CUBE) ? TEXTURE_CUBE : TEXTURE_2D;
samplersPS[index].logicalTextureUnit = 0;
}
else
{
appendToInfoLog("Pixel shader sampler count exceeds MAX_TEXTURE_IMAGE_UNITS (%d).", MAX_TEXTURE_IMAGE_UNITS);
return false;
}
}
else UNREACHABLE();
index++;
}
while(index < registerIndex + arraySize);
}
Uniform *uniform = 0;
GLint location = getUniformLocation(name);
if(location >= 0) // Previously defined, types must match
{
uniform = uniforms[uniformIndex[location].index];
if(uniform->type != type)
{
appendToInfoLog("Types for uniform %s do not match between the vertex and fragment shader", uniform->name.c_str());
return false;
}
if(uniform->precision != precision)
{
appendToInfoLog("Precisions for uniform %s do not match between the vertex and fragment shader", uniform->name.c_str());
return false;
}
}
else
{
uniform = new Uniform(type, precision, name, arraySize);
}
if(!uniform)
{
return false;
}
if(shader == GL_VERTEX_SHADER)
{
uniform->vsRegisterIndex = registerIndex;
}
else if(shader == GL_FRAGMENT_SHADER)
{
uniform->psRegisterIndex = registerIndex;
}
else UNREACHABLE();
if(location == -1) // Not previously defined
{
uniforms.push_back(uniform);
unsigned int index = uniforms.size() - 1;
for(unsigned int i = 0; i < uniform->size(); i++)
{
uniformIndex.push_back(UniformLocation(name, i, index));
}
}
if(shader == GL_VERTEX_SHADER)
{
if(registerIndex + uniform->registerCount() > MAX_VERTEX_UNIFORM_VECTORS)
{
appendToInfoLog("Vertex shader active uniforms exceed GL_MAX_VERTEX_UNIFORM_VECTORS (%d)", MAX_VERTEX_UNIFORM_VECTORS);
return false;
}
}
else if(shader == GL_FRAGMENT_SHADER)
{
if(registerIndex + uniform->registerCount() > MAX_FRAGMENT_UNIFORM_VECTORS)
{
appendToInfoLog("Fragment shader active uniforms exceed GL_MAX_FRAGMENT_UNIFORM_VECTORS (%d)", MAX_FRAGMENT_UNIFORM_VECTORS);
return false;
}
}
else UNREACHABLE();
return true;
}
bool Program::applyUniform1bv(GLint location, GLsizei count, const GLboolean *v)
{
int vector[MAX_UNIFORM_VECTORS][4];
for(int i = 0; i < count; i++)
{
vector[i][0] = (v[0] == GL_FALSE ? 0x00000000 : 0xFFFFFFFF);
vector[i][1] = 0;
vector[i][2] = 0;
vector[i][3] = 0;
v += 1;
}
Uniform *targetUniform = uniforms[uniformIndex[location].index];
if(targetUniform->psRegisterIndex != -1)
{
device->setPixelShaderConstantF(targetUniform->psRegisterIndex, (float*)vector, targetUniform->registerCount());
}
if(targetUniform->vsRegisterIndex != -1)
{
device->setVertexShaderConstantF(targetUniform->vsRegisterIndex, (float*)vector, targetUniform->registerCount());
}
return true;
}
bool Program::applyUniform2bv(GLint location, GLsizei count, const GLboolean *v)
{
int vector[MAX_UNIFORM_VECTORS][4];
for(int i = 0; i < count; i++)
{
vector[i][0] = (v[0] == GL_FALSE ? 0x00000000 : 0xFFFFFFFF);
vector[i][1] = (v[1] == GL_FALSE ? 0x00000000 : 0xFFFFFFFF);
vector[i][2] = 0;
vector[i][3] = 0;
v += 2;
}
Uniform *targetUniform = uniforms[uniformIndex[location].index];
if(targetUniform->psRegisterIndex != -1)
{
device->setPixelShaderConstantF(targetUniform->psRegisterIndex, (float*)vector, targetUniform->registerCount());
}
if(targetUniform->vsRegisterIndex != -1)
{
device->setVertexShaderConstantF(targetUniform->vsRegisterIndex, (float*)vector, targetUniform->registerCount());
}
return true;
}
bool Program::applyUniform3bv(GLint location, GLsizei count, const GLboolean *v)
{
int vector[MAX_UNIFORM_VECTORS][4];
for(int i = 0; i < count; i++)
{
vector[i][0] = (v[0] == GL_FALSE ? 0x00000000 : 0xFFFFFFFF);
vector[i][1] = (v[1] == GL_FALSE ? 0x00000000 : 0xFFFFFFFF);
vector[i][2] = (v[2] == GL_FALSE ? 0x00000000 : 0xFFFFFFFF);
vector[i][3] = 0;
v += 3;
}
Uniform *targetUniform = uniforms[uniformIndex[location].index];
if(targetUniform->psRegisterIndex != -1)
{
device->setPixelShaderConstantF(targetUniform->psRegisterIndex, (float*)vector, targetUniform->registerCount());
}
if(targetUniform->vsRegisterIndex != -1)
{
device->setVertexShaderConstantF(targetUniform->vsRegisterIndex, (float*)vector, targetUniform->registerCount());
}
return true;
}
bool Program::applyUniform4bv(GLint location, GLsizei count, const GLboolean *v)
{
int vector[MAX_UNIFORM_VECTORS][4];
for(int i = 0; i < count; i++)
{
vector[i][0] = (v[0] == GL_FALSE ? 0x00000000 : 0xFFFFFFFF);
vector[i][1] = (v[1] == GL_FALSE ? 0x00000000 : 0xFFFFFFFF);
vector[i][2] = (v[2] == GL_FALSE ? 0x00000000 : 0xFFFFFFFF);
vector[i][3] = (v[3] == GL_FALSE ? 0x00000000 : 0xFFFFFFFF);
v += 4;
}
Uniform *targetUniform = uniforms[uniformIndex[location].index];
if(targetUniform->psRegisterIndex != -1)
{
device->setPixelShaderConstantF(targetUniform->psRegisterIndex, (float*)vector, targetUniform->registerCount());
}
if(targetUniform->vsRegisterIndex != -1)
{
device->setVertexShaderConstantF(targetUniform->vsRegisterIndex, (float*)vector, targetUniform->registerCount());
}
return true;
}
bool Program::applyUniform1fv(GLint location, GLsizei count, const GLfloat *v)
{
float vector[MAX_UNIFORM_VECTORS][4];
for(int i = 0; i < count; i++)
{
vector[i][0] = v[0];
vector[i][1] = 0;
vector[i][2] = 0;
vector[i][3] = 0;
v += 1;
}
Uniform *targetUniform = uniforms[uniformIndex[location].index];
if(targetUniform->psRegisterIndex != -1)
{
device->setPixelShaderConstantF(targetUniform->psRegisterIndex, (float*)vector, targetUniform->registerCount());
}
if(targetUniform->vsRegisterIndex != -1)
{
device->setVertexShaderConstantF(targetUniform->vsRegisterIndex, (float*)vector, targetUniform->registerCount());
}
return true;
}
bool Program::applyUniform2fv(GLint location, GLsizei count, const GLfloat *v)
{
float vector[MAX_UNIFORM_VECTORS][4];
for(int i = 0; i < count; i++)
{
vector[i][0] = v[0];
vector[i][1] = v[1];
vector[i][2] = 0;
vector[i][3] = 0;
v += 2;
}
Uniform *targetUniform = uniforms[uniformIndex[location].index];
if(targetUniform->psRegisterIndex != -1)
{
device->setPixelShaderConstantF(targetUniform->psRegisterIndex, (float*)vector, targetUniform->registerCount());
}
if(targetUniform->vsRegisterIndex != -1)
{
device->setVertexShaderConstantF(targetUniform->vsRegisterIndex, (float*)vector, targetUniform->registerCount());
}
return true;
}
bool Program::applyUniform3fv(GLint location, GLsizei count, const GLfloat *v)
{
float vector[MAX_UNIFORM_VECTORS][4];
for(int i = 0; i < count; i++)
{
vector[i][0] = v[0];
vector[i][1] = v[1];
vector[i][2] = v[2];
vector[i][3] = 0;
v += 3;
}
Uniform *targetUniform = uniforms[uniformIndex[location].index];
if(targetUniform->psRegisterIndex != -1)
{
device->setPixelShaderConstantF(targetUniform->psRegisterIndex, (float*)vector, targetUniform->registerCount());
}
if(targetUniform->vsRegisterIndex != -1)
{
device->setVertexShaderConstantF(targetUniform->vsRegisterIndex, (float*)vector, targetUniform->registerCount());
}
return true;
}
bool Program::applyUniform4fv(GLint location, GLsizei count, const GLfloat *v)
{
Uniform *targetUniform = uniforms[uniformIndex[location].index];
if(targetUniform->psRegisterIndex != -1)
{
device->setPixelShaderConstantF(targetUniform->psRegisterIndex, (float*)v, targetUniform->registerCount());
}
if(targetUniform->vsRegisterIndex != -1)
{
device->setVertexShaderConstantF(targetUniform->vsRegisterIndex, (float*)v, targetUniform->registerCount());
}
return true;
}
bool Program::applyUniformMatrix2fv(GLint location, GLsizei count, const GLfloat *value)
{
float matrix[(MAX_UNIFORM_VECTORS + 1) / 2][2][4];
for(int i = 0; i < count; i++)
{
matrix[i][0][0] = value[0]; matrix[i][0][1] = value[1]; matrix[i][0][2] = 0; matrix[i][0][3] = 0;
matrix[i][1][0] = value[2]; matrix[i][1][1] = value[3]; matrix[i][1][2] = 0; matrix[i][1][3] = 0;
value += 4;
}
Uniform *targetUniform = uniforms[uniformIndex[location].index];
if(targetUniform->psRegisterIndex != -1)
{
device->setPixelShaderConstantF(targetUniform->psRegisterIndex, (float*)matrix, targetUniform->registerCount());
}
if(targetUniform->vsRegisterIndex != -1)
{
device->setVertexShaderConstantF(targetUniform->vsRegisterIndex, (float*)matrix, targetUniform->registerCount());
}
return true;
}
bool Program::applyUniformMatrix3fv(GLint location, GLsizei count, const GLfloat *value)
{
float matrix[(MAX_UNIFORM_VECTORS + 2) / 3][3][4];
for(int i = 0; i < count; i++)
{
matrix[i][0][0] = value[0]; matrix[i][0][1] = value[1]; matrix[i][0][2] = value[2]; matrix[i][0][3] = 0;
matrix[i][1][0] = value[3]; matrix[i][1][1] = value[4]; matrix[i][1][2] = value[5]; matrix[i][1][3] = 0;
matrix[i][2][0] = value[6]; matrix[i][2][1] = value[7]; matrix[i][2][2] = value[8]; matrix[i][2][3] = 0;
value += 9;
}
Uniform *targetUniform = uniforms[uniformIndex[location].index];
if(targetUniform->psRegisterIndex != -1)
{
device->setPixelShaderConstantF(targetUniform->psRegisterIndex, (float*)matrix, targetUniform->registerCount());
}
if(targetUniform->vsRegisterIndex != -1)
{
device->setVertexShaderConstantF(targetUniform->vsRegisterIndex, (float*)matrix, targetUniform->registerCount());
}
return true;
}
bool Program::applyUniformMatrix4fv(GLint location, GLsizei count, const GLfloat *value)
{
Uniform *targetUniform = uniforms[uniformIndex[location].index];
if(targetUniform->psRegisterIndex != -1)
{
device->setPixelShaderConstantF(targetUniform->psRegisterIndex, (float*)value, targetUniform->registerCount());
}
if(targetUniform->vsRegisterIndex != -1)
{
device->setVertexShaderConstantF(targetUniform->vsRegisterIndex, (float*)value, targetUniform->registerCount());
}
return true;
}
bool Program::applyUniform1iv(GLint location, GLsizei count, const GLint *v)
{
float vector[MAX_UNIFORM_VECTORS][4];
for(int i = 0; i < count; i++)
{
vector[i][0] = (float)v[i];
vector[i][1] = 0;
vector[i][2] = 0;
vector[i][3] = 0;
}
Uniform *targetUniform = uniforms[uniformIndex[location].index];
if(targetUniform->psRegisterIndex != -1)
{
if(targetUniform->type == GL_SAMPLER_2D ||
targetUniform->type == GL_SAMPLER_CUBE ||
targetUniform->type == GL_SAMPLER_EXTERNAL_OES)
{
for(int i = 0; i < count; i++)
{
unsigned int samplerIndex = targetUniform->psRegisterIndex + i;
if(samplerIndex < MAX_TEXTURE_IMAGE_UNITS)
{
ASSERT(samplersPS[samplerIndex].active);
samplersPS[samplerIndex].logicalTextureUnit = v[i];
}
}
}
else
{
device->setPixelShaderConstantF(targetUniform->psRegisterIndex, (float*)vector, targetUniform->registerCount());
}
}
if(targetUniform->vsRegisterIndex != -1)
{
if(targetUniform->type == GL_SAMPLER_2D ||
targetUniform->type == GL_SAMPLER_CUBE ||
targetUniform->type == GL_SAMPLER_EXTERNAL_OES)
{
for(int i = 0; i < count; i++)
{
unsigned int samplerIndex = targetUniform->vsRegisterIndex + i;
if(samplerIndex < MAX_VERTEX_TEXTURE_IMAGE_UNITS)
{
ASSERT(samplersVS[samplerIndex].active);
samplersVS[samplerIndex].logicalTextureUnit = v[i];
}
}
}
else
{
device->setVertexShaderConstantF(targetUniform->vsRegisterIndex, (float*)vector, targetUniform->registerCount());
}
}
return true;
}
bool Program::applyUniform2iv(GLint location, GLsizei count, const GLint *v)
{
float vector[MAX_UNIFORM_VECTORS][4];
for(int i = 0; i < count; i++)
{
vector[i][0] = (float)v[0];
vector[i][1] = (float)v[1];
vector[i][2] = 0;
vector[i][3] = 0;
v += 2;
}
Uniform *targetUniform = uniforms[uniformIndex[location].index];
if(targetUniform->psRegisterIndex != -1)
{
device->setPixelShaderConstantF(targetUniform->psRegisterIndex, (float*)vector, targetUniform->registerCount());
}
if(targetUniform->vsRegisterIndex != -1)
{
device->setVertexShaderConstantF(targetUniform->vsRegisterIndex, (float*)vector, targetUniform->registerCount());
}
return true;
}
bool Program::applyUniform3iv(GLint location, GLsizei count, const GLint *v)
{
float vector[MAX_UNIFORM_VECTORS][4];
for(int i = 0; i < count; i++)
{
vector[i][0] = (float)v[0];
vector[i][1] = (float)v[1];
vector[i][2] = (float)v[2];
vector[i][3] = 0;
v += 3;
}
Uniform *targetUniform = uniforms[uniformIndex[location].index];
if(targetUniform->psRegisterIndex != -1)
{
device->setPixelShaderConstantF(targetUniform->psRegisterIndex, (float*)vector, targetUniform->registerCount());
}
if(targetUniform->vsRegisterIndex != -1)
{
device->setVertexShaderConstantF(targetUniform->vsRegisterIndex, (float*)vector, targetUniform->registerCount());
}
return true;
}
bool Program::applyUniform4iv(GLint location, GLsizei count, const GLint *v)
{
float vector[MAX_UNIFORM_VECTORS][4];
for(int i = 0; i < count; i++)
{
vector[i][0] = (float)v[0];
vector[i][1] = (float)v[1];
vector[i][2] = (float)v[2];
vector[i][3] = (float)v[3];
v += 4;
}
Uniform *targetUniform = uniforms[uniformIndex[location].index];
if(targetUniform->psRegisterIndex != -1)
{
device->setPixelShaderConstantF(targetUniform->psRegisterIndex, (float*)vector, targetUniform->registerCount());
}
if(targetUniform->vsRegisterIndex != -1)
{
device->setVertexShaderConstantF(targetUniform->vsRegisterIndex, (float*)vector, targetUniform->registerCount());
}
return true;
}
void Program::appendToInfoLog(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(!infoLog)
{
infoLog = new char[infoLength + 2];
strcpy(infoLog, info);
strcpy(infoLog + infoLength, "\n");
}
else
{
size_t logLength = strlen(infoLog);
char *newLog = new char[logLength + infoLength + 2];
strcpy(newLog, infoLog);
strcpy(newLog + logLength, info);
strcpy(newLog + logLength + infoLength, "\n");
delete[] infoLog;
infoLog = newLog;
}
}
void Program::resetInfoLog()
{
if(infoLog)
{
delete[] infoLog;
infoLog = 0;
}
}
// Returns the program object to an unlinked state, before re-linking, or at destruction
void Program::unlink()
{
delete vertexBinary;
vertexBinary = 0;
delete pixelBinary;
pixelBinary = 0;
for(int index = 0; index < MAX_VERTEX_ATTRIBS; index++)
{
linkedAttribute[index].name.clear();
attributeStream[index] = -1;
}
for(int index = 0; index < MAX_TEXTURE_IMAGE_UNITS; index++)
{
samplersPS[index].active = false;
}
for(int index = 0; index < MAX_VERTEX_TEXTURE_IMAGE_UNITS; index++)
{
samplersVS[index].active = false;
}
while(!uniforms.empty())
{
delete uniforms.back();
uniforms.pop_back();
}
uniformIndex.clear();
delete[] infoLog;
infoLog = 0;
linked = false;
}
bool Program::isLinked()
{
return linked;
}
bool Program::isValidated() const
{
return validated;
}
void Program::release()
{
referenceCount--;
if(referenceCount == 0)
{
delete this;
}
}
void Program::addRef()
{
referenceCount++;
}
unsigned int Program::getRefCount() const
{
return referenceCount;
}
unsigned int Program::getSerial() const
{
return serial;
}
unsigned int Program::issueSerial()
{
return currentSerial++;
}
int Program::getInfoLogLength() const
{
if(!infoLog)
{
return 0;
}
else
{
return strlen(infoLog) + 1;
}
}
void Program::getInfoLog(GLsizei bufSize, GLsizei *length, char *buffer)
{
int index = 0;
if(bufSize > 0)
{
if(infoLog)
{
index = std::min(bufSize - 1, (int)strlen(infoLog));
memcpy(buffer, infoLog, index);
}
buffer[index] = '\0';
}
if(length)
{
*length = index;
}
}
void Program::getAttachedShaders(GLsizei maxCount, GLsizei *count, GLuint *shaders)
{
int total = 0;
if(vertexShader)
{
if(total < maxCount)
{
shaders[total] = vertexShader->getHandle();
}
total++;
}
if(fragmentShader)
{
if(total < maxCount)
{
shaders[total] = fragmentShader->getHandle();
}
total++;
}
if(count)
{
*count = total;
}
}
void Program::getActiveAttribute(GLuint index, GLsizei bufsize, GLsizei *length, GLint *size, GLenum *type, GLchar *name) const
{
// Skip over inactive attributes
unsigned int activeAttribute = 0;
unsigned int attribute;
for(attribute = 0; attribute < MAX_VERTEX_ATTRIBS; attribute++)
{
if(linkedAttribute[attribute].name.empty())
{
continue;
}
if(activeAttribute == index)
{
break;
}
activeAttribute++;
}
if(bufsize > 0)
{
const char *string = linkedAttribute[attribute].name.c_str();
strncpy(name, string, bufsize);
name[bufsize - 1] = '\0';
if(length)
{
*length = strlen(name);
}
}
*size = 1; // Always a single 'type' instance
*type = linkedAttribute[attribute].type;
}
GLint Program::getActiveAttributeCount() const
{
int count = 0;
for(int attributeIndex = 0; attributeIndex < MAX_VERTEX_ATTRIBS; attributeIndex++)
{
if(!linkedAttribute[attributeIndex].name.empty())
{
count++;
}
}
return count;
}
GLint Program::getActiveAttributeMaxLength() const
{
int maxLength = 0;
for(int attributeIndex = 0; attributeIndex < MAX_VERTEX_ATTRIBS; attributeIndex++)
{
if(!linkedAttribute[attributeIndex].name.empty())
{
maxLength = std::max((int)(linkedAttribute[attributeIndex].name.length() + 1), maxLength);
}
}
return maxLength;
}
void Program::getActiveUniform(GLuint index, GLsizei bufsize, GLsizei *length, GLint *size, GLenum *type, GLchar *name) const
{
if(bufsize > 0)
{
std::string string = uniforms[index]->name;
if(uniforms[index]->isArray())
{
string += "[0]";
}
strncpy(name, string.c_str(), bufsize);
name[bufsize - 1] = '\0';
if(length)
{
*length = strlen(name);
}
}
*size = uniforms[index]->size();
*type = uniforms[index]->type;
}
GLint Program::getActiveUniformCount() const
{
return uniforms.size();
}
GLint Program::getActiveUniformMaxLength() const
{
int maxLength = 0;
unsigned int numUniforms = uniforms.size();
for(unsigned int uniformIndex = 0; uniformIndex < numUniforms; uniformIndex++)
{
if(!uniforms[uniformIndex]->name.empty())
{
int length = (int)(uniforms[uniformIndex]->name.length() + 1);
if(uniforms[uniformIndex]->isArray())
{
length += 3; // Counting in "[0]".
}
maxLength = std::max(length, maxLength);
}
}
return maxLength;
}
void Program::flagForDeletion()
{
orphaned = true;
}
bool Program::isFlaggedForDeletion() const
{
return orphaned;
}
void Program::validate()
{
resetInfoLog();
if(!isLinked())
{
appendToInfoLog("Program has not been successfully linked.");
validated = false;
}
else
{
applyUniforms();
if(!validateSamplers(true))
{
validated = false;
}
else
{
validated = true;
}
}
}
bool Program::validateSamplers(bool logErrors)
{
// if any two active samplers in a program are of different types, but refer to the same
// texture image unit, and this is the current program, then ValidateProgram will fail, and
// DrawArrays and DrawElements will issue the INVALID_OPERATION error.
TextureType textureUnitType[MAX_COMBINED_TEXTURE_IMAGE_UNITS];
for(unsigned int i = 0; i < MAX_COMBINED_TEXTURE_IMAGE_UNITS; i++)
{
textureUnitType[i] = TEXTURE_UNKNOWN;
}
for(unsigned int i = 0; i < MAX_TEXTURE_IMAGE_UNITS; i++)
{
if(samplersPS[i].active)
{
unsigned int unit = samplersPS[i].logicalTextureUnit;
if(unit >= MAX_COMBINED_TEXTURE_IMAGE_UNITS)
{
if(logErrors)
{
appendToInfoLog("Sampler uniform (%d) exceeds MAX_COMBINED_TEXTURE_IMAGE_UNITS (%d)", unit, MAX_COMBINED_TEXTURE_IMAGE_UNITS);
}
return false;
}
if(textureUnitType[unit] != TEXTURE_UNKNOWN)
{
if(samplersPS[i].textureType != textureUnitType[unit])
{
if(logErrors)
{
appendToInfoLog("Samplers of conflicting types refer to the same texture image unit (%d).", unit);
}
return false;
}
}
else
{
textureUnitType[unit] = samplersPS[i].textureType;
}
}
}
for(unsigned int i = 0; i < MAX_VERTEX_TEXTURE_IMAGE_UNITS; i++)
{
if(samplersVS[i].active)
{
unsigned int unit = samplersVS[i].logicalTextureUnit;
if(unit >= MAX_COMBINED_TEXTURE_IMAGE_UNITS)
{
if(logErrors)
{
appendToInfoLog("Sampler uniform (%d) exceeds MAX_COMBINED_TEXTURE_IMAGE_UNITS (%d)", unit, MAX_COMBINED_TEXTURE_IMAGE_UNITS);
}
return false;
}
if(textureUnitType[unit] != TEXTURE_UNKNOWN)
{
if(samplersVS[i].textureType != textureUnitType[unit])
{
if(logErrors)
{
appendToInfoLog("Samplers of conflicting types refer to the same texture image unit (%d).", unit);
}
return false;
}
}
else
{
textureUnitType[unit] = samplersVS[i].textureType;
}
}
}
return true;
}
}