GPU/HW: Add an option to use software renderer for readbacks

This commit is contained in:
Connor McLaughlin
2021-05-19 13:43:49 +10:00
parent 063eeeb214
commit 5d877250e9
19 changed files with 303 additions and 28 deletions

View File

@ -3,6 +3,7 @@
#include "common/log.h"
#include "common/state_wrapper.h"
#include "cpu_core.h"
#include "gpu_sw_backend.h"
#include "pgxp.h"
#include "settings.h"
#include "system.h"
@ -34,7 +35,14 @@ GPU_HW::GPU_HW() : GPU()
m_vram_ptr = m_vram_shadow.data();
}
GPU_HW::~GPU_HW() = default;
GPU_HW::~GPU_HW()
{
if (m_sw_renderer)
{
m_sw_renderer->Shutdown();
m_sw_renderer.reset();
}
}
bool GPU_HW::Initialize(HostDisplay* host_display)
{
@ -82,6 +90,9 @@ bool GPU_HW::Initialize(HostDisplay* host_display)
}
m_pgxp_depth_buffer = g_settings.UsingPGXPDepthBuffer();
UpdateSoftwareRenderer(false);
PrintSettingsToLog();
return true;
}
@ -93,6 +104,8 @@ void GPU_HW::Reset(bool clear_vram)
m_batch_current_vertex_ptr = m_batch_start_vertex_ptr;
m_vram_shadow.fill(0);
if (m_sw_renderer)
m_sw_renderer->Reset(clear_vram);
m_batch = {};
m_batch_ubo_data = {};
@ -180,6 +193,8 @@ void GPU_HW::UpdateHWSettings(bool* framebuffer_changed, bool* shaders_changed)
ClearDepthBuffer();
}
UpdateSoftwareRenderer(true);
PrintSettingsToLog();
}
@ -248,6 +263,7 @@ void GPU_HW::PrintSettingsToLog()
Log_InfoPrintf("Using UV limits: %s", m_using_uv_limits ? "YES" : "NO");
Log_InfoPrintf("Depth buffer: %s", m_pgxp_depth_buffer ? "YES" : "NO");
Log_InfoPrintf("Downsampling: %s", Settings::GetDownsampleModeDisplayName(m_downsample_mode));
Log_InfoPrintf("Using software renderer for readbacks: %s", m_sw_renderer ? "YES" : "NO");
}
void GPU_HW::UpdateVRAMReadTexture()
@ -545,6 +561,7 @@ void GPU_HW::LoadVertices()
const u32 num_vertices = rc.quad_polygon ? 4 : 3;
std::array<BatchVertex, 4> vertices;
std::array<std::array<s32, 2>, 4> native_vertex_positions;
std::array<u16, 4> native_texcoords;
bool valid_w = g_settings.gpu_pgxp_texture_correction;
for (u32 i = 0; i < num_vertices; i++)
{
@ -556,6 +573,7 @@ void GPU_HW::LoadVertices()
const s32 native_y = m_drawing_offset.y + vp.y;
native_vertex_positions[i][0] = native_x;
native_vertex_positions[i][1] = native_y;
native_texcoords[i] = texcoord;
vertices[i].Set(static_cast<float>(native_x), static_cast<float>(native_y), depth, 1.0f, color, texpage,
texcoord, 0xFFFF0000u);
@ -659,6 +677,23 @@ void GPU_HW::LoadVertices()
AddVertex(vertices[3]);
}
}
if (m_sw_renderer)
{
GPUBackendDrawPolygonCommand* cmd = m_sw_renderer->NewDrawPolygonCommand(num_vertices);
FillDrawCommand(cmd, rc);
for (u32 i = 0; i < num_vertices; i++)
{
GPUBackendDrawPolygonCommand::Vertex* vert = &cmd->vertices[i];
vert->x = native_vertex_positions[i][0];
vert->y = native_vertex_positions[i][1];
vert->texcoord = native_texcoords[i];
vert->color = vertices[i].color;
}
m_sw_renderer->PushCommand(cmd);
}
}
break;
@ -754,6 +789,19 @@ void GPU_HW::LoadVertices()
m_vram_dirty_rect.Include(clip_left, clip_right, clip_top, clip_bottom);
AddDrawRectangleTicks(clip_right - clip_left, clip_bottom - clip_top, rc.texture_enable, rc.transparency_enable);
if (m_sw_renderer)
{
GPUBackendDrawRectangleCommand* cmd = m_sw_renderer->NewDrawRectangleCommand();
FillDrawCommand(cmd, rc);
cmd->color = color;
cmd->x = pos_x;
cmd->y = pos_y;
cmd->width = static_cast<u16>(rectangle_width);
cmd->height = static_cast<u16>(rectangle_height);
cmd->texcoord = (static_cast<u16>(texcoord_y) << 8) | static_cast<u16>(texcoord_x);
m_sw_renderer->PushCommand(cmd);
}
}
break;
@ -808,6 +856,15 @@ void GPU_HW::LoadVertices()
// TODO: Should we do a PGXP lookup here? Most lines are 2D.
DrawLine(static_cast<float>(start_x), static_cast<float>(start_y), start_color, static_cast<float>(end_x),
static_cast<float>(end_y), end_color, depth);
if (m_sw_renderer)
{
GPUBackendDrawLineCommand* cmd = m_sw_renderer->NewDrawLineCommand(2);
FillDrawCommand(cmd, rc);
cmd->vertices[0].Set(start_x, start_y, start_color);
cmd->vertices[1].Set(end_x, end_y, end_color);
m_sw_renderer->PushCommand(cmd);
}
}
else
{
@ -826,6 +883,18 @@ void GPU_HW::LoadVertices()
s32 start_y = start_vp.y + m_drawing_offset.y;
u32 start_color = rc.color_for_first_vertex;
GPUBackendDrawLineCommand* cmd;
if (m_sw_renderer)
{
cmd = m_sw_renderer->NewDrawLineCommand(num_vertices);
FillDrawCommand(cmd, rc);
cmd->vertices[0].Set(start_x, start_y, start_color);
}
else
{
cmd = nullptr;
}
for (u32 i = 1; i < num_vertices; i++)
{
const u32 end_color = shaded ? (m_blit_buffer[buffer_pos++] & UINT32_C(0x00FFFFFF)) : start_color;
@ -859,7 +928,13 @@ void GPU_HW::LoadVertices()
start_x = end_x;
start_y = end_y;
start_color = end_color;
if (cmd)
cmd->vertices[i].Set(end_x, end_y, end_color);
}
if (cmd)
m_sw_renderer->PushCommand(cmd);
}
}
break;
@ -1017,10 +1092,84 @@ void GPU_HW::ResetBatchVertexDepth()
m_current_depth = 1;
}
void GPU_HW::FillBackendCommandParameters(GPUBackendCommand* cmd) const
{
cmd->params.bits = 0;
cmd->params.check_mask_before_draw = m_GPUSTAT.check_mask_before_draw;
cmd->params.set_mask_while_drawing = m_GPUSTAT.set_mask_while_drawing;
cmd->params.active_line_lsb = m_crtc_state.active_line_lsb;
cmd->params.interlaced_rendering = m_GPUSTAT.SkipDrawingToActiveField();
}
void GPU_HW::FillDrawCommand(GPUBackendDrawCommand* cmd, GPURenderCommand rc) const
{
FillBackendCommandParameters(cmd);
cmd->rc.bits = rc.bits;
cmd->draw_mode.bits = m_draw_mode.mode_reg.bits;
cmd->palette.bits = m_draw_mode.palette_reg;
cmd->window = m_draw_mode.texture_window;
}
void GPU_HW::HandleVRAMReadWithSoftwareRenderer(u32 x, u32 y, u32 width, u32 height)
{
DebugAssert(m_sw_renderer);
m_sw_renderer->Sync(false);
}
void GPU_HW::UpdateSoftwareRenderer(bool copy_vram_from_hw)
{
const bool current_enabled = (m_sw_renderer != nullptr);
const bool new_enabled = g_settings.gpu_use_software_renderer_for_readbacks;
if (current_enabled == new_enabled)
return;
m_vram_ptr = m_vram_shadow.data();
if (!new_enabled)
{
if (m_sw_renderer)
m_sw_renderer->Shutdown();
m_sw_renderer.reset();
return;
}
std::unique_ptr<GPU_SW_Backend> sw_renderer = std::make_unique<GPU_SW_Backend>();
if (!sw_renderer->Initialize(true))
return;
// We need to fill in the SW renderer's VRAM with the current state for hot toggles.
if (copy_vram_from_hw)
{
FlushRender();
ReadVRAM(0, 0, VRAM_WIDTH, VRAM_HEIGHT);
std::memcpy(sw_renderer->GetVRAM(), m_vram_ptr, sizeof(u16) * VRAM_WIDTH * VRAM_HEIGHT);
// Sync the drawing area.
GPUBackendSetDrawingAreaCommand* cmd = sw_renderer->NewSetDrawingAreaCommand();
cmd->new_area = m_drawing_area;
sw_renderer->PushCommand(cmd);
}
m_sw_renderer = std::move(sw_renderer);
m_vram_ptr = m_sw_renderer->GetVRAM();
}
void GPU_HW::FillVRAM(u32 x, u32 y, u32 width, u32 height, u32 color)
{
IncludeVRAMDirtyRectangle(
Common::Rectangle<u32>::FromExtents(x, y, width, height).Clamped(0, 0, VRAM_WIDTH, VRAM_HEIGHT));
if (m_sw_renderer)
{
GPUBackendFillVRAMCommand* cmd = m_sw_renderer->NewFillVRAMCommand();
FillBackendCommandParameters(cmd);
cmd->x = static_cast<u16>(x);
cmd->y = static_cast<u16>(y);
cmd->width = static_cast<u16>(width);
cmd->height = static_cast<u16>(height);
cmd->color = color;
m_sw_renderer->PushCommand(cmd);
}
}
void GPU_HW::UpdateVRAM(u32 x, u32 y, u32 width, u32 height, const void* data, bool set_mask, bool check_mask)
@ -1033,6 +1182,21 @@ void GPU_HW::UpdateVRAM(u32 x, u32 y, u32 width, u32 height, const void* data, b
// set new vertex counter since we want this to take into consideration previous masked pixels
m_current_depth++;
}
if (m_sw_renderer)
{
const u32 num_words = width * height;
GPUBackendUpdateVRAMCommand* cmd = m_sw_renderer->NewUpdateVRAMCommand(num_words);
FillBackendCommandParameters(cmd);
cmd->params.set_mask_while_drawing = set_mask;
cmd->params.check_mask_before_draw = check_mask;
cmd->x = static_cast<u16>(x);
cmd->y = static_cast<u16>(y);
cmd->width = static_cast<u16>(width);
cmd->height = static_cast<u16>(height);
std::memcpy(cmd->data, data, sizeof(u16) * num_words);
m_sw_renderer->PushCommand(cmd);
}
}
void GPU_HW::CopyVRAM(u32 src_x, u32 src_y, u32 dst_x, u32 dst_y, u32 width, u32 height)
@ -1045,6 +1209,19 @@ void GPU_HW::CopyVRAM(u32 src_x, u32 src_y, u32 dst_x, u32 dst_y, u32 width, u32
// set new vertex counter since we want this to take into consideration previous masked pixels
m_current_depth++;
}
if (m_sw_renderer)
{
GPUBackendCopyVRAMCommand* cmd = m_sw_renderer->NewCopyVRAMCommand();
FillBackendCommandParameters(cmd);
cmd->src_x = static_cast<u16>(src_x);
cmd->src_y = static_cast<u16>(src_y);
cmd->dst_x = static_cast<u16>(dst_x);
cmd->dst_y = static_cast<u16>(dst_y);
cmd->width = static_cast<u16>(width);
cmd->height = static_cast<u16>(height);
m_sw_renderer->PushCommand(cmd);
}
}
void GPU_HW::DispatchRenderCommand()
@ -1136,6 +1313,22 @@ void GPU_HW::DispatchRenderCommand()
m_batch_ubo_dirty = true;
}
if (m_drawing_area_changed)
{
m_drawing_area_changed = false;
SetScissorFromDrawingArea();
if (m_pgxp_depth_buffer && m_last_depth_z < 1.0f)
ClearDepthBuffer();
if (m_sw_renderer)
{
GPUBackendSetDrawingAreaCommand* cmd = m_sw_renderer->NewSetDrawingAreaCommand();
cmd->new_area = m_drawing_area;
m_sw_renderer->PushCommand(cmd);
}
}
LoadVertices();
}
@ -1150,15 +1343,6 @@ void GPU_HW::FlushRender()
if (vertex_count == 0)
return;
if (m_drawing_area_changed)
{
m_drawing_area_changed = false;
SetScissorFromDrawingArea();
if (m_pgxp_depth_buffer && m_last_depth_z < 1.0f)
ClearDepthBuffer();
}
if (m_batch_ubo_dirty)
{
UploadUniformBuffer(&m_batch_ubo_data, sizeof(m_batch_ubo_data));