From 827bb08c91086e0e24edb82df79c9ed860baf836 Mon Sep 17 00:00:00 2001 From: Subv Date: Wed, 6 Jun 2018 23:53:43 -0500 Subject: [PATCH 1/4] GPU: Added registers for the CLEAR_BUFFERS and CLEAR_COLOR methods. --- src/video_core/engines/maxwell_3d.h | 29 +++++++++++++++++++++++++++-- 1 file changed, 27 insertions(+), 2 deletions(-) diff --git a/src/video_core/engines/maxwell_3d.h b/src/video_core/engines/maxwell_3d.h index ff67f2a58..e264dd4d8 100644 --- a/src/video_core/engines/maxwell_3d.h +++ b/src/video_core/engines/maxwell_3d.h @@ -436,7 +436,12 @@ public: u32 count; } vertex_buffer; - INSERT_PADDING_WORDS(0x99); + INSERT_PADDING_WORDS(1); + + float clear_color[4]; + float clear_depth; + + INSERT_PADDING_WORDS(0x93); struct { u32 address_high; @@ -584,7 +589,21 @@ public: Cull cull; - INSERT_PADDING_WORDS(0x77); + INSERT_PADDING_WORDS(0x2B); + + union { + u32 raw; + BitField<0, 1, u32> Z; + BitField<1, 1, u32> S; + BitField<2, 1, u32> R; + BitField<3, 1, u32> G; + BitField<4, 1, u32> B; + BitField<5, 1, u32> A; + BitField<6, 4, u32> RT; + BitField<10, 11, u32> layer; + } clear_buffers; + + INSERT_PADDING_WORDS(0x4B); struct { u32 query_address_high; @@ -766,6 +785,9 @@ private: /// Handles writes to the macro uploading registers. void ProcessMacroUpload(u32 data); + /// Handles a write to the CLEAR_BUFFERS register. + void ProcessClearBuffers(); + /// Handles a write to the QUERY_GET register. void ProcessQueryGet(); @@ -788,6 +810,8 @@ ASSERT_REG_POSITION(rt, 0x200); ASSERT_REG_POSITION(viewport_transform[0], 0x280); 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(zeta, 0x3F8); ASSERT_REG_POSITION(vertex_attrib_format[0], 0x458); ASSERT_REG_POSITION(rt_control, 0x487); @@ -803,6 +827,7 @@ ASSERT_REG_POSITION(code_address, 0x582); ASSERT_REG_POSITION(draw, 0x585); ASSERT_REG_POSITION(index_array, 0x5F2); ASSERT_REG_POSITION(cull, 0x646); +ASSERT_REG_POSITION(clear_buffers, 0x674); ASSERT_REG_POSITION(query, 0x6C0); ASSERT_REG_POSITION(vertex_array[0], 0x700); ASSERT_REG_POSITION(independent_blend, 0x780); From be51120d237cb551fae90fbfaebda41669c40403 Mon Sep 17 00:00:00 2001 From: Subv Date: Wed, 6 Jun 2018 23:54:25 -0500 Subject: [PATCH 2/4] GPU: Bind and clear the render target when the CLEAR_BUFFERS register is written to. --- src/video_core/engines/maxwell_3d.cpp | 11 +++ src/video_core/rasterizer_interface.h | 3 + .../renderer_opengl/gl_rasterizer.cpp | 71 +++++++++++++++++++ .../renderer_opengl/gl_rasterizer.h | 1 + 4 files changed, 86 insertions(+) diff --git a/src/video_core/engines/maxwell_3d.cpp b/src/video_core/engines/maxwell_3d.cpp index 93c43c8cb..78f1c0ea7 100644 --- a/src/video_core/engines/maxwell_3d.cpp +++ b/src/video_core/engines/maxwell_3d.cpp @@ -126,6 +126,10 @@ void Maxwell3D::WriteReg(u32 method, u32 value, u32 remaining_params) { DrawArrays(); break; } + case MAXWELL3D_REG_INDEX(clear_buffers): { + ProcessClearBuffers(); + break; + } case MAXWELL3D_REG_INDEX(query.query_get): { ProcessQueryGet(); break; @@ -415,5 +419,12 @@ bool Maxwell3D::IsShaderStageEnabled(Regs::ShaderStage stage) const { UNREACHABLE(); } +void Maxwell3D::ProcessClearBuffers() { + ASSERT(regs.clear_buffers.R && regs.clear_buffers.G && regs.clear_buffers.B && + regs.clear_buffers.A); + + VideoCore::g_renderer->Rasterizer()->Clear(); +} + } // namespace Engines } // namespace Tegra diff --git a/src/video_core/rasterizer_interface.h b/src/video_core/rasterizer_interface.h index 145e58334..499e84b89 100644 --- a/src/video_core/rasterizer_interface.h +++ b/src/video_core/rasterizer_interface.h @@ -19,6 +19,9 @@ public: /// Draw the current batch of vertex arrays virtual void DrawArrays() = 0; + /// Clear the current framebuffer + virtual void Clear() = 0; + /// Notify rasterizer that the specified Maxwell register has been changed virtual void NotifyMaxwellRegisterChanged(u32 method) = 0; diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp index ca3814cfc..8e1171161 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.cpp +++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp @@ -297,6 +297,77 @@ bool RasterizerOpenGL::AccelerateDrawBatch(bool is_indexed) { return true; } +void RasterizerOpenGL::Clear() { + const auto& regs = Core::System().GetInstance().GPU().Maxwell3D().regs; + + // TODO(bunnei): Implement these + const bool has_stencil = false; + const bool using_color_fb = true; + const bool using_depth_fb = regs.zeta.Address() != 0; + const MathUtil::Rectangle viewport_rect{regs.viewport_transform[0].GetRect()}; + + 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); + + Surface color_surface; + Surface depth_surface; + MathUtil::Rectangle surfaces_rect; + std::tie(color_surface, depth_surface, surfaces_rect) = + res_cache.GetFramebufferSurfaces(using_color_fb, using_depth_fb, viewport_rect); + + MathUtil::Rectangle draw_rect{ + static_cast(std::clamp(static_cast(surfaces_rect.left) + viewport_rect.left, + surfaces_rect.left, surfaces_rect.right)), // Left + static_cast(std::clamp(static_cast(surfaces_rect.bottom) + viewport_rect.top, + surfaces_rect.bottom, surfaces_rect.top)), // Top + static_cast(std::clamp(static_cast(surfaces_rect.left) + viewport_rect.right, + surfaces_rect.left, surfaces_rect.right)), // Right + static_cast( + std::clamp(static_cast(surfaces_rect.bottom) + viewport_rect.bottom, + surfaces_rect.bottom, surfaces_rect.top))}; // Bottom + + // Bind the framebuffer surfaces + BindFramebufferSurfaces(color_surface, depth_surface, has_stencil); + + // Sync the viewport + SyncViewport(surfaces_rect); + + // TODO(bunnei): Sync scissorbox uniform(s) here + + // Viewport can have negative offsets or larger dimensions than our framebuffer sub-rect. Enable + // scissor test to prevent drawing outside of the framebuffer region + state.scissor.enabled = true; + state.scissor.x = draw_rect.left; + state.scissor.y = draw_rect.bottom; + state.scissor.width = draw_rect.GetWidth(); + state.scissor.height = draw_rect.GetHeight(); + state.Apply(); + + // TODO(Subv): Support clearing only partial colors. + glClearColor(regs.clear_color[0], regs.clear_color[1], regs.clear_color[2], + regs.clear_color[3]); + glClearDepth(regs.clear_depth); + + GLbitfield clear_mask = GL_COLOR_BUFFER_BIT; + if (regs.clear_buffers.Z) + clear_mask |= GL_DEPTH_BUFFER_BIT; + + glClear(clear_mask); + + // Mark framebuffer surfaces as dirty + if (color_surface != nullptr && write_color_fb) { + res_cache.MarkSurfaceAsDirty(color_surface); + } + if (depth_surface != nullptr && write_depth_fb) { + res_cache.MarkSurfaceAsDirty(depth_surface); + } +} + void RasterizerOpenGL::DrawArrays() { if (accelerate_draw == AccelDraw::Disabled) return; diff --git a/src/video_core/renderer_opengl/gl_rasterizer.h b/src/video_core/renderer_opengl/gl_rasterizer.h index 493aa39e5..0b1e139b0 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.h +++ b/src/video_core/renderer_opengl/gl_rasterizer.h @@ -28,6 +28,7 @@ public: ~RasterizerOpenGL() override; void DrawArrays() override; + void Clear() override; void NotifyMaxwellRegisterChanged(u32 method) override; void FlushAll() override; void FlushRegion(Tegra::GPUVAddr addr, u64 size) override; From c1811ed3d1805e0d0ab536762692e7c0e80f14dc Mon Sep 17 00:00:00 2001 From: Subv Date: Mon, 2 Jul 2018 19:09:03 -0500 Subject: [PATCH 3/4] GPU: Support clears that don't clear the color buffer. --- src/video_core/engines/maxwell_3d.cpp | 5 +++-- .../renderer_opengl/gl_rasterizer.cpp | 18 ++++++++++++++---- 2 files changed, 17 insertions(+), 6 deletions(-) diff --git a/src/video_core/engines/maxwell_3d.cpp b/src/video_core/engines/maxwell_3d.cpp index 78f1c0ea7..4d1a79c55 100644 --- a/src/video_core/engines/maxwell_3d.cpp +++ b/src/video_core/engines/maxwell_3d.cpp @@ -420,8 +420,9 @@ bool Maxwell3D::IsShaderStageEnabled(Regs::ShaderStage stage) const { } void Maxwell3D::ProcessClearBuffers() { - ASSERT(regs.clear_buffers.R && regs.clear_buffers.G && regs.clear_buffers.B && - regs.clear_buffers.A); + ASSERT(regs.clear_buffers.R == regs.clear_buffers.G && + regs.clear_buffers.R == regs.clear_buffers.B && + regs.clear_buffers.R == regs.clear_buffers.A); VideoCore::g_renderer->Rasterizer()->Clear(); } diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp index 8e1171161..08c4df5dc 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.cpp +++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp @@ -300,6 +300,20 @@ bool RasterizerOpenGL::AccelerateDrawBatch(bool is_indexed) { void RasterizerOpenGL::Clear() { const auto& regs = Core::System().GetInstance().GPU().Maxwell3D().regs; + GLbitfield clear_mask = 0; + if (regs.clear_buffers.R && regs.clear_buffers.G && regs.clear_buffers.B && + regs.clear_buffers.A) { + clear_mask |= GL_COLOR_BUFFER_BIT; + } + if (regs.clear_buffers.Z) + clear_mask |= GL_DEPTH_BUFFER_BIT; + + if (clear_mask == 0) + return; + + // Sync the depth test state before configuring the framebuffer surfaces. + SyncDepthTestState(); + // TODO(bunnei): Implement these const bool has_stencil = false; const bool using_color_fb = true; @@ -353,10 +367,6 @@ void RasterizerOpenGL::Clear() { regs.clear_color[3]); glClearDepth(regs.clear_depth); - GLbitfield clear_mask = GL_COLOR_BUFFER_BIT; - if (regs.clear_buffers.Z) - clear_mask |= GL_DEPTH_BUFFER_BIT; - glClear(clear_mask); // Mark framebuffer surfaces as dirty From 78443a7f2929dece8d2509d50642df9478aeb166 Mon Sep 17 00:00:00 2001 From: Subv Date: Tue, 3 Jul 2018 16:55:44 -0500 Subject: [PATCH 4/4] GPU: Factor out the framebuffer configuration code for both Clear and Draw commands. --- .../renderer_opengl/gl_rasterizer.cpp | 152 +++++++----------- .../renderer_opengl/gl_rasterizer.h | 5 + 2 files changed, 62 insertions(+), 95 deletions(-) diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp index 08c4df5dc..32906be7e 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.cpp +++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp @@ -297,92 +297,7 @@ bool RasterizerOpenGL::AccelerateDrawBatch(bool is_indexed) { return true; } -void RasterizerOpenGL::Clear() { - const auto& regs = Core::System().GetInstance().GPU().Maxwell3D().regs; - - GLbitfield clear_mask = 0; - if (regs.clear_buffers.R && regs.clear_buffers.G && regs.clear_buffers.B && - regs.clear_buffers.A) { - clear_mask |= GL_COLOR_BUFFER_BIT; - } - if (regs.clear_buffers.Z) - clear_mask |= GL_DEPTH_BUFFER_BIT; - - if (clear_mask == 0) - return; - - // Sync the depth test state before configuring the framebuffer surfaces. - SyncDepthTestState(); - - // TODO(bunnei): Implement these - const bool has_stencil = false; - const bool using_color_fb = true; - const bool using_depth_fb = regs.zeta.Address() != 0; - const MathUtil::Rectangle viewport_rect{regs.viewport_transform[0].GetRect()}; - - 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); - - Surface color_surface; - Surface depth_surface; - MathUtil::Rectangle surfaces_rect; - std::tie(color_surface, depth_surface, surfaces_rect) = - res_cache.GetFramebufferSurfaces(using_color_fb, using_depth_fb, viewport_rect); - - MathUtil::Rectangle draw_rect{ - static_cast(std::clamp(static_cast(surfaces_rect.left) + viewport_rect.left, - surfaces_rect.left, surfaces_rect.right)), // Left - static_cast(std::clamp(static_cast(surfaces_rect.bottom) + viewport_rect.top, - surfaces_rect.bottom, surfaces_rect.top)), // Top - static_cast(std::clamp(static_cast(surfaces_rect.left) + viewport_rect.right, - surfaces_rect.left, surfaces_rect.right)), // Right - static_cast( - std::clamp(static_cast(surfaces_rect.bottom) + viewport_rect.bottom, - surfaces_rect.bottom, surfaces_rect.top))}; // Bottom - - // Bind the framebuffer surfaces - BindFramebufferSurfaces(color_surface, depth_surface, has_stencil); - - // Sync the viewport - SyncViewport(surfaces_rect); - - // TODO(bunnei): Sync scissorbox uniform(s) here - - // Viewport can have negative offsets or larger dimensions than our framebuffer sub-rect. Enable - // scissor test to prevent drawing outside of the framebuffer region - state.scissor.enabled = true; - state.scissor.x = draw_rect.left; - state.scissor.y = draw_rect.bottom; - state.scissor.width = draw_rect.GetWidth(); - state.scissor.height = draw_rect.GetHeight(); - state.Apply(); - - // TODO(Subv): Support clearing only partial colors. - glClearColor(regs.clear_color[0], regs.clear_color[1], regs.clear_color[2], - regs.clear_color[3]); - glClearDepth(regs.clear_depth); - - glClear(clear_mask); - - // Mark framebuffer surfaces as dirty - if (color_surface != nullptr && write_color_fb) { - res_cache.MarkSurfaceAsDirty(color_surface); - } - if (depth_surface != nullptr && write_depth_fb) { - res_cache.MarkSurfaceAsDirty(depth_surface); - } -} - -void RasterizerOpenGL::DrawArrays() { - if (accelerate_draw == AccelDraw::Disabled) - return; - - MICROPROFILE_SCOPE(OpenGL_Drawing); +std::pair RasterizerOpenGL::ConfigureFramebuffers() { const auto& regs = Core::System().GetInstance().GPU().Maxwell3D().regs; // Sync the depth test state before configuring the framebuffer surfaces. @@ -425,11 +340,6 @@ void RasterizerOpenGL::DrawArrays() { BindFramebufferSurfaces(color_surface, depth_surface, has_stencil); SyncViewport(surfaces_rect); - SyncBlendState(); - SyncCullMode(); - - // TODO(bunnei): Sync framebuffer_scale uniform here - // TODO(bunnei): Sync scissorbox uniform(s) here // Viewport can have negative offsets or larger dimensions than our framebuffer sub-rect. Enable // scissor test to prevent drawing outside of the framebuffer region @@ -440,6 +350,58 @@ void RasterizerOpenGL::DrawArrays() { state.scissor.height = draw_rect.GetHeight(); state.Apply(); + // Only return the surface to be marked as dirty if writing to it is enabled. + return std::make_pair(write_color_fb ? color_surface : nullptr, + write_depth_fb ? depth_surface : nullptr); +} + +void RasterizerOpenGL::Clear() { + const auto& regs = Core::System().GetInstance().GPU().Maxwell3D().regs; + + GLbitfield clear_mask = 0; + if (regs.clear_buffers.R && regs.clear_buffers.G && regs.clear_buffers.B && + regs.clear_buffers.A) { + clear_mask |= GL_COLOR_BUFFER_BIT; + } + if (regs.clear_buffers.Z) + clear_mask |= GL_DEPTH_BUFFER_BIT; + + if (clear_mask == 0) + return; + + auto [dirty_color_surface, dirty_depth_surface] = ConfigureFramebuffers(); + + // TODO(Subv): Support clearing only partial colors. + glClearColor(regs.clear_color[0], regs.clear_color[1], regs.clear_color[2], + regs.clear_color[3]); + glClearDepth(regs.clear_depth); + + glClear(clear_mask); + + // Mark framebuffer surfaces as dirty + if (dirty_color_surface != nullptr) { + res_cache.MarkSurfaceAsDirty(dirty_color_surface); + } + if (dirty_depth_surface != nullptr) { + res_cache.MarkSurfaceAsDirty(dirty_depth_surface); + } +} + +void RasterizerOpenGL::DrawArrays() { + if (accelerate_draw == AccelDraw::Disabled) + return; + + MICROPROFILE_SCOPE(OpenGL_Drawing); + const auto& regs = Core::System().GetInstance().GPU().Maxwell3D().regs; + + auto [dirty_color_surface, dirty_depth_surface] = ConfigureFramebuffers(); + + SyncBlendState(); + SyncCullMode(); + + // TODO(bunnei): Sync framebuffer_scale uniform here + // TODO(bunnei): Sync scissorbox uniform(s) here + // Draw the vertex batch const bool is_indexed = accelerate_draw == AccelDraw::Indexed; const u64 index_buffer_size{regs.index_array.count * regs.index_array.FormatSizeInBytes()}; @@ -520,11 +482,11 @@ void RasterizerOpenGL::DrawArrays() { state.Apply(); // Mark framebuffer surfaces as dirty - if (color_surface != nullptr && write_color_fb) { - res_cache.MarkSurfaceAsDirty(color_surface); + if (dirty_color_surface != nullptr) { + res_cache.MarkSurfaceAsDirty(dirty_color_surface); } - if (depth_surface != nullptr && write_depth_fb) { - res_cache.MarkSurfaceAsDirty(depth_surface); + if (dirty_depth_surface != nullptr) { + res_cache.MarkSurfaceAsDirty(dirty_depth_surface); } } diff --git a/src/video_core/renderer_opengl/gl_rasterizer.h b/src/video_core/renderer_opengl/gl_rasterizer.h index 0b1e139b0..7738f40b1 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.h +++ b/src/video_core/renderer_opengl/gl_rasterizer.h @@ -7,6 +7,7 @@ #include #include #include +#include #include #include #include "common/common_types.h" @@ -82,6 +83,10 @@ private: u32 border_color_a; }; + /// Configures the color and depth framebuffer states and returns the dirty + /// surfaces if writing was enabled. + std::pair ConfigureFramebuffers(); + /// Binds the framebuffer color and depth surface void BindFramebufferSurfaces(const Surface& color_surface, const Surface& depth_surface, bool has_stencil);