mirror of
https://github.com/RPCS3/rpcs3.git
synced 2025-07-14 10:48:36 +12:00
214 lines
4.9 KiB
C++
214 lines
4.9 KiB
C++
#pragma once
|
|
|
|
#include "util/types.hpp"
|
|
#include "GLHelpers.h"
|
|
#include "glutils/vao.hpp"
|
|
#include "../Common/TextGlyphs.h"
|
|
#include <string>
|
|
#include <vector>
|
|
#include <unordered_map>
|
|
|
|
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;
|
|
std::unordered_map<u8, std::pair<u32, u32>> m_offsets;
|
|
|
|
bool initialized = false;
|
|
bool enabled = false;
|
|
|
|
f32 m_scale = 1.0f;
|
|
|
|
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(::glsl::program_domain::glsl_fragment_program, fs);
|
|
m_fs.compile();
|
|
|
|
m_vs.create(::glsl::program_domain::glsl_vertex_program, vs);
|
|
m_vs.compile();
|
|
|
|
m_program.create();
|
|
m_program.attach(m_vs);
|
|
m_program.attach(m_fs);
|
|
m_program.link();
|
|
}
|
|
|
|
void load_program(gl::command_context& cmd, float scale_x, float scale_y, float *offsets, usz nb_offsets, color4f color)
|
|
{
|
|
float scale[] = { scale_x, scale_y };
|
|
|
|
cmd->use_program(m_program.id());
|
|
|
|
m_program.uniforms["draw_color"] = color;
|
|
glProgramUniform2fv(m_program.id(), m_program.uniforms["offsets"].location(), static_cast<GLsizei>(nb_offsets), offsets);
|
|
glProgramUniform2fv(m_program.id(), m_program.uniforms["scale"].location(), 1, scale);
|
|
}
|
|
|
|
public:
|
|
|
|
text_writer() = default;
|
|
~text_writer() = default;
|
|
|
|
void init()
|
|
{
|
|
GlyphManager glyph_source;
|
|
auto points = glyph_source.generate_point_map();
|
|
|
|
const usz buffer_size = points.size() * sizeof(GlyphManager::glyph_point);
|
|
|
|
m_text_buffer.create(gl::buffer::target::array, buffer_size, points.data(), gl::buffer::memory_type::host_visible);
|
|
m_offsets = glyph_source.get_glyph_offsets();
|
|
|
|
//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 set_enabled(bool state)
|
|
{
|
|
enabled = state;
|
|
}
|
|
|
|
bool is_enabled()
|
|
{
|
|
return enabled;
|
|
}
|
|
|
|
void print_text(gl::command_context& cmd, 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;
|
|
|
|
ensure(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 = (static_cast<int>(target_h / m_scale) - y - 16);
|
|
|
|
//Compress [0, w] and [0, h] into range [-1, 1]
|
|
float scale_x = m_scale * 2.f / target_w;
|
|
float scale_y = m_scale * 2.f / target_h;
|
|
|
|
float base_offset = 0.f;
|
|
shader_offsets.reserve(text.length() * 2);
|
|
|
|
while (u8 offset = static_cast<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(cmd, scale_x, scale_y, shader_offsets.data(), counts.size(), color);
|
|
cmd->clip_planes(GL_NONE);
|
|
|
|
m_vao.bind();
|
|
|
|
glMultiDrawArrays(GL_POINTS, offsets.data(), counts.data(), static_cast<GLsizei>(counts.size()));
|
|
glBindVertexArray(old_vao);
|
|
}
|
|
|
|
void close()
|
|
{
|
|
if (initialized)
|
|
{
|
|
m_text_buffer.remove();
|
|
m_vao.remove();
|
|
|
|
m_program.remove();
|
|
m_fs.remove();
|
|
m_vs.remove();
|
|
|
|
initialized = false;
|
|
}
|
|
}
|
|
|
|
void set_scale(double scale)
|
|
{
|
|
// Restrict scale to 2. The dots are gonna be too sparse otherwise.
|
|
m_scale = std::min(static_cast<f32>(scale), 2.0f);
|
|
}
|
|
};
|
|
}
|