GPU/HW: Track dirty area of VRAM shadow texture via drawing area

This commit is contained in:
Connor McLaughlin 2019-11-05 19:19:49 +10:00
parent 65197d4350
commit afbec85f89
9 changed files with 95 additions and 117 deletions

View file

@ -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) {}

View file

@ -1,5 +1,6 @@
#pragma once
#include "common/bitfield.h"
#include "common/rectangle.h"
#include "timers.h"
#include "types.h"
#include <array>
@ -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<u8>(TextureMode::Palette4Bit) | static_cast<u8>(TextureMode::Palette8Bit))) != 0;
}
/// Returns a rectangle comprising the texture page area.
Common::Rectangle<u32> GetTexturePageRectangle() const
{
return Common::Rectangle<u32>::FromExtents(texture_page_x, texture_page_y, TEXTURE_PAGE_WIDTH,
TEXTURE_PAGE_HEIGHT);
}
/// Returns a rectangle comprising the texture palette area.
Common::Rectangle<u32> GetTexturePaletteRectangle() const
{
static constexpr std::array<u32, 4> palette_widths = {{16, 256, 0, 0}};
return Common::Rectangle<u32>::FromExtents(texture_palette_x, texture_palette_y,
palette_widths[static_cast<u8>(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<u32> 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

View file

@ -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;
}

View file

@ -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);

View file

@ -102,12 +102,10 @@ protected:
static_cast<float>(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<u32>(m_batch_end_vertex_ptr - m_batch_current_vertex_ptr); }
u32 GetBatchVertexCount() const { return static_cast<u32>(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<u32> m_vram_dirty_rect;
bool m_vram_read_texture_dirty = false;
private:
static BatchPrimitive GetPrimitiveForCommand(RenderCommand rc);

View file

@ -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;

View file

@ -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;
};

View file

@ -73,9 +73,10 @@ void GPU_HW_OpenGL::RestoreGraphicsAPIState()
glEnable(GL_SCISSOR_TEST);
glDepthMask(GL_FALSE);
glLineWidth(static_cast<float>(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,7 +393,22 @@ void GPU_HW_OpenGL::SetDrawState(BatchRenderMode render_mode)
if (m_drawing_area_changed)
{
m_drawing_area_changed = false;
m_vram_dirty_rect.Include(m_drawing_area);
SetScissorFromDrawingArea();
}
if (m_batch_ubo_dirty)
{
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);
@ -410,13 +421,6 @@ void GPU_HW_OpenGL::SetDrawState(BatchRenderMode render_mode)
glScissor(x, y, width, height);
}
if (m_batch_ubo_dirty)
{
UploadUniformBlock(&m_batch_ubo_data, sizeof(m_batch_ubo_data));
m_batch_ubo_dirty = false;
}
}
void GPU_HW_OpenGL::UploadUniformBlock(const void* data, u32 data_size)
{
const GL::StreamBuffer::MappingResult res = m_uniform_stream_buffer->Map(m_uniform_buffer_alignment, 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;

View file

@ -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;
};