diff options
author | bunnei <bunneidev@gmail.com> | 2018-08-23 18:04:08 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2018-08-23 18:04:08 +0200 |
commit | 3ed0115e36a8b563aa40b32cd645165e12b1ba81 (patch) | |
tree | 37c8529b9f2037dfb2072a248e4d257edc6144d7 /src/video_core/renderer_opengl | |
parent | Merge pull request #1157 from lioncash/vec (diff) | |
parent | gl_rasterizer: Implement stencil test. (diff) | |
download | yuzu-3ed0115e36a8b563aa40b32cd645165e12b1ba81.tar yuzu-3ed0115e36a8b563aa40b32cd645165e12b1ba81.tar.gz yuzu-3ed0115e36a8b563aa40b32cd645165e12b1ba81.tar.bz2 yuzu-3ed0115e36a8b563aa40b32cd645165e12b1ba81.tar.lz yuzu-3ed0115e36a8b563aa40b32cd645165e12b1ba81.tar.xz yuzu-3ed0115e36a8b563aa40b32cd645165e12b1ba81.tar.zst yuzu-3ed0115e36a8b563aa40b32cd645165e12b1ba81.zip |
Diffstat (limited to 'src/video_core/renderer_opengl')
-rw-r--r-- | src/video_core/renderer_opengl/gl_rasterizer.cpp | 89 | ||||
-rw-r--r-- | src/video_core/renderer_opengl/gl_rasterizer.h | 3 | ||||
-rw-r--r-- | src/video_core/renderer_opengl/gl_state.cpp | 53 | ||||
-rw-r--r-- | src/video_core/renderer_opengl/gl_state.h | 18 | ||||
-rw-r--r-- | src/video_core/renderer_opengl/maxwell_to_gl.h | 24 |
5 files changed, 138 insertions, 49 deletions
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp index 516e1b50f..8bfa75b84 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.cpp +++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp @@ -14,6 +14,7 @@ #include "common/logging/log.h" #include "common/math_util.h" #include "common/microprofile.h" +#include "common/scope_exit.h" #include "core/core.h" #include "core/frontend/emu_window.h" #include "core/hle/kernel/process.h" @@ -315,16 +316,14 @@ std::pair<Surface, Surface> RasterizerOpenGL::ConfigureFramebuffers(bool using_c using_color_fb = false; } - // TODO(bunnei): Implement this - const bool has_stencil = false; - + const bool has_stencil = regs.stencil_enable; const bool write_color_fb = state.color_mask.red_enabled == GL_TRUE || state.color_mask.green_enabled == GL_TRUE || state.color_mask.blue_enabled == GL_TRUE || state.color_mask.alpha_enabled == GL_TRUE; const bool write_depth_fb = (state.depth.test_enabled && state.depth.write_mask == GL_TRUE) || - (has_stencil && state.stencil.test_enabled && state.stencil.write_mask != 0); + (has_stencil && (state.stencil.front.write_mask || state.stencil.back.write_mask)); Surface color_surface; Surface depth_surface; @@ -364,41 +363,70 @@ std::pair<Surface, Surface> RasterizerOpenGL::ConfigureFramebuffers(bool using_c } void RasterizerOpenGL::Clear() { - const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs; + const auto prev_state{state}; + SCOPE_EXIT({ prev_state.Apply(); }); + const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs; bool use_color_fb = false; bool use_depth_fb = false; - GLbitfield clear_mask = 0; - if (regs.clear_buffers.R && regs.clear_buffers.G && regs.clear_buffers.B && + OpenGLState clear_state; + clear_state.draw.draw_framebuffer = state.draw.draw_framebuffer; + clear_state.color_mask.red_enabled = regs.clear_buffers.R ? GL_TRUE : GL_FALSE; + clear_state.color_mask.green_enabled = regs.clear_buffers.G ? GL_TRUE : GL_FALSE; + clear_state.color_mask.blue_enabled = regs.clear_buffers.B ? GL_TRUE : GL_FALSE; + clear_state.color_mask.alpha_enabled = regs.clear_buffers.A ? GL_TRUE : GL_FALSE; + + GLbitfield clear_mask{}; + if (regs.clear_buffers.R || regs.clear_buffers.G || regs.clear_buffers.B || regs.clear_buffers.A) { - clear_mask |= GL_COLOR_BUFFER_BIT; - use_color_fb = true; + if (regs.clear_buffers.RT == 0) { + // We only support clearing the first color attachment for now + clear_mask |= GL_COLOR_BUFFER_BIT; + use_color_fb = true; + } else { + // TODO(subv): Add support for the other color attachments + LOG_CRITICAL(HW_GPU, "Clear unimplemented for RT {}", regs.clear_buffers.RT); + } } if (regs.clear_buffers.Z) { + ASSERT_MSG(regs.zeta_enable != 0, "Tried to clear Z but buffer is not enabled!"); + use_depth_fb = true; clear_mask |= GL_DEPTH_BUFFER_BIT; - use_depth_fb = regs.zeta_enable != 0; // Always enable the depth write when clearing the depth buffer. The depth write mask is // ignored when clearing the buffer in the Switch, but OpenGL obeys it so we set it to true. - state.depth.test_enabled = true; - state.depth.write_mask = GL_TRUE; - state.depth.test_func = GL_ALWAYS; - state.Apply(); + clear_state.depth.test_enabled = true; + clear_state.depth.test_func = GL_ALWAYS; + } + if (regs.clear_buffers.S) { + ASSERT_MSG(regs.zeta_enable != 0, "Tried to clear stencil but buffer is not enabled!"); + use_depth_fb = true; + clear_mask |= GL_STENCIL_BUFFER_BIT; + clear_state.stencil.test_enabled = true; } - if (clear_mask == 0) + if (!use_color_fb && !use_depth_fb) { + // No color surface nor depth/stencil surface are enabled return; + } + + if (clear_mask == 0) { + // No clear mask is enabled + return; + } ScopeAcquireGLContext acquire_context{emu_window}; auto [dirty_color_surface, dirty_depth_surface] = ConfigureFramebuffers(use_color_fb, use_depth_fb, false); - // TODO(Subv): Support clearing only partial colors. + clear_state.Apply(); + glClearColor(regs.clear_color[0], regs.clear_color[1], regs.clear_color[2], regs.clear_color[3]); glClearDepth(regs.clear_depth); + glClearStencil(regs.clear_stencil); glClear(clear_mask); @@ -451,6 +479,7 @@ void RasterizerOpenGL::DrawArrays() { ConfigureFramebuffers(true, regs.zeta.Address() != 0 && regs.zeta_enable != 0, true); SyncDepthTestState(); + SyncStencilTestState(); SyncBlendState(); SyncLogicOpState(); SyncCullMode(); @@ -841,6 +870,34 @@ void RasterizerOpenGL::SyncDepthTestState() { state.depth.test_func = MaxwellToGL::ComparisonOp(regs.depth_test_func); } +void RasterizerOpenGL::SyncStencilTestState() { + const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs; + state.stencil.test_enabled = regs.stencil_enable != 0; + + if (!regs.stencil_enable) { + return; + } + + // TODO(bunnei): Verify behavior when this is not set + ASSERT(regs.stencil_two_side_enable); + + state.stencil.front.test_func = MaxwellToGL::ComparisonOp(regs.stencil_front_func_func); + state.stencil.front.test_ref = regs.stencil_front_func_ref; + state.stencil.front.test_mask = regs.stencil_front_func_mask; + state.stencil.front.action_stencil_fail = MaxwellToGL::StencilOp(regs.stencil_front_op_fail); + state.stencil.front.action_depth_fail = MaxwellToGL::StencilOp(regs.stencil_front_op_zfail); + state.stencil.front.action_depth_pass = MaxwellToGL::StencilOp(regs.stencil_front_op_zpass); + state.stencil.front.write_mask = regs.stencil_front_mask; + + state.stencil.back.test_func = MaxwellToGL::ComparisonOp(regs.stencil_back_func_func); + state.stencil.back.test_ref = regs.stencil_back_func_ref; + state.stencil.back.test_mask = regs.stencil_back_func_mask; + state.stencil.back.action_stencil_fail = MaxwellToGL::StencilOp(regs.stencil_back_op_fail); + state.stencil.back.action_depth_fail = MaxwellToGL::StencilOp(regs.stencil_back_op_zfail); + state.stencil.back.action_depth_pass = MaxwellToGL::StencilOp(regs.stencil_back_op_zpass); + state.stencil.back.write_mask = regs.stencil_back_mask; +} + void RasterizerOpenGL::SyncBlendState() { const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs; diff --git a/src/video_core/renderer_opengl/gl_rasterizer.h b/src/video_core/renderer_opengl/gl_rasterizer.h index 59b727de0..531b04046 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.h +++ b/src/video_core/renderer_opengl/gl_rasterizer.h @@ -141,6 +141,9 @@ private: /// Syncs the depth test state to match the guest state void SyncDepthTestState(); + /// Syncs the stencil test state to match the guest state + void SyncStencilTestState(); + /// Syncs the blend state to match the guest state void SyncBlendState(); diff --git a/src/video_core/renderer_opengl/gl_state.cpp b/src/video_core/renderer_opengl/gl_state.cpp index e1a887d67..60a4defd1 100644 --- a/src/video_core/renderer_opengl/gl_state.cpp +++ b/src/video_core/renderer_opengl/gl_state.cpp @@ -27,13 +27,17 @@ OpenGLState::OpenGLState() { color_mask.alpha_enabled = GL_TRUE; stencil.test_enabled = false; - stencil.test_func = GL_ALWAYS; - stencil.test_ref = 0; - stencil.test_mask = 0xFF; - stencil.write_mask = 0xFF; - stencil.action_depth_fail = GL_KEEP; - stencil.action_depth_pass = GL_KEEP; - stencil.action_stencil_fail = GL_KEEP; + auto reset_stencil = [](auto& config) { + config.test_func = GL_ALWAYS; + config.test_ref = 0; + config.test_mask = 0xFFFFFFFF; + config.write_mask = 0xFFFFFFFF; + config.action_depth_fail = GL_KEEP; + config.action_depth_pass = GL_KEEP; + config.action_stencil_fail = GL_KEEP; + }; + reset_stencil(stencil.front); + reset_stencil(stencil.back); blend.enabled = true; blend.rgb_equation = GL_FUNC_ADD; @@ -129,24 +133,23 @@ void OpenGLState::Apply() const { glDisable(GL_STENCIL_TEST); } } - - if (stencil.test_func != cur_state.stencil.test_func || - stencil.test_ref != cur_state.stencil.test_ref || - stencil.test_mask != cur_state.stencil.test_mask) { - glStencilFunc(stencil.test_func, stencil.test_ref, stencil.test_mask); - } - - if (stencil.action_depth_fail != cur_state.stencil.action_depth_fail || - stencil.action_depth_pass != cur_state.stencil.action_depth_pass || - stencil.action_stencil_fail != cur_state.stencil.action_stencil_fail) { - glStencilOp(stencil.action_stencil_fail, stencil.action_depth_fail, - stencil.action_depth_pass); - } - - // Stencil mask - if (stencil.write_mask != cur_state.stencil.write_mask) { - glStencilMask(stencil.write_mask); - } + auto config_stencil = [](GLenum face, const auto& config, const auto& prev_config) { + if (config.test_func != prev_config.test_func || config.test_ref != prev_config.test_ref || + config.test_mask != prev_config.test_mask) { + glStencilFuncSeparate(face, config.test_func, config.test_ref, config.test_mask); + } + if (config.action_depth_fail != prev_config.action_depth_fail || + config.action_depth_pass != prev_config.action_depth_pass || + config.action_stencil_fail != prev_config.action_stencil_fail) { + glStencilOpSeparate(face, config.action_stencil_fail, config.action_depth_fail, + config.action_depth_pass); + } + if (config.write_mask != prev_config.write_mask) { + glStencilMaskSeparate(face, config.write_mask); + } + }; + config_stencil(GL_FRONT, stencil.front, cur_state.stencil.front); + config_stencil(GL_BACK, stencil.back, cur_state.stencil.back); // Blending if (blend.enabled != cur_state.blend.enabled) { diff --git a/src/video_core/renderer_opengl/gl_state.h b/src/video_core/renderer_opengl/gl_state.h index 22b0b1e41..46e96a97d 100644 --- a/src/video_core/renderer_opengl/gl_state.h +++ b/src/video_core/renderer_opengl/gl_state.h @@ -58,14 +58,16 @@ public: } color_mask; // GL_COLOR_WRITEMASK struct { - bool test_enabled; // GL_STENCIL_TEST - GLenum test_func; // GL_STENCIL_FUNC - GLint test_ref; // GL_STENCIL_REF - GLuint test_mask; // GL_STENCIL_VALUE_MASK - GLuint write_mask; // GL_STENCIL_WRITEMASK - GLenum action_stencil_fail; // GL_STENCIL_FAIL - GLenum action_depth_fail; // GL_STENCIL_PASS_DEPTH_FAIL - GLenum action_depth_pass; // GL_STENCIL_PASS_DEPTH_PASS + bool test_enabled; // GL_STENCIL_TEST + struct { + GLenum test_func; // GL_STENCIL_FUNC + GLint test_ref; // GL_STENCIL_REF + GLuint test_mask; // GL_STENCIL_VALUE_MASK + GLuint write_mask; // GL_STENCIL_WRITEMASK + GLenum action_stencil_fail; // GL_STENCIL_FAIL + GLenum action_depth_fail; // GL_STENCIL_PASS_DEPTH_FAIL + GLenum action_depth_pass; // GL_STENCIL_PASS_DEPTH_PASS + } front, back; } stencil; struct { diff --git a/src/video_core/renderer_opengl/maxwell_to_gl.h b/src/video_core/renderer_opengl/maxwell_to_gl.h index 0343759a6..67273e164 100644 --- a/src/video_core/renderer_opengl/maxwell_to_gl.h +++ b/src/video_core/renderer_opengl/maxwell_to_gl.h @@ -295,6 +295,30 @@ inline GLenum ComparisonOp(Maxwell::ComparisonOp comparison) { return {}; } +inline GLenum StencilOp(Maxwell::StencilOp stencil) { + switch (stencil) { + case Maxwell::StencilOp::Keep: + return GL_KEEP; + case Maxwell::StencilOp::Zero: + return GL_ZERO; + case Maxwell::StencilOp::Replace: + return GL_REPLACE; + case Maxwell::StencilOp::Incr: + return GL_INCR; + case Maxwell::StencilOp::Decr: + return GL_DECR; + case Maxwell::StencilOp::Invert: + return GL_INVERT; + case Maxwell::StencilOp::IncrWrap: + return GL_INCR_WRAP; + case Maxwell::StencilOp::DecrWrap: + return GL_DECR_WRAP; + } + LOG_CRITICAL(Render_OpenGL, "Unimplemented stencil op={}", static_cast<u32>(stencil)); + UNREACHABLE(); + return {}; +} + inline GLenum FrontFace(Maxwell::Cull::FrontFace front_face) { switch (front_face) { case Maxwell::Cull::FrontFace::ClockWise: |