diff --git a/src/core/gpu.cpp b/src/core/gpu.cpp index 8d9a3cfcd..c23cd8c2c 100644 --- a/src/core/gpu.cpp +++ b/src/core/gpu.cpp @@ -38,8 +38,10 @@ void GPU::Reset() void GPU::SoftReset() { m_GPUSTAT.bits = 0x14802000; - m_drawing_area = {}; + m_drawing_area.Set(0, 0, 0, 0); + m_drawing_area_changed = true; m_drawing_offset = {}; + m_drawing_offset_changed = true; std::memset(&m_crtc_state, 0, sizeof(m_crtc_state)); m_crtc_state.regs.display_address_start = 0; m_crtc_state.regs.horizontal_display_range = 0xC60260; @@ -50,8 +52,6 @@ void GPU::SoftReset() m_render_state.texture_page_changed = true; UpdateGPUSTAT(); UpdateCRTCConfig(); - UpdateDrawingArea(); - UpdateDrawingOffset(); } bool GPU::DoState(StateWrapper& sw) @@ -115,8 +115,8 @@ bool GPU::DoState(StateWrapper& sw) { m_render_state.texture_page_changed = true; m_render_state.texture_window_changed = true; - UpdateDrawingArea(); - UpdateDrawingOffset(); + m_drawing_area_changed = true; + m_drawing_offset_changed = true; UpdateGPUSTAT(); } @@ -665,10 +665,6 @@ void GPU::HandleGetGPUInfoCommand(u32 value) void GPU::UpdateDisplay() {} -void GPU::UpdateDrawingArea() {} - -void GPU::UpdateDrawingOffset() {} - void GPU::ReadVRAM(u32 x, u32 y, u32 width, u32 height, void* buffer) {} void GPU::FillVRAM(u32 x, u32 y, u32 width, u32 height, u32 color) {} diff --git a/src/core/gpu.h b/src/core/gpu.h index 85d292f39..2b090cfb0 100644 --- a/src/core/gpu.h +++ b/src/core/gpu.h @@ -1,5 +1,6 @@ #pragma once #include "common/bitfield.h" +#include "common/rectangle.h" #include "timers.h" #include "types.h" #include @@ -289,8 +290,6 @@ protected: // Rendering in the backend virtual void UpdateDisplay(); - virtual void UpdateDrawingArea(); - virtual void UpdateDrawingOffset(); virtual void ReadVRAM(u32 x, u32 y, u32 width, u32 height, void* buffer); virtual void FillVRAM(u32 x, u32 y, u32 width, u32 height, u32 color); virtual void UpdateVRAM(u32 x, u32 y, u32 width, u32 height, const void* data); @@ -370,6 +369,21 @@ protected: (static_cast(TextureMode::Palette4Bit) | static_cast(TextureMode::Palette8Bit))) != 0; } + /// Returns a rectangle comprising the texture page area. + Common::Rectangle GetTexturePageRectangle() const + { + return Common::Rectangle::FromExtents(texture_page_x, texture_page_y, TEXTURE_PAGE_WIDTH, + TEXTURE_PAGE_HEIGHT); + } + + /// Returns a rectangle comprising the texture palette area. + Common::Rectangle GetTexturePaletteRectangle() const + { + static constexpr std::array palette_widths = {{16, 256, 0, 0}}; + return Common::Rectangle::FromExtents(texture_palette_x, texture_palette_y, + palette_widths[static_cast(texture_mode) & 3], 1); + } + bool IsTexturePageChanged() const { return texture_page_changed; } void ClearTexturePageChangedFlag() { texture_page_changed = false; } @@ -384,11 +398,7 @@ protected: void SetTextureWindow(u32 value); } m_render_state = {}; - struct DrawingArea - { - u32 left, top; - u32 right, bottom; - } m_drawing_area = {}; + Common::Rectangle m_drawing_area; struct DrawingOffset { @@ -396,6 +406,9 @@ protected: s32 y; } m_drawing_offset = {}; + bool m_drawing_area_changed = false; + bool m_drawing_offset_changed = false; + struct CRTCState { struct Regs diff --git a/src/core/gpu_commands.cpp b/src/core/gpu_commands.cpp index 982614986..849b6a305 100644 --- a/src/core/gpu_commands.cpp +++ b/src/core/gpu_commands.cpp @@ -110,11 +110,9 @@ bool GPU::HandleSetDrawingAreaTopLeftCommand(const u32*& command_ptr, u32 comman Log_DebugPrintf("Set drawing area top-left: (%u, %u)", left, top); if (m_drawing_area.left != left || m_drawing_area.top != top) { - FlushRender(); - m_drawing_area.left = left; m_drawing_area.top = top; - UpdateDrawingArea(); + m_drawing_area_changed = true; } return true; @@ -129,11 +127,9 @@ bool GPU::HandleSetDrawingAreaBottomRightCommand(const u32*& command_ptr, u32 co Log_DebugPrintf("Set drawing area bottom-right: (%u, %u)", m_drawing_area.right, m_drawing_area.bottom); if (m_drawing_area.right != right || m_drawing_area.bottom != bottom) { - FlushRender(); - m_drawing_area.right = right; m_drawing_area.bottom = bottom; - UpdateDrawingArea(); + m_drawing_area_changed = true; } return true; @@ -147,11 +143,9 @@ bool GPU::HandleSetDrawingOffsetCommand(const u32*& command_ptr, u32 command_siz Log_DebugPrintf("Set drawing offset (%d, %d)", m_drawing_offset.x, m_drawing_offset.y); if (m_drawing_offset.x != x || m_drawing_offset.y != y) { - FlushRender(); - m_drawing_offset.x = x; m_drawing_offset.y = y; - UpdateDrawingOffset(); + m_drawing_offset_changed = true; } return true; } diff --git a/src/core/gpu_hw.cpp b/src/core/gpu_hw.cpp index e4bb786c7..0a04de0d6 100644 --- a/src/core/gpu_hw.cpp +++ b/src/core/gpu_hw.cpp @@ -41,15 +41,6 @@ void GPU_HW::UpdateSettings() m_true_color = m_system->GetSettings().gpu_true_color; } -void GPU_HW::UpdateDrawingOffset() -{ - GPU::UpdateDrawingOffset(); - - m_batch_ubo_data.u_pos_offset[0] = m_drawing_offset.x; - m_batch_ubo_data.u_pos_offset[1] = m_drawing_offset.y; - m_batch_ubo_dirty = true; -} - void GPU_HW::LoadVertices(RenderCommand rc, u32 num_vertices, const u32* command_ptr) { const u32 texpage = @@ -231,28 +222,14 @@ void GPU_HW::DispatchRenderCommand(RenderCommand rc, u32 num_vertices, const u32 if (m_render_state.IsTexturePageChanged()) { m_render_state.ClearTexturePageChangedFlag(); - - const u32 texture_page_left = m_render_state.texture_page_x; - const u32 texture_page_right = m_render_state.texture_page_y + TEXTURE_PAGE_WIDTH; - const u32 texture_page_top = m_render_state.texture_page_y; - const u32 texture_page_bottom = texture_page_top + TEXTURE_PAGE_HEIGHT; - const bool texture_page_overlaps = - (texture_page_left < m_drawing_area.right && texture_page_right > m_drawing_area.left && - texture_page_top > m_drawing_area.bottom && texture_page_bottom < m_drawing_area.top); - const u32 texture_palette_left = m_render_state.texture_palette_x; - const u32 texture_palette_right = m_render_state.texture_palette_x + 256; - const bool texture_palette_overlaps = - m_render_state.IsUsingPalette() && texture_palette_left < m_drawing_area.right && - texture_palette_right > m_drawing_area.left && m_render_state.texture_palette_y < m_drawing_area.bottom && - m_render_state.texture_palette_y >= m_drawing_area.top; - - // we only need to update the copy texture if the render area intersects with the texture page - if (texture_page_overlaps || texture_palette_overlaps) + if (m_vram_dirty_rect.Valid() && (m_render_state.GetTexturePageRectangle().Intersects(m_vram_dirty_rect) || + m_render_state.GetTexturePaletteRectangle().Intersects(m_vram_dirty_rect))) { - Log_WarningPrintf("Invalidating VRAM read cache due to drawing area overlap"); if (!IsFlushed()) FlushRender(); - InvalidateVRAMReadCache(); + + Log_WarningPrintf("Invalidating VRAM read cache due to drawing area overlap"); + m_vram_read_texture_dirty = true; } } @@ -267,7 +244,8 @@ void GPU_HW::DispatchRenderCommand(RenderCommand rc, u32 num_vertices, const u32 const bool buffer_overflow = GetBatchVertexSpace() < max_added_vertices; if (buffer_overflow || rc_primitive == BatchPrimitive::LineStrip || m_batch.texture_mode != texture_mode || m_batch.transparency_mode != transparency_mode || m_batch.primitive != rc_primitive || - dithering_enable != m_batch.dithering || m_render_state.IsTextureWindowChanged()) + dithering_enable != m_batch.dithering || m_drawing_area_changed || m_drawing_offset_changed || + m_render_state.IsTextureWindowChanged()) { FlushRender(); } @@ -282,6 +260,14 @@ void GPU_HW::DispatchRenderCommand(RenderCommand rc, u32 num_vertices, const u32 m_batch_ubo_dirty = true; } + if (m_drawing_offset_changed) + { + m_drawing_offset_changed = false; + m_batch_ubo_data.u_pos_offset[0] = m_drawing_offset.x; + m_batch_ubo_data.u_pos_offset[1] = m_drawing_offset.y; + m_batch_ubo_dirty = true; + } + // map buffer if it's not already done if (!m_batch_current_vertex_ptr) MapBatchVertexPointer(max_added_vertices); diff --git a/src/core/gpu_hw.h b/src/core/gpu_hw.h index a1e68d6dd..2768868db 100644 --- a/src/core/gpu_hw.h +++ b/src/core/gpu_hw.h @@ -102,12 +102,10 @@ protected: static_cast(rgba >> 24) * (1.0f / 255.0f)); } - virtual void UpdateDrawingOffset() override; - - virtual void InvalidateVRAMReadCache() = 0; - virtual void MapBatchVertexPointer(u32 required_vertices) = 0; + void InvalidateVRAMReadTexture() { m_vram_read_texture_dirty = true; } + u32 GetBatchVertexSpace() const { return static_cast(m_batch_end_vertex_ptr - m_batch_current_vertex_ptr); } u32 GetBatchVertexCount() const { return static_cast(m_batch_current_vertex_ptr - m_batch_start_vertex_ptr); } @@ -135,6 +133,10 @@ protected: BatchUBOData m_batch_ubo_data = {}; bool m_batch_ubo_dirty = true; + // Bounding box of VRAM area that the GPU has drawn into. + Common::Rectangle m_vram_dirty_rect; + bool m_vram_read_texture_dirty = false; + private: static BatchPrimitive GetPrimitiveForCommand(RenderCommand rc); diff --git a/src/core/gpu_hw_d3d11.cpp b/src/core/gpu_hw_d3d11.cpp index b915a3de4..509d25e5d 100644 --- a/src/core/gpu_hw_d3d11.cpp +++ b/src/core/gpu_hw_d3d11.cpp @@ -107,7 +107,7 @@ void GPU_HW_D3D11::RestoreGraphicsAPIState() m_context->OMSetRenderTargets(1, m_vram_texture.GetD3DRTVArray(), nullptr); m_context->RSSetState(m_cull_none_rasterizer_state.Get()); SetViewport(0, 0, m_vram_texture.GetWidth(), m_vram_texture.GetHeight()); - m_drawing_area_changed = true; + SetScissorFromDrawingArea(); m_batch_ubo_dirty = true; } @@ -172,11 +172,6 @@ void GPU_HW_D3D11::DrawRendererStatsWindow() ImGui::End(); } -void GPU_HW_D3D11::InvalidateVRAMReadCache() -{ - m_vram_read_texture_dirty = true; -} - void GPU_HW_D3D11::MapBatchVertexPointer(u32 required_vertices) { Assert(!m_batch_start_vertex_ptr); @@ -536,12 +531,8 @@ void GPU_HW_D3D11::SetDrawState(BatchRenderMode render_mode) if (m_drawing_area_changed) { m_drawing_area_changed = false; - - int left, top, right, bottom; - CalcScissorRect(&left, &top, &right, &bottom); - - CD3D11_RECT rc(left, top, right, bottom); - m_context->RSSetScissorRects(1, &rc); + m_vram_dirty_rect.Include(m_drawing_area); + SetScissorFromDrawingArea(); } if (m_batch_ubo_dirty) @@ -549,11 +540,18 @@ void GPU_HW_D3D11::SetDrawState(BatchRenderMode render_mode) UploadUniformBlock(&m_batch_ubo_data, sizeof(m_batch_ubo_data)); m_batch_ubo_dirty = false; } + + if (m_vram_read_texture_dirty) + UpdateVRAMReadTexture(); } -void GPU_HW_D3D11::UpdateDrawingArea() +void GPU_HW_D3D11::SetScissorFromDrawingArea() { - m_drawing_area_changed = true; + int left, top, right, bottom; + CalcScissorRect(&left, &top, &right, &bottom); + + CD3D11_RECT rc(left, top, right, bottom); + m_context->RSSetScissorRects(1, &rc); } void GPU_HW_D3D11::UpdateDisplay() @@ -659,7 +657,7 @@ void GPU_HW_D3D11::FillVRAM(u32 x, u32 y, u32 width, u32 height, u32 color) DrawUtilityShader(m_fill_pixel_shader.Get(), uniforms, sizeof(uniforms)); RestoreGraphicsAPIState(); - InvalidateVRAMReadCache(); + InvalidateVRAMReadTexture(); } void GPU_HW_D3D11::UpdateVRAM(u32 x, u32 y, u32 width, u32 height, const void* data) @@ -678,7 +676,7 @@ void GPU_HW_D3D11::UpdateVRAM(u32 x, u32 y, u32 width, u32 height, const void* d DrawUtilityShader(m_vram_write_pixel_shader.Get(), uniforms, sizeof(uniforms)); RestoreGraphicsAPIState(); - InvalidateVRAMReadCache(); + InvalidateVRAMReadTexture(); m_stats.num_vram_writes++; } @@ -693,13 +691,14 @@ void GPU_HW_D3D11::CopyVRAM(u32 src_x, u32 src_y, u32 dst_x, u32 dst_y, u32 widt const CD3D11_BOX src_box(src_x, src_y, 0, src_x + width, src_y + height, 1); m_context->CopySubresourceRegion(m_vram_texture, 0, dst_x, dst_y, 0, m_vram_texture, 0, &src_box); - InvalidateVRAMReadCache(); + InvalidateVRAMReadTexture(); } void GPU_HW_D3D11::UpdateVRAMReadTexture() { m_stats.num_vram_read_texture_updates++; m_vram_read_texture_dirty = false; + m_vram_dirty_rect.SetInvalid(); const CD3D11_BOX src_box(0, 0, 0, m_vram_texture.GetWidth(), m_vram_texture.GetHeight(), 1); m_context->CopySubresourceRegion(m_vram_read_texture, 0, 0, 0, 0, m_vram_texture, 0, &src_box); @@ -711,9 +710,6 @@ void GPU_HW_D3D11::FlushRender() if (vertex_count == 0) return; - if (m_vram_read_texture_dirty) - UpdateVRAMReadTexture(); - m_stats.num_batches++; m_stats.num_vertices += vertex_count; diff --git a/src/core/gpu_hw_d3d11.h b/src/core/gpu_hw_d3d11.h index 03a7bb4b0..d13dcee32 100644 --- a/src/core/gpu_hw_d3d11.h +++ b/src/core/gpu_hw_d3d11.h @@ -30,13 +30,11 @@ public: protected: void UpdateDisplay() override; - void UpdateDrawingArea() override; void ReadVRAM(u32 x, u32 y, u32 width, u32 height, void* buffer) override; void FillVRAM(u32 x, u32 y, u32 width, u32 height, u32 color) override; void UpdateVRAM(u32 x, u32 y, u32 width, u32 height, const void* data) override; void CopyVRAM(u32 src_x, u32 src_y, u32 dst_x, u32 dst_y, u32 width, u32 height) override; void FlushRender() override; - void InvalidateVRAMReadCache() override; void MapBatchVertexPointer(u32 required_vertices) override; private: @@ -64,6 +62,7 @@ private: bool CompileShaders(); void SetDrawState(BatchRenderMode render_mode); + void SetScissorFromDrawingArea(); void UploadUniformBlock(const void* data, u32 data_size); void SetViewport(u32 x, u32 y, u32 width, u32 height); void SetScissor(u32 x, u32 y, u32 width, u32 height); @@ -117,7 +116,5 @@ private: GLStats m_stats = {}; GLStats m_last_stats = {}; - bool m_vram_read_texture_dirty = true; - bool m_drawing_area_changed = true; bool m_show_renderer_statistics = false; }; diff --git a/src/core/gpu_hw_opengl.cpp b/src/core/gpu_hw_opengl.cpp index c31288074..49a2edf51 100644 --- a/src/core/gpu_hw_opengl.cpp +++ b/src/core/gpu_hw_opengl.cpp @@ -73,9 +73,10 @@ void GPU_HW_OpenGL::RestoreGraphicsAPIState() glEnable(GL_SCISSOR_TEST); glDepthMask(GL_FALSE); glLineWidth(static_cast(m_resolution_scale)); - UpdateDrawingArea(); - glBindVertexArray(m_vao_id); + + SetScissorFromDrawingArea(); + m_batch_ubo_dirty = true; } void GPU_HW_OpenGL::UpdateSettings() @@ -139,11 +140,6 @@ void GPU_HW_OpenGL::DrawRendererStatsWindow() ImGui::End(); } -void GPU_HW_OpenGL::InvalidateVRAMReadCache() -{ - m_vram_read_texture_dirty = true; -} - void GPU_HW_OpenGL::MapBatchVertexPointer(u32 required_vertices) { Assert(!m_batch_start_vertex_ptr); @@ -397,17 +393,8 @@ void GPU_HW_OpenGL::SetDrawState(BatchRenderMode render_mode) if (m_drawing_area_changed) { m_drawing_area_changed = false; - - int left, top, right, bottom; - CalcScissorRect(&left, &top, &right, &bottom); - - const int width = right - left; - const int height = bottom - top; - const int x = left; - const int y = m_vram_texture->GetHeight() - bottom; - - Log_DebugPrintf("SetScissor: (%d-%d, %d-%d)", x, x + width, y, y + height); - glScissor(x, y, width, height); + m_vram_dirty_rect.Include(m_drawing_area); + SetScissorFromDrawingArea(); } if (m_batch_ubo_dirty) @@ -415,6 +402,23 @@ void GPU_HW_OpenGL::SetDrawState(BatchRenderMode render_mode) UploadUniformBlock(&m_batch_ubo_data, sizeof(m_batch_ubo_data)); m_batch_ubo_dirty = false; } + + if (m_vram_read_texture_dirty) + UpdateVRAMReadTexture(); +} + +void GPU_HW_OpenGL::SetScissorFromDrawingArea() +{ + int left, top, right, bottom; + CalcScissorRect(&left, &top, &right, &bottom); + + const int width = right - left; + const int height = bottom - top; + const int x = left; + const int y = m_vram_texture->GetHeight() - bottom; + + Log_DebugPrintf("SetScissor: (%d-%d, %d-%d)", x, x + width, y, y + height); + glScissor(x, y, width, height); } void GPU_HW_OpenGL::UploadUniformBlock(const void* data, u32 data_size) @@ -428,11 +432,6 @@ void GPU_HW_OpenGL::UploadUniformBlock(const void* data, u32 data_size) m_stats.num_uniform_buffer_updates++; } -void GPU_HW_OpenGL::UpdateDrawingArea() -{ - m_drawing_area_changed = true; -} - void GPU_HW_OpenGL::UpdateDisplay() { GPU_HW::UpdateDisplay(); @@ -616,8 +615,8 @@ void GPU_HW_OpenGL::FillVRAM(u32 x, u32 y, u32 width, u32 height, u32 color) glClearColor(r, g, b, a); glClear(GL_COLOR_BUFFER_BIT); - UpdateDrawingArea(); - InvalidateVRAMReadCache(); + SetScissorFromDrawingArea(); + InvalidateVRAMReadTexture(); } void GPU_HW_OpenGL::UpdateVRAM(u32 x, u32 y, u32 width, u32 height, const void* data) @@ -700,10 +699,10 @@ void GPU_HW_OpenGL::UpdateVRAM(u32 x, u32 y, u32 width, u32 height, const void* glDrawArrays(GL_TRIANGLES, 0, 3); - UpdateDrawingArea(); + SetScissorFromDrawingArea(); #endif - InvalidateVRAMReadCache(); + InvalidateVRAMReadTexture(); m_stats.num_vram_writes++; } @@ -726,13 +725,14 @@ void GPU_HW_OpenGL::CopyVRAM(u32 src_x, u32 src_y, u32 dst_x, u32 dst_y, u32 wid GL_COLOR_BUFFER_BIT, GL_NEAREST); glEnable(GL_SCISSOR_TEST); - InvalidateVRAMReadCache(); + InvalidateVRAMReadTexture(); } void GPU_HW_OpenGL::UpdateVRAMReadTexture() { m_stats.num_vram_read_texture_updates++; m_vram_read_texture_dirty = false; + m_vram_dirty_rect.SetInvalid(); // TODO: Fallback blit path, and partial updates. glCopyImageSubData(m_vram_texture->GetGLId(), GL_TEXTURE_2D, 0, 0, 0, 0, m_vram_read_texture->GetGLId(), @@ -745,9 +745,6 @@ void GPU_HW_OpenGL::FlushRender() if (vertex_count == 0) return; - if (m_vram_read_texture_dirty) - UpdateVRAMReadTexture(); - m_stats.num_batches++; m_stats.num_vertices += vertex_count; diff --git a/src/core/gpu_hw_opengl.h b/src/core/gpu_hw_opengl.h index d0856cfcd..2297f2b93 100644 --- a/src/core/gpu_hw_opengl.h +++ b/src/core/gpu_hw_opengl.h @@ -26,13 +26,11 @@ public: protected: void UpdateDisplay() override; - void UpdateDrawingArea() override; void ReadVRAM(u32 x, u32 y, u32 width, u32 height, void* buffer) override; void FillVRAM(u32 x, u32 y, u32 width, u32 height, u32 color) override; void UpdateVRAM(u32 x, u32 y, u32 width, u32 height, const void* data) override; void CopyVRAM(u32 src_x, u32 src_y, u32 dst_x, u32 dst_y, u32 width, u32 height) override; void FlushRender() override; - void InvalidateVRAMReadCache() override; void MapBatchVertexPointer(u32 required_vertices) override; private: @@ -60,6 +58,7 @@ private: bool CompilePrograms(); void SetDrawState(BatchRenderMode render_mode); + void SetScissorFromDrawingArea(); void UploadUniformBlock(const void* data, u32 data_size); // downsample texture - used for readbacks at >1xIR. @@ -86,7 +85,5 @@ private: GLStats m_stats = {}; GLStats m_last_stats = {}; - bool m_vram_read_texture_dirty = true; - bool m_drawing_area_changed = true; bool m_show_renderer_statistics = false; };