KDE/app/src/main/cpp/Renderer.cpp
2022-10-27 15:38:24 +02:00

364 lines
15 KiB
C++

#include "Renderer.h"
#include "Log.h"
#include "AssetManager.h"
#include "Texture.h"
#include <assert.h>
Renderer* Renderer::ptr = nullptr;
static const char* g_vert_src = "#version 300 es \n"
"precision mediump float; \n"
"uniform vec2 res; // resolution \n"
"layout (location = 0) in vec2 v_p0; // top left corner on screen \n"
"layout (location = 1) in vec2 v_p1; // top right corner on screen \n"
"layout (location = 2) in vec2 v_src_p0; \n"
"layout (location = 3) in vec2 v_src_p1; \n"
"layout (location = 4) in vec4 v_color; // color \n"
"layout (location = 5) in int v_expand_r; // \n"
"out vec4 f_color; \n"
"out vec2 f_uv; \n"
"flat out int f_expand_r; \n"
"const vec2 vertices[6] = vec2[6]( \n"
" vec2(-1,-1), \n"
" vec2(+1, -1), \n"
" vec2(-1, +1), \n"
" vec2(+1, -1), \n"
" vec2(+1, +1), \n"
" vec2(-1, +1) \n"
"); \n"
"void main() \n"
"{ \n"
" vec2 v = vertices[gl_VertexID]; \n"
" // destination on screen \n"
" //vec2 dst_half_size = (v_p1 - v_p0) / 2.0; \n"
" //vec2 dst_center = (v_p1 + v_p0) / 2.0; \n"
" vec2 dst_pos = v * ((v_p1 - v_p0) / 2.0) + ((v_p1 + v_p0) / 2.0); \n"
" dst_pos.y = res.y - dst_pos.y; \n"
" vec2 src = v * ((v_src_p1 - v_src_p0) / 2.0) + ((v_src_p1 + v_src_p0) / 2.0); \n"
" gl_Position = vec4(2.0 * dst_pos.x / res.x - 1.0, \n"
" 2.0 * dst_pos.y / res.y - 1.0, \n"
" 0.0, \n"
" 1.0); \n"
" f_color = v_color; \n"
" f_uv = src; \n"
" f_expand_r = v_expand_r; \n"
"} \n";
static const char* g_frag_src = "#version 300 es \n"
"precision mediump float; \n"
"in vec2 f_uv; \n"
"in vec4 f_color; \n"
"flat in int f_expand_r; \n"
"layout (location = 0) out vec4 frag_color; \n"
"uniform sampler2D s_texture; \n"
"void main() \n"
"{ \n"
" vec4 tex_color = texture(s_texture, f_uv); \n"
" if (f_expand_r == 1) \n"
" tex_color = tex_color.rrrr; \n"
" frag_color = tex_color * f_color; \n"
"} \n";
void Renderer::create()
{
Renderer::ptr = new Renderer;
}
void Renderer::destroy()
{
delete Renderer::ptr;
Renderer::ptr = nullptr;
}
Renderer::Renderer()
{
GLuint vertex = 0, fragment = 0;
vertex = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(vertex, 1, &g_vert_src, nullptr);
glCompileShader(vertex);
int status = 0;
glGetShaderiv(vertex, GL_COMPILE_STATUS, &status);
if (status != GL_TRUE) {
char info[512];
glGetShaderInfoLog(vertex, 512, nullptr, info);
ALOGE("Failed to compile ui vertex shader: %s", info);
}
fragment = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(fragment, 1, &g_frag_src, nullptr);
glCompileShader(fragment);
glGetShaderiv(fragment, GL_COMPILE_STATUS, &status);
if (status != GL_TRUE) {
char info[512];
glGetShaderInfoLog(fragment, 512, nullptr, info);
ALOGE("Failed to compile ui fragment shader: %s", info);
}
m_shader = glCreateProgram();
glAttachShader(m_shader, vertex);
glAttachShader(m_shader, fragment);
glLinkProgram(m_shader);
glGetProgramiv(m_shader, GL_LINK_STATUS, &status);
if (status != GL_TRUE) {
char info[512];
glGetProgramInfoLog(m_shader, 512, nullptr, info);
ALOGE("Failed to link ui shader: %s", info);
}
glDeleteShader(vertex);
glDeleteShader(fragment);
glGenBuffers(1, &m_vbo);
glGenVertexArrays(1, &m_vao);
glBindVertexArray(m_vao);
glBindBuffer(GL_ARRAY_BUFFER, m_vbo);
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, sizeof(Rect), (GLvoid*)0);
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, sizeof(Rect), (GLvoid*)(2 * sizeof(float)));
glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, sizeof(Rect), (GLvoid*)(4 * sizeof(float)));
glVertexAttribPointer(3, 2, GL_FLOAT, GL_FALSE, sizeof(Rect), (GLvoid*)(6 * sizeof(float)));
glVertexAttribPointer(4, 4, GL_FLOAT, GL_FALSE, sizeof(Rect), (GLvoid*)(8 * sizeof(float)));
glVertexAttribPointer(5, 1, GL_INT, GL_FALSE, sizeof(Rect), (GLvoid*)(12 * sizeof(float)));
glEnableVertexAttribArray(0);
glEnableVertexAttribArray(1);
glEnableVertexAttribArray(2);
glEnableVertexAttribArray(3);
glEnableVertexAttribArray(4);
glEnableVertexAttribArray(5);
glVertexAttribDivisor(0, 1);
glVertexAttribDivisor(1, 1);
glVertexAttribDivisor(2, 1);
glVertexAttribDivisor(3, 1);
glVertexAttribDivisor(4, 1);
glVertexAttribDivisor(5, 1);
glBindVertexArray(0);
// Dummy texture (1x1px white)
unsigned char tex[] = {255, 255, 255, 255};
Texture dummy_texture(1, 1, tex);
m_textures.insert(std::make_pair(0, dummy_texture));
m_dummy_texture = 0;
}
Renderer::~Renderer()
{
for (auto texture : m_textures)
texture.second.destroy();
glDeleteProgram(m_shader);
glDeleteVertexArrays(1, &m_vao);
glDeleteBuffers(1, &m_vbo);
}
void Renderer::addRect(float x, float y, float w, float h)
{
addRect(x, y, w, h, 1.f, 1.f, 1.f, 1.f, m_dummy_texture, 0.f, 0.f, 1.f, 1.f);
}
void Renderer::addRect(float x, float y, float w, float h, StringHandle texture)
{
addRect(x, y, w, h, 1.f, 1.f, 1.f, 1.f, texture, 0.f, 0.f, 1.f, 1.f);
}
void Renderer::addRect(float x, float y, float w, float h, float r, float g, float b, float a)
{
addRect(x, y, w, h, r, g, b, a, m_dummy_texture, 0.f, 0.f, 1.f, 1.f);
}
void Renderer::addRect(float x, float y, float w, float h, float r, float g, float b, float a, StringHandle texture)
{
addRect(x, y, w, h, r, g, b, a, texture, 0.f, 0.f, 1.f, 1.f);
}
void Renderer::addRect(float x,
float y,
float w,
float h,
StringHandle texture,
float src_x,
float src_y,
float src_w,
float src_h)
{
addRect(x, y, w, h, 1.f, 1.f, 1.f, 1.f, texture, src_x, src_y, src_w, src_h);
}
void Renderer::addRect(float x,
float y,
float w,
float h,
float r,
float g,
float b,
float a,
StringHandle texture,
float src_x,
float src_y,
float src_w,
float src_h)
{
if (m_textures.find(texture) == m_textures.end()) {
Texture tex;
if (!AssetManager::ptr->loadTexture(StringRepository::global->getString(texture), &tex)) {
ALOGE("Failed to load texture");
return;
}
m_textures.insert(std::make_pair(texture, tex));
}
Rect rect = {};
rect.dst_p0_x = x;
rect.dst_p0_y = y;
rect.dst_p1_x = x + w;
rect.dst_p1_y = y + h;
rect.src_p0_x = src_x;
rect.src_p0_y = src_y;
rect.src_p1_x = src_x + src_w;
rect.src_p1_y = src_y + src_h;
rect.r = r;
rect.g = g;
rect.b = b;
rect.a = a;
rect.expand_r = 0;
m_rects.push_back(rect);
m_draw_textures.push_back(texture);
}
void Renderer::addFontRect(float x,
float y,
float w,
float h,
float char_height,
StringHandle font,
StringHandle id,
float src_x0,
float src_y0,
float src_x1,
float src_y1)
{
addFontRect(x, y, w, h, 0.f, 0.f, 0.f, 1.f, char_height, font, id, src_x0, src_y0, src_x1, src_y1);
}
void Renderer::addFontRect(float x,
float y,
float w,
float h,
float r,
float g,
float b,
float a,
float char_height,
StringHandle font,
StringHandle id,
float src_x0,
float src_y0,
float src_x1,
float src_y1)
{
if (m_textures.find(id) == m_textures.end()) {
Texture tex;
if (!AssetManager::ptr->loadFontBitmap(StringRepository::global->getString(font), char_height, &tex)) {
ALOGE("Failed to load texture");
return;
}
m_textures.insert(std::make_pair(id, tex));
}
Rect rect = {};
rect.dst_p0_x = x;
rect.dst_p0_y = y;
rect.dst_p1_x = x + w;
rect.dst_p1_y = y + h;
rect.src_p0_x = src_x0;
rect.src_p0_y = src_y0;
rect.src_p1_x = src_x1;
rect.src_p1_y = src_y1;
rect.r = r;
rect.g = g;
rect.b = b;
rect.a = a;
rect.expand_r = 1;
m_rects.push_back(rect);
m_draw_textures.push_back(id);
}
void Renderer::renderFrame(float width, float height)
{
assert(m_rects.size() == m_draw_textures.size());
glViewport(0, 0, static_cast<int>(width), static_cast<int>(height));
//glClearColor(0.8f, 0.3f, 0.3f, 1.f);
glClearColor(0.0f, 0.0f, 0.0f, 1.f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
const auto rect_count = static_cast<GLsizei>(m_rects.size());
if (rect_count == 0)
return;
glDisable(GL_DEPTH_TEST);
glDisable(GL_CULL_FACE);
glEnable(GL_BLEND);
glBlendEquation(GL_FUNC_ADD);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glBindVertexArray(m_vao);
glUseProgram(m_shader);
glUniform2f(glGetUniformLocation(m_shader, "res"), width, height);
glUniform1i(glGetUniformLocation(m_shader, "s_texture"), 0);
glBindBuffer(GL_ARRAY_BUFFER, m_vbo);
size_t start = 0;
StringHandle current_texture = m_draw_textures[0];
while (start < rect_count) {
glActiveTexture(GL_TEXTURE0);
m_textures[current_texture].bind();
for (size_t i = start; i < rect_count; ++i) {
StringHandle texture = m_draw_textures[i];
if ((texture != current_texture)) {
// Draw everything in [start, i - 1]
const size_t count = i - start;
glBufferData(GL_ARRAY_BUFFER,
static_cast<GLsizeiptr>(sizeof(Rect) * count),
&m_rects[start],
GL_STREAM_DRAW);
glDrawArraysInstanced(GL_TRIANGLES, 0, 6, static_cast<GLsizei>(count));
start = i;
current_texture = texture;
break;
}
if (i == rect_count - 1) {
// just draw it
const size_t count = rect_count - start;
glBufferData(GL_ARRAY_BUFFER,
static_cast<GLsizeiptr>(sizeof(Rect) * count),
&m_rects[start],
GL_STREAM_DRAW);
glDrawArraysInstanced(GL_TRIANGLES, 0, 6, static_cast<GLsizei>(count));
start = rect_count;
current_texture = texture;
}
}
}
m_rects.clear();
m_draw_textures.clear();
}
void Renderer::getTextureSize(
StringHandle texture,
unsigned int* w,
unsigned int* h)
{
if (m_textures.find(texture) == m_textures.end()) {
Texture tex;
if (!AssetManager::ptr->loadTexture(StringRepository::global->getString(texture), &tex)) {
ALOGE("Failed to load texture");
return;
}
m_textures.insert(std::make_pair(texture, tex));
}
Texture tex = m_textures[texture];
tex.getTextureSize(w, h);
}