mirror of
https://github.com/smallmain/cocos-enhance-kit.git
synced 2025-01-14 15:01:07 +00:00
399 lines
13 KiB
C++
399 lines
13 KiB
C++
/****************************************************************************
|
|
Copyright (c) 2018 Xiamen Yaji Software Co., Ltd.
|
|
|
|
http://www.cocos2d-x.org
|
|
|
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
of this software and associated documentation files (the "Software"), to deal
|
|
in the Software without restriction, including without limitation the rights
|
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
copies of the Software, and to permit persons to whom the Software is
|
|
furnished to do so, subject to the following conditions:
|
|
|
|
The above copyright notice and this permission notice shall be included in
|
|
all copies or substantial portions of the Software.
|
|
|
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
THE SOFTWARE.
|
|
****************************************************************************/
|
|
|
|
#include "Program.h"
|
|
#include "GFXUtils.h"
|
|
|
|
#include <unordered_map>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
namespace {
|
|
|
|
uint32_t _genID = 0;
|
|
|
|
std::string logForOpenGLShader(GLuint shader)
|
|
{
|
|
GLint logLength = 0;
|
|
|
|
GL_CHECK(glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &logLength));
|
|
if (logLength < 1)
|
|
return "";
|
|
|
|
char *logBytes = (char*)malloc(sizeof(char) * logLength);
|
|
GL_CHECK(glGetShaderInfoLog(shader, logLength, nullptr, logBytes));
|
|
std::string ret(logBytes);
|
|
|
|
free(logBytes);
|
|
return ret;
|
|
}
|
|
|
|
std::string logForOpenGLProgram(GLuint program)
|
|
{
|
|
GLint logLength = 0;
|
|
|
|
GL_CHECK(glGetProgramiv(program, GL_INFO_LOG_LENGTH, &logLength));
|
|
if (logLength < 1)
|
|
return "";
|
|
|
|
char *logBytes = (char*)malloc(sizeof(char) * logLength);
|
|
GL_CHECK(glGetProgramInfoLog(program, logLength, nullptr, logBytes));
|
|
std::string ret(logBytes);
|
|
|
|
free(logBytes);
|
|
return ret;
|
|
}
|
|
|
|
bool _createShader(GLenum type, const std::string& src, GLuint* outShader)
|
|
{
|
|
assert(outShader != nullptr);
|
|
GLuint shader = glCreateShader(type);
|
|
const GLchar* sources[] = { src.c_str() };
|
|
GL_CHECK(glShaderSource(shader, 1, sources, nullptr));
|
|
GL_CHECK(glCompileShader(shader));
|
|
|
|
GLint status;
|
|
GL_CHECK(glGetShaderiv(shader, GL_COMPILE_STATUS, &status));
|
|
|
|
if (!status)
|
|
{
|
|
GLsizei length;
|
|
GL_CHECK(glGetShaderiv(shader, GL_SHADER_SOURCE_LENGTH, &length));
|
|
GLchar* source = (GLchar *)malloc(sizeof(GLchar) * length);
|
|
|
|
GL_CHECK(glGetShaderSource(shader, length, nullptr, source));
|
|
RENDERER_LOGE("ERROR: Failed to compile shader:\n%s", source);
|
|
|
|
std::string shaderLog = logForOpenGLShader(shader);
|
|
RENDERER_LOGE("%s", shaderLog.c_str());
|
|
|
|
free(source);
|
|
|
|
*outShader = 0;
|
|
return false;
|
|
}
|
|
|
|
*outShader = shader;
|
|
return true;
|
|
}
|
|
|
|
#define DEF_TO_INT(pointer) (*(int*)(pointer))
|
|
#define DEF_TO_FLOAT(pointer) (*(float*)(pointer))
|
|
|
|
void setUniform1i(GLint location, GLsizei count , const void* value, cocos2d::renderer::UniformElementType elementType)
|
|
{
|
|
assert(count == 1);
|
|
if (elementType == cocos2d::renderer::UniformElementType::INT)
|
|
{
|
|
glUniform1i(location, DEF_TO_INT(value));
|
|
}
|
|
else
|
|
{
|
|
float floatVal = *((float*)value);
|
|
GLint intVal = (GLint)floatVal;
|
|
glUniform1i(location, intVal);
|
|
}
|
|
}
|
|
|
|
void setUniform1iv(GLint location, GLsizei count , const void* value, cocos2d::renderer::UniformElementType elementType)
|
|
{
|
|
glUniform1iv(location, count, (const GLint*)value);
|
|
}
|
|
|
|
void setUniform2iv(GLint location, GLsizei count , const void* value, cocos2d::renderer::UniformElementType elementType)
|
|
{
|
|
glUniform2iv(location, count, (const GLint*)value);
|
|
}
|
|
|
|
void setUniform3iv(GLint location, GLsizei count , const void* value, cocos2d::renderer::UniformElementType elementType)
|
|
{
|
|
glUniform3iv(location, count, (const GLint*)value);
|
|
}
|
|
|
|
void setUniform4iv(GLint location, GLsizei count , const void* value, cocos2d::renderer::UniformElementType elementType)
|
|
{
|
|
glUniform4iv(location, count, (const GLint*)value);
|
|
}
|
|
|
|
void setUniform1f(GLint location, GLsizei count , const void* value, cocos2d::renderer::UniformElementType elementType)
|
|
{
|
|
assert(count == 1);
|
|
glUniform1f(location, DEF_TO_FLOAT(value));
|
|
}
|
|
|
|
void setUniform1fv(GLint location, GLsizei count , const void* value, cocos2d::renderer::UniformElementType elementType)
|
|
{
|
|
glUniform1fv(location, count, (const GLfloat*)value);
|
|
}
|
|
|
|
void setUniform2fv(GLint location, GLsizei count , const void* value, cocos2d::renderer::UniformElementType elementType)
|
|
{
|
|
glUniform2fv(location, count, (const GLfloat*)value);
|
|
}
|
|
|
|
void setUniform3fv(GLint location, GLsizei count , const void* value, cocos2d::renderer::UniformElementType elementType)
|
|
{
|
|
glUniform3fv(location, count, (const GLfloat*)value);
|
|
}
|
|
|
|
void setUniform4fv(GLint location, GLsizei count , const void* value, cocos2d::renderer::UniformElementType elementType)
|
|
{
|
|
glUniform4fv(location, count, (const GLfloat*)value);
|
|
}
|
|
|
|
void setUniformMatrix2fv(GLint location, GLsizei count, const void *value, cocos2d::renderer::UniformElementType elementType)
|
|
{
|
|
glUniformMatrix2fv(location, count, GL_FALSE, (const GLfloat*)value);
|
|
}
|
|
|
|
void setUniformMatrix3fv(GLint location, GLsizei count, const void *value, cocos2d::renderer::UniformElementType elementType)
|
|
{
|
|
glUniformMatrix3fv(location, count, GL_FALSE, (const GLfloat*)value);
|
|
}
|
|
|
|
void setUniformMatrix4fv(GLint location, GLsizei count, const void *value, cocos2d::renderer::UniformElementType elementType)
|
|
{
|
|
glUniformMatrix4fv(location, count, GL_FALSE, (const GLfloat*)value);
|
|
}
|
|
|
|
/**
|
|
* _type2uniformCommit
|
|
*/
|
|
std::unordered_map<GLenum, cocos2d::renderer::Program::Uniform::SetUniformCallback> _type2uniformCommit = {
|
|
{ GL_INT, setUniform1i },
|
|
{ GL_FLOAT, setUniform1f },
|
|
{ GL_FLOAT_VEC2, setUniform2fv },
|
|
{ GL_FLOAT_VEC3, setUniform3fv },
|
|
{ GL_FLOAT_VEC4, setUniform4fv },
|
|
{ GL_INT_VEC2, setUniform2iv },
|
|
{ GL_INT_VEC3, setUniform3iv },
|
|
{ GL_INT_VEC4, setUniform4iv },
|
|
{ GL_BOOL, setUniform1i },
|
|
{ GL_BOOL_VEC2, setUniform2iv },
|
|
{ GL_BOOL_VEC3, setUniform3iv },
|
|
{ GL_BOOL_VEC4, setUniform4iv },
|
|
{ GL_FLOAT_MAT2, setUniformMatrix2fv },
|
|
{ GL_FLOAT_MAT3, setUniformMatrix3fv },
|
|
{ GL_FLOAT_MAT4, setUniformMatrix4fv },
|
|
{ GL_SAMPLER_2D, setUniform1i },
|
|
{ GL_SAMPLER_CUBE, setUniform1i }
|
|
};
|
|
|
|
/**
|
|
* _type2uniformArrayCommit
|
|
*/
|
|
std::unordered_map<GLenum, cocos2d::renderer::Program::Uniform::SetUniformCallback> _type2uniformArrayCommit = {
|
|
{ GL_INT, setUniform1iv },
|
|
{ GL_FLOAT, setUniform1fv },
|
|
{ GL_FLOAT_VEC2, setUniform2fv },
|
|
{ GL_FLOAT_VEC3, setUniform3fv },
|
|
{ GL_FLOAT_VEC4, setUniform4fv },
|
|
{ GL_INT_VEC2, setUniform2iv },
|
|
{ GL_INT_VEC3, setUniform3iv },
|
|
{ GL_INT_VEC4, setUniform4iv },
|
|
{ GL_BOOL, setUniform1iv },
|
|
{ GL_BOOL_VEC2, setUniform2iv },
|
|
{ GL_BOOL_VEC3, setUniform3iv },
|
|
{ GL_BOOL_VEC4, setUniform4iv },
|
|
{ GL_FLOAT_MAT2, setUniformMatrix2fv },
|
|
{ GL_FLOAT_MAT3, setUniformMatrix3fv },
|
|
{ GL_FLOAT_MAT4, setUniformMatrix4fv },
|
|
{ GL_SAMPLER_2D, setUniform1iv },
|
|
{ GL_SAMPLER_CUBE, setUniform1iv }
|
|
};
|
|
} // namespace {
|
|
|
|
RENDERER_BEGIN
|
|
|
|
void Program::Uniform::setUniform(const void* value, UniformElementType elementType, size_t uniformCount) const
|
|
{
|
|
// uniformCount may bigger than size.
|
|
if (size >= 1 && size < uniformCount) uniformCount = size;
|
|
GLsizei count = size == -1 ? 1 : (GLsizei)uniformCount;
|
|
_callback(location, count, value, elementType);
|
|
}
|
|
|
|
Program::Program()
|
|
: _device(nullptr)
|
|
, _id(0)
|
|
, _linked(false)
|
|
{
|
|
|
|
}
|
|
|
|
Program::~Program()
|
|
{
|
|
GL_CHECK(glDeleteProgram(_glID));
|
|
}
|
|
|
|
bool Program::init(DeviceGraphics* device, const char* vertSource, const char* fragSource)
|
|
{
|
|
assert(device);
|
|
assert(vertSource);
|
|
assert(fragSource);
|
|
|
|
_device = device;
|
|
_vertSource = vertSource;
|
|
_fragSource = fragSource;
|
|
_id = _genID++;
|
|
_linked = false;
|
|
return true;
|
|
}
|
|
|
|
void Program::link()
|
|
{
|
|
if (_linked) {
|
|
return;
|
|
}
|
|
|
|
GLuint vertShader;
|
|
bool ok = _createShader(GL_VERTEX_SHADER, _vertSource, &vertShader);
|
|
if (!ok)
|
|
return;
|
|
|
|
GLuint fragShader;
|
|
ok = _createShader(GL_FRAGMENT_SHADER, _fragSource, &fragShader);
|
|
if (!ok)
|
|
{
|
|
glDeleteShader(vertShader);
|
|
return;
|
|
}
|
|
|
|
GLuint program = glCreateProgram();
|
|
GL_CHECK(glAttachShader(program, vertShader));
|
|
GL_CHECK(glAttachShader(program, fragShader));
|
|
GL_CHECK(glLinkProgram(program));
|
|
|
|
GLint status = GL_TRUE;
|
|
GL_CHECK(glGetProgramiv(program, GL_LINK_STATUS, &status));
|
|
|
|
if (status == GL_FALSE)
|
|
{
|
|
RENDERER_LOGE("ERROR: Failed to link program: %u", program);
|
|
std::string programLog = logForOpenGLProgram(program);
|
|
RENDERER_LOGE("%s", programLog.c_str());
|
|
glDeleteShader(vertShader);
|
|
glDeleteShader(fragShader);
|
|
glDeleteProgram(program);
|
|
return;
|
|
}
|
|
|
|
glDeleteShader(vertShader);
|
|
glDeleteShader(fragShader);
|
|
|
|
_glID = program;
|
|
|
|
// parse attribute
|
|
GLint numAttributes;
|
|
glGetProgramiv(program, GL_ACTIVE_ATTRIBUTES, &numAttributes);
|
|
|
|
if (numAttributes > 0)
|
|
{
|
|
GLint length;
|
|
glGetProgramiv(program, GL_ACTIVE_ATTRIBUTE_MAX_LENGTH, &length);
|
|
|
|
if (length > 0)
|
|
{
|
|
GLchar* attribName = (GLchar*) malloc(length + 1);
|
|
Attribute attribute;
|
|
for (GLint i = 0; i < numAttributes; ++i) {
|
|
// Query attribute info.
|
|
glGetActiveAttrib(program, i, length, nullptr, &attribute.size, &attribute.type, attribName);
|
|
attribName[length] = '\0';
|
|
attribute.name = attribName;
|
|
attribute.hashName = std::hash<std::string>{}(attribName);
|
|
// Query the pre-assigned attribute location
|
|
attribute.location = glGetAttribLocation(program, attribName);
|
|
|
|
_attributes.push_back(std::move(attribute));
|
|
}
|
|
free(attribName);
|
|
}
|
|
}
|
|
|
|
// Query and store uniforms from the program.
|
|
GLint activeUniforms;
|
|
glGetProgramiv(program, GL_ACTIVE_UNIFORMS, &activeUniforms);
|
|
if (activeUniforms > 0)
|
|
{
|
|
GLint length;
|
|
glGetProgramiv(program, GL_ACTIVE_UNIFORM_MAX_LENGTH, &length);
|
|
if (length > 0)
|
|
{
|
|
GLchar* uniformName = (GLchar*) malloc(length + 1);
|
|
|
|
Uniform uniform;
|
|
for (int i = 0; i < activeUniforms; ++i)
|
|
{
|
|
// Query uniform info.
|
|
GL_CHECK(glGetActiveUniform(program, i, length, nullptr, &uniform.size, &uniform.type, uniformName));
|
|
uniformName[length] = '\0';
|
|
bool isArray = false;
|
|
// remove possible array '[]' from uniform name
|
|
if (length > 3)
|
|
{
|
|
char* c = strrchr(uniformName, '[');
|
|
if (c)
|
|
{
|
|
*c = '\0';
|
|
isArray = true;
|
|
}
|
|
}
|
|
|
|
uniform.name = uniformName;
|
|
uniform.hashName = std::hash<std::string>{}(uniformName);
|
|
GL_CHECK(uniform.location = glGetUniformLocation(program, uniformName));
|
|
|
|
GLenum err = glGetError();
|
|
if (err != GL_NO_ERROR)
|
|
{
|
|
RENDERER_LOGE("error: 0x%x uniformName: %s", (int)err, uniformName);
|
|
}
|
|
assert(err == GL_NO_ERROR);
|
|
|
|
if (!isArray)
|
|
{
|
|
uniform.size = -1;
|
|
auto iter = _type2uniformCommit.find(uniform.type);
|
|
assert(iter != _type2uniformCommit.end());
|
|
uniform._callback = iter->second;
|
|
}
|
|
else
|
|
{
|
|
auto iter = _type2uniformArrayCommit.find(uniform.type);
|
|
assert(iter != _type2uniformArrayCommit.end());
|
|
uniform._callback = iter->second;
|
|
}
|
|
_uniforms.push_back(std::move(uniform));
|
|
}
|
|
|
|
free(uniformName);
|
|
}
|
|
}
|
|
|
|
_linked = true;
|
|
}
|
|
|
|
RENDERER_END
|