Skip to content

Commit

Permalink
GPU: Add wireframe rendering/overlay
Browse files Browse the repository at this point in the history
  • Loading branch information
stenzek committed Sep 2, 2023
1 parent e804b5e commit 7ad1b8d
Show file tree
Hide file tree
Showing 11 changed files with 309 additions and 89 deletions.
9 changes: 7 additions & 2 deletions src/core/fullscreen_ui.cpp
Expand Up @@ -2872,8 +2872,8 @@ void FullscreenUI::DrawBIOSSettingsPage()
FSUI_CSTR("Patches the BIOS to skip the boot animation. Safe to enable."), "BIOS", "PatchFastBoot",
Settings::DEFAULT_FAST_BOOT_VALUE);
DrawToggleSetting(bsi, FSUI_CSTR("Enable TTY Logging"),
FSUI_CSTR("Logs BIOS calls to printf(). Not all games contain debugging messages."),
"BIOS", "TTYLogging", false);
FSUI_CSTR("Logs BIOS calls to printf(). Not all games contain debugging messages."), "BIOS",
"TTYLogging", false);

EndMenuButtons();
}
Expand Down Expand Up @@ -4695,6 +4695,11 @@ void FullscreenUI::DrawAdvancedSettingsPage()
bsi, FSUI_CSTR("Stretch Display Vertically"),
FSUI_CSTR("Stretches the display to match the aspect ratio by multiplying vertically instead of horizontally."),
"Display", "StretchVertically", false);
DrawEnumSetting(bsi, FSUI_CSTR("Wireframe Rendering"),
FSUI_CSTR("Overlays or replaces normal triangle drawing with a wireframe/line view."), "GPU",
"WireframeMode", GPUWireframeMode::Disabled, &Settings::ParseGPUWireframeMode,
&Settings::GetGPUWireframeModeName, &Settings::GetGPUWireframeModeDisplayName,
GPUWireframeMode::Count);

MenuHeading(FSUI_CSTR("PGXP Settings"));

Expand Down
115 changes: 91 additions & 24 deletions src/core/gpu_hw.cpp
Expand Up @@ -45,6 +45,11 @@ ALWAYS_INLINE static constexpr std::tuple<T, T> MinMax(T v1, T v2)
return std::tie(v1, v2);
}

ALWAYS_INLINE static u32 GetMaxResolutionScale()
{
return g_gpu_device->GetMaxTextureSize() / VRAM_WIDTH;
}

ALWAYS_INLINE static bool ShouldUseUVLimits()
{
// We only need UV limits if PGXP is enabled, or texture filtering is enabled.
Expand All @@ -57,7 +62,7 @@ ALWAYS_INLINE static bool ShouldDisableColorPerspective()
}

/// Returns true if the specified texture filtering mode requires dual-source blending.
static bool TextureFilterRequiresDualSourceBlend(GPUTextureFilter filter)
ALWAYS_INLINE static bool TextureFilterRequiresDualSourceBlend(GPUTextureFilter filter)
{
return (filter == GPUTextureFilter::Bilinear || filter == GPUTextureFilter::JINC2 || filter == GPUTextureFilter::xBR);
}
Expand Down Expand Up @@ -148,42 +153,50 @@ bool GPU_HW::Initialize()
return false;

const GPUDevice::Features features = g_gpu_device->GetFeatures();
m_max_resolution_scale = g_gpu_device->GetMaxTextureSize() / VRAM_WIDTH;
m_supports_dual_source_blend = features.dual_source_blend;
m_supports_per_sample_shading = features.per_sample_shading;
m_supports_disable_color_perspective = features.noperspective_interpolation;

m_resolution_scale = CalculateResolutionScale();
m_multisamples = std::min(g_settings.gpu_multisamples, g_gpu_device->GetMaxMultisamples());
m_per_sample_shading = g_settings.gpu_per_sample_shading && m_supports_per_sample_shading;
m_per_sample_shading = g_settings.gpu_per_sample_shading && features.per_sample_shading;
m_true_color = g_settings.gpu_true_color;
m_scaled_dithering = g_settings.gpu_scaled_dithering;
m_texture_filtering = g_settings.gpu_texture_filter;
m_using_uv_limits = ShouldUseUVLimits();
m_chroma_smoothing = g_settings.gpu_24bit_chroma_smoothing;
m_downsample_mode = GetDownsampleMode(m_resolution_scale);
m_disable_color_perspective = m_supports_disable_color_perspective && ShouldDisableColorPerspective();
m_wireframe_mode = g_settings.gpu_wireframe_mode;
m_disable_color_perspective = features.noperspective_interpolation && ShouldDisableColorPerspective();

if (m_multisamples != g_settings.gpu_multisamples)
{
Host::AddFormattedOSDMessage(20.0f, TRANSLATE("OSDMessage", "%ux MSAA is not supported, using %ux instead."),
Host::AddFormattedOSDMessage(Host::OSD_CRITICAL_ERROR_DURATION,
TRANSLATE("OSDMessage", "%ux MSAA is not supported, using %ux instead."),
g_settings.gpu_multisamples, m_multisamples);
}
if (!m_per_sample_shading && g_settings.gpu_per_sample_shading)
{
Host::AddOSDMessage(TRANSLATE_STR("OSDMessage", "SSAA is not supported, using MSAA instead."), 20.0f);
}
if (!m_supports_dual_source_blend && TextureFilterRequiresDualSourceBlend(m_texture_filtering))
if (!features.dual_source_blend && TextureFilterRequiresDualSourceBlend(m_texture_filtering))
{
Host::AddFormattedOSDMessage(
20.0f, TRANSLATE("OSDMessage", "Texture filter '%s' is not supported with the current renderer."),
Host::OSD_CRITICAL_ERROR_DURATION,
TRANSLATE("OSDMessage", "Texture filter '%s' is not supported with the current renderer."),
Settings::GetTextureFilterDisplayName(m_texture_filtering));
m_texture_filtering = GPUTextureFilter::Nearest;
}

if (!m_supports_disable_color_perspective && !ShouldDisableColorPerspective())
if (!features.noperspective_interpolation && !ShouldDisableColorPerspective())
Log_WarningPrint("Disable color perspective not supported, but should be used.");

if (!features.geometry_shaders && m_wireframe_mode != GPUWireframeMode::Disabled)
{
Host::AddOSDMessage(
TRANSLATE("OSDMessage",
"Geometry shaders are not supported by your GPU, and are required for wireframe rendering."),
Host::OSD_CRITICAL_ERROR_DURATION);
m_wireframe_mode = GPUWireframeMode::Disabled;
}

m_pgxp_depth_buffer = g_settings.UsingPGXPDepthBuffer();

UpdateSoftwareRenderer(false);
Expand Down Expand Up @@ -290,12 +303,16 @@ void GPU_HW::UpdateSettings(const Settings& old_settings)
{
GPU::UpdateSettings(old_settings);

const GPUDevice::Features features = g_gpu_device->GetFeatures();

const u32 resolution_scale = CalculateResolutionScale();
const u32 multisamples = std::min(g_settings.gpu_multisamples, g_gpu_device->GetMaxMultisamples());
const bool per_sample_shading = g_settings.gpu_per_sample_shading && m_supports_per_sample_shading;
const bool per_sample_shading = g_settings.gpu_per_sample_shading && features.noperspective_interpolation;
const GPUDownsampleMode downsample_mode = GetDownsampleMode(resolution_scale);
const GPUWireframeMode wireframe_mode =
features.geometry_shaders ? g_settings.gpu_wireframe_mode : GPUWireframeMode::Disabled;
const bool use_uv_limits = ShouldUseUVLimits();
const bool disable_color_perspective = m_supports_disable_color_perspective && ShouldDisableColorPerspective();
const bool disable_color_perspective = features.noperspective_interpolation && ShouldDisableColorPerspective();

// TODO: Use old_settings
const bool framebuffer_changed =
Expand All @@ -305,7 +322,8 @@ void GPU_HW::UpdateSettings(const Settings& old_settings)
m_true_color != g_settings.gpu_true_color || m_per_sample_shading != per_sample_shading ||
m_scaled_dithering != g_settings.gpu_scaled_dithering || m_texture_filtering != g_settings.gpu_texture_filter ||
m_using_uv_limits != use_uv_limits || m_chroma_smoothing != g_settings.gpu_24bit_chroma_smoothing ||
m_downsample_mode != downsample_mode || m_pgxp_depth_buffer != g_settings.UsingPGXPDepthBuffer() ||
m_downsample_mode != downsample_mode || m_wireframe_mode != wireframe_mode ||
m_pgxp_depth_buffer != g_settings.UsingPGXPDepthBuffer() ||
m_disable_color_perspective != disable_color_perspective);

if (m_resolution_scale != resolution_scale)
Expand Down Expand Up @@ -348,6 +366,7 @@ void GPU_HW::UpdateSettings(const Settings& old_settings)
m_using_uv_limits = use_uv_limits;
m_chroma_smoothing = g_settings.gpu_24bit_chroma_smoothing;
m_downsample_mode = downsample_mode;
m_wireframe_mode = wireframe_mode;
m_disable_color_perspective = disable_color_perspective;

if (!m_supports_dual_source_blend && TextureFilterRequiresDualSourceBlend(m_texture_filtering))
Expand Down Expand Up @@ -387,10 +406,12 @@ void GPU_HW::UpdateSettings(const Settings& old_settings)

u32 GPU_HW::CalculateResolutionScale() const
{
const u32 max_resolution_scale = GetMaxResolutionScale();

u32 scale;
if (g_settings.gpu_resolution_scale != 0)
{
scale = std::clamp<u32>(g_settings.gpu_resolution_scale, 1, m_max_resolution_scale);
scale = std::clamp<u32>(g_settings.gpu_resolution_scale, 1, max_resolution_scale);
}
else
{
Expand All @@ -404,7 +425,7 @@ u32 GPU_HW::CalculateResolutionScale() const
static_cast<s32>(std::ceil(static_cast<float>(g_gpu_device->GetWindowHeight()) / height));
Log_InfoPrintf("Height = %d, preferred scale = %d", height, preferred_scale);

scale = static_cast<u32>(std::clamp<s32>(preferred_scale, 1, m_max_resolution_scale));
scale = static_cast<u32>(std::clamp<s32>(preferred_scale, 1, max_resolution_scale));
}

if (g_settings.gpu_downsample_mode == GPUDownsampleMode::Adaptive && scale > 1 && !Common::IsPow2(scale))
Expand Down Expand Up @@ -474,7 +495,7 @@ std::tuple<u32, u32> GPU_HW::GetFullDisplayResolution(bool scaled /* = true */)
void GPU_HW::PrintSettingsToLog()
{
Log_InfoPrintf("Resolution Scale: %u (%ux%u), maximum %u", m_resolution_scale, VRAM_WIDTH * m_resolution_scale,
VRAM_HEIGHT * m_resolution_scale, m_max_resolution_scale);
VRAM_HEIGHT * m_resolution_scale, GetMaxResolutionScale());
Log_InfoPrintf("Multisampling: %ux%s", m_multisamples, m_per_sample_shading ? " (per sample shading)" : "");
Log_InfoPrintf("Dithering: %s%s", m_true_color ? "Disabled" : "Enabled",
(!m_true_color && m_scaled_dithering) ? " (Scaled)" : "");
Expand All @@ -483,6 +504,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("Wireframe rendering: %s", Settings::GetGPUWireframeModeDisplayName(m_wireframe_mode));
Log_InfoPrintf("Using software renderer for readbacks: %s", m_sw_renderer ? "YES" : "NO");
}

Expand Down Expand Up @@ -772,6 +794,39 @@ bool GPU_HW::CompilePipelines()
}
}

if (m_wireframe_mode != GPUWireframeMode::Disabled)
{
std::unique_ptr<GPUShader> gs =
g_gpu_device->CreateShader(GPUShaderStage::Geometry, shadergen.GenerateWireframeGeometryShader());
std::unique_ptr<GPUShader> fs =
g_gpu_device->CreateShader(GPUShaderStage::Fragment, shadergen.GenerateWireframeFragmentShader());
if (!gs || !fs)
return false;

GL_OBJECT_NAME(gs, "Batch Wireframe Geometry Shader");
GL_OBJECT_NAME(fs, "Batch Wireframe Fragment Shader");

plconfig.input_layout.vertex_attributes =
gsl::span<const GPUPipeline::VertexAttribute>(vertex_attributes, NUM_BATCH_VERTEX_ATTRIBUTES);
plconfig.blend = (m_wireframe_mode == GPUWireframeMode::OverlayWireframe) ?
GPUPipeline::BlendState::GetAlphaBlendingState() :
GPUPipeline::BlendState::GetNoBlendingState();
plconfig.blend.write_mask = 0x7;
plconfig.depth = GPUPipeline::DepthState::GetNoTestsState();
plconfig.vertex_shader = batch_vertex_shaders[0].get();
plconfig.geometry_shader = gs.get();
plconfig.fragment_shader = fs.get();

if (!(m_wireframe_pipeline = g_gpu_device->CreatePipeline(plconfig)))
return false;

GL_OBJECT_NAME(m_wireframe_pipeline, "Batch Wireframe Pipeline");

plconfig.vertex_shader = nullptr;
plconfig.geometry_shader = nullptr;
plconfig.fragment_shader = nullptr;
}

batch_shader_guard.Run();

std::unique_ptr<GPUShader> fullscreen_quad_vertex_shader =
Expand Down Expand Up @@ -1026,6 +1081,8 @@ void GPU_HW::DestroyPipelines()
{
static constexpr auto destroy = [](std::unique_ptr<GPUPipeline>& p) { p.reset(); };

m_wireframe_pipeline.reset();

m_batch_pipelines.enumerate(destroy);

m_vram_fill_pipelines.enumerate(destroy);
Expand Down Expand Up @@ -1136,7 +1193,7 @@ void GPU_HW::UnmapBatchVertexPointer(u32 used_vertices)
m_batch_current_vertex_ptr = nullptr;
}

void GPU_HW::DrawBatchVertices(BatchRenderMode render_mode, u32 base_vertex, u32 num_vertices)
void GPU_HW::DrawBatchVertices(BatchRenderMode render_mode, u32 num_vertices, u32 base_vertex)
{
// [depth_test][render_mode][texture_mode][transparency_mode][dithering][interlacing]
const u8 depth_test = m_batch.use_depth_buffer ? static_cast<u8>(2) : BoolToUInt8(m_batch.check_mask_before_draw);
Expand Down Expand Up @@ -2365,16 +2422,26 @@ void GPU_HW::FlushRender()
m_batch_ubo_dirty = false;
}

if (NeedsTwoPassRendering())
if (m_wireframe_mode != GPUWireframeMode::OnlyWireframe)
{
m_renderer_stats.num_batches += 2;
DrawBatchVertices(BatchRenderMode::OnlyOpaque, m_batch_base_vertex, vertex_count);
DrawBatchVertices(BatchRenderMode::OnlyTransparent, m_batch_base_vertex, vertex_count);
if (NeedsTwoPassRendering())
{
m_renderer_stats.num_batches += 2;
DrawBatchVertices(BatchRenderMode::OnlyOpaque, vertex_count, m_batch_base_vertex);
DrawBatchVertices(BatchRenderMode::OnlyTransparent, vertex_count, m_batch_base_vertex);
}
else
{
m_renderer_stats.num_batches++;
DrawBatchVertices(m_batch.GetRenderMode(), vertex_count, m_batch_base_vertex);
}
}
else

if (m_wireframe_mode != GPUWireframeMode::Disabled)
{
m_renderer_stats.num_batches++;
DrawBatchVertices(m_batch.GetRenderMode(), m_batch_base_vertex, vertex_count);
g_gpu_device->SetPipeline(m_wireframe_pipeline.get());
g_gpu_device->Draw(vertex_count, m_batch_base_vertex);
}
}

Expand Down
19 changes: 9 additions & 10 deletions src/core/gpu_hw.h
Expand Up @@ -157,7 +157,7 @@ class GPU_HW final : public GPU
void SetScissor();
void MapBatchVertexPointer(u32 required_vertices);
void UnmapBatchVertexPointer(u32 used_vertices);
void DrawBatchVertices(BatchRenderMode render_mode, u32 base_vertex, u32 num_vertices);
void DrawBatchVertices(BatchRenderMode render_mode, u32 num_vertices, u32 base_vertex);
void ClearDisplay() override;
void UpdateDisplay() override;

Expand Down Expand Up @@ -266,24 +266,22 @@ class GPU_HW final : public GPU

u32 m_resolution_scale = 1;
u32 m_multisamples = 1;
u32 m_max_resolution_scale = 1;
bool m_true_color = true;

union
{
BitField<u8, bool, 0, 1> m_supports_per_sample_shading;
BitField<u8, bool, 1, 1> m_supports_dual_source_blend;
BitField<u8, bool, 2, 1> m_supports_disable_color_perspective;
BitField<u8, bool, 3, 1> m_per_sample_shading;
BitField<u8, bool, 4, 1> m_scaled_dithering;
BitField<u8, bool, 5, 1> m_chroma_smoothing;
BitField<u8, bool, 6, 1> m_disable_color_perspective;
BitField<u8, bool, 0, 1> m_supports_dual_source_blend;
BitField<u8, bool, 1, 1> m_per_sample_shading;
BitField<u8, bool, 2, 1> m_scaled_dithering;
BitField<u8, bool, 3, 1> m_chroma_smoothing;
BitField<u8, bool, 4, 1> m_disable_color_perspective;

u8 bits = 0;
};

GPUTextureFilter m_texture_filtering = GPUTextureFilter::Nearest;
GPUDownsampleMode m_downsample_mode = GPUDownsampleMode::Disabled;
GPUWireframeMode m_wireframe_mode = GPUWireframeMode::Disabled;
bool m_true_color = true;
bool m_using_uv_limits = false;
bool m_pgxp_depth_buffer = false;

Expand All @@ -298,6 +296,7 @@ class GPU_HW final : public GPU

// [depth_test][render_mode][texture_mode][transparency_mode][dithering][interlacing]
DimensionalArray<std::unique_ptr<GPUPipeline>, 2, 2, 5, 9, 4, 3> m_batch_pipelines{};
std::unique_ptr<GPUPipeline> m_wireframe_pipeline;

// [wrapped][interlaced]
DimensionalArray<std::unique_ptr<GPUPipeline>, 2, 2> m_vram_fill_pipelines{};
Expand Down

0 comments on commit 7ad1b8d

Please sign in to comment.