Skip to content

Commit

Permalink
[WIP] PlatformCore: Revise shader chain handling
Browse files Browse the repository at this point in the history
  • Loading branch information
GranMinigun committed Nov 14, 2023
1 parent fa3a8a3 commit 63bf8cb
Show file tree
Hide file tree
Showing 2 changed files with 135 additions and 60 deletions.
21 changes: 17 additions & 4 deletions src/platform/core/include/platform/device/ogl_video_device.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -36,12 +36,25 @@ struct OGLVideoDevice : VideoDevice {
GLenum type,
char const* source
) -> std::pair<bool, GLuint>;

auto CompileProgram(
char const* vertex_src,
char const* fragment_src
) -> std::pair<bool, GLuint>;

static constexpr size_t input_index = 0;
static constexpr size_t output_index = 1;
static constexpr size_t history_index = 2;
static constexpr size_t xbrz_output_index = 3;

struct ShaderPass {
GLuint program = 0;
struct {
std::vector<GLuint> inputs = {input_index};
GLuint output = output_index;
} textures = {};
};

int view_x = 0;
int view_y = 0;
int view_width = 1;
Expand All @@ -51,9 +64,9 @@ struct OGLVideoDevice : VideoDevice {
GLuint quad_vao;
GLuint quad_vbo;
GLuint fbo;
GLuint texture[4];
std::vector<GLuint> programs;
GLenum texture_filter = GL_NEAREST;
std::array<GLuint, 4> textures = {};
std::vector<ShaderPass> shader_passes = {};
GLint texture_filter = GL_NEAREST;

std::shared_ptr<PlatformConfig> config;
};
Expand Down
174 changes: 118 additions & 56 deletions src/platform/core/src/device/ogl_video_device.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ OGLVideoDevice::~OGLVideoDevice() {
glDeleteVertexArrays(1, &quad_vao);
glDeleteBuffers(1, &quad_vbo);
glDeleteFramebuffers(1, &fbo);
glDeleteTextures(3, texture);
glDeleteTextures(4, textures.data());
}

void OGLVideoDevice::Initialize() {
Expand All @@ -62,7 +62,7 @@ void OGLVideoDevice::Initialize() {

// Create three textures and a framebuffer for postprocessing.
glGenFramebuffers(1, &fbo);
glGenTextures(3, texture);
glGenTextures(4, textures.data());

glEnable(GL_FRAMEBUFFER_SRGB);

Expand All @@ -84,11 +84,11 @@ void OGLVideoDevice::ReloadConfig() {
}

void OGLVideoDevice::UpdateTextures() {
const auto texture_format =
config->video.color == Video::Color::No ? GL_SRGB8_ALPHA8 : GL_RGBA8;
const bool color_correction_active = config->video.color != Video::Color::No;
GLint texture_format = color_correction_active ? GL_RGBA8 : GL_SRGB8_ALPHA8;

for(auto& texture_ref : texture) {
glBindTexture(GL_TEXTURE_2D, texture_ref);
for(const auto texture : {textures[input_index], textures[output_index]}) {
glBindTexture(GL_TEXTURE_2D, texture);

glTexImage2D(GL_TEXTURE_2D, 0, texture_format, gba_screen_width,
gba_screen_height, 0, GL_BGRA, GL_UNSIGNED_BYTE, nullptr);
Expand All @@ -99,6 +99,33 @@ void OGLVideoDevice::UpdateTextures() {
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
}

const bool xbrz_ghosting = config->video.filter == Video::Filter::xBRZ && config->video.lcd_ghosting;
const GLsizei texture_width = xbrz_ghosting ? view_width : gba_screen_width;
const GLsizei texture_height = xbrz_ghosting ? view_height : gba_screen_height;
const GLint history_texture_format = (!color_correction_active || xbrz_ghosting) ? GL_SRGB8_ALPHA8 : GL_RGBA8;

glBindTexture(GL_TEXTURE_2D, textures[history_index]);

glTexImage2D(GL_TEXTURE_2D, 0, history_texture_format, texture_width,
texture_height, 0, GL_BGRA, GL_UNSIGNED_BYTE, nullptr);

glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, texture_filter);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, texture_filter);

glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);

glBindTexture(GL_TEXTURE_2D, textures[xbrz_output_index]);

glTexImage2D(GL_TEXTURE_2D, 0, texture_format, texture_width,
texture_height, 0, GL_BGRA, GL_UNSIGNED_BYTE, nullptr);

glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, texture_filter);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, texture_filter);

glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
}

void OGLVideoDevice::CreateShaderPrograms() {
Expand All @@ -111,14 +138,14 @@ void OGLVideoDevice::CreateShaderPrograms() {
case Video::Color::higan: {
auto [success, program] = CompileProgram(color_higan_vert, color_higan_frag);
if(success) {
programs.push_back(program);
shader_passes.push_back({.program = program});
}
break;
}
case Video::Color::AGB: {
auto [success, program] = CompileProgram(color_agb_vert, color_agb_frag);
if(success) {
programs.push_back(program);
shader_passes.push_back({.program = program});
}
break;
}
Expand All @@ -128,7 +155,13 @@ void OGLVideoDevice::CreateShaderPrograms() {
if(video.lcd_ghosting) {
auto [success, program] = CompileProgram(lcd_ghosting_vert, lcd_ghosting_frag);
if(success) {
programs.push_back(program);
const GLuint input_texture = video.filter == Video::Filter::xBRZ ? xbrz_output_index : input_index;
shader_passes.push_back({
.program = program,
.textures = {
.inputs = {input_texture, history_index}
}
});
}
}

Expand All @@ -140,8 +173,23 @@ void OGLVideoDevice::CreateShaderPrograms() {
auto [success1, program1] = CompileProgram(xbrz1_vert, xbrz1_frag);

if(success0 && success1) {
programs.push_back(program0);
programs.push_back(program1);
if (video.lcd_ghosting) {
shader_passes.insert(std::prev(shader_passes.end()),
{.program = program0});
shader_passes.insert(
std::prev(shader_passes.end()),
{.program = program1,
.textures = {.inputs = {output_index, input_index},
.output = xbrz_output_index}});
} else {
shader_passes.push_back({.program = program0});
shader_passes.push_back({
.program = program1,
.textures = {
.inputs = {output_index, input_index}
}
});
}
} else {
if(success0) glDeleteProgram(program0);
if(success1) glDeleteProgram(program1);
Expand All @@ -152,7 +200,7 @@ void OGLVideoDevice::CreateShaderPrograms() {
default: {
auto [success, program] = CompileProgram(output_vert, output_frag);
if(success) {
programs.push_back(program);
shader_passes.push_back({.program = program});
}
break;
}
Expand All @@ -163,47 +211,47 @@ void OGLVideoDevice::CreateShaderPrograms() {

void OGLVideoDevice::UpdateShaderUniforms() {
// Set constant shader uniforms.
for(const auto program : programs) {
glUseProgram(program);
for(const auto& shader_pass : shader_passes) {
glUseProgram(shader_pass.program);

const auto input_map = glGetUniformLocation(program, "u_input_map");
const auto input_map = glGetUniformLocation(shader_pass.program, "u_input_map");
if(input_map != -1) {
glUniform1i(input_map, 0);
}

const auto history_map = glGetUniformLocation(program, "u_history_map");
const auto history_map = glGetUniformLocation(shader_pass.program, "u_history_map");
if(history_map != -1) {
glUniform1i(history_map, 1);
}

const auto info_map = glGetUniformLocation(program, "u_info_map");
const auto info_map = glGetUniformLocation(shader_pass.program, "u_info_map");
if(info_map != -1) {
glUniform1i(info_map, 2);
glUniform1i(info_map, 1);
}

const auto output_size = glGetUniformLocation(program, "u_output_size");
const auto output_size = glGetUniformLocation(shader_pass.program, "u_output_size");
glUniform2f(output_size, view_width, view_height);
}
}

void OGLVideoDevice::ReleaseShaderPrograms() {
for(auto program : programs) {
glDeleteProgram(program);
for (const auto& shader_pass : shader_passes) {
glDeleteProgram(shader_pass.program);
}
programs.clear();
shader_passes.clear();
}

auto OGLVideoDevice::CompileShader(
GLenum type,
char const* source
) -> std::pair<bool, GLuint> {
char const* source_array[] = { source };

auto shader = glCreateShader(type);

glShaderSource(shader, 1, source_array, nullptr);
glCompileShader(shader);

GLint compiled = 0;
glGetShaderiv(shader, GL_COMPILE_STATUS, &compiled);
if(compiled == GL_FALSE) {
Expand All @@ -225,7 +273,7 @@ auto OGLVideoDevice::CompileProgram(
) -> std::pair<bool, GLuint> {
auto [vert_success, vert_id] = CompileShader(GL_VERTEX_SHADER, vertex_src);
auto [frag_success, frag_id] = CompileShader(GL_FRAGMENT_SHADER, fragment_src);

if(!vert_success || !frag_success) {
return std::make_pair<bool, GLuint>(false, 0);
} else {
Expand All @@ -247,6 +295,7 @@ void OGLVideoDevice::SetViewport(int x, int y, int width, int height) {
view_width = width;
view_height = height;

UpdateTextures();
UpdateShaderUniforms();
}

Expand All @@ -255,51 +304,64 @@ void OGLVideoDevice::SetDefaultFBO(GLuint fbo) {
}

void OGLVideoDevice::Draw(u32* buffer) {
// Bind LCD history map
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, texture[2]);

// Update and bind LCD screen texture
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, texture[0]);
glBindTexture(GL_TEXTURE_2D, textures[input_index]);
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, gba_screen_width, gba_screen_height,
GL_BGRA, GL_UNSIGNED_BYTE, buffer);

glViewport(0, 0, gba_screen_width, gba_screen_height);
glBindVertexArray(quad_vao);
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fbo);

for(auto pass_iter = programs.begin(); pass_iter != programs.end(); ++pass_iter) {
glUseProgram(*pass_iter);

if(pass_iter != programs.begin()) {
// Swap input and output textures.
std::swap(texture[0], texture[1]);
}
if(pass_iter == std::prev(programs.end())) {
// Before rendering to screen, copy the current texture content
// into the history texture for the ghosting effect shader.
if(config->video.filter == Video::Filter::xBRZ) {
glActiveTexture(GL_TEXTURE2);
glBindTexture(GL_TEXTURE_2D, texture[0]);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, texture[1]);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture[1], 0);
} else {
glBindTexture(GL_TEXTURE_2D, texture[0]);
}
glActiveTexture(GL_TEXTURE1);
glReadBuffer(GL_COLOR_ATTACHMENT0);
glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, gba_screen_width, gba_screen_height);
for (auto shader_pass = shader_passes.begin(); shader_pass != shader_passes.end(); ++shader_pass) {
glUseProgram(shader_pass->program);

if (shader_pass == std::prev(shader_passes.end())) {
glBindFramebuffer(GL_FRAMEBUFFER, default_fbo);
glViewport(view_x, view_y, view_width, view_height);
} else {
glBindTexture(GL_TEXTURE_2D, texture[0]);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture[1], 0);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, textures[shader_pass->textures.output]);

GLint output_width = 0;
GLint output_height = 0;
glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_WIDTH, &output_width);
glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_HEIGHT, &output_height);

glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, textures[shader_pass->textures.output], 0);

glViewport(0, 0, output_width, output_height);
}

for (size_t i = 0; i < shader_pass->textures.inputs.size(); ++i) {
glActiveTexture(GL_TEXTURE0 + i);
glBindTexture(GL_TEXTURE_2D, textures[shader_pass->textures.inputs[i]]);
}

glDrawArrays(GL_TRIANGLES, 0, 6);

std::swap(textures[input_index], textures[output_index]);
}

if (config->video.lcd_ghosting) {
GLint copy_area_x = 0;
GLint copy_area_y = 0;
GLsizei copy_area_width = gba_screen_width;
GLsizei copy_area_height = gba_screen_height;
if (config->video.filter == Video::Filter::xBRZ) {
copy_area_x = view_x;
copy_area_y = view_y;
copy_area_width = view_width;
copy_area_height = view_height;
} else {
glBindFramebuffer(GL_READ_FRAMEBUFFER, fbo);
glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, textures[output_index], 0);
}

glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, textures[history_index]);
glReadBuffer(GL_COLOR_ATTACHMENT0);
glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, copy_area_x, copy_area_y, copy_area_width, copy_area_height);
}
}

Expand Down

0 comments on commit 63bf8cb

Please sign in to comment.