summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorbunnei <bunneidev@gmail.com>2018-08-23 18:04:08 +0200
committerGitHub <noreply@github.com>2018-08-23 18:04:08 +0200
commit3ed0115e36a8b563aa40b32cd645165e12b1ba81 (patch)
tree37c8529b9f2037dfb2072a248e4d257edc6144d7
parentMerge pull request #1157 from lioncash/vec (diff)
parentgl_rasterizer: Implement stencil test. (diff)
downloadyuzu-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
-rw-r--r--src/video_core/engines/maxwell_3d.h70
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer.cpp89
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer.h3
-rw-r--r--src/video_core/renderer_opengl/gl_state.cpp53
-rw-r--r--src/video_core/renderer_opengl/gl_state.h18
-rw-r--r--src/video_core/renderer_opengl/maxwell_to_gl.h24
6 files changed, 188 insertions, 69 deletions
diff --git a/src/video_core/engines/maxwell_3d.h b/src/video_core/engines/maxwell_3d.h
index d03bc1c0c..92bfda053 100644
--- a/src/video_core/engines/maxwell_3d.h
+++ b/src/video_core/engines/maxwell_3d.h
@@ -330,6 +330,17 @@ public:
Set = 0x150F,
};
+ enum class StencilOp : u32 {
+ Keep = 1,
+ Zero = 2,
+ Replace = 3,
+ Incr = 4,
+ Decr = 5,
+ Invert = 6,
+ IncrWrap = 7,
+ DecrWrap = 8,
+ };
+
struct Cull {
enum class FrontFace : u32 {
ClockWise = 0x0900,
@@ -508,8 +519,16 @@ public:
float clear_color[4];
float clear_depth;
+ INSERT_PADDING_WORDS(0x3);
+ s32 clear_stencil;
+
+ INSERT_PADDING_WORDS(0x6C);
+
+ s32 stencil_back_func_ref;
+ u32 stencil_back_mask;
+ u32 stencil_back_func_mask;
- INSERT_PADDING_WORDS(0x93);
+ INSERT_PADDING_WORDS(0x20);
struct {
u32 address_high;
@@ -573,16 +592,14 @@ public:
u32 enable[NumRenderTargets];
} blend;
- struct {
- u32 enable;
- u32 front_op_fail;
- u32 front_op_zfail;
- u32 front_op_zpass;
- u32 front_func_func;
- u32 front_func_ref;
- u32 front_func_mask;
- u32 front_mask;
- } stencil;
+ u32 stencil_enable;
+ StencilOp stencil_front_op_fail;
+ StencilOp stencil_front_op_zfail;
+ StencilOp stencil_front_op_zpass;
+ ComparisonOp stencil_front_func_func;
+ s32 stencil_front_func_ref;
+ u32 stencil_front_func_mask;
+ u32 stencil_front_mask;
INSERT_PADDING_WORDS(0x3);
@@ -626,13 +643,11 @@ public:
INSERT_PADDING_WORDS(0x5);
- struct {
- u32 enable;
- u32 back_op_fail;
- u32 back_op_zfail;
- u32 back_op_zpass;
- u32 back_func_func;
- } stencil_two_side;
+ u32 stencil_two_side_enable;
+ StencilOp stencil_back_op_fail;
+ StencilOp stencil_back_op_zfail;
+ StencilOp stencil_back_op_zpass;
+ ComparisonOp stencil_back_func_func;
INSERT_PADDING_WORDS(0x17);
@@ -944,6 +959,10 @@ ASSERT_REG_POSITION(viewport, 0x300);
ASSERT_REG_POSITION(vertex_buffer, 0x35D);
ASSERT_REG_POSITION(clear_color[0], 0x360);
ASSERT_REG_POSITION(clear_depth, 0x364);
+ASSERT_REG_POSITION(clear_stencil, 0x368);
+ASSERT_REG_POSITION(stencil_back_func_ref, 0x3D5);
+ASSERT_REG_POSITION(stencil_back_mask, 0x3D6);
+ASSERT_REG_POSITION(stencil_back_func_mask, 0x3D7);
ASSERT_REG_POSITION(zeta, 0x3F8);
ASSERT_REG_POSITION(vertex_attrib_format[0], 0x458);
ASSERT_REG_POSITION(rt_control, 0x487);
@@ -955,13 +974,24 @@ ASSERT_REG_POSITION(depth_write_enabled, 0x4BA);
ASSERT_REG_POSITION(d3d_cull_mode, 0x4C2);
ASSERT_REG_POSITION(depth_test_func, 0x4C3);
ASSERT_REG_POSITION(blend, 0x4CF);
-ASSERT_REG_POSITION(stencil, 0x4E0);
+ASSERT_REG_POSITION(stencil_enable, 0x4E0);
+ASSERT_REG_POSITION(stencil_front_op_fail, 0x4E1);
+ASSERT_REG_POSITION(stencil_front_op_zfail, 0x4E2);
+ASSERT_REG_POSITION(stencil_front_op_zpass, 0x4E3);
+ASSERT_REG_POSITION(stencil_front_func_func, 0x4E4);
+ASSERT_REG_POSITION(stencil_front_func_ref, 0x4E5);
+ASSERT_REG_POSITION(stencil_front_func_mask, 0x4E6);
+ASSERT_REG_POSITION(stencil_front_mask, 0x4E7);
ASSERT_REG_POSITION(screen_y_control, 0x4EB);
ASSERT_REG_POSITION(vb_element_base, 0x50D);
ASSERT_REG_POSITION(zeta_enable, 0x54E);
ASSERT_REG_POSITION(tsc, 0x557);
ASSERT_REG_POSITION(tic, 0x55D);
-ASSERT_REG_POSITION(stencil_two_side, 0x565);
+ASSERT_REG_POSITION(stencil_two_side_enable, 0x565);
+ASSERT_REG_POSITION(stencil_back_op_fail, 0x566);
+ASSERT_REG_POSITION(stencil_back_op_zfail, 0x567);
+ASSERT_REG_POSITION(stencil_back_op_zpass, 0x568);
+ASSERT_REG_POSITION(stencil_back_func_func, 0x569);
ASSERT_REG_POSITION(point_coord_replace, 0x581);
ASSERT_REG_POSITION(code_address, 0x582);
ASSERT_REG_POSITION(draw, 0x585);
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: