GPU: Add wireframe rendering/overlay

This commit is contained in:
Stenzek 2023-09-02 22:26:03 +10:00
parent e804b5e701
commit 7ad1b8d093
11 changed files with 314 additions and 94 deletions

View file

@ -2872,8 +2872,8 @@ void FullscreenUI::DrawBIOSSettingsPage()
FSUI_CSTR("Patches the BIOS to skip the boot animation. Safe to enable."), "BIOS", "PatchFastBoot", FSUI_CSTR("Patches the BIOS to skip the boot animation. Safe to enable."), "BIOS", "PatchFastBoot",
Settings::DEFAULT_FAST_BOOT_VALUE); Settings::DEFAULT_FAST_BOOT_VALUE);
DrawToggleSetting(bsi, FSUI_CSTR("Enable TTY Logging"), DrawToggleSetting(bsi, FSUI_CSTR("Enable TTY Logging"),
FSUI_CSTR("Logs BIOS calls to printf(). Not all games contain debugging messages."), FSUI_CSTR("Logs BIOS calls to printf(). Not all games contain debugging messages."), "BIOS",
"BIOS", "TTYLogging", false); "TTYLogging", false);
EndMenuButtons(); EndMenuButtons();
} }
@ -4695,6 +4695,11 @@ void FullscreenUI::DrawAdvancedSettingsPage()
bsi, FSUI_CSTR("Stretch Display Vertically"), bsi, FSUI_CSTR("Stretch Display Vertically"),
FSUI_CSTR("Stretches the display to match the aspect ratio by multiplying vertically instead of horizontally."), FSUI_CSTR("Stretches the display to match the aspect ratio by multiplying vertically instead of horizontally."),
"Display", "StretchVertically", false); "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")); MenuHeading(FSUI_CSTR("PGXP Settings"));

View file

@ -45,6 +45,11 @@ ALWAYS_INLINE static constexpr std::tuple<T, T> MinMax(T v1, T v2)
return std::tie(v1, v2); return std::tie(v1, v2);
} }
ALWAYS_INLINE static u32 GetMaxResolutionScale()
{
return g_gpu_device->GetMaxTextureSize() / VRAM_WIDTH;
}
ALWAYS_INLINE static bool ShouldUseUVLimits() ALWAYS_INLINE static bool ShouldUseUVLimits()
{ {
// We only need UV limits if PGXP is enabled, or texture filtering is enabled. // We only need UV limits if PGXP is enabled, or texture filtering is enabled.
@ -57,7 +62,7 @@ ALWAYS_INLINE static bool ShouldDisableColorPerspective()
} }
/// Returns true if the specified texture filtering mode requires dual-source blending. /// 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); return (filter == GPUTextureFilter::Bilinear || filter == GPUTextureFilter::JINC2 || filter == GPUTextureFilter::xBR);
} }
@ -148,42 +153,50 @@ bool GPU_HW::Initialize()
return false; return false;
const GPUDevice::Features features = g_gpu_device->GetFeatures(); 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_resolution_scale = CalculateResolutionScale();
m_multisamples = std::min(g_settings.gpu_multisamples, g_gpu_device->GetMaxMultisamples()); 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_true_color = g_settings.gpu_true_color;
m_scaled_dithering = g_settings.gpu_scaled_dithering; m_scaled_dithering = g_settings.gpu_scaled_dithering;
m_texture_filtering = g_settings.gpu_texture_filter; m_texture_filtering = g_settings.gpu_texture_filter;
m_using_uv_limits = ShouldUseUVLimits(); m_using_uv_limits = ShouldUseUVLimits();
m_chroma_smoothing = g_settings.gpu_24bit_chroma_smoothing; m_chroma_smoothing = g_settings.gpu_24bit_chroma_smoothing;
m_downsample_mode = GetDownsampleMode(m_resolution_scale); 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) 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); g_settings.gpu_multisamples, m_multisamples);
} }
if (!m_per_sample_shading && g_settings.gpu_per_sample_shading) 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); 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( 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)); Settings::GetTextureFilterDisplayName(m_texture_filtering));
m_texture_filtering = GPUTextureFilter::Nearest; 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."); 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(); m_pgxp_depth_buffer = g_settings.UsingPGXPDepthBuffer();
UpdateSoftwareRenderer(false); UpdateSoftwareRenderer(false);
@ -290,12 +303,16 @@ void GPU_HW::UpdateSettings(const Settings& old_settings)
{ {
GPU::UpdateSettings(old_settings); GPU::UpdateSettings(old_settings);
const GPUDevice::Features features = g_gpu_device->GetFeatures();
const u32 resolution_scale = CalculateResolutionScale(); const u32 resolution_scale = CalculateResolutionScale();
const u32 multisamples = std::min(g_settings.gpu_multisamples, g_gpu_device->GetMaxMultisamples()); 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 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 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 // TODO: Use old_settings
const bool framebuffer_changed = const bool framebuffer_changed =
@ -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_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_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_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); m_disable_color_perspective != disable_color_perspective);
if (m_resolution_scale != resolution_scale) if (m_resolution_scale != resolution_scale)
@ -348,6 +366,7 @@ void GPU_HW::UpdateSettings(const Settings& old_settings)
m_using_uv_limits = use_uv_limits; m_using_uv_limits = use_uv_limits;
m_chroma_smoothing = g_settings.gpu_24bit_chroma_smoothing; m_chroma_smoothing = g_settings.gpu_24bit_chroma_smoothing;
m_downsample_mode = downsample_mode; m_downsample_mode = downsample_mode;
m_wireframe_mode = wireframe_mode;
m_disable_color_perspective = disable_color_perspective; m_disable_color_perspective = disable_color_perspective;
if (!m_supports_dual_source_blend && TextureFilterRequiresDualSourceBlend(m_texture_filtering)) if (!m_supports_dual_source_blend && TextureFilterRequiresDualSourceBlend(m_texture_filtering))
@ -387,10 +406,12 @@ void GPU_HW::UpdateSettings(const Settings& old_settings)
u32 GPU_HW::CalculateResolutionScale() const u32 GPU_HW::CalculateResolutionScale() const
{ {
const u32 max_resolution_scale = GetMaxResolutionScale();
u32 scale; u32 scale;
if (g_settings.gpu_resolution_scale != 0) 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 else
{ {
@ -404,7 +425,7 @@ u32 GPU_HW::CalculateResolutionScale() const
static_cast<s32>(std::ceil(static_cast<float>(g_gpu_device->GetWindowHeight()) / height)); static_cast<s32>(std::ceil(static_cast<float>(g_gpu_device->GetWindowHeight()) / height));
Log_InfoPrintf("Height = %d, preferred scale = %d", height, preferred_scale); 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)) if (g_settings.gpu_downsample_mode == GPUDownsampleMode::Adaptive && scale > 1 && !Common::IsPow2(scale))
@ -474,7 +495,7 @@ std::tuple<u32, u32> GPU_HW::GetFullDisplayResolution(bool scaled /* = true */)
void GPU_HW::PrintSettingsToLog() void GPU_HW::PrintSettingsToLog()
{ {
Log_InfoPrintf("Resolution Scale: %u (%ux%u), maximum %u", m_resolution_scale, VRAM_WIDTH * m_resolution_scale, 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("Multisampling: %ux%s", m_multisamples, m_per_sample_shading ? " (per sample shading)" : "");
Log_InfoPrintf("Dithering: %s%s", m_true_color ? "Disabled" : "Enabled", Log_InfoPrintf("Dithering: %s%s", m_true_color ? "Disabled" : "Enabled",
(!m_true_color && m_scaled_dithering) ? " (Scaled)" : ""); (!m_true_color && m_scaled_dithering) ? " (Scaled)" : "");
@ -483,6 +504,7 @@ void GPU_HW::PrintSettingsToLog()
Log_InfoPrintf("Using UV limits: %s", m_using_uv_limits ? "YES" : "NO"); 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("Depth buffer: %s", m_pgxp_depth_buffer ? "YES" : "NO");
Log_InfoPrintf("Downsampling: %s", Settings::GetDownsampleModeDisplayName(m_downsample_mode)); 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"); Log_InfoPrintf("Using software renderer for readbacks: %s", m_sw_renderer ? "YES" : "NO");
} }
@ -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(); batch_shader_guard.Run();
std::unique_ptr<GPUShader> fullscreen_quad_vertex_shader = std::unique_ptr<GPUShader> fullscreen_quad_vertex_shader =
@ -1026,6 +1081,8 @@ void GPU_HW::DestroyPipelines()
{ {
static constexpr auto destroy = [](std::unique_ptr<GPUPipeline>& p) { p.reset(); }; static constexpr auto destroy = [](std::unique_ptr<GPUPipeline>& p) { p.reset(); };
m_wireframe_pipeline.reset();
m_batch_pipelines.enumerate(destroy); m_batch_pipelines.enumerate(destroy);
m_vram_fill_pipelines.enumerate(destroy); m_vram_fill_pipelines.enumerate(destroy);
@ -1136,7 +1193,7 @@ void GPU_HW::UnmapBatchVertexPointer(u32 used_vertices)
m_batch_current_vertex_ptr = nullptr; 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] // [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); const u8 depth_test = m_batch.use_depth_buffer ? static_cast<u8>(2) : BoolToUInt8(m_batch.check_mask_before_draw);
@ -2365,16 +2422,26 @@ void GPU_HW::FlushRender()
m_batch_ubo_dirty = false; m_batch_ubo_dirty = false;
} }
if (m_wireframe_mode != GPUWireframeMode::OnlyWireframe)
{
if (NeedsTwoPassRendering()) if (NeedsTwoPassRendering())
{ {
m_renderer_stats.num_batches += 2; m_renderer_stats.num_batches += 2;
DrawBatchVertices(BatchRenderMode::OnlyOpaque, m_batch_base_vertex, vertex_count); DrawBatchVertices(BatchRenderMode::OnlyOpaque, vertex_count, m_batch_base_vertex);
DrawBatchVertices(BatchRenderMode::OnlyTransparent, m_batch_base_vertex, vertex_count); DrawBatchVertices(BatchRenderMode::OnlyTransparent, vertex_count, m_batch_base_vertex);
} }
else else
{ {
m_renderer_stats.num_batches++; m_renderer_stats.num_batches++;
DrawBatchVertices(m_batch.GetRenderMode(), m_batch_base_vertex, vertex_count); DrawBatchVertices(m_batch.GetRenderMode(), vertex_count, m_batch_base_vertex);
}
}
if (m_wireframe_mode != GPUWireframeMode::Disabled)
{
m_renderer_stats.num_batches++;
g_gpu_device->SetPipeline(m_wireframe_pipeline.get());
g_gpu_device->Draw(vertex_count, m_batch_base_vertex);
} }
} }

View file

@ -157,7 +157,7 @@ private:
void SetScissor(); void SetScissor();
void MapBatchVertexPointer(u32 required_vertices); void MapBatchVertexPointer(u32 required_vertices);
void UnmapBatchVertexPointer(u32 used_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 ClearDisplay() override;
void UpdateDisplay() override; void UpdateDisplay() override;
@ -266,24 +266,22 @@ private:
u32 m_resolution_scale = 1; u32 m_resolution_scale = 1;
u32 m_multisamples = 1; u32 m_multisamples = 1;
u32 m_max_resolution_scale = 1;
bool m_true_color = true;
union union
{ {
BitField<u8, bool, 0, 1> m_supports_per_sample_shading; BitField<u8, bool, 0, 1> m_supports_dual_source_blend;
BitField<u8, bool, 1, 1> m_supports_dual_source_blend; BitField<u8, bool, 1, 1> m_per_sample_shading;
BitField<u8, bool, 2, 1> m_supports_disable_color_perspective; BitField<u8, bool, 2, 1> m_scaled_dithering;
BitField<u8, bool, 3, 1> m_per_sample_shading; BitField<u8, bool, 3, 1> m_chroma_smoothing;
BitField<u8, bool, 4, 1> m_scaled_dithering; BitField<u8, bool, 4, 1> m_disable_color_perspective;
BitField<u8, bool, 5, 1> m_chroma_smoothing;
BitField<u8, bool, 6, 1> m_disable_color_perspective;
u8 bits = 0; u8 bits = 0;
}; };
GPUTextureFilter m_texture_filtering = GPUTextureFilter::Nearest; GPUTextureFilter m_texture_filtering = GPUTextureFilter::Nearest;
GPUDownsampleMode m_downsample_mode = GPUDownsampleMode::Disabled; 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_using_uv_limits = false;
bool m_pgxp_depth_buffer = false; bool m_pgxp_depth_buffer = false;
@ -298,6 +296,7 @@ private:
// [depth_test][render_mode][texture_mode][transparency_mode][dithering][interlacing] // [depth_test][render_mode][texture_mode][transparency_mode][dithering][interlacing]
DimensionalArray<std::unique_ptr<GPUPipeline>, 2, 2, 5, 9, 4, 3> m_batch_pipelines{}; DimensionalArray<std::unique_ptr<GPUPipeline>, 2, 2, 5, 9, 4, 3> m_batch_pipelines{};
std::unique_ptr<GPUPipeline> m_wireframe_pipeline;
// [wrapped][interlaced] // [wrapped][interlaced]
DimensionalArray<std::unique_ptr<GPUPipeline>, 2, 2> m_vram_fill_pipelines{}; DimensionalArray<std::unique_ptr<GPUPipeline>, 2, 2> m_vram_fill_pipelines{};

View file

@ -1087,6 +1087,96 @@ float3 SampleVRAM24Smoothed(uint2 icoords)
return ss.str(); return ss.str();
} }
std::string GPU_HW_ShaderGen::GenerateWireframeGeometryShader()
{
std::stringstream ss;
WriteHeader(ss);
WriteCommonFunctions(ss);
if (m_glsl)
{
ss << R"(
layout(triangles) in;
layout(line_strip, max_vertices = 6) out;
void main()
{
gl_Position = gl_in[0].gl_Position;
EmitVertex();
gl_Position = gl_in[1].gl_Position;
EmitVertex();
EndPrimitive();
gl_Position = gl_in[1].gl_Position;
EmitVertex();
gl_Position = gl_in[2].gl_Position;
EmitVertex();
EndPrimitive();
gl_Position = gl_in[2].gl_Position;
EmitVertex();
gl_Position = gl_in[0].gl_Position;
EmitVertex();
EndPrimitive();
}
)";
}
else
{
ss << R"(
struct GSInput
{
float4 col0 : COLOR0;
float4 pos : SV_Position;
};
struct GSOutput
{
float4 pos : SV_Position;
};
GSOutput GetVertex(GSInput vi)
{
GSOutput vo;
vo.pos = vi.pos;
return vo;
}
[maxvertexcount(6)]
void main(triangle GSInput input[3], inout LineStream<GSOutput> output)
{
output.Append(GetVertex(input[0]));
output.Append(GetVertex(input[1]));
output.RestartStrip();
output.Append(GetVertex(input[1]));
output.Append(GetVertex(input[2]));
output.RestartStrip();
output.Append(GetVertex(input[2]));
output.Append(GetVertex(input[0]));
output.RestartStrip();
}
)";
}
return ss.str();
}
std::string GPU_HW_ShaderGen::GenerateWireframeFragmentShader()
{
std::stringstream ss;
WriteHeader(ss);
WriteCommonFunctions(ss);
DeclareFragmentEntryPoint(ss, 0, 0, {}, false, 1);
ss << R"(
{
o_col0 = float4(1.0, 1.0, 1.0, 0.5);
}
)";
return ss.str();
}
std::string GPU_HW_ShaderGen::GenerateVRAMReadFragmentShader() std::string GPU_HW_ShaderGen::GenerateVRAMReadFragmentShader()
{ {
std::stringstream ss; std::stringstream ss;

View file

@ -18,6 +18,8 @@ public:
bool dithering, bool interlacing); bool dithering, bool interlacing);
std::string GenerateDisplayFragmentShader(bool depth_24bit, GPU_HW::InterlacedRenderMode interlace_mode, std::string GenerateDisplayFragmentShader(bool depth_24bit, GPU_HW::InterlacedRenderMode interlace_mode,
bool smooth_chroma); bool smooth_chroma);
std::string GenerateWireframeGeometryShader();
std::string GenerateWireframeFragmentShader();
std::string GenerateVRAMReadFragmentShader(); std::string GenerateVRAMReadFragmentShader();
std::string GenerateVRAMWriteFragmentShader(bool use_ssbo); std::string GenerateVRAMWriteFragmentShader(bool use_ssbo);
std::string GenerateVRAMCopyFragmentShader(); std::string GenerateVRAMCopyFragmentShader();

View file

@ -225,6 +225,10 @@ void Settings::Load(SettingsInterface& si)
ParseDownsampleModeName( ParseDownsampleModeName(
si.GetStringValue("GPU", "DownsampleMode", GetDownsampleModeName(DEFAULT_GPU_DOWNSAMPLE_MODE)).c_str()) si.GetStringValue("GPU", "DownsampleMode", GetDownsampleModeName(DEFAULT_GPU_DOWNSAMPLE_MODE)).c_str())
.value_or(DEFAULT_GPU_DOWNSAMPLE_MODE); .value_or(DEFAULT_GPU_DOWNSAMPLE_MODE);
gpu_wireframe_mode =
ParseGPUWireframeMode(
si.GetStringValue("GPU", "WireframeMode", GetGPUWireframeModeName(DEFAULT_GPU_WIREFRAME_MODE)).c_str())
.value_or(DEFAULT_GPU_WIREFRAME_MODE);
gpu_disable_interlacing = si.GetBoolValue("GPU", "DisableInterlacing", true); gpu_disable_interlacing = si.GetBoolValue("GPU", "DisableInterlacing", true);
gpu_force_ntsc_timings = si.GetBoolValue("GPU", "ForceNTSCTimings", false); gpu_force_ntsc_timings = si.GetBoolValue("GPU", "ForceNTSCTimings", false);
gpu_widescreen_hack = si.GetBoolValue("GPU", "WidescreenHack", false); gpu_widescreen_hack = si.GetBoolValue("GPU", "WidescreenHack", false);
@ -330,7 +334,7 @@ void Settings::Load(SettingsInterface& si)
.c_str()) .c_str())
.value_or(DEFAULT_CONTROLLER_1_TYPE); .value_or(DEFAULT_CONTROLLER_1_TYPE);
const std::array<bool, 2> mtap_enabled = { {IsPort1MultitapEnabled(), IsPort2MultitapEnabled()} }; const std::array<bool, 2> mtap_enabled = {{IsPort1MultitapEnabled(), IsPort2MultitapEnabled()}};
for (u32 i = 1; i < NUM_CONTROLLER_AND_CARD_PORTS; i++) for (u32 i = 1; i < NUM_CONTROLLER_AND_CARD_PORTS; i++)
{ {
// Ignore types when multitap not enabled // Ignore types when multitap not enabled
@ -462,6 +466,7 @@ void Settings::Save(SettingsInterface& si) const
si.SetBoolValue("GPU", "ScaledDithering", gpu_scaled_dithering); si.SetBoolValue("GPU", "ScaledDithering", gpu_scaled_dithering);
si.SetStringValue("GPU", "TextureFilter", GetTextureFilterName(gpu_texture_filter)); si.SetStringValue("GPU", "TextureFilter", GetTextureFilterName(gpu_texture_filter));
si.SetStringValue("GPU", "DownsampleMode", GetDownsampleModeName(gpu_downsample_mode)); si.SetStringValue("GPU", "DownsampleMode", GetDownsampleModeName(gpu_downsample_mode));
si.SetStringValue("GPU", "WireframeMode", GetGPUWireframeModeName(gpu_wireframe_mode));
si.SetBoolValue("GPU", "DisableInterlacing", gpu_disable_interlacing); si.SetBoolValue("GPU", "DisableInterlacing", gpu_disable_interlacing);
si.SetBoolValue("GPU", "ForceNTSCTimings", gpu_force_ntsc_timings); si.SetBoolValue("GPU", "ForceNTSCTimings", gpu_force_ntsc_timings);
si.SetBoolValue("GPU", "WidescreenHack", gpu_widescreen_hack); si.SetBoolValue("GPU", "WidescreenHack", gpu_widescreen_hack);
@ -1052,6 +1057,35 @@ const char* Settings::GetDownsampleModeDisplayName(GPUDownsampleMode mode)
return Host::TranslateToCString("GPUDownsampleMode", s_downsample_mode_display_names[static_cast<int>(mode)]); return Host::TranslateToCString("GPUDownsampleMode", s_downsample_mode_display_names[static_cast<int>(mode)]);
} }
static constexpr auto s_wireframe_mode_names = make_array("Disabled", "OverlayWireframe", "OnlyWireframe");
static constexpr auto s_wireframe_mode_display_names =
make_array(TRANSLATE_NOOP("GPUWireframeMode", "Disabled"), TRANSLATE_NOOP("GPUWireframeMode", "Overlay Wireframe"),
TRANSLATE_NOOP("GPUWireframeMode", "Only Wireframe"));
std::optional<GPUWireframeMode> Settings::ParseGPUWireframeMode(const char* str)
{
int index = 0;
for (const char* name : s_wireframe_mode_names)
{
if (StringUtil::Strcasecmp(name, str) == 0)
return static_cast<GPUWireframeMode>(index);
index++;
}
return std::nullopt;
}
const char* Settings::GetGPUWireframeModeName(GPUWireframeMode mode)
{
return s_wireframe_mode_names[static_cast<int>(mode)];
}
const char* Settings::GetGPUWireframeModeDisplayName(GPUWireframeMode mode)
{
return Host::TranslateToCString("GPUWireframeMode", s_wireframe_mode_display_names[static_cast<int>(mode)]);
}
static std::array<const char*, 3> s_display_crop_mode_names = {{"None", "Overscan", "Borders"}}; static std::array<const char*, 3> s_display_crop_mode_names = {{"None", "Overscan", "Borders"}};
static std::array<const char*, 3> s_display_crop_mode_display_names = { static std::array<const char*, 3> s_display_crop_mode_display_names = {
{TRANSLATE_NOOP("DisplayCropMode", "None"), TRANSLATE_NOOP("DisplayCropMode", "Only Overscan Area"), {TRANSLATE_NOOP("DisplayCropMode", "None"), TRANSLATE_NOOP("DisplayCropMode", "Only Overscan Area"),
@ -1082,9 +1116,8 @@ const char* Settings::GetDisplayCropModeDisplayName(DisplayCropMode crop_mode)
} }
static std::array<const char*, static_cast<size_t>(DisplayAspectRatio::Count)> s_display_aspect_ratio_names = { static std::array<const char*, static_cast<size_t>(DisplayAspectRatio::Count)> s_display_aspect_ratio_names = {
{TRANSLATE_NOOP("DisplayAspectRatio", "Auto (Game Native)"), {TRANSLATE_NOOP("DisplayAspectRatio", "Auto (Game Native)"), TRANSLATE_NOOP("DisplayAspectRatio", "Stretch To Fill"),
TRANSLATE_NOOP("DisplayAspectRatio", "Stretch To Fill"), TRANSLATE_NOOP("DisplayAspectRatio", "Custom"), "4:3", TRANSLATE_NOOP("DisplayAspectRatio", "Custom"), "4:3", "16:9", "19:9", "20:9", "PAR 1:1"}};
"16:9", "19:9", "20:9", "PAR 1:1"}};
static constexpr std::array<float, static_cast<size_t>(DisplayAspectRatio::Count)> s_display_aspect_ratio_values = { static constexpr std::array<float, static_cast<size_t>(DisplayAspectRatio::Count)> s_display_aspect_ratio_values = {
{-1.0f, -1.0f, -1.0f, 4.0f / 3.0f, 16.0f / 9.0f, 19.0f / 9.0f, 20.0f / 9.0f, -1.0f}}; {-1.0f, -1.0f, -1.0f, 4.0f / 3.0f, 16.0f / 9.0f, 19.0f / 9.0f, 20.0f / 9.0f, -1.0f}};

View file

@ -106,6 +106,7 @@ struct Settings
bool gpu_scaled_dithering = true; bool gpu_scaled_dithering = true;
GPUTextureFilter gpu_texture_filter = DEFAULT_GPU_TEXTURE_FILTER; GPUTextureFilter gpu_texture_filter = DEFAULT_GPU_TEXTURE_FILTER;
GPUDownsampleMode gpu_downsample_mode = DEFAULT_GPU_DOWNSAMPLE_MODE; GPUDownsampleMode gpu_downsample_mode = DEFAULT_GPU_DOWNSAMPLE_MODE;
GPUWireframeMode gpu_wireframe_mode = DEFAULT_GPU_WIREFRAME_MODE;
bool gpu_disable_interlacing = true; bool gpu_disable_interlacing = true;
bool gpu_force_ntsc_timings = false; bool gpu_force_ntsc_timings = false;
bool gpu_widescreen_hack = false; bool gpu_widescreen_hack = false;
@ -373,6 +374,10 @@ struct Settings
static const char* GetDownsampleModeName(GPUDownsampleMode mode); static const char* GetDownsampleModeName(GPUDownsampleMode mode);
static const char* GetDownsampleModeDisplayName(GPUDownsampleMode mode); static const char* GetDownsampleModeDisplayName(GPUDownsampleMode mode);
static std::optional<GPUWireframeMode> ParseGPUWireframeMode(const char* str);
static const char* GetGPUWireframeModeName(GPUWireframeMode mode);
static const char* GetGPUWireframeModeDisplayName(GPUWireframeMode mode);
static std::optional<DisplayCropMode> ParseDisplayCropMode(const char* str); static std::optional<DisplayCropMode> ParseDisplayCropMode(const char* str);
static const char* GetDisplayCropModeName(DisplayCropMode crop_mode); static const char* GetDisplayCropModeName(DisplayCropMode crop_mode);
static const char* GetDisplayCropModeDisplayName(DisplayCropMode crop_mode); static const char* GetDisplayCropModeDisplayName(DisplayCropMode crop_mode);
@ -421,6 +426,7 @@ struct Settings
#endif #endif
static constexpr GPUTextureFilter DEFAULT_GPU_TEXTURE_FILTER = GPUTextureFilter::Nearest; static constexpr GPUTextureFilter DEFAULT_GPU_TEXTURE_FILTER = GPUTextureFilter::Nearest;
static constexpr GPUDownsampleMode DEFAULT_GPU_DOWNSAMPLE_MODE = GPUDownsampleMode::Disabled; static constexpr GPUDownsampleMode DEFAULT_GPU_DOWNSAMPLE_MODE = GPUDownsampleMode::Disabled;
static constexpr GPUWireframeMode DEFAULT_GPU_WIREFRAME_MODE = GPUWireframeMode::Disabled;
static constexpr ConsoleRegion DEFAULT_CONSOLE_REGION = ConsoleRegion::Auto; static constexpr ConsoleRegion DEFAULT_CONSOLE_REGION = ConsoleRegion::Auto;
static constexpr float DEFAULT_GPU_PGXP_DEPTH_THRESHOLD = 300.0f; static constexpr float DEFAULT_GPU_PGXP_DEPTH_THRESHOLD = 300.0f;
static constexpr float GPU_PGXP_DEPTH_THRESHOLD_SCALE = 4096.0f; static constexpr float GPU_PGXP_DEPTH_THRESHOLD_SCALE = 4096.0f;

View file

@ -3589,6 +3589,7 @@ void System::CheckForSettingsChanges(const Settings& old_settings)
g_settings.gpu_force_ntsc_timings != old_settings.gpu_force_ntsc_timings || g_settings.gpu_force_ntsc_timings != old_settings.gpu_force_ntsc_timings ||
g_settings.gpu_24bit_chroma_smoothing != old_settings.gpu_24bit_chroma_smoothing || g_settings.gpu_24bit_chroma_smoothing != old_settings.gpu_24bit_chroma_smoothing ||
g_settings.gpu_downsample_mode != old_settings.gpu_downsample_mode || g_settings.gpu_downsample_mode != old_settings.gpu_downsample_mode ||
g_settings.gpu_wireframe_mode != old_settings.gpu_wireframe_mode ||
g_settings.display_crop_mode != old_settings.display_crop_mode || g_settings.display_crop_mode != old_settings.display_crop_mode ||
g_settings.display_aspect_ratio != old_settings.display_aspect_ratio || g_settings.display_aspect_ratio != old_settings.display_aspect_ratio ||
g_settings.display_alignment != old_settings.display_alignment || g_settings.display_alignment != old_settings.display_alignment ||

View file

@ -95,6 +95,14 @@ enum class GPUDownsampleMode : u8
Count Count
}; };
enum class GPUWireframeMode : u8
{
Disabled,
OverlayWireframe,
OnlyWireframe,
Count,
};
enum class DisplayCropMode : u8 enum class DisplayCropMode : u8
{ {
None, None,

View file

@ -279,6 +279,11 @@ void AdvancedSettingsWidget::addTweakOptions()
addMSAATweakOption(m_dialog, m_ui.tweakOptionTable, tr("Multisample Antialiasing")); addMSAATweakOption(m_dialog, m_ui.tweakOptionTable, tr("Multisample Antialiasing"));
addChoiceTweakOption(m_dialog, m_ui.tweakOptionTable, tr("Wireframe Mode"), "GPU", "WireframeMode",
Settings::ParseGPUWireframeMode, Settings::GetGPUWireframeModeName,
Settings::GetGPUWireframeModeDisplayName, static_cast<u32>(GPUWireframeMode::Count),
GPUWireframeMode::Disabled);
if (m_dialog->isPerGameSettings()) if (m_dialog->isPerGameSettings())
{ {
addIntRangeTweakOption(m_dialog, m_ui.tweakOptionTable, tr("Display Active Start Offset"), "Display", addIntRangeTweakOption(m_dialog, m_ui.tweakOptionTable, tr("Display Active Start Offset"), "Display",
@ -365,6 +370,7 @@ void AdvancedSettingsWidget::onResetToDefaultClicked()
setBooleanTweakOption(m_ui.tweakOptionTable, i++, true); // Apply compatibility settings setBooleanTweakOption(m_ui.tweakOptionTable, i++, true); // Apply compatibility settings
setIntRangeTweakOption(m_ui.tweakOptionTable, i++, 0); // Display FPS limit setIntRangeTweakOption(m_ui.tweakOptionTable, i++, 0); // Display FPS limit
setChoiceTweakOption(m_ui.tweakOptionTable, i++, 0); // Multisample antialiasing setChoiceTweakOption(m_ui.tweakOptionTable, i++, 0); // Multisample antialiasing
setChoiceTweakOption(m_ui.tweakOptionTable, i++, 0); // Wireframe mode
setBooleanTweakOption(m_ui.tweakOptionTable, i++, false); // PGXP vertex cache setBooleanTweakOption(m_ui.tweakOptionTable, i++, false); // PGXP vertex cache
setFloatRangeTweakOption(m_ui.tweakOptionTable, i++, -1.0f); // PGXP geometry tolerance setFloatRangeTweakOption(m_ui.tweakOptionTable, i++, -1.0f); // PGXP geometry tolerance
setFloatRangeTweakOption(m_ui.tweakOptionTable, i++, setFloatRangeTweakOption(m_ui.tweakOptionTable, i++,

View file

@ -488,6 +488,8 @@ void ShaderGen::DeclareFragmentEntryPoint(
bool noperspective_color /* = false */) bool noperspective_color /* = false */)
{ {
if (m_glsl) if (m_glsl)
{
if (num_color_inputs > 0 || num_texcoord_inputs > 0 || additional_inputs.size() > 0)
{ {
if (m_use_glsl_interface_blocks) if (m_use_glsl_interface_blocks)
{ {
@ -526,6 +528,7 @@ void ShaderGen::DeclareFragmentEntryPoint(
ss << qualifier_to_use << " in " << name << ";\n"; ss << qualifier_to_use << " in " << name << ";\n";
} }
} }
}
if (declare_fragcoord) if (declare_fragcoord)
ss << "#define v_pos gl_FragCoord\n"; ss << "#define v_pos gl_FragCoord\n";