rpcs3/rpcs3/Emu/RSX/GL/GLTextOut.h

224 lines
No EOL
5.2 KiB
C++

#pragma once
#include "stdafx.h"
#include "GLHelpers.h"
#include "../Common/TextGlyphs.h"
namespace gl
{
class text_writer
{
private:
gl::glsl::program m_program;
gl::glsl::shader m_vs;
gl::glsl::shader m_fs;
gl::vao m_vao;
gl::buffer m_text_buffer;
gl::buffer m_scale_offsets_buffer;
std::unordered_map<u8, std::pair<u32, u32>> m_offsets;
bool initialized = false;
bool enabled = false;
void init_program()
{
std::string vs =
{
"#version 420\n"
"#extension GL_ARB_shader_draw_parameters: enable\n"
"layout(location=0) in vec2 pos;\n"
"uniform vec2 offsets[255];\n"
"uniform vec2 scale;\n"
"\n"
"void main()\n"
"{\n"
" vec2 offset = offsets[gl_DrawIDARB];\n"
" gl_Position = vec4(pos, 0., 1.);\n"
" gl_Position.xy = gl_Position.xy * scale + offset;\n"
"}\n"
};
std::string fs =
{
"#version 420\n"
"layout(location=0) out vec4 col0;\n"
"uniform vec4 draw_color;\n"
"\n"
"void main()\n"
"{\n"
" col0 = draw_color;\n"
"}\n"
};
m_fs.create(gl::glsl::shader::type::fragment);
m_fs.source(fs);
m_fs.compile();
m_vs.create(gl::glsl::shader::type::vertex);
m_vs.source(vs);
m_vs.compile();
m_program.create();
m_program.attach(m_vs);
m_program.attach(m_fs);
m_program.make();
}
void load_program(float scale_x, float scale_y, float *offsets, int nb_offsets, color4f color)
{
float scale[] = { scale_x, scale_y };
m_program.use();
m_program.uniforms["draw_color"] = color;
glProgramUniform2fv(m_program.id(), m_program.uniforms["offsets"].location(), nb_offsets, offsets);
glProgramUniform2fv(m_program.id(), m_program.uniforms["scale"].location(), 1, scale);
}
public:
text_writer() {}
~text_writer(){}
void init()
{
//Check for ARB_shader_draw_parameters
//While it is possible to draw text without full multidraw support, issuing separate draw calls per character is not effecient
int ext_count;
glGetIntegerv(GL_NUM_EXTENSIONS, &ext_count);
for (int i = 0; i < ext_count; i++)
{
const char *ext = (const char*)glGetStringi(GL_EXTENSIONS, i);
if (std::string(ext) == "GL_ARB_shader_draw_parameters")
{
enabled = true;
break;
}
}
if (!enabled)
{
LOG_ERROR(RSX, "Debug overlay could not start because ARB_shader_draw_parameters is not supported by your GPU");
return;
}
m_text_buffer.create();
m_scale_offsets_buffer.create();
GlyphManager glyph_source;
auto points = glyph_source.generate_point_map();
const u32 buffer_size = points.size() * sizeof(GlyphManager::glyph_point);
m_text_buffer.data(buffer_size, points.data());
m_offsets = glyph_source.get_glyph_offsets();
m_scale_offsets_buffer.data(512 * 4 * sizeof(float));
//Init VAO
int old_vao;
glGetIntegerv(GL_VERTEX_ARRAY_BINDING, &old_vao);
m_vao.create();
m_vao.bind();
//This is saved as part of the bound VAO's state
m_vao.array_buffer = m_text_buffer;
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 2, GL_FLOAT, false, sizeof(GlyphManager::glyph_point), 0);
glBindVertexArray(old_vao);
init_program();
initialized = true;
}
void print_text(int x, int y, int target_w, int target_h, const std::string &text, color4f color = { 0.3f, 1.f, 0.3f, 1.f })
{
if (!enabled) return;
verify(HERE), initialized;
std::vector<GLint> offsets;
std::vector<GLsizei> counts;
std::vector<float> shader_offsets;
char *s = const_cast<char *>(text.c_str());
//Y is in raster coordinates: convert to bottom-left origin
y = (target_h - y - 16);
//Compress [0, w] and [0, h] into range [-1, 1]
float scale_x = 2.f / target_w;
float scale_y = 2.f / target_h;
float base_offset = 0.f;
shader_offsets.reserve(text.length() * 2);
while (*s)
{
u8 offset = (u8)*s;
bool to_draw = false; //Can be false for space or unsupported characters
auto o = m_offsets.find(offset);
if (o != m_offsets.end())
{
if (o->second.second > 0)
{
to_draw = true;
offsets.push_back(o->second.first);
counts.push_back(o->second.second);
}
}
if (to_draw)
{
//Generate a scale_offset pair for this entry
float offset_x = scale_x * (x + base_offset);
offset_x -= 1.f;
float offset_y = scale_y * y;
offset_y -= 1.f;
shader_offsets.push_back(offset_x);
shader_offsets.push_back(offset_y);
}
base_offset += 9.f;
s++;
}
//TODO: Add drop shadow if deemed necessary for visibility
int old_vao;
glGetIntegerv(GL_VERTEX_ARRAY_BINDING, &old_vao);
load_program(scale_x, scale_y, shader_offsets.data(), counts.size(), color);
m_vao.bind();
glMultiDrawArrays(GL_POINTS, offsets.data(), counts.data(), counts.size());
glBindVertexArray(old_vao);
}
void close()
{
if (initialized)
{
m_scale_offsets_buffer.remove();
m_text_buffer.remove();
m_vao.remove();
m_program.remove();
m_fs.remove();
m_vs.remove();
initialized = false;
}
}
};
}