mirror of https://github.com/auygun/kaliber.git
646 lines
19 KiB
C++
646 lines
19 KiB
C++
#include "engine/renderer/opengl/renderer_opengl.h"
|
|
|
|
#include <algorithm>
|
|
#include <cstring>
|
|
#include <sstream>
|
|
#include <unordered_set>
|
|
|
|
#include "base/hash.h"
|
|
#include "base/log.h"
|
|
#include "base/vecmath.h"
|
|
#include "engine/asset/image.h"
|
|
#include "engine/asset/mesh.h"
|
|
#include "engine/asset/shader_source.h"
|
|
#include "engine/renderer/geometry.h"
|
|
#include "engine/renderer/shader.h"
|
|
#include "engine/renderer/texture.h"
|
|
|
|
using namespace base;
|
|
|
|
namespace {
|
|
|
|
constexpr GLenum kGlPrimitive[eng::kPrimitive_Max] = {GL_TRIANGLES,
|
|
GL_TRIANGLE_STRIP};
|
|
|
|
constexpr GLenum kGlDataType[eng::kDataType_Max] = {
|
|
GL_UNSIGNED_BYTE, GL_FLOAT, GL_INT,
|
|
GL_SHORT, GL_UNSIGNED_INT, GL_UNSIGNED_SHORT};
|
|
|
|
const std::string kAttributeNames[eng::kAttribType_Max] = {
|
|
"in_color", "in_normal", "in_position", "in_tex_coord"};
|
|
|
|
} // namespace
|
|
|
|
namespace eng {
|
|
|
|
RendererOpenGL::RendererOpenGL(base::Closure context_lost_cb)
|
|
: Renderer(context_lost_cb) {}
|
|
|
|
RendererOpenGL::~RendererOpenGL() {
|
|
Shutdown();
|
|
OnDestroy();
|
|
}
|
|
|
|
void RendererOpenGL::OnWindowResized(int width, int height) {
|
|
screen_width_ = width;
|
|
screen_height_ = height;
|
|
glViewport(0, 0, screen_width_, screen_height_);
|
|
}
|
|
|
|
int RendererOpenGL::GetScreenWidth() const {
|
|
return screen_width_;
|
|
}
|
|
|
|
int RendererOpenGL::GetScreenHeight() const {
|
|
return screen_height_;
|
|
}
|
|
|
|
uint64_t RendererOpenGL::CreateGeometry(std::unique_ptr<Mesh> mesh) {
|
|
// Verify that we have a valid layout and get the total byte size per vertex.
|
|
GLuint vertex_size = mesh->GetVertexSize();
|
|
if (!vertex_size) {
|
|
LOG(0) << "Invalid vertex layout";
|
|
return 0;
|
|
}
|
|
|
|
GLuint vertex_array_id = 0;
|
|
if (vertex_array_objects_) {
|
|
glGenVertexArrays(1, &vertex_array_id);
|
|
glBindVertexArray(vertex_array_id);
|
|
}
|
|
|
|
// Create the vertex buffer and upload the data.
|
|
GLuint vertex_buffer_id = 0;
|
|
glGenBuffers(1, &vertex_buffer_id);
|
|
glBindBuffer(GL_ARRAY_BUFFER, vertex_buffer_id);
|
|
glBufferData(GL_ARRAY_BUFFER, mesh->num_vertices() * vertex_size,
|
|
mesh->GetVertices(), GL_STATIC_DRAW);
|
|
|
|
// Make sure the vertex format is understood and the attribute pointers are
|
|
// set up.
|
|
std::vector<GeometryOpenGL::Element> vertex_layout;
|
|
if (!SetupVertexLayout(mesh->vertex_description(), vertex_size,
|
|
vertex_array_objects_, vertex_layout)) {
|
|
LOG(0) << "Invalid vertex layout";
|
|
return 0;
|
|
}
|
|
|
|
// Create the index buffer and upload the data.
|
|
GLuint index_buffer_id = 0;
|
|
if (mesh->GetIndices()) {
|
|
glGenBuffers(1, &index_buffer_id);
|
|
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, index_buffer_id);
|
|
glBufferData(GL_ELEMENT_ARRAY_BUFFER,
|
|
mesh->num_indices() * mesh->GetIndexSize(), mesh->GetIndices(),
|
|
GL_STATIC_DRAW);
|
|
}
|
|
|
|
if (vertex_array_id) {
|
|
// De-activate the buffer again and we're done.
|
|
glBindVertexArray(0);
|
|
} else {
|
|
// De-activate the individual buffers.
|
|
glBindBuffer(GL_ARRAY_BUFFER, 0);
|
|
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
|
|
}
|
|
|
|
uint64_t resource_id = ++last_resource_id_;
|
|
geometries_[resource_id] = {(GLsizei)mesh->num_vertices(),
|
|
(GLsizei)mesh->num_indices(),
|
|
kGlPrimitive[mesh->primitive()],
|
|
kGlDataType[mesh->index_description()],
|
|
vertex_layout,
|
|
vertex_size,
|
|
vertex_array_id,
|
|
vertex_buffer_id,
|
|
index_buffer_id};
|
|
return resource_id;
|
|
}
|
|
|
|
void RendererOpenGL::DestroyGeometry(uint64_t resource_id) {
|
|
auto it = geometries_.find(resource_id);
|
|
if (it == geometries_.end())
|
|
return;
|
|
|
|
if (it->second.index_buffer_id)
|
|
glDeleteBuffers(1, &(it->second.index_buffer_id));
|
|
if (it->second.vertex_buffer_id)
|
|
glDeleteBuffers(1, &(it->second.vertex_buffer_id));
|
|
if (it->second.vertex_array_id)
|
|
glDeleteVertexArrays(1, &(it->second.vertex_array_id));
|
|
|
|
geometries_.erase(it);
|
|
}
|
|
|
|
void RendererOpenGL::Draw(uint64_t resource_id) {
|
|
auto it = geometries_.find(resource_id);
|
|
if (it == geometries_.end())
|
|
return;
|
|
|
|
// Set up the vertex data.
|
|
if (it->second.vertex_array_id)
|
|
glBindVertexArray(it->second.vertex_array_id);
|
|
else {
|
|
glBindBuffer(GL_ARRAY_BUFFER, it->second.vertex_buffer_id);
|
|
for (GLuint attribute_index = 0;
|
|
attribute_index < (GLuint)it->second.vertex_layout.size();
|
|
++attribute_index) {
|
|
GeometryOpenGL::Element& e = it->second.vertex_layout[attribute_index];
|
|
glEnableVertexAttribArray(attribute_index);
|
|
glVertexAttribPointer(attribute_index, e.num_elements, e.type, GL_FALSE,
|
|
it->second.vertex_size,
|
|
(const GLvoid*)e.vertex_offset);
|
|
}
|
|
|
|
if (it->second.num_indices > 0)
|
|
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, it->second.index_buffer_id);
|
|
}
|
|
|
|
// Draw the primitive.
|
|
if (it->second.num_indices > 0)
|
|
glDrawElements(it->second.primitive, it->second.num_indices,
|
|
it->second.index_type, NULL);
|
|
else
|
|
glDrawArrays(it->second.primitive, 0, it->second.num_vertices);
|
|
|
|
// Clean up states.
|
|
if (it->second.vertex_array_id)
|
|
glBindVertexArray(0);
|
|
else {
|
|
for (GLuint attribute_index = 0;
|
|
attribute_index < (GLuint)it->second.vertex_layout.size();
|
|
++attribute_index)
|
|
glDisableVertexAttribArray(attribute_index);
|
|
|
|
glBindBuffer(GL_ARRAY_BUFFER, 0);
|
|
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
|
|
}
|
|
}
|
|
|
|
uint64_t RendererOpenGL::CreateTexture() {
|
|
GLuint gl_id = 0;
|
|
glGenTextures(1, &gl_id);
|
|
|
|
BindTexture(gl_id);
|
|
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
|
|
|
uint64_t resource_id = ++last_resource_id_;
|
|
textures_[resource_id] = gl_id;
|
|
return resource_id;
|
|
}
|
|
|
|
void RendererOpenGL::UpdateTexture(uint64_t resource_id,
|
|
std::unique_ptr<Image> image) {
|
|
auto it = textures_.find(resource_id);
|
|
if (it == textures_.end())
|
|
return;
|
|
|
|
BindTexture(it->second);
|
|
if (image->IsCompressed()) {
|
|
GLenum format = 0;
|
|
switch (image->GetFormat()) {
|
|
case Image::kDXT1:
|
|
format = GL_COMPRESSED_RGB_S3TC_DXT1_EXT;
|
|
break;
|
|
case Image::kDXT5:
|
|
format = GL_COMPRESSED_RGBA_S3TC_DXT5_EXT;
|
|
break;
|
|
case Image::kETC1:
|
|
format = GL_ETC1_RGB8_OES;
|
|
break;
|
|
#if defined(__ANDROID__)
|
|
case Image::kATC:
|
|
format = GL_ATC_RGB_AMD;
|
|
break;
|
|
case Image::kATCIA:
|
|
format = GL_ATC_RGBA_INTERPOLATED_ALPHA_AMD;
|
|
break;
|
|
#endif
|
|
default:
|
|
NOTREACHED() << "- Unhandled texure format: " << image->GetFormat();
|
|
}
|
|
|
|
glCompressedTexImage2D(GL_TEXTURE_2D, 0, format, image->GetWidth(),
|
|
image->GetHeight(), 0, image->GetSize(),
|
|
image->GetBuffer());
|
|
|
|
// On some devices the first glCompressedTexImage2D call after context-lost
|
|
// returns GL_INVALID_VALUE for some reason.
|
|
GLenum err = glGetError();
|
|
if (err == GL_INVALID_VALUE) {
|
|
glCompressedTexImage2D(GL_TEXTURE_2D, 0, format, image->GetWidth(),
|
|
image->GetHeight(), 0, image->GetSize(),
|
|
image->GetBuffer());
|
|
err = glGetError();
|
|
}
|
|
|
|
if (err != GL_NO_ERROR)
|
|
LOG(0) << "GL ERROR after glCompressedTexImage2D: " << (int)err;
|
|
} else {
|
|
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, image->GetWidth(),
|
|
image->GetHeight(), 0, GL_RGBA, GL_UNSIGNED_BYTE,
|
|
image->GetBuffer());
|
|
}
|
|
}
|
|
|
|
void RendererOpenGL::DestroyTexture(uint64_t resource_id) {
|
|
auto it = textures_.find(resource_id);
|
|
if (it == textures_.end())
|
|
return;
|
|
|
|
glDeleteTextures(1, &(it->second));
|
|
textures_.erase(it);
|
|
}
|
|
|
|
void RendererOpenGL::ActivateTexture(uint64_t resource_id) {
|
|
auto it = textures_.find(resource_id);
|
|
if (it == textures_.end()) {
|
|
return;
|
|
}
|
|
|
|
BindTexture(it->second);
|
|
}
|
|
|
|
uint64_t RendererOpenGL::CreateShader(
|
|
std::unique_ptr<ShaderSource> source,
|
|
const VertexDescription& vertex_description,
|
|
Primitive primitive,
|
|
bool enable_depth_test) {
|
|
GLuint vertex_shader =
|
|
CreateShader(source->GetVertexSource(), GL_VERTEX_SHADER);
|
|
if (!vertex_shader)
|
|
return 0;
|
|
|
|
GLuint fragment_shader =
|
|
CreateShader(source->GetFragmentSource(), GL_FRAGMENT_SHADER);
|
|
if (!fragment_shader)
|
|
return 0;
|
|
|
|
GLuint id = glCreateProgram();
|
|
if (id) {
|
|
glAttachShader(id, vertex_shader);
|
|
glAttachShader(id, fragment_shader);
|
|
if (!BindAttributeLocation(id, vertex_description)) {
|
|
glDeleteProgram(id);
|
|
return 0;
|
|
}
|
|
|
|
glLinkProgram(id);
|
|
GLint linkStatus = GL_FALSE;
|
|
glGetProgramiv(id, GL_LINK_STATUS, &linkStatus);
|
|
if (linkStatus != GL_TRUE) {
|
|
GLint length = 0;
|
|
glGetProgramiv(id, GL_INFO_LOG_LENGTH, &length);
|
|
if (length > 0) {
|
|
char* buffer = (char*)malloc(length);
|
|
if (buffer) {
|
|
glGetProgramInfoLog(id, length, NULL, buffer);
|
|
LOG(0) << "Could not link program:\n" << buffer;
|
|
free(buffer);
|
|
}
|
|
}
|
|
glDeleteProgram(id);
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
uint64_t resource_id = ++last_resource_id_;
|
|
shaders_[resource_id] = {id, {}, enable_depth_test};
|
|
return resource_id;
|
|
}
|
|
|
|
void RendererOpenGL::DestroyShader(uint64_t resource_id) {
|
|
auto it = shaders_.find(resource_id);
|
|
if (it == shaders_.end())
|
|
return;
|
|
|
|
glDeleteProgram(it->second.id);
|
|
shaders_.erase(it);
|
|
}
|
|
|
|
void RendererOpenGL::ActivateShader(uint64_t resource_id) {
|
|
auto it = shaders_.find(resource_id);
|
|
if (it == shaders_.end())
|
|
return;
|
|
|
|
if (it->second.id != active_shader_id_) {
|
|
glUseProgram(it->second.id);
|
|
active_shader_id_ = it->second.id;
|
|
if (it->second.enable_depth_test)
|
|
glEnable(GL_DEPTH_TEST);
|
|
else
|
|
glDisable(GL_DEPTH_TEST);
|
|
}
|
|
}
|
|
|
|
void RendererOpenGL::SetUniform(uint64_t resource_id,
|
|
const std::string& name,
|
|
const base::Vector2f& val) {
|
|
auto it = shaders_.find(resource_id);
|
|
if (it == shaders_.end())
|
|
return;
|
|
|
|
GLint index = GetUniformLocation(it->second.id, name, it->second.uniforms);
|
|
if (index >= 0)
|
|
glUniform2fv(index, 1, val.GetData());
|
|
}
|
|
|
|
void RendererOpenGL::SetUniform(uint64_t resource_id,
|
|
const std::string& name,
|
|
const base::Vector3f& val) {
|
|
auto it = shaders_.find(resource_id);
|
|
if (it == shaders_.end())
|
|
return;
|
|
|
|
GLint index = GetUniformLocation(it->second.id, name, it->second.uniforms);
|
|
if (index >= 0)
|
|
glUniform3fv(index, 1, val.GetData());
|
|
}
|
|
|
|
void RendererOpenGL::SetUniform(uint64_t resource_id,
|
|
const std::string& name,
|
|
const base::Vector4f& val) {
|
|
auto it = shaders_.find(resource_id);
|
|
if (it == shaders_.end())
|
|
return;
|
|
|
|
GLint index = GetUniformLocation(it->second.id, name, it->second.uniforms);
|
|
if (index >= 0)
|
|
glUniform4fv(index, 1, val.GetData());
|
|
}
|
|
|
|
void RendererOpenGL::SetUniform(uint64_t resource_id,
|
|
const std::string& name,
|
|
const base::Matrix4f& val) {
|
|
auto it = shaders_.find(resource_id);
|
|
if (it == shaders_.end())
|
|
return;
|
|
|
|
GLint index = GetUniformLocation(it->second.id, name, it->second.uniforms);
|
|
if (index >= 0)
|
|
glUniformMatrix4fv(index, 1, GL_FALSE, val.GetData());
|
|
}
|
|
|
|
void RendererOpenGL::SetUniform(uint64_t resource_id,
|
|
const std::string& name,
|
|
float val) {
|
|
auto it = shaders_.find(resource_id);
|
|
if (it == shaders_.end())
|
|
return;
|
|
|
|
GLint index = GetUniformLocation(it->second.id, name, it->second.uniforms);
|
|
if (index >= 0)
|
|
glUniform1f(index, val);
|
|
}
|
|
|
|
void RendererOpenGL::SetUniform(uint64_t resource_id,
|
|
const std::string& name,
|
|
int val) {
|
|
auto it = shaders_.find(resource_id);
|
|
if (it == shaders_.end())
|
|
return;
|
|
|
|
GLint index = GetUniformLocation(it->second.id, name, it->second.uniforms);
|
|
if (index >= 0)
|
|
glUniform1i(index, val);
|
|
}
|
|
|
|
void RendererOpenGL::ContextLost() {
|
|
LOG(0) << "Context lost.";
|
|
|
|
DestroyAllResources();
|
|
context_lost_cb_();
|
|
}
|
|
|
|
size_t RendererOpenGL::GetAndResetFPS() {
|
|
int ret = fps_;
|
|
fps_ = 0;
|
|
return ret;
|
|
}
|
|
|
|
bool RendererOpenGL::InitCommon() {
|
|
// Get information about the currently active context.
|
|
const char* renderer =
|
|
reinterpret_cast<const char*>(glGetString(GL_RENDERER));
|
|
const char* version = reinterpret_cast<const char*>(glGetString(GL_VERSION));
|
|
|
|
LOG(0) << "OpenGL:";
|
|
LOG(0) << " vendor: " << (const char*)glGetString(GL_VENDOR);
|
|
LOG(0) << " renderer: " << renderer;
|
|
LOG(0) << " version: " << version;
|
|
LOG(0) << " shader version: "
|
|
<< (const char*)glGetString(GL_SHADING_LANGUAGE_VERSION);
|
|
LOG(0) << "Screen size: " << screen_width_ << ", " << screen_height_;
|
|
|
|
// Setup extensions.
|
|
std::stringstream stream((const char*)glGetString(GL_EXTENSIONS));
|
|
std::string token;
|
|
std::unordered_set<std::string> extensions;
|
|
while (std::getline(stream, token, ' '))
|
|
extensions.insert(token);
|
|
|
|
#if 0
|
|
LOG(0) << " extensions:";
|
|
for (auto& ext : extensions)
|
|
LOG(0) << " " << ext.c_str());
|
|
#endif
|
|
|
|
// Check for supported texture compression extensions.
|
|
if (extensions.find("GL_OES_compressed_ETC1_RGB8_texture") !=
|
|
extensions.end())
|
|
texture_compression_.etc1 = true;
|
|
if (extensions.find("GL_EXT_texture_compression_dxt1") != extensions.end())
|
|
texture_compression_.dxt1 = true;
|
|
if (extensions.find("GL_EXT_texture_compression_latc") != extensions.end())
|
|
texture_compression_.latc = true;
|
|
if (extensions.find("GL_EXT_texture_compression_s3tc") != extensions.end())
|
|
texture_compression_.s3tc = true;
|
|
if (extensions.find("GL_IMG_texture_compression_pvrtc") != extensions.end())
|
|
texture_compression_.pvrtc = true;
|
|
if (extensions.find("GL_AMD_compressed_ATC_texture") != extensions.end() ||
|
|
extensions.find("GL_ATI_texture_compression_atitc") != extensions.end())
|
|
texture_compression_.atc = true;
|
|
|
|
if (extensions.find("GL_OES_vertex_array_object") != extensions.end()) {
|
|
// This extension seems to be broken on older PowerVR drivers.
|
|
if (!strstr(renderer, "PowerVR SGX 53") &&
|
|
!strstr(renderer, "PowerVR SGX 54") &&
|
|
!strstr(renderer, "Android Emulator")) {
|
|
vertex_array_objects_ = true;
|
|
}
|
|
}
|
|
|
|
if (extensions.count("GL_ARB_texture_non_power_of_two") ||
|
|
extensions.count("GL_OES_texture_npot")) {
|
|
npot_ = true;
|
|
}
|
|
|
|
// Ancient hardware is not supported.
|
|
if (!npot_)
|
|
LOG(0) << "NPOT not supported.";
|
|
|
|
if (vertex_array_objects_)
|
|
LOG(0) << "Supports Vertex Array Objects.";
|
|
|
|
LOG(0) << "TextureCompression:";
|
|
LOG(0) << " atc: " << texture_compression_.atc;
|
|
LOG(0) << " dxt1: " << texture_compression_.dxt1;
|
|
LOG(0) << " etc1: " << texture_compression_.etc1;
|
|
LOG(0) << " s3tc: " << texture_compression_.s3tc;
|
|
|
|
glEnable(GL_BLEND);
|
|
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
|
|
|
glClearColor(0, 0, 0, 1);
|
|
|
|
is_initialized_ = true;
|
|
|
|
return true;
|
|
}
|
|
|
|
void RendererOpenGL::DestroyAllResources() {
|
|
std::vector<uint64_t> resource_ids;
|
|
for (auto& r : geometries_)
|
|
resource_ids.push_back(r.first);
|
|
for (auto& r : resource_ids)
|
|
DestroyGeometry(r);
|
|
|
|
resource_ids.clear();
|
|
for (auto& r : shaders_)
|
|
resource_ids.push_back(r.first);
|
|
for (auto& r : resource_ids)
|
|
DestroyShader(r);
|
|
|
|
resource_ids.clear();
|
|
for (auto& r : textures_)
|
|
resource_ids.push_back(r.first);
|
|
for (auto& r : resource_ids)
|
|
DestroyTexture(r);
|
|
|
|
DCHECK(geometries_.size() == 0);
|
|
DCHECK(shaders_.size() == 0);
|
|
DCHECK(textures_.size() == 0);
|
|
}
|
|
|
|
void RendererOpenGL::BindTexture(GLuint id) {
|
|
if (id != active_texture_id_) {
|
|
glBindTexture(GL_TEXTURE_2D, id);
|
|
active_texture_id_ = id;
|
|
}
|
|
}
|
|
|
|
bool RendererOpenGL::SetupVertexLayout(
|
|
const VertexDescription& vd,
|
|
GLuint vertex_size,
|
|
bool use_vao,
|
|
std::vector<GeometryOpenGL::Element>& vertex_layout) {
|
|
GLuint attribute_index = 0;
|
|
size_t vertex_offset = 0;
|
|
|
|
for (auto& attr : vd) {
|
|
// There's a limitation of 16 attributes in OpenGL ES 2.0
|
|
if (attribute_index >= 16)
|
|
return false;
|
|
|
|
auto [attrib_type, data_type, num_elements, type_size] = attr;
|
|
|
|
// The data type is needed, the most common ones are supported.
|
|
GLenum type = kGlDataType[data_type];
|
|
|
|
// We got all we need to define this attribute.
|
|
if (use_vao) {
|
|
// This will be saved into the vertex array object.
|
|
glEnableVertexAttribArray(attribute_index);
|
|
glVertexAttribPointer(attribute_index, num_elements, type, GL_FALSE,
|
|
vertex_size, (const GLvoid*)vertex_offset);
|
|
} else {
|
|
// Need to keep this information for when rendering.
|
|
GeometryOpenGL::Element element;
|
|
element.num_elements = num_elements;
|
|
element.type = type;
|
|
element.vertex_offset = vertex_offset;
|
|
vertex_layout.push_back(element);
|
|
}
|
|
|
|
// Move on to the next attribute.
|
|
++attribute_index;
|
|
vertex_offset += num_elements * type_size;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
GLuint RendererOpenGL::CreateShader(const char* source, GLenum type) {
|
|
GLuint shader = glCreateShader(type);
|
|
if (shader) {
|
|
glShaderSource(shader, 1, &source, NULL);
|
|
glCompileShader(shader);
|
|
GLint compiled = 0;
|
|
glGetShaderiv(shader, GL_COMPILE_STATUS, &compiled);
|
|
if (!compiled) {
|
|
GLint length = 0;
|
|
glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &length);
|
|
if (length) {
|
|
char* buffer = (char*)malloc(length);
|
|
if (buffer) {
|
|
glGetShaderInfoLog(shader, length, NULL, buffer);
|
|
LOG(0) << "Could not compile shader " << type << ":\n" << buffer;
|
|
free(buffer);
|
|
}
|
|
glDeleteShader(shader);
|
|
shader = 0;
|
|
}
|
|
}
|
|
}
|
|
return shader;
|
|
}
|
|
|
|
bool RendererOpenGL::BindAttributeLocation(GLuint id,
|
|
const VertexDescription& vd) {
|
|
int current = 0;
|
|
int tex_coord = 0;
|
|
|
|
for (auto& attr : vd) {
|
|
AttribType attrib_type = std::get<0>(attr);
|
|
std::string attrib_name = kAttributeNames[attrib_type];
|
|
if (attrib_type == kAttribType_TexCoord)
|
|
attrib_name += std::to_string(tex_coord++);
|
|
glBindAttribLocation(id, current++, attrib_name.c_str());
|
|
}
|
|
return current > 0;
|
|
}
|
|
|
|
GLint RendererOpenGL::GetUniformLocation(
|
|
GLuint id,
|
|
const std::string& name,
|
|
std::vector<std::pair<size_t, GLuint>>& uniforms) {
|
|
// Check if we've encountered this uniform before.
|
|
auto hash = KR2Hash(name);
|
|
auto it = std::find_if(uniforms.begin(), uniforms.end(),
|
|
[&](auto& r) { return hash == std::get<0>(r); });
|
|
GLint index;
|
|
if (it != uniforms.end()) {
|
|
// Yes, we already have the mapping.
|
|
index = std::get<1>(*it);
|
|
} else {
|
|
// No, ask the driver for the mapping and save it.
|
|
index = glGetUniformLocation(id, name.c_str());
|
|
if (index >= 0) {
|
|
DCHECK(std::find_if(uniforms.begin(), uniforms.end(),
|
|
[&](auto& r) { return hash == std::get<0>(r); }) ==
|
|
uniforms.end())
|
|
<< "Hash collision";
|
|
uniforms.emplace_back(std::make_pair(hash, index));
|
|
} else {
|
|
LOG(0) << "Cannot find uniform " << name.c_str() << " (shader: " << id
|
|
<< ")";
|
|
}
|
|
}
|
|
return index;
|
|
}
|
|
|
|
} // namespace eng
|