diff options
| author | 2015-08-29 20:06:25 -0400 | |
|---|---|---|
| committer | 2015-08-29 20:06:25 -0400 | |
| commit | 58e9f78844168e2770bf0b43d6931569642f27fb (patch) | |
| tree | f0a3705f7e09bf6a067467553eac2119a691984b | |
| parent | Merge pull request #1080 from yuriks/linear-heap-base-typo (diff) | |
| parent | HWRenderer: Added a workaround for the Intel Windows driver bug that causes g... (diff) | |
| download | yuzu-58e9f78844168e2770bf0b43d6931569642f27fb.tar.gz yuzu-58e9f78844168e2770bf0b43d6931569642f27fb.tar.xz yuzu-58e9f78844168e2770bf0b43d6931569642f27fb.zip | |
Merge pull request #1049 from Subv/stencil
Rasterizer: Corrected the stencil implementation.
| -rw-r--r-- | src/video_core/pica.h | 22 | ||||
| -rw-r--r-- | src/video_core/rasterizer.cpp | 58 | ||||
| -rw-r--r-- | src/video_core/renderer_opengl/gl_rasterizer.cpp | 24 | ||||
| -rw-r--r-- | src/video_core/renderer_opengl/gl_state.cpp | 9 | ||||
| -rw-r--r-- | src/video_core/renderer_opengl/gl_state.h | 3 | ||||
| -rw-r--r-- | src/video_core/renderer_opengl/pica_to_gl.h | 23 |
6 files changed, 111 insertions, 28 deletions
diff --git a/src/video_core/pica.h b/src/video_core/pica.h index bb689f2a9..f40684d83 100644 --- a/src/video_core/pica.h +++ b/src/video_core/pica.h | |||
| @@ -441,8 +441,14 @@ struct Regs { | |||
| 441 | }; | 441 | }; |
| 442 | 442 | ||
| 443 | enum class StencilAction : u32 { | 443 | enum class StencilAction : u32 { |
| 444 | Keep = 0, | 444 | Keep = 0, |
| 445 | Xor = 5, | 445 | Zero = 1, |
| 446 | Replace = 2, | ||
| 447 | Increment = 3, | ||
| 448 | Decrement = 4, | ||
| 449 | Invert = 5, | ||
| 450 | IncrementWrap = 6, | ||
| 451 | DecrementWrap = 7 | ||
| 446 | }; | 452 | }; |
| 447 | 453 | ||
| 448 | struct { | 454 | struct { |
| @@ -481,23 +487,29 @@ struct Regs { | |||
| 481 | 487 | ||
| 482 | struct { | 488 | struct { |
| 483 | union { | 489 | union { |
| 490 | // Raw value of this register | ||
| 491 | u32 raw_func; | ||
| 492 | |||
| 484 | // If true, enable stencil testing | 493 | // If true, enable stencil testing |
| 485 | BitField< 0, 1, u32> enable; | 494 | BitField< 0, 1, u32> enable; |
| 486 | 495 | ||
| 487 | // Comparison operation for stencil testing | 496 | // Comparison operation for stencil testing |
| 488 | BitField< 4, 3, CompareFunc> func; | 497 | BitField< 4, 3, CompareFunc> func; |
| 489 | 498 | ||
| 490 | // Value to calculate the new stencil value from | 499 | // Mask used to control writing to the stencil buffer |
| 491 | BitField< 8, 8, u32> replacement_value; | 500 | BitField< 8, 8, u32> write_mask; |
| 492 | 501 | ||
| 493 | // Value to compare against for stencil testing | 502 | // Value to compare against for stencil testing |
| 494 | BitField<16, 8, u32> reference_value; | 503 | BitField<16, 8, u32> reference_value; |
| 495 | 504 | ||
| 496 | // Mask to apply on stencil test inputs | 505 | // Mask to apply on stencil test inputs |
| 497 | BitField<24, 8, u32> mask; | 506 | BitField<24, 8, u32> input_mask; |
| 498 | }; | 507 | }; |
| 499 | 508 | ||
| 500 | union { | 509 | union { |
| 510 | // Raw value of this register | ||
| 511 | u32 raw_op; | ||
| 512 | |||
| 501 | // Action to perform when the stencil test fails | 513 | // Action to perform when the stencil test fails |
| 502 | BitField< 0, 3, StencilAction> action_stencil_fail; | 514 | BitField< 0, 3, StencilAction> action_stencil_fail; |
| 503 | 515 | ||
diff --git a/src/video_core/rasterizer.cpp b/src/video_core/rasterizer.cpp index 4a159da8e..77eadda9e 100644 --- a/src/video_core/rasterizer.cpp +++ b/src/video_core/rasterizer.cpp | |||
| @@ -216,14 +216,33 @@ static void SetStencil(int x, int y, u8 value) { | |||
| 216 | } | 216 | } |
| 217 | } | 217 | } |
| 218 | 218 | ||
| 219 | // TODO: Should the stencil mask be applied to the "dest" or "ref" operands? Most likely not! | 219 | static u8 PerformStencilAction(Regs::StencilAction action, u8 old_stencil, u8 ref) { |
| 220 | static u8 PerformStencilAction(Regs::StencilAction action, u8 dest, u8 ref) { | ||
| 221 | switch (action) { | 220 | switch (action) { |
| 222 | case Regs::StencilAction::Keep: | 221 | case Regs::StencilAction::Keep: |
| 223 | return dest; | 222 | return old_stencil; |
| 224 | 223 | ||
| 225 | case Regs::StencilAction::Xor: | 224 | case Regs::StencilAction::Zero: |
| 226 | return dest ^ ref; | 225 | return 0; |
| 226 | |||
| 227 | case Regs::StencilAction::Replace: | ||
| 228 | return ref; | ||
| 229 | |||
| 230 | case Regs::StencilAction::Increment: | ||
| 231 | // Saturated increment | ||
| 232 | return std::min<u8>(old_stencil, 254) + 1; | ||
| 233 | |||
| 234 | case Regs::StencilAction::Decrement: | ||
| 235 | // Saturated decrement | ||
| 236 | return std::max<u8>(old_stencil, 1) - 1; | ||
| 237 | |||
| 238 | case Regs::StencilAction::Invert: | ||
| 239 | return ~old_stencil; | ||
| 240 | |||
| 241 | case Regs::StencilAction::IncrementWrap: | ||
| 242 | return old_stencil + 1; | ||
| 243 | |||
| 244 | case Regs::StencilAction::DecrementWrap: | ||
| 245 | return old_stencil - 1; | ||
| 227 | 246 | ||
| 228 | default: | 247 | default: |
| 229 | LOG_CRITICAL(HW_GPU, "Unknown stencil action %x", (int)action); | 248 | LOG_CRITICAL(HW_GPU, "Unknown stencil action %x", (int)action); |
| @@ -783,10 +802,16 @@ static void ProcessTriangleInternal(const Shader::OutputVertex& v0, | |||
| 783 | } | 802 | } |
| 784 | 803 | ||
| 785 | u8 old_stencil = 0; | 804 | u8 old_stencil = 0; |
| 805 | |||
| 806 | auto UpdateStencil = [stencil_test, x, y, &old_stencil](Pica::Regs::StencilAction action) { | ||
| 807 | u8 new_stencil = PerformStencilAction(action, old_stencil, stencil_test.reference_value); | ||
| 808 | SetStencil(x >> 4, y >> 4, (new_stencil & stencil_test.write_mask) | (old_stencil & ~stencil_test.write_mask)); | ||
| 809 | }; | ||
| 810 | |||
| 786 | if (stencil_action_enable) { | 811 | if (stencil_action_enable) { |
| 787 | old_stencil = GetStencil(x >> 4, y >> 4); | 812 | old_stencil = GetStencil(x >> 4, y >> 4); |
| 788 | u8 dest = old_stencil & stencil_test.mask; | 813 | u8 dest = old_stencil & stencil_test.input_mask; |
| 789 | u8 ref = stencil_test.reference_value & stencil_test.mask; | 814 | u8 ref = stencil_test.reference_value & stencil_test.input_mask; |
| 790 | 815 | ||
| 791 | bool pass = false; | 816 | bool pass = false; |
| 792 | switch (stencil_test.func) { | 817 | switch (stencil_test.func) { |
| @@ -824,8 +849,7 @@ static void ProcessTriangleInternal(const Shader::OutputVertex& v0, | |||
| 824 | } | 849 | } |
| 825 | 850 | ||
| 826 | if (!pass) { | 851 | if (!pass) { |
| 827 | u8 new_stencil = PerformStencilAction(stencil_test.action_stencil_fail, old_stencil, stencil_test.replacement_value); | 852 | UpdateStencil(stencil_test.action_stencil_fail); |
| 828 | SetStencil(x >> 4, y >> 4, new_stencil); | ||
| 829 | continue; | 853 | continue; |
| 830 | } | 854 | } |
| 831 | } | 855 | } |
| @@ -875,23 +899,19 @@ static void ProcessTriangleInternal(const Shader::OutputVertex& v0, | |||
| 875 | } | 899 | } |
| 876 | 900 | ||
| 877 | if (!pass) { | 901 | if (!pass) { |
| 878 | if (stencil_action_enable) { | 902 | if (stencil_action_enable) |
| 879 | u8 new_stencil = PerformStencilAction(stencil_test.action_depth_fail, old_stencil, stencil_test.replacement_value); | 903 | UpdateStencil(stencil_test.action_depth_fail); |
| 880 | SetStencil(x >> 4, y >> 4, new_stencil); | ||
| 881 | } | ||
| 882 | continue; | 904 | continue; |
| 883 | } | 905 | } |
| 884 | 906 | ||
| 885 | if (output_merger.depth_write_enable) | 907 | if (output_merger.depth_write_enable) |
| 886 | SetDepth(x >> 4, y >> 4, z); | 908 | SetDepth(x >> 4, y >> 4, z); |
| 887 | |||
| 888 | if (stencil_action_enable) { | ||
| 889 | // TODO: What happens if stencil testing is enabled, but depth testing is not? Will stencil get updated anyway? | ||
| 890 | u8 new_stencil = PerformStencilAction(stencil_test.action_depth_pass, old_stencil, stencil_test.replacement_value); | ||
| 891 | SetStencil(x >> 4, y >> 4, new_stencil); | ||
| 892 | } | ||
| 893 | } | 909 | } |
| 894 | 910 | ||
| 911 | // The stencil depth_pass action is executed even if depth testing is disabled | ||
| 912 | if (stencil_action_enable) | ||
| 913 | UpdateStencil(stencil_test.action_depth_pass); | ||
| 914 | |||
| 895 | auto dest = GetPixel(x >> 4, y >> 4); | 915 | auto dest = GetPixel(x >> 4, y >> 4); |
| 896 | Math::Vec4<u8> blend_output = combiner_output; | 916 | Math::Vec4<u8> blend_output = combiner_output; |
| 897 | 917 | ||
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp index f0ccc2397..d29049508 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.cpp +++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp | |||
| @@ -269,7 +269,8 @@ void RasterizerOpenGL::NotifyPicaRegisterChanged(u32 id) { | |||
| 269 | break; | 269 | break; |
| 270 | 270 | ||
| 271 | // Stencil test | 271 | // Stencil test |
| 272 | case PICA_REG_INDEX(output_merger.stencil_test): | 272 | case PICA_REG_INDEX(output_merger.stencil_test.raw_func): |
| 273 | case PICA_REG_INDEX(output_merger.stencil_test.raw_op): | ||
| 273 | SyncStencilTest(); | 274 | SyncStencilTest(); |
| 274 | break; | 275 | break; |
| 275 | 276 | ||
| @@ -676,7 +677,15 @@ void RasterizerOpenGL::SyncLogicOp() { | |||
| 676 | } | 677 | } |
| 677 | 678 | ||
| 678 | void RasterizerOpenGL::SyncStencilTest() { | 679 | void RasterizerOpenGL::SyncStencilTest() { |
| 679 | // TODO: Implement stencil test, mask, and op | 680 | const auto& regs = Pica::g_state.regs; |
| 681 | state.stencil.test_enabled = regs.output_merger.stencil_test.enable && regs.framebuffer.depth_format == Pica::Regs::DepthFormat::D24S8; | ||
| 682 | state.stencil.test_func = PicaToGL::CompareFunc(regs.output_merger.stencil_test.func); | ||
| 683 | state.stencil.test_ref = regs.output_merger.stencil_test.reference_value; | ||
| 684 | state.stencil.test_mask = regs.output_merger.stencil_test.input_mask; | ||
| 685 | state.stencil.write_mask = regs.output_merger.stencil_test.write_mask; | ||
| 686 | state.stencil.action_stencil_fail = PicaToGL::StencilOp(regs.output_merger.stencil_test.action_stencil_fail); | ||
| 687 | state.stencil.action_depth_fail = PicaToGL::StencilOp(regs.output_merger.stencil_test.action_depth_fail); | ||
| 688 | state.stencil.action_depth_pass = PicaToGL::StencilOp(regs.output_merger.stencil_test.action_depth_pass); | ||
| 680 | } | 689 | } |
| 681 | 690 | ||
| 682 | void RasterizerOpenGL::SyncDepthTest() { | 691 | void RasterizerOpenGL::SyncDepthTest() { |
| @@ -867,8 +876,15 @@ void RasterizerOpenGL::ReloadDepthBuffer() { | |||
| 867 | state.Apply(); | 876 | state.Apply(); |
| 868 | 877 | ||
| 869 | glActiveTexture(GL_TEXTURE0); | 878 | glActiveTexture(GL_TEXTURE0); |
| 870 | glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, fb_depth_texture.width, fb_depth_texture.height, | 879 | if (fb_depth_texture.format == Pica::Regs::DepthFormat::D24S8) { |
| 871 | fb_depth_texture.gl_format, fb_depth_texture.gl_type, temp_fb_depth_buffer.get()); | 880 | // TODO(Subv): There is a bug with Intel Windows drivers that makes glTexSubImage2D not change the stencil buffer. |
| 881 | // The bug has been reported to Intel (https://communities.intel.com/message/324464) | ||
| 882 | glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH24_STENCIL8, fb_depth_texture.width, fb_depth_texture.height, 0, | ||
| 883 | GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8, temp_fb_depth_buffer.get()); | ||
| 884 | } else { | ||
| 885 | glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, fb_depth_texture.width, fb_depth_texture.height, | ||
| 886 | fb_depth_texture.gl_format, fb_depth_texture.gl_type, temp_fb_depth_buffer.get()); | ||
| 887 | } | ||
| 872 | 888 | ||
| 873 | state.texture_units[0].texture_2d = 0; | 889 | state.texture_units[0].texture_2d = 0; |
| 874 | state.Apply(); | 890 | state.Apply(); |
diff --git a/src/video_core/renderer_opengl/gl_state.cpp b/src/video_core/renderer_opengl/gl_state.cpp index 871324014..ba47ce8b8 100644 --- a/src/video_core/renderer_opengl/gl_state.cpp +++ b/src/video_core/renderer_opengl/gl_state.cpp | |||
| @@ -26,6 +26,9 @@ OpenGLState::OpenGLState() { | |||
| 26 | stencil.test_ref = 0; | 26 | stencil.test_ref = 0; |
| 27 | stencil.test_mask = -1; | 27 | stencil.test_mask = -1; |
| 28 | stencil.write_mask = -1; | 28 | stencil.write_mask = -1; |
| 29 | stencil.action_depth_fail = GL_KEEP; | ||
| 30 | stencil.action_depth_pass = GL_KEEP; | ||
| 31 | stencil.action_stencil_fail = GL_KEEP; | ||
| 29 | 32 | ||
| 30 | blend.enabled = false; | 33 | blend.enabled = false; |
| 31 | blend.src_rgb_func = GL_ONE; | 34 | blend.src_rgb_func = GL_ONE; |
| @@ -105,6 +108,12 @@ void OpenGLState::Apply() { | |||
| 105 | glStencilFunc(stencil.test_func, stencil.test_ref, stencil.test_mask); | 108 | glStencilFunc(stencil.test_func, stencil.test_ref, stencil.test_mask); |
| 106 | } | 109 | } |
| 107 | 110 | ||
| 111 | if (stencil.action_depth_fail != cur_state.stencil.action_depth_fail || | ||
| 112 | stencil.action_depth_pass != cur_state.stencil.action_depth_pass || | ||
| 113 | stencil.action_stencil_fail != cur_state.stencil.action_stencil_fail) { | ||
| 114 | glStencilOp(stencil.action_stencil_fail, stencil.action_depth_fail, stencil.action_depth_pass); | ||
| 115 | } | ||
| 116 | |||
| 108 | // Stencil mask | 117 | // Stencil mask |
| 109 | if (stencil.write_mask != cur_state.stencil.write_mask) { | 118 | if (stencil.write_mask != cur_state.stencil.write_mask) { |
| 110 | glStencilMask(stencil.write_mask); | 119 | glStencilMask(stencil.write_mask); |
diff --git a/src/video_core/renderer_opengl/gl_state.h b/src/video_core/renderer_opengl/gl_state.h index 3e2379021..81e7e0877 100644 --- a/src/video_core/renderer_opengl/gl_state.h +++ b/src/video_core/renderer_opengl/gl_state.h | |||
| @@ -32,6 +32,9 @@ public: | |||
| 32 | GLint test_ref; // GL_STENCIL_REF | 32 | GLint test_ref; // GL_STENCIL_REF |
| 33 | GLuint test_mask; // GL_STENCIL_VALUE_MASK | 33 | GLuint test_mask; // GL_STENCIL_VALUE_MASK |
| 34 | GLuint write_mask; // GL_STENCIL_WRITEMASK | 34 | GLuint write_mask; // GL_STENCIL_WRITEMASK |
| 35 | GLenum action_stencil_fail; // GL_STENCIL_FAIL | ||
| 36 | GLenum action_depth_fail; // GL_STENCIL_PASS_DEPTH_FAIL | ||
| 37 | GLenum action_depth_pass; // GL_STENCIL_PASS_DEPTH_PASS | ||
| 35 | } stencil; | 38 | } stencil; |
| 36 | 39 | ||
| 37 | struct { | 40 | struct { |
diff --git a/src/video_core/renderer_opengl/pica_to_gl.h b/src/video_core/renderer_opengl/pica_to_gl.h index 3b562da86..12806fad5 100644 --- a/src/video_core/renderer_opengl/pica_to_gl.h +++ b/src/video_core/renderer_opengl/pica_to_gl.h | |||
| @@ -152,6 +152,29 @@ inline GLenum CompareFunc(Pica::Regs::CompareFunc func) { | |||
| 152 | return compare_func_table[(unsigned)func]; | 152 | return compare_func_table[(unsigned)func]; |
| 153 | } | 153 | } |
| 154 | 154 | ||
| 155 | inline GLenum StencilOp(Pica::Regs::StencilAction action) { | ||
| 156 | static const GLenum stencil_op_table[] = { | ||
| 157 | GL_KEEP, // StencilAction::Keep | ||
| 158 | GL_ZERO, // StencilAction::Zero | ||
| 159 | GL_REPLACE, // StencilAction::Replace | ||
| 160 | GL_INCR, // StencilAction::Increment | ||
| 161 | GL_DECR, // StencilAction::Decrement | ||
| 162 | GL_INVERT, // StencilAction::Invert | ||
| 163 | GL_INCR_WRAP, // StencilAction::IncrementWrap | ||
| 164 | GL_DECR_WRAP // StencilAction::DecrementWrap | ||
| 165 | }; | ||
| 166 | |||
| 167 | // Range check table for input | ||
| 168 | if ((unsigned)action >= ARRAY_SIZE(stencil_op_table)) { | ||
| 169 | LOG_CRITICAL(Render_OpenGL, "Unknown stencil op %d", action); | ||
| 170 | UNREACHABLE(); | ||
| 171 | |||
| 172 | return GL_KEEP; | ||
| 173 | } | ||
| 174 | |||
| 175 | return stencil_op_table[(unsigned)action]; | ||
| 176 | } | ||
| 177 | |||
| 155 | inline std::array<GLfloat, 4> ColorRGBA8(const u8* bytes) { | 178 | inline std::array<GLfloat, 4> ColorRGBA8(const u8* bytes) { |
| 156 | return { { bytes[0] / 255.0f, | 179 | return { { bytes[0] / 255.0f, |
| 157 | bytes[1] / 255.0f, | 180 | bytes[1] / 255.0f, |