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",
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();
}
@ -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"));

View file

@ -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.
@ -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);
}
@ -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);
@ -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 =
@ -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)
@ -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))
@ -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
{
@ -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))
@ -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)" : "");
@ -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");
}
@ -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 =
@ -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);
@ -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);
@ -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);
}
}

View file

@ -157,7 +157,7 @@ private:
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;
@ -266,24 +266,22 @@ private:
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;
@ -298,6 +296,7 @@ private:
// [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{};

View file

@ -1087,6 +1087,96 @@ float3 SampleVRAM24Smoothed(uint2 icoords)
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::stringstream ss;

View file

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

View file

@ -161,7 +161,7 @@ void Settings::Load(SettingsInterface& si)
region =
ParseConsoleRegionName(
si.GetStringValue("Console", "Region", Settings::GetConsoleRegionName(Settings::DEFAULT_CONSOLE_REGION)).c_str())
.value_or(DEFAULT_CONSOLE_REGION);
.value_or(DEFAULT_CONSOLE_REGION);
enable_8mb_ram = si.GetBoolValue("Console", "Enable8MBRAM", false);
emulation_speed = si.GetFloatValue("Main", "EmulationSpeed", 1.0f);
@ -192,7 +192,7 @@ void Settings::Load(SettingsInterface& si)
cpu_execution_mode =
ParseCPUExecutionMode(
si.GetStringValue("CPU", "ExecutionMode", GetCPUExecutionModeName(DEFAULT_CPU_EXECUTION_MODE)).c_str())
.value_or(DEFAULT_CPU_EXECUTION_MODE);
.value_or(DEFAULT_CPU_EXECUTION_MODE);
cpu_overclock_numerator = std::max(si.GetIntValue("CPU", "OverclockNumerator", 1), 1);
cpu_overclock_denominator = std::max(si.GetIntValue("CPU", "OverclockDenominator", 1), 1);
cpu_overclock_enable = si.GetBoolValue("CPU", "OverclockEnable", false);
@ -201,11 +201,11 @@ void Settings::Load(SettingsInterface& si)
cpu_recompiler_block_linking = si.GetBoolValue("CPU", "RecompilerBlockLinking", true);
cpu_recompiler_icache = si.GetBoolValue("CPU", "RecompilerICache", false);
cpu_fastmem_mode = ParseCPUFastmemMode(
si.GetStringValue("CPU", "FastmemMode", GetCPUFastmemModeName(DEFAULT_CPU_FASTMEM_MODE)).c_str())
.value_or(DEFAULT_CPU_FASTMEM_MODE);
si.GetStringValue("CPU", "FastmemMode", GetCPUFastmemModeName(DEFAULT_CPU_FASTMEM_MODE)).c_str())
.value_or(DEFAULT_CPU_FASTMEM_MODE);
gpu_renderer = ParseRendererName(si.GetStringValue("GPU", "Renderer", GetRendererName(DEFAULT_GPU_RENDERER)).c_str())
.value_or(DEFAULT_GPU_RENDERER);
.value_or(DEFAULT_GPU_RENDERER);
gpu_adapter = si.GetStringValue("GPU", "Adapter", "");
gpu_resolution_scale = static_cast<u32>(si.GetIntValue("GPU", "ResolutionScale", 1));
gpu_multisamples = static_cast<u32>(si.GetIntValue("GPU", "Multisamples", 1));
@ -220,11 +220,15 @@ void Settings::Load(SettingsInterface& si)
gpu_texture_filter =
ParseTextureFilterName(
si.GetStringValue("GPU", "TextureFilter", GetTextureFilterName(DEFAULT_GPU_TEXTURE_FILTER)).c_str())
.value_or(DEFAULT_GPU_TEXTURE_FILTER);
.value_or(DEFAULT_GPU_TEXTURE_FILTER);
gpu_downsample_mode =
ParseDownsampleModeName(
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_force_ntsc_timings = si.GetBoolValue("GPU", "ForceNTSCTimings", false);
gpu_widescreen_hack = si.GetBoolValue("GPU", "WidescreenHack", false);
@ -243,11 +247,11 @@ void Settings::Load(SettingsInterface& si)
display_crop_mode =
ParseDisplayCropMode(
si.GetStringValue("Display", "CropMode", GetDisplayCropModeName(DEFAULT_DISPLAY_CROP_MODE)).c_str())
.value_or(DEFAULT_DISPLAY_CROP_MODE);
.value_or(DEFAULT_DISPLAY_CROP_MODE);
display_aspect_ratio =
ParseDisplayAspectRatio(
si.GetStringValue("Display", "AspectRatio", GetDisplayAspectRatioName(DEFAULT_DISPLAY_ASPECT_RATIO)).c_str())
.value_or(DEFAULT_DISPLAY_ASPECT_RATIO);
.value_or(DEFAULT_DISPLAY_ASPECT_RATIO);
display_aspect_ratio_custom_numerator = static_cast<u16>(
std::clamp<int>(si.GetIntValue("Display", "CustomAspectRatioNumerator", 4), 1, std::numeric_limits<u16>::max()));
display_aspect_ratio_custom_denominator = static_cast<u16>(
@ -255,10 +259,10 @@ void Settings::Load(SettingsInterface& si)
display_alignment =
ParseDisplayAlignment(
si.GetStringValue("Display", "Alignment", GetDisplayAlignmentName(DEFAULT_DISPLAY_ALIGNMENT)).c_str())
.value_or(DEFAULT_DISPLAY_ALIGNMENT);
.value_or(DEFAULT_DISPLAY_ALIGNMENT);
display_scaling =
ParseDisplayScaling(si.GetStringValue("Display", "Scaling", GetDisplayScalingName(DEFAULT_DISPLAY_SCALING)).c_str())
.value_or(DEFAULT_DISPLAY_SCALING);
.value_or(DEFAULT_DISPLAY_SCALING);
display_force_4_3_for_24bit = si.GetBoolValue("Display", "Force4_3For24Bit", false);
display_active_start_offset = static_cast<s16>(si.GetIntValue("Display", "ActiveStartOffset", 0));
display_active_end_offset = static_cast<s16>(si.GetIntValue("Display", "ActiveEndOffset", 0));
@ -292,13 +296,13 @@ void Settings::Load(SettingsInterface& si)
audio_backend =
ParseAudioBackend(si.GetStringValue("Audio", "Backend", GetAudioBackendName(DEFAULT_AUDIO_BACKEND)).c_str())
.value_or(DEFAULT_AUDIO_BACKEND);
.value_or(DEFAULT_AUDIO_BACKEND);
audio_driver = si.GetStringValue("Audio", "Driver");
audio_output_device = si.GetStringValue("Audio", "OutputDevice");
audio_stretch_mode =
AudioStream::ParseStretchMode(
si.GetStringValue("Audio", "StretchMode", AudioStream::GetStretchModeName(DEFAULT_AUDIO_STRETCH_MODE)).c_str())
.value_or(DEFAULT_AUDIO_STRETCH_MODE);
.value_or(DEFAULT_AUDIO_STRETCH_MODE);
audio_output_latency_ms = si.GetUIntValue("Audio", "OutputLatencyMS", DEFAULT_AUDIO_OUTPUT_LATENCY_MS);
audio_buffer_ms = si.GetUIntValue("Audio", "BufferMS", DEFAULT_AUDIO_BUFFER_MS);
audio_output_volume = si.GetUIntValue("Audio", "OutputVolume", 100);
@ -323,14 +327,14 @@ void Settings::Load(SettingsInterface& si)
multitap_mode =
ParseMultitapModeName(
si.GetStringValue("ControllerPorts", "MultitapMode", GetMultitapModeName(DEFAULT_MULTITAP_MODE)).c_str())
.value_or(DEFAULT_MULTITAP_MODE);
.value_or(DEFAULT_MULTITAP_MODE);
controller_types[0] = ParseControllerTypeName(si.GetStringValue(Controller::GetSettingsSection(0).c_str(), "Type",
GetControllerTypeName(DEFAULT_CONTROLLER_1_TYPE))
.c_str())
.value_or(DEFAULT_CONTROLLER_1_TYPE);
GetControllerTypeName(DEFAULT_CONTROLLER_1_TYPE))
.c_str())
.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++)
{
// Ignore types when multitap not enabled
@ -342,19 +346,19 @@ void Settings::Load(SettingsInterface& si)
}
controller_types[i] = ParseControllerTypeName(si.GetStringValue(Controller::GetSettingsSection(i).c_str(), "Type",
GetControllerTypeName(DEFAULT_CONTROLLER_2_TYPE))
.c_str())
.value_or(DEFAULT_CONTROLLER_2_TYPE);
GetControllerTypeName(DEFAULT_CONTROLLER_2_TYPE))
.c_str())
.value_or(DEFAULT_CONTROLLER_2_TYPE);
}
memory_card_types[0] =
ParseMemoryCardTypeName(
si.GetStringValue("MemoryCards", "Card1Type", GetMemoryCardTypeName(DEFAULT_MEMORY_CARD_1_TYPE)).c_str())
.value_or(DEFAULT_MEMORY_CARD_1_TYPE);
.value_or(DEFAULT_MEMORY_CARD_1_TYPE);
memory_card_types[1] =
ParseMemoryCardTypeName(
si.GetStringValue("MemoryCards", "Card2Type", GetMemoryCardTypeName(DEFAULT_MEMORY_CARD_2_TYPE)).c_str())
.value_or(DEFAULT_MEMORY_CARD_2_TYPE);
.value_or(DEFAULT_MEMORY_CARD_2_TYPE);
memory_card_paths[0] = si.GetStringValue("MemoryCards", "Card1Path", "");
memory_card_paths[1] = si.GetStringValue("MemoryCards", "Card2Path", "");
memory_card_use_playlist_title = si.GetBoolValue("MemoryCards", "UsePlaylistTitle", true);
@ -372,7 +376,7 @@ void Settings::Load(SettingsInterface& si)
achievements_use_raintegration = si.GetBoolValue("Cheevos", "UseRAIntegration", false);
log_level = ParseLogLevelName(si.GetStringValue("Logging", "LogLevel", GetLogLevelName(DEFAULT_LOG_LEVEL)).c_str())
.value_or(DEFAULT_LOG_LEVEL);
.value_or(DEFAULT_LOG_LEVEL);
log_filter = si.GetStringValue("Logging", "LogFilter", "");
log_to_console = si.GetBoolValue("Logging", "LogToConsole", DEFAULT_LOG_TO_CONSOLE);
log_to_debug = si.GetBoolValue("Logging", "LogToDebug", false);
@ -462,6 +466,7 @@ void Settings::Save(SettingsInterface& si) const
si.SetBoolValue("GPU", "ScaledDithering", gpu_scaled_dithering);
si.SetStringValue("GPU", "TextureFilter", GetTextureFilterName(gpu_texture_filter));
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", "ForceNTSCTimings", gpu_force_ntsc_timings);
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)]);
}
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_display_names = {
{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 = {
{TRANSLATE_NOOP("DisplayAspectRatio", "Auto (Game Native)"),
TRANSLATE_NOOP("DisplayAspectRatio", "Stretch To Fill"), TRANSLATE_NOOP("DisplayAspectRatio", "Custom"), "4:3",
"16:9", "19:9", "20:9", "PAR 1:1"}};
{TRANSLATE_NOOP("DisplayAspectRatio", "Auto (Game Native)"), TRANSLATE_NOOP("DisplayAspectRatio", "Stretch To Fill"),
TRANSLATE_NOOP("DisplayAspectRatio", "Custom"), "4:3", "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 = {
{-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;
GPUTextureFilter gpu_texture_filter = DEFAULT_GPU_TEXTURE_FILTER;
GPUDownsampleMode gpu_downsample_mode = DEFAULT_GPU_DOWNSAMPLE_MODE;
GPUWireframeMode gpu_wireframe_mode = DEFAULT_GPU_WIREFRAME_MODE;
bool gpu_disable_interlacing = true;
bool gpu_force_ntsc_timings = false;
bool gpu_widescreen_hack = false;
@ -373,6 +374,10 @@ struct Settings
static const char* GetDownsampleModeName(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 const char* GetDisplayCropModeName(DisplayCropMode crop_mode);
static const char* GetDisplayCropModeDisplayName(DisplayCropMode crop_mode);
@ -421,6 +426,7 @@ struct Settings
#endif
static constexpr GPUTextureFilter DEFAULT_GPU_TEXTURE_FILTER = GPUTextureFilter::Nearest;
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 float DEFAULT_GPU_PGXP_DEPTH_THRESHOLD = 300.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_24bit_chroma_smoothing != old_settings.gpu_24bit_chroma_smoothing ||
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_aspect_ratio != old_settings.display_aspect_ratio ||
g_settings.display_alignment != old_settings.display_alignment ||

View file

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

View file

@ -279,6 +279,11 @@ void AdvancedSettingsWidget::addTweakOptions()
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())
{
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
setIntRangeTweakOption(m_ui.tweakOptionTable, i++, 0); // Display FPS limit
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
setFloatRangeTweakOption(m_ui.tweakOptionTable, i++, -1.0f); // PGXP geometry tolerance
setFloatRangeTweakOption(m_ui.tweakOptionTable, i++,

View file

@ -489,41 +489,44 @@ void ShaderGen::DeclareFragmentEntryPoint(
{
if (m_glsl)
{
if (m_use_glsl_interface_blocks)
if (num_color_inputs > 0 || num_texcoord_inputs > 0 || additional_inputs.size() > 0)
{
const char* qualifier = GetInterpolationQualifier(true, msaa, ssaa, false);
if (m_spirv)
ss << "layout(location = 0) ";
ss << "in VertexData {\n";
for (u32 i = 0; i < num_color_inputs; i++)
ss << " " << qualifier << (noperspective_color ? "noperspective " : "") << "float4 v_col" << i << ";\n";
for (u32 i = 0; i < num_texcoord_inputs; i++)
ss << " " << qualifier << "float2 v_tex" << i << ";\n";
for (const auto& [qualifiers, name] : additional_inputs)
if (m_use_glsl_interface_blocks)
{
const char* qualifier_to_use = (std::strlen(qualifiers) > 0) ? qualifiers : qualifier;
ss << " " << qualifier_to_use << " " << name << ";\n";
const char* qualifier = GetInterpolationQualifier(true, msaa, ssaa, false);
if (m_spirv)
ss << "layout(location = 0) ";
ss << "in VertexData {\n";
for (u32 i = 0; i < num_color_inputs; i++)
ss << " " << qualifier << (noperspective_color ? "noperspective " : "") << "float4 v_col" << i << ";\n";
for (u32 i = 0; i < num_texcoord_inputs; i++)
ss << " " << qualifier << "float2 v_tex" << i << ";\n";
for (const auto& [qualifiers, name] : additional_inputs)
{
const char* qualifier_to_use = (std::strlen(qualifiers) > 0) ? qualifiers : qualifier;
ss << " " << qualifier_to_use << " " << name << ";\n";
}
ss << "};\n";
}
ss << "};\n";
}
else
{
const char* qualifier = GetInterpolationQualifier(false, msaa, ssaa, false);
for (u32 i = 0; i < num_color_inputs; i++)
ss << qualifier << (noperspective_color ? "noperspective " : "") << "in float4 v_col" << i << ";\n";
for (u32 i = 0; i < num_texcoord_inputs; i++)
ss << qualifier << "in float2 v_tex" << i << ";\n";
for (const auto& [qualifiers, name] : additional_inputs)
else
{
const char* qualifier_to_use = (std::strlen(qualifiers) > 0) ? qualifiers : qualifier;
ss << qualifier_to_use << " in " << name << ";\n";
const char* qualifier = GetInterpolationQualifier(false, msaa, ssaa, false);
for (u32 i = 0; i < num_color_inputs; i++)
ss << qualifier << (noperspective_color ? "noperspective " : "") << "in float4 v_col" << i << ";\n";
for (u32 i = 0; i < num_texcoord_inputs; i++)
ss << qualifier << "in float2 v_tex" << i << ";\n";
for (const auto& [qualifiers, name] : additional_inputs)
{
const char* qualifier_to_use = (std::strlen(qualifiers) > 0) ? qualifiers : qualifier;
ss << qualifier_to_use << " in " << name << ";\n";
}
}
}