HWRasterizer: Imm state tracker + track active tex

This commit is contained in:
tfarley 2016-04-27 13:43:08 -04:00
parent a6e76c0903
commit d9ef255f64
7 changed files with 447 additions and 294 deletions

View file

@ -37,10 +37,13 @@ static bool IsPassThroughTevStage(const Pica::Regs::TevStageConfig& stage) {
} }
RasterizerOpenGL::RasterizerOpenGL() : shader_dirty(true) { RasterizerOpenGL::RasterizerOpenGL() : shader_dirty(true) {
state.MakeCurrent();
// Create sampler objects // Create sampler objects
for (size_t i = 0; i < texture_samplers.size(); ++i) { for (size_t i = 0; i < texture_samplers.size(); ++i) {
texture_samplers[i].Create(); texture_samplers[i].Create();
state.texture_units[i].sampler = texture_samplers[i].sampler.handle; state.SetActiveTextureUnit(GL_TEXTURE0 + i);
state.SetSampler(texture_samplers[i].sampler.handle);
} }
// Generate VBO, VAO and UBO // Generate VBO, VAO and UBO
@ -48,10 +51,9 @@ RasterizerOpenGL::RasterizerOpenGL() : shader_dirty(true) {
vertex_array.Create(); vertex_array.Create();
uniform_buffer.Create(); uniform_buffer.Create();
state.draw.vertex_array = vertex_array.handle; state.SetVertexArray(vertex_array.handle);
state.draw.vertex_buffer = vertex_buffer.handle; state.SetVertexBuffer(vertex_buffer.handle);
state.draw.uniform_buffer = uniform_buffer.handle; state.SetUniformBuffer(uniform_buffer.handle);
state.Apply();
// Bind the UBO to binding point 0 // Bind the UBO to binding point 0
glBindBufferBase(GL_UNIFORM_BUFFER, 0, uniform_buffer.handle); glBindBufferBase(GL_UNIFORM_BUFFER, 0, uniform_buffer.handle);
@ -88,12 +90,8 @@ RasterizerOpenGL::RasterizerOpenGL() : shader_dirty(true) {
// Allocate and bind lighting lut textures // Allocate and bind lighting lut textures
for (size_t i = 0; i < lighting_luts.size(); ++i) { for (size_t i = 0; i < lighting_luts.size(); ++i) {
lighting_luts[i].Create(); lighting_luts[i].Create();
state.lighting_luts[i].texture_1d = lighting_luts[i].handle; state.SetActiveTextureUnit(GL_TEXTURE3 + i);
} state.SetLUTTexture1D(lighting_luts[i].handle);
state.Apply();
for (size_t i = 0; i < lighting_luts.size(); ++i) {
glActiveTexture(GL_TEXTURE3 + i);
glTexImage1D(GL_TEXTURE_1D, 0, GL_RGBA32F, 256, 0, GL_RGBA, GL_FLOAT, nullptr); glTexImage1D(GL_TEXTURE_1D, 0, GL_RGBA32F, 256, 0, GL_RGBA, GL_FLOAT, nullptr);
glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
@ -151,6 +149,8 @@ void RasterizerOpenGL::DrawTriangles() {
if (vertex_batch.empty()) if (vertex_batch.empty())
return; return;
state.MakeCurrent();
const auto& regs = Pica::g_state.regs; const auto& regs = Pica::g_state.regs;
// Sync and bind the framebuffer surfaces // Sync and bind the framebuffer surfaces
@ -159,8 +159,7 @@ void RasterizerOpenGL::DrawTriangles() {
MathUtil::Rectangle<int> rect; MathUtil::Rectangle<int> rect;
std::tie(color_surface, depth_surface, rect) = res_cache.GetFramebufferSurfaces(regs.framebuffer); std::tie(color_surface, depth_surface, rect) = res_cache.GetFramebufferSurfaces(regs.framebuffer);
state.draw.draw_framebuffer = framebuffer.handle; state.SetDrawFramebuffer(framebuffer.handle);
state.Apply();
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, color_surface != nullptr ? color_surface->texture.handle : 0, 0); glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, color_surface != nullptr ? color_surface->texture.handle : 0, 0);
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, depth_surface != nullptr ? depth_surface->texture.handle : 0, 0); glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, depth_surface != nullptr ? depth_surface->texture.handle : 0, 0);
@ -188,14 +187,16 @@ void RasterizerOpenGL::DrawTriangles() {
if (texture.enabled) { if (texture.enabled) {
texture_samplers[texture_index].SyncWithConfig(texture.config); texture_samplers[texture_index].SyncWithConfig(texture.config);
CachedSurface* surface = res_cache.GetTextureSurface(texture); CachedSurface* surface = res_cache.GetTextureSurface(texture);
state.SetActiveTextureUnit(GL_TEXTURE0 + texture_index);
if (surface != nullptr) { if (surface != nullptr) {
state.texture_units[texture_index].texture_2d = surface->texture.handle; state.SetTexture2D(surface->texture.handle);
} else { } else {
// Can occur when texture addr is null or its memory is unmapped/invalid // Can occur when texture addr is null or its memory is unmapped/invalid
state.texture_units[texture_index].texture_2d = 0; state.SetTexture2D(0);
} }
} else { } else {
state.texture_units[texture_index].texture_2d = 0; state.SetActiveTextureUnit(GL_TEXTURE0 + texture_index);
state.SetTexture2D(0);
} }
} }
@ -219,8 +220,6 @@ void RasterizerOpenGL::DrawTriangles() {
uniform_block_data.dirty = false; uniform_block_data.dirty = false;
} }
state.Apply();
// Draw the vertex batch // Draw the vertex batch
glBufferData(GL_ARRAY_BUFFER, vertex_batch.size() * sizeof(HardwareVertex), vertex_batch.data(), GL_STREAM_DRAW); glBufferData(GL_ARRAY_BUFFER, vertex_batch.size() * sizeof(HardwareVertex), vertex_batch.data(), GL_STREAM_DRAW);
glDrawArrays(GL_TRIANGLES, 0, (GLsizei)vertex_batch.size()); glDrawArrays(GL_TRIANGLES, 0, (GLsizei)vertex_batch.size());
@ -240,14 +239,16 @@ void RasterizerOpenGL::DrawTriangles() {
// Unbind textures for potential future use as framebuffer attachments // Unbind textures for potential future use as framebuffer attachments
for (unsigned texture_index = 0; texture_index < pica_textures.size(); ++texture_index) { for (unsigned texture_index = 0; texture_index < pica_textures.size(); ++texture_index) {
state.texture_units[texture_index].texture_2d = 0; state.SetActiveTextureUnit(GL_TEXTURE0 + texture_index);
state.SetTexture2D(0);
} }
state.Apply();
} }
void RasterizerOpenGL::NotifyPicaRegisterChanged(u32 id) { void RasterizerOpenGL::NotifyPicaRegisterChanged(u32 id) {
const auto& regs = Pica::g_state.regs; const auto& regs = Pica::g_state.regs;
state.MakeCurrent();
switch(id) { switch(id) {
// Culling // Culling
case PICA_REG_INDEX(cull_mode): case PICA_REG_INDEX(cull_mode):
@ -609,20 +610,20 @@ bool RasterizerOpenGL::AccelerateFill(const GPU::Regs::MemoryFillConfig& config)
return false; return false;
} }
OpenGLState cur_state = OpenGLState::GetCurState();
SurfaceType dst_type = CachedSurface::GetFormatType(dst_surface->pixel_format); SurfaceType dst_type = CachedSurface::GetFormatType(dst_surface->pixel_format);
GLuint old_fb = cur_state.draw.draw_framebuffer; OpenGLState* old_state = OpenGLState::GetCurrentState();
cur_state.draw.draw_framebuffer = framebuffer.handle; utility_state.MakeCurrent();
// TODO: When scissor test is implemented, need to disable scissor test in cur_state here so Clear call isn't affected
cur_state.Apply(); utility_state.SetDrawFramebuffer(framebuffer.handle);
// TODO: When scissor test is implemented, need to disable scissor test in the state here so Clear call isn't affected
if (dst_type == SurfaceType::Color || dst_type == SurfaceType::Texture) { if (dst_type == SurfaceType::Color || dst_type == SurfaceType::Texture) {
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, dst_surface->texture.handle, 0); glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, dst_surface->texture.handle, 0);
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, 0, 0); glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, 0, 0);
if (OpenGLState::CheckFBStatus(GL_DRAW_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) { if (OpenGLState::CheckFBStatus(GL_DRAW_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
old_state->MakeCurrent();
return false; return false;
} }
@ -640,6 +641,7 @@ bool RasterizerOpenGL::AccelerateFill(const GPU::Regs::MemoryFillConfig& config)
color_values[2] = config.value_24bit_b / 255.0f; color_values[2] = config.value_24bit_b / 255.0f;
break; break;
default: default:
old_state->MakeCurrent();
return false; return false;
} }
} else if (config.fill_32bit) { } else if (config.fill_32bit) {
@ -653,6 +655,7 @@ bool RasterizerOpenGL::AccelerateFill(const GPU::Regs::MemoryFillConfig& config)
color_values[3] = (value & 0xFF) / 255.0f; color_values[3] = (value & 0xFF) / 255.0f;
break; break;
default: default:
old_state->MakeCurrent();
return false; return false;
} }
} else { } else {
@ -692,15 +695,12 @@ bool RasterizerOpenGL::AccelerateFill(const GPU::Regs::MemoryFillConfig& config)
color_values[1] = (value_16bit & 0xFF) / 255.0f; color_values[1] = (value_16bit & 0xFF) / 255.0f;
break; break;
default: default:
old_state->MakeCurrent();
return false; return false;
} }
} }
cur_state.color_mask.red_enabled = true; utility_state.SetColorMask(true, true, true, true);
cur_state.color_mask.green_enabled = true;
cur_state.color_mask.blue_enabled = true;
cur_state.color_mask.alpha_enabled = true;
cur_state.Apply();
glClearBufferfv(GL_COLOR, 0, color_values); glClearBufferfv(GL_COLOR, 0, color_values);
} else if (dst_type == SurfaceType::Depth) { } else if (dst_type == SurfaceType::Depth) {
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0); glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0);
@ -708,6 +708,7 @@ bool RasterizerOpenGL::AccelerateFill(const GPU::Regs::MemoryFillConfig& config)
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_TEXTURE_2D, 0, 0); glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_TEXTURE_2D, 0, 0);
if (OpenGLState::CheckFBStatus(GL_DRAW_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) { if (OpenGLState::CheckFBStatus(GL_DRAW_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
old_state->MakeCurrent();
return false; return false;
} }
@ -718,29 +719,26 @@ bool RasterizerOpenGL::AccelerateFill(const GPU::Regs::MemoryFillConfig& config)
value_float = config.value_32bit / 16777215.0f; // 2^24 - 1 value_float = config.value_32bit / 16777215.0f; // 2^24 - 1
} }
cur_state.depth.write_mask = true; utility_state.SetDepthWriteMask(true);
cur_state.Apply();
glClearBufferfv(GL_DEPTH, 0, &value_float); glClearBufferfv(GL_DEPTH, 0, &value_float);
} else if (dst_type == SurfaceType::DepthStencil) { } else if (dst_type == SurfaceType::DepthStencil) {
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0); glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0);
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, dst_surface->texture.handle, 0); glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, dst_surface->texture.handle, 0);
if (OpenGLState::CheckFBStatus(GL_DRAW_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) { if (OpenGLState::CheckFBStatus(GL_DRAW_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
old_state->MakeCurrent();
return false; return false;
} }
GLfloat value_float = (config.value_32bit & 0xFFFFFF) / 16777215.0f; // 2^24 - 1 GLfloat value_float = (config.value_32bit & 0xFFFFFF) / 16777215.0f; // 2^24 - 1
GLint value_int = (config.value_32bit >> 24); GLint value_int = (config.value_32bit >> 24);
cur_state.depth.write_mask = true; utility_state.SetDepthWriteMask(true);
cur_state.stencil.write_mask = true; utility_state.SetStencilWriteMask(true);
cur_state.Apply();
glClearBufferfi(GL_DEPTH_STENCIL, 0, value_float, value_int); glClearBufferfi(GL_DEPTH_STENCIL, 0, value_float, value_int);
} }
cur_state.draw.draw_framebuffer = old_fb; old_state->MakeCurrent();
// TODO: Return scissor test to previous value when scissor test is implemented
cur_state.Apply();
dst_surface->dirty = true; dst_surface->dirty = true;
res_cache.FlushRegion(dst_surface->addr, dst_surface->size, dst_surface, true); res_cache.FlushRegion(dst_surface->addr, dst_surface->size, dst_surface, true);
@ -824,20 +822,20 @@ void RasterizerOpenGL::SetShader() {
PicaShaderConfig config = PicaShaderConfig::CurrentConfig(); PicaShaderConfig config = PicaShaderConfig::CurrentConfig();
std::unique_ptr<PicaShader> shader = std::make_unique<PicaShader>(); std::unique_ptr<PicaShader> shader = std::make_unique<PicaShader>();
state.MakeCurrent();
// Find (or generate) the GLSL shader for the current TEV state // Find (or generate) the GLSL shader for the current TEV state
auto cached_shader = shader_cache.find(config); auto cached_shader = shader_cache.find(config);
if (cached_shader != shader_cache.end()) { if (cached_shader != shader_cache.end()) {
current_shader = cached_shader->second.get(); current_shader = cached_shader->second.get();
state.draw.shader_program = current_shader->shader.handle; state.SetShaderProgram(current_shader->shader.handle);
state.Apply();
} else { } else {
LOG_DEBUG(Render_OpenGL, "Creating new shader"); LOG_DEBUG(Render_OpenGL, "Creating new shader");
shader->shader.Create(GLShader::GenerateVertexShader().c_str(), GLShader::GenerateFragmentShader(config).c_str()); shader->shader.Create(GLShader::GenerateVertexShader().c_str(), GLShader::GenerateFragmentShader(config).c_str());
state.draw.shader_program = shader->shader.handle; state.SetShaderProgram(shader->shader.handle);
state.Apply();
// Set the texture samplers to correspond to different texture units // Set the texture samplers to correspond to different texture units
GLuint uniform_tex = glGetUniformLocation(shader->shader.handle, "tex[0]"); GLuint uniform_tex = glGetUniformLocation(shader->shader.handle, "tex[0]");
@ -889,17 +887,17 @@ void RasterizerOpenGL::SyncCullMode() {
switch (regs.cull_mode) { switch (regs.cull_mode) {
case Pica::Regs::CullMode::KeepAll: case Pica::Regs::CullMode::KeepAll:
state.cull.enabled = false; state.SetCullEnabled(false);
break; break;
case Pica::Regs::CullMode::KeepClockWise: case Pica::Regs::CullMode::KeepClockWise:
state.cull.enabled = true; state.SetCullEnabled(true);
state.cull.front_face = GL_CW; state.SetCullFrontFace(GL_CW);
break; break;
case Pica::Regs::CullMode::KeepCounterClockWise: case Pica::Regs::CullMode::KeepCounterClockWise:
state.cull.enabled = true; state.SetCullEnabled(true);
state.cull.front_face = GL_CCW; state.SetCullFrontFace(GL_CCW);
break; break;
default: default:
@ -919,23 +917,23 @@ void RasterizerOpenGL::SyncDepthModifiers() {
} }
void RasterizerOpenGL::SyncBlendEnabled() { void RasterizerOpenGL::SyncBlendEnabled() {
state.blend.enabled = (Pica::g_state.regs.output_merger.alphablend_enable == 1); state.SetBlendEnabled(Pica::g_state.regs.output_merger.alphablend_enable == 1);
} }
void RasterizerOpenGL::SyncBlendFuncs() { void RasterizerOpenGL::SyncBlendFuncs() {
const auto& regs = Pica::g_state.regs; const auto& regs = Pica::g_state.regs;
state.blend.src_rgb_func = PicaToGL::BlendFunc(regs.output_merger.alpha_blending.factor_source_rgb); state.SetBlendFunc(PicaToGL::BlendFunc(regs.output_merger.alpha_blending.factor_source_rgb),
state.blend.dst_rgb_func = PicaToGL::BlendFunc(regs.output_merger.alpha_blending.factor_dest_rgb); PicaToGL::BlendFunc(regs.output_merger.alpha_blending.factor_dest_rgb),
state.blend.src_a_func = PicaToGL::BlendFunc(regs.output_merger.alpha_blending.factor_source_a); PicaToGL::BlendFunc(regs.output_merger.alpha_blending.factor_source_a),
state.blend.dst_a_func = PicaToGL::BlendFunc(regs.output_merger.alpha_blending.factor_dest_a); PicaToGL::BlendFunc(regs.output_merger.alpha_blending.factor_dest_a));
} }
void RasterizerOpenGL::SyncBlendColor() { void RasterizerOpenGL::SyncBlendColor() {
auto blend_color = PicaToGL::ColorRGBA8(Pica::g_state.regs.output_merger.blend_const.raw); auto blend_color = PicaToGL::ColorRGBA8(Pica::g_state.regs.output_merger.blend_const.raw);
state.blend.color.red = blend_color[0]; state.SetBlendColor(blend_color[0],
state.blend.color.green = blend_color[1]; blend_color[1],
state.blend.color.blue = blend_color[2]; blend_color[2],
state.blend.color.alpha = blend_color[3]; blend_color[3]);
} }
void RasterizerOpenGL::SyncAlphaTest() { void RasterizerOpenGL::SyncAlphaTest() {
@ -947,7 +945,7 @@ void RasterizerOpenGL::SyncAlphaTest() {
} }
void RasterizerOpenGL::SyncLogicOp() { void RasterizerOpenGL::SyncLogicOp() {
state.logic_op = PicaToGL::LogicOp(Pica::g_state.regs.output_merger.logic_op); state.SetLogicOp(PicaToGL::LogicOp(Pica::g_state.regs.output_merger.logic_op));
} }
void RasterizerOpenGL::SyncColorWriteMask() { void RasterizerOpenGL::SyncColorWriteMask() {
@ -957,43 +955,43 @@ void RasterizerOpenGL::SyncColorWriteMask() {
return (regs.framebuffer.allow_color_write != 0 && value != 0) ? GL_TRUE : GL_FALSE; return (regs.framebuffer.allow_color_write != 0 && value != 0) ? GL_TRUE : GL_FALSE;
}; };
state.color_mask.red_enabled = IsColorWriteEnabled(regs.output_merger.red_enable); state.SetColorMask(IsColorWriteEnabled(regs.output_merger.red_enable),
state.color_mask.green_enabled = IsColorWriteEnabled(regs.output_merger.green_enable); IsColorWriteEnabled(regs.output_merger.green_enable),
state.color_mask.blue_enabled = IsColorWriteEnabled(regs.output_merger.blue_enable); IsColorWriteEnabled(regs.output_merger.blue_enable),
state.color_mask.alpha_enabled = IsColorWriteEnabled(regs.output_merger.alpha_enable); IsColorWriteEnabled(regs.output_merger.alpha_enable));
} }
void RasterizerOpenGL::SyncStencilWriteMask() { void RasterizerOpenGL::SyncStencilWriteMask() {
const auto& regs = Pica::g_state.regs; const auto& regs = Pica::g_state.regs;
state.stencil.write_mask = (regs.framebuffer.allow_depth_stencil_write != 0) state.SetStencilWriteMask((regs.framebuffer.allow_depth_stencil_write != 0)
? static_cast<GLuint>(regs.output_merger.stencil_test.write_mask) ? static_cast<GLuint>(regs.output_merger.stencil_test.write_mask)
: 0; : 0);
} }
void RasterizerOpenGL::SyncDepthWriteMask() { void RasterizerOpenGL::SyncDepthWriteMask() {
const auto& regs = Pica::g_state.regs; const auto& regs = Pica::g_state.regs;
state.depth.write_mask = (regs.framebuffer.allow_depth_stencil_write != 0 && regs.output_merger.depth_write_enable) state.SetDepthWriteMask((regs.framebuffer.allow_depth_stencil_write != 0 && regs.output_merger.depth_write_enable)
? GL_TRUE ? GL_TRUE
: GL_FALSE; : GL_FALSE);
} }
void RasterizerOpenGL::SyncStencilTest() { void RasterizerOpenGL::SyncStencilTest() {
const auto& regs = Pica::g_state.regs; const auto& regs = Pica::g_state.regs;
state.stencil.test_enabled = regs.output_merger.stencil_test.enable && regs.framebuffer.depth_format == Pica::Regs::DepthFormat::D24S8; state.SetStencilTestEnabled(regs.output_merger.stencil_test.enable && regs.framebuffer.depth_format == Pica::Regs::DepthFormat::D24S8);
state.stencil.test_func = PicaToGL::CompareFunc(regs.output_merger.stencil_test.func); state.SetStencilFunc(PicaToGL::CompareFunc(regs.output_merger.stencil_test.func),
state.stencil.test_ref = regs.output_merger.stencil_test.reference_value; regs.output_merger.stencil_test.reference_value,
state.stencil.test_mask = regs.output_merger.stencil_test.input_mask; regs.output_merger.stencil_test.input_mask);
state.stencil.action_stencil_fail = PicaToGL::StencilOp(regs.output_merger.stencil_test.action_stencil_fail); state.SetStencilOp(PicaToGL::StencilOp(regs.output_merger.stencil_test.action_stencil_fail),
state.stencil.action_depth_fail = PicaToGL::StencilOp(regs.output_merger.stencil_test.action_depth_fail); PicaToGL::StencilOp(regs.output_merger.stencil_test.action_depth_fail),
state.stencil.action_depth_pass = PicaToGL::StencilOp(regs.output_merger.stencil_test.action_depth_pass); PicaToGL::StencilOp(regs.output_merger.stencil_test.action_depth_pass));
} }
void RasterizerOpenGL::SyncDepthTest() { void RasterizerOpenGL::SyncDepthTest() {
const auto& regs = Pica::g_state.regs; const auto& regs = Pica::g_state.regs;
state.depth.test_enabled = regs.output_merger.depth_test_enable == 1 || state.SetDepthTestEnabled(regs.output_merger.depth_test_enable == 1 ||
regs.output_merger.depth_write_enable == 1; regs.output_merger.depth_write_enable == 1);
state.depth.test_func = regs.output_merger.depth_test_enable == 1 ? state.SetDepthFunc(regs.output_merger.depth_test_enable == 1 ?
PicaToGL::CompareFunc(regs.output_merger.depth_test_func) : GL_ALWAYS; PicaToGL::CompareFunc(regs.output_merger.depth_test_func) : GL_ALWAYS);
} }
void RasterizerOpenGL::SyncCombinerColor() { void RasterizerOpenGL::SyncCombinerColor() {
@ -1032,7 +1030,7 @@ void RasterizerOpenGL::SyncLightingLUT(unsigned lut_index) {
if (new_data != lighting_lut_data[lut_index]) { if (new_data != lighting_lut_data[lut_index]) {
lighting_lut_data[lut_index] = new_data; lighting_lut_data[lut_index] = new_data;
glActiveTexture(GL_TEXTURE3 + lut_index); state.SetActiveTextureUnit(GL_TEXTURE3 + lut_index);
glTexSubImage1D(GL_TEXTURE_1D, 0, 0, 256, GL_RGBA, GL_FLOAT, lighting_lut_data[lut_index].data()); glTexSubImage1D(GL_TEXTURE_1D, 0, 0, 256, GL_RGBA, GL_FLOAT, lighting_lut_data[lut_index].data());
} }
} }

View file

@ -351,6 +351,7 @@ private:
void SyncLightPosition(int light_index); void SyncLightPosition(int light_index);
OpenGLState state; OpenGLState state;
OpenGLState utility_state;
RasterizerCacheOpenGL res_cache; RasterizerCacheOpenGL res_cache;

View file

@ -105,17 +105,15 @@ static void MortonCopyPixels(CachedSurface::PixelFormat pixel_format, u32 width,
bool RasterizerCacheOpenGL::BlitTextures(GLuint src_tex, GLuint dst_tex, CachedSurface::SurfaceType type, const MathUtil::Rectangle<int>& src_rect, const MathUtil::Rectangle<int>& dst_rect) { bool RasterizerCacheOpenGL::BlitTextures(GLuint src_tex, GLuint dst_tex, CachedSurface::SurfaceType type, const MathUtil::Rectangle<int>& src_rect, const MathUtil::Rectangle<int>& dst_rect) {
using SurfaceType = CachedSurface::SurfaceType; using SurfaceType = CachedSurface::SurfaceType;
OpenGLState cur_state = OpenGLState::GetCurState(); OpenGLState* old_state = OpenGLState::GetCurrentState();
utility_state.MakeCurrent();
// Make sure textures aren't bound to texture units, since going to bind them to framebuffer components // Make sure textures aren't bound to texture units, since going to bind them to framebuffer components
OpenGLState::ResetTexture(src_tex); OpenGLState::ResetTexture(src_tex);
OpenGLState::ResetTexture(dst_tex); OpenGLState::ResetTexture(dst_tex);
// Keep track of previous framebuffer bindings utility_state.SetReadFramebuffer(transfer_framebuffers[0].handle);
GLuint old_fbs[2] = { cur_state.draw.read_framebuffer, cur_state.draw.draw_framebuffer }; utility_state.SetDrawFramebuffer(transfer_framebuffers[1].handle);
cur_state.draw.read_framebuffer = transfer_framebuffers[0].handle;
cur_state.draw.draw_framebuffer = transfer_framebuffers[1].handle;
cur_state.Apply();
u32 buffers = 0; u32 buffers = 0;
@ -148,10 +146,12 @@ bool RasterizerCacheOpenGL::BlitTextures(GLuint src_tex, GLuint dst_tex, CachedS
} }
if (OpenGLState::CheckFBStatus(GL_READ_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) { if (OpenGLState::CheckFBStatus(GL_READ_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
old_state->MakeCurrent();
return false; return false;
} }
if (OpenGLState::CheckFBStatus(GL_DRAW_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) { if (OpenGLState::CheckFBStatus(GL_DRAW_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
old_state->MakeCurrent();
return false; return false;
} }
@ -159,10 +159,7 @@ bool RasterizerCacheOpenGL::BlitTextures(GLuint src_tex, GLuint dst_tex, CachedS
dst_rect.left, dst_rect.top, dst_rect.right, dst_rect.bottom, dst_rect.left, dst_rect.top, dst_rect.right, dst_rect.bottom,
buffers, buffers == GL_COLOR_BUFFER_BIT ? GL_LINEAR : GL_NEAREST); buffers, buffers == GL_COLOR_BUFFER_BIT ? GL_LINEAR : GL_NEAREST);
// Restore previous framebuffer bindings old_state->MakeCurrent();
cur_state.draw.read_framebuffer = old_fbs[0];
cur_state.draw.draw_framebuffer = old_fbs[1];
cur_state.Apply();
return true; return true;
} }
@ -177,17 +174,15 @@ bool RasterizerCacheOpenGL::TryBlitSurfaces(CachedSurface* src_surface, const Ma
return BlitTextures(src_surface->texture.handle, dst_surface->texture.handle, CachedSurface::GetFormatType(src_surface->pixel_format), src_rect, dst_rect); return BlitTextures(src_surface->texture.handle, dst_surface->texture.handle, CachedSurface::GetFormatType(src_surface->pixel_format), src_rect, dst_rect);
} }
static void AllocateSurfaceTexture(GLuint texture, CachedSurface::PixelFormat pixel_format, u32 width, u32 height) { void RasterizerCacheOpenGL::AllocateSurfaceTexture(GLuint texture, CachedSurface::PixelFormat pixel_format, u32 width, u32 height) {
// Allocate an uninitialized texture of appropriate size and format for the surface // Allocate an uninitialized texture of appropriate size and format for the surface
using SurfaceType = CachedSurface::SurfaceType; using SurfaceType = CachedSurface::SurfaceType;
OpenGLState cur_state = OpenGLState::GetCurState(); OpenGLState* old_state = OpenGLState::GetCurrentState();
utility_state.MakeCurrent();
// Keep track of previous texture bindings utility_state.SetActiveTextureUnit(GL_TEXTURE0);
GLuint old_tex = cur_state.texture_units[0].texture_2d; utility_state.SetTexture2D(texture);
cur_state.texture_units[0].texture_2d = texture;
cur_state.Apply();
glActiveTexture(GL_TEXTURE0);
SurfaceType type = CachedSurface::GetFormatType(pixel_format); SurfaceType type = CachedSurface::GetFormatType(pixel_format);
@ -211,9 +206,7 @@ static void AllocateSurfaceTexture(GLuint texture, CachedSurface::PixelFormat pi
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
// Restore previous texture bindings old_state->MakeCurrent();
cur_state.texture_units[0].texture_2d = old_tex;
cur_state.Apply();
} }
MICROPROFILE_DEFINE(OpenGL_SurfaceUpload, "OpenGL", "Surface Upload", MP_RGB(128, 64, 192)); MICROPROFILE_DEFINE(OpenGL_SurfaceUpload, "OpenGL", "Surface Upload", MP_RGB(128, 64, 192));
@ -295,12 +288,11 @@ CachedSurface* RasterizerCacheOpenGL::GetSurface(const CachedSurface& params, bo
Memory::RasterizerFlushRegion(params.addr, params_size); Memory::RasterizerFlushRegion(params.addr, params_size);
// Load data from memory to the new surface // Load data from memory to the new surface
OpenGLState cur_state = OpenGLState::GetCurState(); OpenGLState* old_state = OpenGLState::GetCurrentState();
utility_state.MakeCurrent();
GLuint old_tex = cur_state.texture_units[0].texture_2d; utility_state.SetActiveTextureUnit(GL_TEXTURE0);
cur_state.texture_units[0].texture_2d = new_surface->texture.handle; utility_state.SetTexture2D(new_surface->texture.handle);
cur_state.Apply();
glActiveTexture(GL_TEXTURE0);
glPixelStorei(GL_UNPACK_ROW_LENGTH, (GLint)new_surface->stride); glPixelStorei(GL_UNPACK_ROW_LENGTH, (GLint)new_surface->stride);
if (!new_surface->is_tiled) { if (!new_surface->is_tiled) {
@ -375,8 +367,6 @@ CachedSurface* RasterizerCacheOpenGL::GetSurface(const CachedSurface& params, bo
new_surface->texture.Release(); new_surface->texture.Release();
new_surface->texture.handle = scaled_texture.handle; new_surface->texture.handle = scaled_texture.handle;
scaled_texture.handle = 0; scaled_texture.handle = 0;
cur_state.texture_units[0].texture_2d = new_surface->texture.handle;
cur_state.Apply();
} }
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0);
@ -384,8 +374,7 @@ CachedSurface* RasterizerCacheOpenGL::GetSurface(const CachedSurface& params, bo
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
cur_state.texture_units[0].texture_2d = old_tex; old_state->MakeCurrent();
cur_state.Apply();
} }
Memory::RasterizerMarkRegionCached(new_surface->addr, new_surface->size, 1); Memory::RasterizerMarkRegionCached(new_surface->addr, new_surface->size, 1);
@ -607,8 +596,8 @@ void RasterizerCacheOpenGL::FlushSurface(CachedSurface* surface) {
return; return;
} }
OpenGLState cur_state = OpenGLState::GetCurState(); OpenGLState* old_state = OpenGLState::GetCurrentState();
GLuint old_tex = cur_state.texture_units[0].texture_2d; utility_state.MakeCurrent();
OGLTexture unscaled_tex; OGLTexture unscaled_tex;
GLuint texture_to_flush = surface->texture.handle; GLuint texture_to_flush = surface->texture.handle;
@ -625,9 +614,8 @@ void RasterizerCacheOpenGL::FlushSurface(CachedSurface* surface) {
texture_to_flush = unscaled_tex.handle; texture_to_flush = unscaled_tex.handle;
} }
cur_state.texture_units[0].texture_2d = texture_to_flush; utility_state.SetActiveTextureUnit(GL_TEXTURE0);
cur_state.Apply(); utility_state.SetTexture2D(texture_to_flush);
glActiveTexture(GL_TEXTURE0);
glPixelStorei(GL_PACK_ROW_LENGTH, (GLint)surface->stride); glPixelStorei(GL_PACK_ROW_LENGTH, (GLint)surface->stride);
if (!surface->is_tiled) { if (!surface->is_tiled) {
@ -676,8 +664,7 @@ void RasterizerCacheOpenGL::FlushSurface(CachedSurface* surface) {
surface->dirty = false; surface->dirty = false;
cur_state.texture_units[0].texture_2d = old_tex; old_state->MakeCurrent();
cur_state.Apply();
} }
void RasterizerCacheOpenGL::FlushRegion(PAddr addr, u32 size, const CachedSurface* skip_surface, bool invalidate) { void RasterizerCacheOpenGL::FlushRegion(PAddr addr, u32 size, const CachedSurface* skip_surface, bool invalidate) {

View file

@ -217,6 +217,10 @@ public:
void FlushAll(); void FlushAll();
private: private:
void AllocateSurfaceTexture(GLuint texture, CachedSurface::PixelFormat pixel_format, u32 width, u32 height);
OpenGLState utility_state;
SurfaceCache surface_cache; SurfaceCache surface_cache;
OGLFramebuffer transfer_framebuffers[2]; OGLFramebuffer transfer_framebuffers[2];
}; };

View file

@ -9,7 +9,8 @@
#include "video_core/renderer_opengl/gl_state.h" #include "video_core/renderer_opengl/gl_state.h"
OpenGLState OpenGLState::cur_state; static OpenGLState default_state;
OpenGLState* OpenGLState::cur_state = &default_state;
OpenGLState::OpenGLState() { OpenGLState::OpenGLState() {
// These all match default OpenGL values // These all match default OpenGL values
@ -56,6 +57,8 @@ OpenGLState::OpenGLState() {
lut.texture_1d = 0; lut.texture_1d = 0;
} }
active_texture_unit = GL_TEXTURE0;
draw.read_framebuffer = 0; draw.read_framebuffer = 0;
draw.draw_framebuffer = 0; draw.draw_framebuffer = 0;
draw.vertex_array = 0; draw.vertex_array = 0;
@ -64,159 +67,290 @@ OpenGLState::OpenGLState() {
draw.shader_program = 0; draw.shader_program = 0;
} }
void OpenGLState::Apply() const { OpenGLState* OpenGLState::GetCurrentState() {
// Culling return cur_state;
if (cull.enabled != cur_state.cull.enabled) { }
if (cull.enabled) {
void OpenGLState::SetCullEnabled(bool n_enabled) {
if (n_enabled != cur_state->cull.enabled) {
if (n_enabled) {
glEnable(GL_CULL_FACE); glEnable(GL_CULL_FACE);
} else { } else {
glDisable(GL_CULL_FACE); glDisable(GL_CULL_FACE);
} }
} }
cull.enabled = n_enabled;
}
if (cull.mode != cur_state.cull.mode) { void OpenGLState::SetCullMode(GLenum n_mode) {
glCullFace(cull.mode); if (n_mode != cur_state->cull.mode) {
glCullFace(n_mode);
} }
cull.mode = n_mode;
}
if (cull.front_face != cur_state.cull.front_face) { void OpenGLState::SetCullFrontFace(GLenum n_front_face) {
glFrontFace(cull.front_face); if (n_front_face != cur_state->cull.front_face) {
glFrontFace(n_front_face);
} }
cull.front_face = n_front_face;
}
// Depth test void OpenGLState::SetDepthTestEnabled(bool n_test_enabled) {
if (depth.test_enabled != cur_state.depth.test_enabled) { if (n_test_enabled != cur_state->depth.test_enabled) {
if (depth.test_enabled) { if (n_test_enabled) {
glEnable(GL_DEPTH_TEST); glEnable(GL_DEPTH_TEST);
} else { } else {
glDisable(GL_DEPTH_TEST); glDisable(GL_DEPTH_TEST);
} }
} }
depth.test_enabled = n_test_enabled;
}
if (depth.test_func != cur_state.depth.test_func) { void OpenGLState::SetDepthFunc(GLenum n_test_func) {
glDepthFunc(depth.test_func); if (n_test_func != cur_state->depth.test_func) {
glDepthFunc(n_test_func);
} }
depth.test_func = n_test_func;
}
// Depth mask void OpenGLState::SetDepthWriteMask(GLboolean n_write_mask) {
if (depth.write_mask != cur_state.depth.write_mask) { if (n_write_mask != cur_state->depth.write_mask) {
glDepthMask(depth.write_mask); glDepthMask(n_write_mask);
} }
depth.write_mask = n_write_mask;
}
// Color mask void OpenGLState::SetColorMask(GLboolean n_red_enabled, GLboolean n_green_enabled, GLboolean n_blue_enabled, GLboolean n_alpha_enabled) {
if (color_mask.red_enabled != cur_state.color_mask.red_enabled || if (n_red_enabled != cur_state->color_mask.red_enabled ||
color_mask.green_enabled != cur_state.color_mask.green_enabled || n_green_enabled != cur_state->color_mask.green_enabled ||
color_mask.blue_enabled != cur_state.color_mask.blue_enabled || n_blue_enabled != cur_state->color_mask.blue_enabled ||
color_mask.alpha_enabled != cur_state.color_mask.alpha_enabled) { n_alpha_enabled != cur_state->color_mask.alpha_enabled) {
glColorMask(color_mask.red_enabled, color_mask.green_enabled, glColorMask(n_red_enabled, n_green_enabled,
color_mask.blue_enabled, color_mask.alpha_enabled); n_blue_enabled, n_alpha_enabled);
} }
color_mask.red_enabled = n_red_enabled;
color_mask.green_enabled = n_green_enabled;
color_mask.blue_enabled = n_blue_enabled;
color_mask.alpha_enabled = n_alpha_enabled;
}
// Stencil test void OpenGLState::SetStencilTestEnabled(bool n_test_enabled) {
if (stencil.test_enabled != cur_state.stencil.test_enabled) { if (n_test_enabled != cur_state->stencil.test_enabled) {
if (stencil.test_enabled) { if (n_test_enabled) {
glEnable(GL_STENCIL_TEST); glEnable(GL_STENCIL_TEST);
} else { } else {
glDisable(GL_STENCIL_TEST); glDisable(GL_STENCIL_TEST);
} }
} }
stencil.test_enabled = n_test_enabled;
}
if (stencil.test_func != cur_state.stencil.test_func || void OpenGLState::SetStencilFunc(GLenum n_test_func, GLint n_test_ref, GLuint n_test_mask) {
stencil.test_ref != cur_state.stencil.test_ref || if (n_test_func != cur_state->stencil.test_func ||
stencil.test_mask != cur_state.stencil.test_mask) { n_test_ref != cur_state->stencil.test_ref ||
glStencilFunc(stencil.test_func, stencil.test_ref, stencil.test_mask); n_test_mask != cur_state->stencil.test_mask) {
glStencilFunc(n_test_func, n_test_ref, n_test_mask);
} }
stencil.test_func = n_test_func;
stencil.test_ref = n_test_ref;
stencil.test_mask = n_test_mask;
}
if (stencil.action_depth_fail != cur_state.stencil.action_depth_fail || void OpenGLState::SetStencilOp(GLenum n_action_stencil_fail, GLenum n_action_depth_fail, GLenum n_action_depth_pass) {
stencil.action_depth_pass != cur_state.stencil.action_depth_pass || if (n_action_stencil_fail != cur_state->stencil.action_depth_fail ||
stencil.action_stencil_fail != cur_state.stencil.action_stencil_fail) { n_action_depth_fail != cur_state->stencil.action_depth_pass ||
glStencilOp(stencil.action_stencil_fail, stencil.action_depth_fail, stencil.action_depth_pass); n_action_depth_pass != cur_state->stencil.action_stencil_fail) {
glStencilOp(n_action_stencil_fail, n_action_depth_fail, n_action_depth_pass);
} }
stencil.action_depth_fail = n_action_stencil_fail;
stencil.action_depth_pass = n_action_depth_fail;
stencil.action_stencil_fail = n_action_depth_pass;
}
// Stencil mask void OpenGLState::SetStencilWriteMask(GLuint n_write_mask) {
if (stencil.write_mask != cur_state.stencil.write_mask) { if (n_write_mask != cur_state->stencil.write_mask) {
glStencilMask(stencil.write_mask); glStencilMask(n_write_mask);
} }
stencil.write_mask = n_write_mask;
}
// Blending void OpenGLState::SetBlendEnabled(bool n_enabled) {
if (blend.enabled != cur_state.blend.enabled) { if (n_enabled != cur_state->blend.enabled) {
if (blend.enabled) { if (n_enabled) {
glEnable(GL_BLEND); glEnable(GL_BLEND);
cur_state.logic_op = GL_COPY; SetLogicOp(GL_COPY);
glLogicOp(cur_state.logic_op);
glDisable(GL_COLOR_LOGIC_OP); glDisable(GL_COLOR_LOGIC_OP);
} else { } else {
glDisable(GL_BLEND); glDisable(GL_BLEND);
glEnable(GL_COLOR_LOGIC_OP); glEnable(GL_COLOR_LOGIC_OP);
} }
} }
blend.enabled = n_enabled;
}
if (blend.color.red != cur_state.blend.color.red || void OpenGLState::SetBlendFunc(GLenum n_src_rgb_func, GLenum n_dst_rgb_func, GLenum n_src_a_func, GLenum n_dst_a_func) {
blend.color.green != cur_state.blend.color.green || if (n_src_rgb_func != cur_state->blend.src_rgb_func ||
blend.color.blue != cur_state.blend.color.blue || n_dst_rgb_func != cur_state->blend.dst_rgb_func ||
blend.color.alpha != cur_state.blend.color.alpha) { n_src_a_func != cur_state->blend.src_a_func ||
glBlendColor(blend.color.red, blend.color.green, n_dst_a_func != cur_state->blend.dst_a_func) {
blend.color.blue, blend.color.alpha); glBlendFuncSeparate(n_src_rgb_func, n_dst_rgb_func,
n_src_a_func, n_dst_a_func);
}
blend.src_rgb_func = n_src_rgb_func;
blend.dst_rgb_func = n_dst_rgb_func;
blend.src_a_func = n_src_a_func;
blend.dst_a_func = n_dst_a_func;
}
void OpenGLState::SetBlendColor(GLclampf n_red, GLclampf n_green, GLclampf n_blue, GLclampf n_alpha) {
if (n_red != cur_state->blend.color.red ||
n_green != cur_state->blend.color.green ||
n_blue != cur_state->blend.color.blue ||
n_alpha != cur_state->blend.color.alpha) {
glBlendColor(n_red, n_green,
n_blue, n_alpha);
}
blend.color.red = n_red;
blend.color.green = n_green;
blend.color.blue = n_blue;
blend.color.alpha = n_alpha;
}
void OpenGLState::SetLogicOp(GLenum n_logic_op) {
if (n_logic_op != cur_state->logic_op) {
glLogicOp(n_logic_op);
}
logic_op = n_logic_op;
}
void OpenGLState::SetTexture2D(GLuint n_texture_2d) {
unsigned unit_index = active_texture_unit - GL_TEXTURE0;
ASSERT(unit_index < ARRAY_SIZE(texture_units));
if (n_texture_2d != cur_state->texture_units[unit_index].texture_2d) {
glBindTexture(GL_TEXTURE_2D, n_texture_2d);
}
texture_units[unit_index].texture_2d = n_texture_2d;
}
void OpenGLState::SetSampler(GLuint n_sampler) {
unsigned unit_index = active_texture_unit - GL_TEXTURE0;
ASSERT(unit_index < ARRAY_SIZE(texture_units));
if (n_sampler != cur_state->texture_units[unit_index].sampler) {
glBindSampler(unit_index, n_sampler);
}
texture_units[unit_index].sampler = n_sampler;
}
void OpenGLState::SetLUTTexture1D(GLuint n_texture_1d) {
unsigned unit_index = active_texture_unit - GL_TEXTURE3;
ASSERT(unit_index < ARRAY_SIZE(lighting_luts));
if (n_texture_1d != cur_state->lighting_luts[unit_index].texture_1d) {
glBindTexture(GL_TEXTURE_1D, n_texture_1d);
}
lighting_luts[unit_index].texture_1d = n_texture_1d;
}
void OpenGLState::SetActiveTextureUnit(GLenum n_active_texture_unit) {
if (n_active_texture_unit != cur_state->active_texture_unit) {
glActiveTexture(n_active_texture_unit);
}
active_texture_unit = n_active_texture_unit;
}
void OpenGLState::SetReadFramebuffer(GLuint n_read_framebuffer) {
if (n_read_framebuffer != cur_state->draw.read_framebuffer) {
glBindFramebuffer(GL_READ_FRAMEBUFFER, n_read_framebuffer);
}
draw.read_framebuffer = n_read_framebuffer;
}
void OpenGLState::SetDrawFramebuffer(GLuint n_draw_framebuffer) {
if (n_draw_framebuffer != cur_state->draw.draw_framebuffer) {
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, n_draw_framebuffer);
}
draw.draw_framebuffer = n_draw_framebuffer;
}
void OpenGLState::SetVertexArray(GLuint n_vertex_array) {
if (n_vertex_array != cur_state->draw.vertex_array) {
glBindVertexArray(n_vertex_array);
}
draw.vertex_array = n_vertex_array;
}
void OpenGLState::SetVertexBuffer(GLuint n_vertex_buffer) {
if (n_vertex_buffer != cur_state->draw.vertex_buffer) {
glBindBuffer(GL_ARRAY_BUFFER, n_vertex_buffer);
}
draw.vertex_buffer = n_vertex_buffer;
}
void OpenGLState::SetUniformBuffer(GLuint n_uniform_buffer) {
if (n_uniform_buffer != cur_state->draw.uniform_buffer) {
glBindBuffer(GL_UNIFORM_BUFFER, n_uniform_buffer);
}
draw.uniform_buffer = n_uniform_buffer;
}
void OpenGLState::SetShaderProgram(GLuint n_shader_program) {
if (n_shader_program != cur_state->draw.shader_program) {
glUseProgram(n_shader_program);
}
draw.shader_program = n_shader_program;
}
void OpenGLState::MakeCurrent() {
if (cur_state == this) {
return;
} }
if (blend.src_rgb_func != cur_state.blend.src_rgb_func || SetCullEnabled(cull.enabled);
blend.dst_rgb_func != cur_state.blend.dst_rgb_func || SetCullMode(cull.mode);
blend.src_a_func != cur_state.blend.src_a_func || SetCullFrontFace(cull.front_face);
blend.dst_a_func != cur_state.blend.dst_a_func) {
glBlendFuncSeparate(blend.src_rgb_func, blend.dst_rgb_func,
blend.src_a_func, blend.dst_a_func);
}
if (logic_op != cur_state.logic_op) { SetDepthTestEnabled(depth.test_enabled);
glLogicOp(logic_op); SetDepthFunc(depth.test_func);
} SetDepthWriteMask(depth.write_mask);
SetColorMask(color_mask.red_enabled, color_mask.green_enabled, color_mask.blue_enabled, color_mask.alpha_enabled);
SetStencilTestEnabled(stencil.test_enabled);
SetStencilFunc(stencil.test_func, stencil.test_ref, stencil.test_mask);
SetStencilOp(stencil.action_stencil_fail, stencil.action_depth_fail, stencil.action_depth_pass);
SetStencilWriteMask(stencil.write_mask);
SetBlendEnabled(blend.enabled);
SetBlendFunc(blend.src_rgb_func, blend.dst_rgb_func, blend.src_a_func, blend.dst_a_func);
SetBlendColor(blend.color.red, blend.color.green, blend.color.blue, blend.color.alpha);
SetLogicOp(logic_op);
// Textures
for (unsigned i = 0; i < ARRAY_SIZE(texture_units); ++i) { for (unsigned i = 0; i < ARRAY_SIZE(texture_units); ++i) {
if (texture_units[i].texture_2d != cur_state.texture_units[i].texture_2d) { SetActiveTextureUnit(GL_TEXTURE0 + i);
glActiveTexture(GL_TEXTURE0 + i); SetTexture2D(texture_units[i].texture_2d);
glBindTexture(GL_TEXTURE_2D, texture_units[i].texture_2d); SetSampler(texture_units[i].sampler);
}
if (texture_units[i].sampler != cur_state.texture_units[i].sampler) {
glBindSampler(i, texture_units[i].sampler);
}
} }
// Lighting LUTs
for (unsigned i = 0; i < ARRAY_SIZE(lighting_luts); ++i) { for (unsigned i = 0; i < ARRAY_SIZE(lighting_luts); ++i) {
if (lighting_luts[i].texture_1d != cur_state.lighting_luts[i].texture_1d) { SetActiveTextureUnit(GL_TEXTURE3 + i);
glActiveTexture(GL_TEXTURE3 + i); SetLUTTexture1D(lighting_luts[i].texture_1d);
glBindTexture(GL_TEXTURE_1D, lighting_luts[i].texture_1d);
}
} }
// Framebuffer SetActiveTextureUnit(active_texture_unit);
if (draw.read_framebuffer != cur_state.draw.read_framebuffer) {
glBindFramebuffer(GL_READ_FRAMEBUFFER, draw.read_framebuffer);
}
if (draw.draw_framebuffer != cur_state.draw.draw_framebuffer) {
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, draw.draw_framebuffer);
}
// Vertex array SetReadFramebuffer(draw.read_framebuffer);
if (draw.vertex_array != cur_state.draw.vertex_array) { SetDrawFramebuffer(draw.draw_framebuffer);
glBindVertexArray(draw.vertex_array); SetVertexArray(draw.vertex_array);
} SetVertexBuffer(draw.vertex_buffer);
SetUniformBuffer(draw.uniform_buffer);
SetShaderProgram(draw.shader_program);
// Vertex buffer cur_state = this;
if (draw.vertex_buffer != cur_state.draw.vertex_buffer) {
glBindBuffer(GL_ARRAY_BUFFER, draw.vertex_buffer);
}
// Uniform buffer
if (draw.uniform_buffer != cur_state.draw.uniform_buffer) {
glBindBuffer(GL_UNIFORM_BUFFER, draw.uniform_buffer);
}
// Shader program
if (draw.shader_program != cur_state.draw.shader_program) {
glUseProgram(draw.shader_program);
}
cur_state = *this;
} }
GLenum OpenGLState::CheckFBStatus(GLenum target) { GLenum OpenGLState::CheckFBStatus(GLenum target) {
@ -230,47 +364,55 @@ GLenum OpenGLState::CheckFBStatus(GLenum target) {
} }
void OpenGLState::ResetTexture(GLuint handle) { void OpenGLState::ResetTexture(GLuint handle) {
for (auto& unit : cur_state.texture_units) { for (unsigned i = 0; i < ARRAY_SIZE(texture_units); ++i) {
if (unit.texture_2d == handle) { if (cur_state->texture_units[i].texture_2d == handle) {
unit.texture_2d = 0; cur_state->SetActiveTextureUnit(GL_TEXTURE0 + i);
cur_state->SetTexture2D(0);
}
}
for (unsigned i = 0; i < ARRAY_SIZE(lighting_luts); ++i) {
if (cur_state->lighting_luts[i].texture_1d == handle) {
cur_state->SetActiveTextureUnit(GL_TEXTURE3 + i);
cur_state->SetLUTTexture1D(0);
} }
} }
} }
void OpenGLState::ResetSampler(GLuint handle) { void OpenGLState::ResetSampler(GLuint handle) {
for (auto& unit : cur_state.texture_units) { for (unsigned i = 0; i < ARRAY_SIZE(texture_units); ++i) {
if (unit.sampler == handle) { if (cur_state->texture_units[i].texture_2d == handle) {
unit.sampler = 0; cur_state->SetActiveTextureUnit(GL_TEXTURE0 + i);
cur_state->SetSampler(0);
} }
} }
} }
void OpenGLState::ResetProgram(GLuint handle) { void OpenGLState::ResetProgram(GLuint handle) {
if (cur_state.draw.shader_program == handle) { if (cur_state->draw.shader_program == handle) {
cur_state.draw.shader_program = 0; cur_state->SetShaderProgram(0);
} }
} }
void OpenGLState::ResetBuffer(GLuint handle) { void OpenGLState::ResetBuffer(GLuint handle) {
if (cur_state.draw.vertex_buffer == handle) { if (cur_state->draw.vertex_buffer == handle) {
cur_state.draw.vertex_buffer = 0; cur_state->SetVertexBuffer(0);
} }
if (cur_state.draw.uniform_buffer == handle) { if (cur_state->draw.uniform_buffer == handle) {
cur_state.draw.uniform_buffer = 0; cur_state->SetUniformBuffer(0);
} }
} }
void OpenGLState::ResetVertexArray(GLuint handle) { void OpenGLState::ResetVertexArray(GLuint handle) {
if (cur_state.draw.vertex_array == handle) { if (cur_state->draw.vertex_array == handle) {
cur_state.draw.vertex_array = 0; cur_state->SetVertexArray(0);
} }
} }
void OpenGLState::ResetFramebuffer(GLuint handle) { void OpenGLState::ResetFramebuffer(GLuint handle) {
if (cur_state.draw.read_framebuffer == handle) { if (cur_state->draw.read_framebuffer == handle) {
cur_state.draw.read_framebuffer = 0; cur_state->SetReadFramebuffer(0);
} }
if (cur_state.draw.draw_framebuffer == handle) { if (cur_state->draw.draw_framebuffer == handle) {
cur_state.draw.draw_framebuffer = 0; cur_state->SetDrawFramebuffer(0);
} }
} }

View file

@ -8,6 +8,60 @@
class OpenGLState { class OpenGLState {
public: public:
OpenGLState();
static OpenGLState* GetCurrentState();
/// Apply this state as the current OpenGL state
void MakeCurrent();
/// Check the status of the current OpenGL read or draw framebuffer configuration
static GLenum CheckFBStatus(GLenum target);
/// Resets and unbinds any references to the given resource in the current OpenGL state
static void ResetTexture(GLuint handle);
static void ResetSampler(GLuint handle);
static void ResetProgram(GLuint handle);
static void ResetBuffer(GLuint handle);
static void ResetVertexArray(GLuint handle);
static void ResetFramebuffer(GLuint handle);
void SetCullEnabled(bool n_enabled);
void SetCullMode(GLenum n_mode);
void SetCullFrontFace(GLenum n_front_face);
void SetDepthTestEnabled(bool n_test_enabled);
void SetDepthFunc(GLenum n_test_func);
void SetDepthWriteMask(GLboolean n_write_mask);
void SetColorMask(GLboolean n_red_enabled, GLboolean n_green_enabled, GLboolean n_blue_enabled, GLboolean n_alpha_enabled);
void SetStencilTestEnabled(bool n_test_enabled);
void SetStencilFunc(GLenum n_test_func, GLint n_test_ref, GLuint n_test_mask);
void SetStencilOp(GLenum n_action_stencil_fail, GLenum n_action_depth_fail, GLenum n_action_depth_pass);
void SetStencilWriteMask(GLuint n_write_mask);
void SetBlendEnabled(bool n_enabled);
void SetBlendFunc(GLenum n_src_rgb_func, GLenum n_dst_rgb_func, GLenum n_src_a_func, GLenum n_dst_a_func);
void SetBlendColor(GLclampf n_red, GLclampf n_green, GLclampf n_blue, GLclampf n_alpha);
void SetLogicOp(GLenum n_logic_op);
void SetTexture2D(GLuint n_texture_2d);
void SetSampler(GLuint n_sampler);
void SetLUTTexture1D(GLuint n_texture_1d);
void SetActiveTextureUnit(GLenum n_active_texture_unit);
void SetReadFramebuffer(GLuint n_read_framebuffer);
void SetDrawFramebuffer(GLuint n_draw_framebuffer);
void SetVertexArray(GLuint n_vertex_array);
void SetVertexBuffer(GLuint n_vertex_buffer);
void SetUniformBuffer(GLuint n_uniform_buffer);
void SetShaderProgram(GLuint n_shader_program);
private:
struct { struct {
bool enabled; // GL_CULL_FACE bool enabled; // GL_CULL_FACE
GLenum mode; // GL_CULL_FACE_MODE GLenum mode; // GL_CULL_FACE_MODE
@ -32,10 +86,10 @@ public:
GLenum test_func; // GL_STENCIL_FUNC GLenum test_func; // GL_STENCIL_FUNC
GLint test_ref; // GL_STENCIL_REF GLint test_ref; // GL_STENCIL_REF
GLuint test_mask; // GL_STENCIL_VALUE_MASK GLuint test_mask; // GL_STENCIL_VALUE_MASK
GLuint write_mask; // GL_STENCIL_WRITEMASK
GLenum action_stencil_fail; // GL_STENCIL_FAIL GLenum action_stencil_fail; // GL_STENCIL_FAIL
GLenum action_depth_fail; // GL_STENCIL_PASS_DEPTH_FAIL GLenum action_depth_fail; // GL_STENCIL_PASS_DEPTH_FAIL
GLenum action_depth_pass; // GL_STENCIL_PASS_DEPTH_PASS GLenum action_depth_pass; // GL_STENCIL_PASS_DEPTH_PASS
GLuint write_mask; // GL_STENCIL_WRITEMASK
} stencil; } stencil;
struct { struct {
@ -65,6 +119,8 @@ public:
GLuint texture_1d; // GL_TEXTURE_BINDING_1D GLuint texture_1d; // GL_TEXTURE_BINDING_1D
} lighting_luts[6]; } lighting_luts[6];
GLenum active_texture_unit; // GL_ACTIVE_TEXTURE
struct { struct {
GLuint read_framebuffer; // GL_READ_FRAMEBUFFER_BINDING GLuint read_framebuffer; // GL_READ_FRAMEBUFFER_BINDING
GLuint draw_framebuffer; // GL_DRAW_FRAMEBUFFER_BINDING GLuint draw_framebuffer; // GL_DRAW_FRAMEBUFFER_BINDING
@ -74,27 +130,5 @@ public:
GLuint shader_program; // GL_CURRENT_PROGRAM GLuint shader_program; // GL_CURRENT_PROGRAM
} draw; } draw;
OpenGLState(); static OpenGLState* cur_state;
/// Get the currently active OpenGL state
static const OpenGLState& GetCurState() {
return cur_state;
}
/// Apply this state as the current OpenGL state
void Apply() const;
/// Check the status of the current OpenGL read or draw framebuffer configuration
static GLenum CheckFBStatus(GLenum target);
/// Resets and unbinds any references to the given resource in the current OpenGL state
static void ResetTexture(GLuint handle);
static void ResetSampler(GLuint handle);
static void ResetProgram(GLuint handle);
static void ResetBuffer(GLuint handle);
static void ResetVertexArray(GLuint handle);
static void ResetFramebuffer(GLuint handle);
private:
static OpenGLState cur_state;
}; };

View file

@ -108,9 +108,7 @@ RendererOpenGL::~RendererOpenGL() {
/// Swap buffers (render frame) /// Swap buffers (render frame)
void RendererOpenGL::SwapBuffers() { void RendererOpenGL::SwapBuffers() {
// Maintain the rasterizer's state as a priority state.MakeCurrent();
OpenGLState prev_state = OpenGLState::GetCurState();
state.Apply();
for (int i : {0, 1}) { for (int i : {0, 1}) {
const auto& framebuffer = GPU::g_regs.framebuffer_config[i]; const auto& framebuffer = GPU::g_regs.framebuffer_config[i];
@ -157,8 +155,6 @@ void RendererOpenGL::SwapBuffers() {
render_window->PollEvents(); render_window->PollEvents();
render_window->SwapBuffers(); render_window->SwapBuffers();
prev_state.Apply();
profiler.BeginFrame(); profiler.BeginFrame();
RefreshRasterizerSetting(); RefreshRasterizerSetting();
@ -201,10 +197,9 @@ void RendererOpenGL::LoadFBToScreenInfo(const GPU::Regs::FramebufferConfig& fram
const u8* framebuffer_data = Memory::GetPhysicalPointer(framebuffer_addr); const u8* framebuffer_data = Memory::GetPhysicalPointer(framebuffer_addr);
state.texture_units[0].texture_2d = screen_info.texture.resource.handle; state.SetActiveTextureUnit(GL_TEXTURE0);
state.Apply(); state.SetTexture2D(screen_info.texture.resource.handle);
glActiveTexture(GL_TEXTURE0);
glPixelStorei(GL_UNPACK_ROW_LENGTH, (GLint)pixel_stride); glPixelStorei(GL_UNPACK_ROW_LENGTH, (GLint)pixel_stride);
// Update existing texture // Update existing texture
@ -217,8 +212,7 @@ void RendererOpenGL::LoadFBToScreenInfo(const GPU::Regs::FramebufferConfig& fram
glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
state.texture_units[0].texture_2d = 0; state.SetTexture2D(0);
state.Apply();
} }
} }
@ -229,29 +223,28 @@ void RendererOpenGL::LoadFBToScreenInfo(const GPU::Regs::FramebufferConfig& fram
*/ */
void RendererOpenGL::LoadColorToActiveGLTexture(u8 color_r, u8 color_g, u8 color_b, void RendererOpenGL::LoadColorToActiveGLTexture(u8 color_r, u8 color_g, u8 color_b,
const TextureInfo& texture) { const TextureInfo& texture) {
state.texture_units[0].texture_2d = texture.resource.handle; state.SetActiveTextureUnit(GL_TEXTURE0);
state.Apply(); state.SetTexture2D(texture.resource.handle);
glActiveTexture(GL_TEXTURE0);
u8 framebuffer_data[3] = { color_r, color_g, color_b }; u8 framebuffer_data[3] = { color_r, color_g, color_b };
// Update existing texture // Update existing texture
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 1, 1, 0, GL_RGB, GL_UNSIGNED_BYTE, framebuffer_data); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 1, 1, 0, GL_RGB, GL_UNSIGNED_BYTE, framebuffer_data);
state.texture_units[0].texture_2d = 0; state.SetTexture2D(0);
state.Apply();
} }
/** /**
* Initializes the OpenGL state and creates persistent objects. * Initializes the OpenGL state and creates persistent objects.
*/ */
void RendererOpenGL::InitOpenGLObjects() { void RendererOpenGL::InitOpenGLObjects() {
state.MakeCurrent();
glClearColor(Settings::values.bg_red, Settings::values.bg_green, Settings::values.bg_blue, 0.0f); glClearColor(Settings::values.bg_red, Settings::values.bg_green, Settings::values.bg_blue, 0.0f);
// Link shaders and get variable locations // Link shaders and get variable locations
shader.Create(vertex_shader, fragment_shader); shader.Create(vertex_shader, fragment_shader);
state.draw.shader_program = shader.handle; state.SetShaderProgram(shader.handle);
state.Apply();
uniform_modelview_matrix = glGetUniformLocation(shader.handle, "modelview_matrix"); uniform_modelview_matrix = glGetUniformLocation(shader.handle, "modelview_matrix");
uniform_color_texture = glGetUniformLocation(shader.handle, "color_texture"); uniform_color_texture = glGetUniformLocation(shader.handle, "color_texture");
attrib_position = glGetAttribLocation(shader.handle, "vert_position"); attrib_position = glGetAttribLocation(shader.handle, "vert_position");
@ -263,10 +256,9 @@ void RendererOpenGL::InitOpenGLObjects() {
// Generate VAO // Generate VAO
vertex_array.Create(); vertex_array.Create();
state.draw.vertex_array = vertex_array.handle; state.SetVertexArray(vertex_array.handle);
state.draw.vertex_buffer = vertex_buffer.handle; state.SetVertexBuffer(vertex_buffer.handle);
state.draw.uniform_buffer = 0; state.SetUniformBuffer(0);
state.Apply();
// Attach vertex data to VAO // Attach vertex data to VAO
glBufferData(GL_ARRAY_BUFFER, sizeof(ScreenRectVertex) * 4, nullptr, GL_STREAM_DRAW); glBufferData(GL_ARRAY_BUFFER, sizeof(ScreenRectVertex) * 4, nullptr, GL_STREAM_DRAW);
@ -282,21 +274,19 @@ void RendererOpenGL::InitOpenGLObjects() {
// Allocation of storage is deferred until the first frame, when we // Allocation of storage is deferred until the first frame, when we
// know the framebuffer size. // know the framebuffer size.
state.texture_units[0].texture_2d = screen_info.texture.resource.handle; state.SetActiveTextureUnit(GL_TEXTURE0);
state.Apply(); state.SetTexture2D(screen_info.texture.resource.handle);
glActiveTexture(GL_TEXTURE0);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
state.SetTexture2D(0);
screen_info.display_texture = screen_info.texture.resource.handle; screen_info.display_texture = screen_info.texture.resource.handle;
} }
state.texture_units[0].texture_2d = 0;
state.Apply();
} }
void RendererOpenGL::ConfigureFramebufferTexture(TextureInfo& texture, void RendererOpenGL::ConfigureFramebufferTexture(TextureInfo& texture,
@ -347,15 +337,13 @@ void RendererOpenGL::ConfigureFramebufferTexture(TextureInfo& texture,
UNIMPLEMENTED(); UNIMPLEMENTED();
} }
state.texture_units[0].texture_2d = texture.resource.handle; state.SetActiveTextureUnit(GL_TEXTURE0);
state.Apply(); state.SetTexture2D(texture.resource.handle);
glActiveTexture(GL_TEXTURE0);
glTexImage2D(GL_TEXTURE_2D, 0, internal_format, texture.width, texture.height, 0, glTexImage2D(GL_TEXTURE_2D, 0, internal_format, texture.width, texture.height, 0,
texture.gl_format, texture.gl_type, nullptr); texture.gl_format, texture.gl_type, nullptr);
state.texture_units[0].texture_2d = 0; state.SetTexture2D(0);
state.Apply();
} }
/** /**
@ -371,14 +359,13 @@ void RendererOpenGL::DrawSingleScreenRotated(const ScreenInfo& screen_info, floa
ScreenRectVertex(x+w, y+h, texcoords.top, texcoords.right), ScreenRectVertex(x+w, y+h, texcoords.top, texcoords.right),
}}; }};
state.texture_units[0].texture_2d = screen_info.display_texture; state.SetActiveTextureUnit(GL_TEXTURE0);
state.Apply(); state.SetTexture2D(screen_info.display_texture);
glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(vertices), vertices.data()); glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(vertices), vertices.data());
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
state.texture_units[0].texture_2d = 0; state.SetTexture2D(0);
state.Apply();
} }
/** /**
@ -396,7 +383,7 @@ void RendererOpenGL::DrawScreens() {
glUniformMatrix3x2fv(uniform_modelview_matrix, 1, GL_FALSE, ortho_matrix.data()); glUniformMatrix3x2fv(uniform_modelview_matrix, 1, GL_FALSE, ortho_matrix.data());
// Bind texture in Texture Unit 0 // Bind texture in Texture Unit 0
glActiveTexture(GL_TEXTURE0); state.SetActiveTextureUnit(GL_TEXTURE0);
glUniform1i(uniform_color_texture, 0); glUniform1i(uniform_color_texture, 0);
DrawSingleScreenRotated(screen_infos[0], (float)layout.top_screen.left, (float)layout.top_screen.top, DrawSingleScreenRotated(screen_infos[0], (float)layout.top_screen.left, (float)layout.top_screen.top,