GPU: Rewrite deinterlacing and add adaptive/blend modes
This commit is contained in:
parent
8b2b4ce8d9
commit
1ab7850ed0
|
@ -3941,6 +3941,14 @@ void FullscreenUI::DrawDisplaySettingsPage()
|
||||||
&Settings::GetDisplayAspectRatioName, &Settings::GetDisplayAspectRatioDisplayName,
|
&Settings::GetDisplayAspectRatioName, &Settings::GetDisplayAspectRatioDisplayName,
|
||||||
DisplayAspectRatio::Count);
|
DisplayAspectRatio::Count);
|
||||||
|
|
||||||
|
DrawEnumSetting(
|
||||||
|
bsi, FSUI_CSTR("Deinterlacing Mode"),
|
||||||
|
FSUI_CSTR(
|
||||||
|
"Determines which algorithm is used to convert interlaced frames to progressive for display on your system."),
|
||||||
|
"Display", "DeinterlacingMode", Settings::DEFAULT_DISPLAY_DEINTERLACING_MODE,
|
||||||
|
&Settings::ParseDisplayDeinterlacingMode, &Settings::GetDisplayDeinterlacingModeName,
|
||||||
|
&Settings::GetDisplayDeinterlacingModeDisplayName, DisplayDeinterlacingMode::Count);
|
||||||
|
|
||||||
DrawEnumSetting(bsi, FSUI_CSTR("Crop Mode"),
|
DrawEnumSetting(bsi, FSUI_CSTR("Crop Mode"),
|
||||||
FSUI_CSTR("Determines how much of the area typically not visible on a consumer TV set to crop/hide."),
|
FSUI_CSTR("Determines how much of the area typically not visible on a consumer TV set to crop/hide."),
|
||||||
"Display", "CropMode", Settings::DEFAULT_DISPLAY_CROP_MODE, &Settings::ParseDisplayCropMode,
|
"Display", "CropMode", Settings::DEFAULT_DISPLAY_CROP_MODE, &Settings::ParseDisplayCropMode,
|
||||||
|
|
379
src/core/gpu.cpp
379
src/core/gpu.cpp
|
@ -56,6 +56,8 @@ GPU::GPU()
|
||||||
GPU::~GPU()
|
GPU::~GPU()
|
||||||
{
|
{
|
||||||
JoinScreenshotThreads();
|
JoinScreenshotThreads();
|
||||||
|
DestroyDeinterlaceTextures();
|
||||||
|
g_gpu_device->RecycleTexture(std::move(m_chroma_smoothing_texture));
|
||||||
|
|
||||||
if (g_gpu_device)
|
if (g_gpu_device)
|
||||||
g_gpu_device->SetGPUTimingEnabled(false);
|
g_gpu_device->SetGPUTimingEnabled(false);
|
||||||
|
@ -78,7 +80,7 @@ bool GPU::Initialize()
|
||||||
m_console_is_pal = System::IsPALRegion();
|
m_console_is_pal = System::IsPALRegion();
|
||||||
UpdateCRTCConfig();
|
UpdateCRTCConfig();
|
||||||
|
|
||||||
if (!CompileDisplayPipeline())
|
if (!CompileDisplayPipelines(true, true, g_settings.gpu_24bit_chroma_smoothing))
|
||||||
{
|
{
|
||||||
Host::ReportErrorAsync("Error", "Failed to compile base GPU pipelines.");
|
Host::ReportErrorAsync("Error", "Failed to compile base GPU pipelines.");
|
||||||
return false;
|
return false;
|
||||||
|
@ -107,11 +109,21 @@ void GPU::UpdateSettings(const Settings& old_settings)
|
||||||
// Crop mode calls this, so recalculate the display area
|
// Crop mode calls this, so recalculate the display area
|
||||||
UpdateCRTCDisplayParameters();
|
UpdateCRTCDisplayParameters();
|
||||||
|
|
||||||
if (g_settings.display_scaling != old_settings.display_scaling)
|
if (g_settings.display_scaling != old_settings.display_scaling ||
|
||||||
|
g_settings.display_deinterlacing_mode != old_settings.display_deinterlacing_mode ||
|
||||||
|
g_settings.gpu_24bit_chroma_smoothing != old_settings.gpu_24bit_chroma_smoothing)
|
||||||
|
{
|
||||||
|
// Toss buffers on mode change.
|
||||||
|
if (g_settings.display_deinterlacing_mode != old_settings.display_deinterlacing_mode)
|
||||||
|
DestroyDeinterlaceTextures();
|
||||||
|
|
||||||
|
if (!CompileDisplayPipelines(g_settings.display_scaling != old_settings.display_scaling,
|
||||||
|
g_settings.display_deinterlacing_mode != old_settings.display_deinterlacing_mode,
|
||||||
|
g_settings.gpu_24bit_chroma_smoothing != old_settings.gpu_24bit_chroma_smoothing))
|
||||||
{
|
{
|
||||||
if (!CompileDisplayPipeline())
|
|
||||||
Panic("Failed to compile display pipeline on settings change.");
|
Panic("Failed to compile display pipeline on settings change.");
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
g_gpu_device->SetGPUTimingEnabled(g_settings.display_show_gpu_usage);
|
g_gpu_device->SetGPUTimingEnabled(g_settings.display_show_gpu_usage);
|
||||||
}
|
}
|
||||||
|
@ -1359,6 +1371,10 @@ void GPU::HandleGetGPUInfoCommand(u32 value)
|
||||||
|
|
||||||
void GPU::ClearDisplay()
|
void GPU::ClearDisplay()
|
||||||
{
|
{
|
||||||
|
ClearDisplayTexture();
|
||||||
|
|
||||||
|
// Just recycle the textures, it'll get re-fetched.
|
||||||
|
DestroyDeinterlaceTextures();
|
||||||
}
|
}
|
||||||
|
|
||||||
void GPU::UpdateDisplay()
|
void GPU::UpdateDisplay()
|
||||||
|
@ -1587,22 +1603,26 @@ void GPU::SetTextureWindow(u32 value)
|
||||||
m_draw_mode.texture_window_changed = true;
|
m_draw_mode.texture_window_changed = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool GPU::CompileDisplayPipeline()
|
bool GPU::CompileDisplayPipelines(bool display, bool deinterlace, bool chroma_smoothing)
|
||||||
{
|
{
|
||||||
GPUShaderGen shadergen(g_gpu_device->GetRenderAPI(), g_gpu_device->GetFeatures().dual_source_blend,
|
GPUShaderGen shadergen(g_gpu_device->GetRenderAPI(), g_gpu_device->GetFeatures().dual_source_blend,
|
||||||
g_gpu_device->GetFeatures().framebuffer_fetch);
|
g_gpu_device->GetFeatures().framebuffer_fetch);
|
||||||
|
|
||||||
GPUPipeline::GraphicsConfig plconfig;
|
GPUPipeline::GraphicsConfig plconfig;
|
||||||
plconfig.layout = GPUPipeline::Layout::SingleTextureAndPushConstants;
|
|
||||||
plconfig.input_layout.vertex_stride = 0;
|
plconfig.input_layout.vertex_stride = 0;
|
||||||
plconfig.primitive = GPUPipeline::Primitive::Triangles;
|
plconfig.primitive = GPUPipeline::Primitive::Triangles;
|
||||||
plconfig.rasterization = GPUPipeline::RasterizationState::GetNoCullState();
|
plconfig.rasterization = GPUPipeline::RasterizationState::GetNoCullState();
|
||||||
plconfig.depth = GPUPipeline::DepthState::GetNoTestsState();
|
plconfig.depth = GPUPipeline::DepthState::GetNoTestsState();
|
||||||
plconfig.blend = GPUPipeline::BlendState::GetNoBlendingState();
|
plconfig.blend = GPUPipeline::BlendState::GetNoBlendingState();
|
||||||
plconfig.SetTargetFormats(g_gpu_device->HasSurface() ? g_gpu_device->GetWindowFormat() : GPUTexture::Format::RGBA8);
|
|
||||||
plconfig.depth_format = GPUTexture::Format::Unknown;
|
plconfig.depth_format = GPUTexture::Format::Unknown;
|
||||||
plconfig.samples = 1;
|
plconfig.samples = 1;
|
||||||
plconfig.per_sample_shading = false;
|
plconfig.per_sample_shading = false;
|
||||||
|
plconfig.geometry_shader = nullptr;
|
||||||
|
|
||||||
|
if (display)
|
||||||
|
{
|
||||||
|
plconfig.layout = GPUPipeline::Layout::SingleTextureAndPushConstants;
|
||||||
|
plconfig.SetTargetFormats(g_gpu_device->HasSurface() ? g_gpu_device->GetWindowFormat() : GPUTexture::Format::RGBA8);
|
||||||
|
|
||||||
std::string vs = shadergen.GenerateDisplayVertexShader();
|
std::string vs = shadergen.GenerateDisplayVertexShader();
|
||||||
std::string fs;
|
std::string fs;
|
||||||
|
@ -1628,15 +1648,138 @@ bool GPU::CompileDisplayPipeline()
|
||||||
if (!vso || !fso)
|
if (!vso || !fso)
|
||||||
return false;
|
return false;
|
||||||
GL_OBJECT_NAME(vso, "Display Vertex Shader");
|
GL_OBJECT_NAME(vso, "Display Vertex Shader");
|
||||||
GL_OBJECT_NAME_FMT(fso, "Display Fragment Shader [{}]", Settings::GetDisplayScalingName(g_settings.display_scaling));
|
GL_OBJECT_NAME_FMT(fso, "Display Fragment Shader [{}]",
|
||||||
|
Settings::GetDisplayScalingName(g_settings.display_scaling));
|
||||||
|
|
||||||
plconfig.vertex_shader = vso.get();
|
plconfig.vertex_shader = vso.get();
|
||||||
plconfig.fragment_shader = fso.get();
|
plconfig.fragment_shader = fso.get();
|
||||||
plconfig.geometry_shader = nullptr;
|
|
||||||
if (!(m_display_pipeline = g_gpu_device->CreatePipeline(plconfig)))
|
if (!(m_display_pipeline = g_gpu_device->CreatePipeline(plconfig)))
|
||||||
return false;
|
return false;
|
||||||
GL_OBJECT_NAME_FMT(m_display_pipeline, "Display Pipeline [{}]",
|
GL_OBJECT_NAME_FMT(m_display_pipeline, "Display Pipeline [{}]",
|
||||||
Settings::GetDisplayScalingName(g_settings.display_scaling));
|
Settings::GetDisplayScalingName(g_settings.display_scaling));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (deinterlace)
|
||||||
|
{
|
||||||
|
plconfig.SetTargetFormats(GPUTexture::Format::RGBA8);
|
||||||
|
|
||||||
|
std::unique_ptr<GPUShader> vso =
|
||||||
|
g_gpu_device->CreateShader(GPUShaderStage::Vertex, shadergen.GenerateScreenQuadVertexShader());
|
||||||
|
if (!vso)
|
||||||
|
return false;
|
||||||
|
GL_OBJECT_NAME(vso, "Deinterlace Vertex Shader");
|
||||||
|
|
||||||
|
std::unique_ptr<GPUShader> fso;
|
||||||
|
if (!(fso = g_gpu_device->CreateShader(GPUShaderStage::Fragment,
|
||||||
|
shadergen.GenerateInterleavedFieldExtractFragmentShader())))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
GL_OBJECT_NAME(fso, "Deinterlace Field Extract Fragment Shader");
|
||||||
|
|
||||||
|
plconfig.layout = GPUPipeline::Layout::SingleTextureAndPushConstants;
|
||||||
|
plconfig.vertex_shader = vso.get();
|
||||||
|
plconfig.fragment_shader = fso.get();
|
||||||
|
if (!(m_deinterlace_extract_pipeline = g_gpu_device->CreatePipeline(plconfig)))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
GL_OBJECT_NAME(m_deinterlace_extract_pipeline, "Deinterlace Field Extract Pipeline");
|
||||||
|
|
||||||
|
switch (g_settings.display_deinterlacing_mode)
|
||||||
|
{
|
||||||
|
case DisplayDeinterlacingMode::Disabled:
|
||||||
|
break;
|
||||||
|
|
||||||
|
case DisplayDeinterlacingMode::Weave:
|
||||||
|
{
|
||||||
|
if (!(fso = g_gpu_device->CreateShader(GPUShaderStage::Fragment,
|
||||||
|
shadergen.GenerateDeinterlaceWeaveFragmentShader())))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
GL_OBJECT_NAME(fso, "Weave Deinterlace Fragment Shader");
|
||||||
|
|
||||||
|
plconfig.layout = GPUPipeline::Layout::SingleTextureAndPushConstants;
|
||||||
|
plconfig.vertex_shader = vso.get();
|
||||||
|
plconfig.fragment_shader = fso.get();
|
||||||
|
if (!(m_deinterlace_pipeline = g_gpu_device->CreatePipeline(plconfig)))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
GL_OBJECT_NAME(m_deinterlace_pipeline, "Weave Deinterlace Pipeline");
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case DisplayDeinterlacingMode::Blend:
|
||||||
|
{
|
||||||
|
if (!(fso = g_gpu_device->CreateShader(GPUShaderStage::Fragment,
|
||||||
|
shadergen.GenerateDeinterlaceBlendFragmentShader())))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
GL_OBJECT_NAME(fso, "Blend Deinterlace Fragment Shader");
|
||||||
|
|
||||||
|
plconfig.layout = GPUPipeline::Layout::MultiTextureAndPushConstants;
|
||||||
|
plconfig.vertex_shader = vso.get();
|
||||||
|
plconfig.fragment_shader = fso.get();
|
||||||
|
if (!(m_deinterlace_pipeline = g_gpu_device->CreatePipeline(plconfig)))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
GL_OBJECT_NAME(m_deinterlace_pipeline, "Blend Deinterlace Pipeline");
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case DisplayDeinterlacingMode::Adaptive:
|
||||||
|
{
|
||||||
|
fso =
|
||||||
|
g_gpu_device->CreateShader(GPUShaderStage::Fragment, shadergen.GenerateFastMADReconstructFragmentShader());
|
||||||
|
if (!fso)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
GL_OBJECT_NAME(fso, "FastMAD Reconstruct Fragment Shader");
|
||||||
|
|
||||||
|
plconfig.layout = GPUPipeline::Layout::MultiTextureAndPushConstants;
|
||||||
|
plconfig.fragment_shader = fso.get();
|
||||||
|
if (!(m_deinterlace_pipeline = g_gpu_device->CreatePipeline(plconfig)))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
GL_OBJECT_NAME(m_deinterlace_pipeline, "FastMAD Reconstruct Pipeline");
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
UnreachableCode();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (chroma_smoothing)
|
||||||
|
{
|
||||||
|
m_chroma_smoothing_pipeline.reset();
|
||||||
|
g_gpu_device->RecycleTexture(std::move(m_chroma_smoothing_texture));
|
||||||
|
|
||||||
|
if (g_settings.gpu_24bit_chroma_smoothing)
|
||||||
|
{
|
||||||
|
plconfig.layout = GPUPipeline::Layout::SingleTextureAndPushConstants;
|
||||||
|
plconfig.SetTargetFormats(GPUTexture::Format::RGBA8);
|
||||||
|
|
||||||
|
std::unique_ptr<GPUShader> vso =
|
||||||
|
g_gpu_device->CreateShader(GPUShaderStage::Vertex, shadergen.GenerateScreenQuadVertexShader());
|
||||||
|
std::unique_ptr<GPUShader> fso =
|
||||||
|
g_gpu_device->CreateShader(GPUShaderStage::Fragment, shadergen.GenerateChromaSmoothingFragmentShader());
|
||||||
|
if (!vso || !fso)
|
||||||
|
return false;
|
||||||
|
GL_OBJECT_NAME(vso, "Chroma Smoothing Vertex Shader");
|
||||||
|
GL_OBJECT_NAME(fso, "Chroma Smoothing Fragment Shader");
|
||||||
|
|
||||||
|
plconfig.vertex_shader = vso.get();
|
||||||
|
plconfig.fragment_shader = fso.get();
|
||||||
|
if (!(m_chroma_smoothing_pipeline = g_gpu_device->CreatePipeline(plconfig)))
|
||||||
|
return false;
|
||||||
|
GL_OBJECT_NAME(m_chroma_smoothing_pipeline, "Chroma Smoothing Pipeline");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -1804,6 +1947,226 @@ bool GPU::RenderDisplay(GPUTexture* target, const Common::Rectangle<s32>& draw_r
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void GPU::DestroyDeinterlaceTextures()
|
||||||
|
{
|
||||||
|
for (std::unique_ptr<GPUTexture>& tex : m_deinterlace_buffers)
|
||||||
|
g_gpu_device->RecycleTexture(std::move(tex));
|
||||||
|
g_gpu_device->RecycleTexture(std::move(m_deinterlace_texture));
|
||||||
|
m_current_deinterlace_buffer = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool GPU::Deinterlace(GPUTexture* src, u32 x, u32 y, u32 width, u32 height, u32 field, u32 line_skip)
|
||||||
|
{
|
||||||
|
switch (g_settings.display_deinterlacing_mode)
|
||||||
|
{
|
||||||
|
case DisplayDeinterlacingMode::Disabled:
|
||||||
|
{
|
||||||
|
if (line_skip == 0)
|
||||||
|
{
|
||||||
|
SetDisplayTexture(src, x, y, width, height);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Still have to extract the field.
|
||||||
|
if (!DeinterlaceExtractField(0, src, x, y, width, height, line_skip)) [[unlikely]]
|
||||||
|
return false;
|
||||||
|
|
||||||
|
SetDisplayTexture(m_deinterlace_buffers[0].get(), 0, 0, width, height);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
case DisplayDeinterlacingMode::Weave:
|
||||||
|
{
|
||||||
|
GL_SCOPE_FMT("DeinterlaceWeave({{{},{}}}, {}x{}, field={}, line_skip={})", x, y, width, height, field, line_skip);
|
||||||
|
|
||||||
|
const u32 full_height = height * 2;
|
||||||
|
if (!DeinterlaceSetTargetSize(width, full_height, true)) [[unlikely]]
|
||||||
|
{
|
||||||
|
ClearDisplayTexture();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
src->MakeReadyForSampling();
|
||||||
|
|
||||||
|
g_gpu_device->SetRenderTarget(m_deinterlace_texture.get());
|
||||||
|
g_gpu_device->SetPipeline(m_deinterlace_pipeline.get());
|
||||||
|
g_gpu_device->SetTextureSampler(0, src, g_gpu_device->GetNearestSampler());
|
||||||
|
const u32 uniforms[] = {x, y, field, line_skip};
|
||||||
|
g_gpu_device->PushUniformBuffer(uniforms, sizeof(uniforms));
|
||||||
|
g_gpu_device->SetViewportAndScissor(0, 0, width, full_height);
|
||||||
|
g_gpu_device->Draw(3, 0);
|
||||||
|
|
||||||
|
m_deinterlace_texture->MakeReadyForSampling();
|
||||||
|
SetDisplayTexture(m_deinterlace_texture.get(), 0, 0, width, full_height);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
case DisplayDeinterlacingMode::Blend:
|
||||||
|
{
|
||||||
|
constexpr u32 NUM_BLEND_BUFFERS = 2;
|
||||||
|
|
||||||
|
GL_SCOPE_FMT("DeinterlaceBlend({{{},{}}}, {}x{}, field={}, line_skip={})", x, y, width, height, field, line_skip);
|
||||||
|
|
||||||
|
const u32 this_buffer = m_current_deinterlace_buffer;
|
||||||
|
m_current_deinterlace_buffer = (m_current_deinterlace_buffer + 1u) % NUM_BLEND_BUFFERS;
|
||||||
|
GL_INS_FMT("Current buffer: {}", this_buffer);
|
||||||
|
if (!DeinterlaceExtractField(this_buffer, src, x, y, width, height, line_skip) ||
|
||||||
|
!DeinterlaceSetTargetSize(width, height, false)) [[unlikely]]
|
||||||
|
{
|
||||||
|
ClearDisplayTexture();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: could be implemented with alpha blending instead..
|
||||||
|
|
||||||
|
g_gpu_device->InvalidateRenderTarget(m_deinterlace_texture.get());
|
||||||
|
g_gpu_device->SetRenderTarget(m_deinterlace_texture.get());
|
||||||
|
g_gpu_device->SetPipeline(m_deinterlace_pipeline.get());
|
||||||
|
g_gpu_device->SetTextureSampler(0, m_deinterlace_buffers[this_buffer].get(), g_gpu_device->GetNearestSampler());
|
||||||
|
g_gpu_device->SetTextureSampler(1, m_deinterlace_buffers[(this_buffer - 1) % NUM_BLEND_BUFFERS].get(),
|
||||||
|
g_gpu_device->GetNearestSampler());
|
||||||
|
g_gpu_device->SetViewportAndScissor(0, 0, width, height);
|
||||||
|
g_gpu_device->Draw(3, 0);
|
||||||
|
|
||||||
|
m_deinterlace_texture->MakeReadyForSampling();
|
||||||
|
SetDisplayTexture(m_deinterlace_texture.get(), 0, 0, width, height);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
case DisplayDeinterlacingMode::Adaptive:
|
||||||
|
{
|
||||||
|
GL_SCOPE_FMT("DeinterlaceAdaptive({{{},{}}}, {}x{}, field={}, line_skip={})", x, y, width, height, field,
|
||||||
|
line_skip);
|
||||||
|
|
||||||
|
const u32 full_height = height * 2;
|
||||||
|
const u32 this_buffer = m_current_deinterlace_buffer;
|
||||||
|
m_current_deinterlace_buffer = (m_current_deinterlace_buffer + 1u) % DEINTERLACE_BUFFER_COUNT;
|
||||||
|
GL_INS_FMT("Current buffer: {}", this_buffer);
|
||||||
|
if (!DeinterlaceExtractField(this_buffer, src, x, y, width, height, line_skip) ||
|
||||||
|
!DeinterlaceSetTargetSize(width, full_height, false)) [[unlikely]]
|
||||||
|
{
|
||||||
|
ClearDisplayTexture();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
g_gpu_device->SetRenderTarget(m_deinterlace_texture.get());
|
||||||
|
g_gpu_device->SetPipeline(m_deinterlace_pipeline.get());
|
||||||
|
g_gpu_device->SetTextureSampler(0, m_deinterlace_buffers[this_buffer].get(), g_gpu_device->GetNearestSampler());
|
||||||
|
g_gpu_device->SetTextureSampler(1, m_deinterlace_buffers[(this_buffer - 1) % DEINTERLACE_BUFFER_COUNT].get(),
|
||||||
|
g_gpu_device->GetNearestSampler());
|
||||||
|
g_gpu_device->SetTextureSampler(2, m_deinterlace_buffers[(this_buffer - 2) % DEINTERLACE_BUFFER_COUNT].get(),
|
||||||
|
g_gpu_device->GetNearestSampler());
|
||||||
|
g_gpu_device->SetTextureSampler(3, m_deinterlace_buffers[(this_buffer - 3) % DEINTERLACE_BUFFER_COUNT].get(),
|
||||||
|
g_gpu_device->GetNearestSampler());
|
||||||
|
const u32 uniforms[] = {field, full_height};
|
||||||
|
g_gpu_device->PushUniformBuffer(uniforms, sizeof(uniforms));
|
||||||
|
g_gpu_device->SetViewportAndScissor(0, 0, width, full_height);
|
||||||
|
g_gpu_device->Draw(3, 0);
|
||||||
|
|
||||||
|
m_deinterlace_texture->MakeReadyForSampling();
|
||||||
|
SetDisplayTexture(m_deinterlace_texture.get(), 0, 0, width, full_height);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
UnreachableCode();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool GPU::DeinterlaceExtractField(u32 dst_bufidx, GPUTexture* src, u32 x, u32 y, u32 width, u32 height, u32 line_skip)
|
||||||
|
{
|
||||||
|
if (!m_deinterlace_buffers[dst_bufidx] || m_deinterlace_buffers[dst_bufidx]->GetWidth() != width ||
|
||||||
|
m_deinterlace_buffers[dst_bufidx]->GetHeight() != height)
|
||||||
|
{
|
||||||
|
if (!g_gpu_device->ResizeTexture(&m_deinterlace_buffers[dst_bufidx], width, height, GPUTexture::Type::RenderTarget,
|
||||||
|
GPUTexture::Format::RGBA8, false)) [[unlikely]]
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
GL_OBJECT_NAME_FMT(m_deinterlace_buffers[dst_bufidx], "Blend Deinterlace Buffer {}", dst_bufidx);
|
||||||
|
}
|
||||||
|
|
||||||
|
GPUTexture* dst = m_deinterlace_buffers[dst_bufidx].get();
|
||||||
|
g_gpu_device->InvalidateRenderTarget(dst);
|
||||||
|
|
||||||
|
// If we're not skipping lines, then we can simply copy the texture.
|
||||||
|
if (line_skip == 0 && src->GetFormat() == dst->GetFormat())
|
||||||
|
{
|
||||||
|
GL_INS_FMT("DeinterlaceExtractField({{{},{}}} {}x{} line_skip={}) => copy direct", x, y, width, height, line_skip);
|
||||||
|
g_gpu_device->CopyTextureRegion(dst, 0, 0, 0, 0, src, x, y, 0, 0, width, height);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
GL_SCOPE_FMT("DeinterlaceExtractField({{{},{}}} {}x{} line_skip={}) => shader copy", x, y, width, height,
|
||||||
|
line_skip);
|
||||||
|
|
||||||
|
// Otherwise, we need to extract every other line from the texture.
|
||||||
|
src->MakeReadyForSampling();
|
||||||
|
g_gpu_device->SetRenderTarget(dst);
|
||||||
|
g_gpu_device->SetPipeline(m_deinterlace_extract_pipeline.get());
|
||||||
|
g_gpu_device->SetTextureSampler(0, src, g_gpu_device->GetNearestSampler());
|
||||||
|
const u32 uniforms[] = {x, y, line_skip};
|
||||||
|
g_gpu_device->PushUniformBuffer(uniforms, sizeof(uniforms));
|
||||||
|
g_gpu_device->SetViewportAndScissor(0, 0, width, height);
|
||||||
|
g_gpu_device->Draw(3, 0);
|
||||||
|
|
||||||
|
GL_POP();
|
||||||
|
}
|
||||||
|
|
||||||
|
dst->MakeReadyForSampling();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool GPU::DeinterlaceSetTargetSize(u32 width, u32 height, bool preserve)
|
||||||
|
{
|
||||||
|
if (!m_deinterlace_texture || m_deinterlace_texture->GetWidth() != width ||
|
||||||
|
m_deinterlace_texture->GetHeight() != height)
|
||||||
|
{
|
||||||
|
if (!g_gpu_device->ResizeTexture(&m_deinterlace_texture, width, height, GPUTexture::Type::RenderTarget,
|
||||||
|
GPUTexture::Format::RGBA8, preserve)) [[unlikely]]
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
GL_OBJECT_NAME(m_deinterlace_texture, "Deinterlace target texture");
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool GPU::ApplyChromaSmoothing(GPUTexture* src, u32 x, u32 y, u32 width, u32 height)
|
||||||
|
{
|
||||||
|
if (!m_chroma_smoothing_texture || m_chroma_smoothing_texture->GetWidth() != width ||
|
||||||
|
m_chroma_smoothing_texture->GetHeight() != height)
|
||||||
|
{
|
||||||
|
if (!g_gpu_device->ResizeTexture(&m_chroma_smoothing_texture, width, height, GPUTexture::Type::RenderTarget,
|
||||||
|
GPUTexture::Format::RGBA8, false))
|
||||||
|
{
|
||||||
|
ClearDisplayTexture();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
GL_OBJECT_NAME(m_chroma_smoothing_texture, "Chroma smoothing texture");
|
||||||
|
}
|
||||||
|
|
||||||
|
GL_SCOPE_FMT("ApplyChromaSmoothing({{{},{}}}, {}x{})", x, y, width, height);
|
||||||
|
|
||||||
|
src->MakeReadyForSampling();
|
||||||
|
g_gpu_device->InvalidateRenderTarget(m_chroma_smoothing_texture.get());
|
||||||
|
g_gpu_device->SetRenderTarget(m_chroma_smoothing_texture.get());
|
||||||
|
g_gpu_device->SetPipeline(m_chroma_smoothing_pipeline.get());
|
||||||
|
g_gpu_device->SetTextureSampler(0, src, g_gpu_device->GetNearestSampler());
|
||||||
|
const u32 uniforms[] = {x, y, width - 1, height - 1};
|
||||||
|
g_gpu_device->PushUniformBuffer(uniforms, sizeof(uniforms));
|
||||||
|
g_gpu_device->SetViewportAndScissor(0, 0, width, height);
|
||||||
|
g_gpu_device->Draw(3, 0);
|
||||||
|
|
||||||
|
m_chroma_smoothing_texture->MakeReadyForSampling();
|
||||||
|
SetDisplayTexture(m_chroma_smoothing_texture.get(), 0, 0, width, height);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
Common::Rectangle<float> GPU::CalculateDrawRect(s32 window_width, s32 window_height, float* out_left_padding,
|
Common::Rectangle<float> GPU::CalculateDrawRect(s32 window_width, s32 window_height, float* out_left_padding,
|
||||||
float* out_top_padding, float* out_scale, float* out_x_scale,
|
float* out_top_padding, float* out_scale, float* out_x_scale,
|
||||||
bool apply_aspect_ratio /* = true */) const
|
bool apply_aspect_ratio /* = true */) const
|
||||||
|
|
|
@ -61,6 +61,7 @@ public:
|
||||||
DOT_TIMER_INDEX = 0,
|
DOT_TIMER_INDEX = 0,
|
||||||
HBLANK_TIMER_INDEX = 1,
|
HBLANK_TIMER_INDEX = 1,
|
||||||
MAX_RESOLUTION_SCALE = 32,
|
MAX_RESOLUTION_SCALE = 32,
|
||||||
|
DEINTERLACE_BUFFER_COUNT = 4,
|
||||||
};
|
};
|
||||||
|
|
||||||
enum : u16
|
enum : u16
|
||||||
|
@ -239,6 +240,7 @@ protected:
|
||||||
bool remove_alpha);
|
bool remove_alpha);
|
||||||
|
|
||||||
void SoftReset();
|
void SoftReset();
|
||||||
|
void ClearDisplay();
|
||||||
|
|
||||||
// Sets dots per scanline
|
// Sets dots per scanline
|
||||||
void UpdateCRTCConfig();
|
void UpdateCRTCConfig();
|
||||||
|
@ -313,7 +315,6 @@ protected:
|
||||||
virtual void UpdateVRAM(u32 x, u32 y, u32 width, u32 height, const void* data, bool set_mask, bool check_mask);
|
virtual void UpdateVRAM(u32 x, u32 y, u32 width, u32 height, const void* data, bool set_mask, bool check_mask);
|
||||||
virtual void CopyVRAM(u32 src_x, u32 src_y, u32 dst_x, u32 dst_y, u32 width, u32 height);
|
virtual void CopyVRAM(u32 src_x, u32 src_y, u32 dst_x, u32 dst_y, u32 width, u32 height);
|
||||||
virtual void DispatchRenderCommand();
|
virtual void DispatchRenderCommand();
|
||||||
virtual void ClearDisplay();
|
|
||||||
virtual void UpdateDisplay();
|
virtual void UpdateDisplay();
|
||||||
virtual void DrawRendererStats();
|
virtual void DrawRendererStats();
|
||||||
|
|
||||||
|
@ -578,6 +579,12 @@ protected:
|
||||||
|
|
||||||
bool RenderDisplay(GPUTexture* target, const Common::Rectangle<s32>& draw_rect, bool postfx);
|
bool RenderDisplay(GPUTexture* target, const Common::Rectangle<s32>& draw_rect, bool postfx);
|
||||||
|
|
||||||
|
bool Deinterlace(GPUTexture* src, u32 x, u32 y, u32 width, u32 height, u32 field, u32 line_skip);
|
||||||
|
bool DeinterlaceExtractField(u32 dst_bufidx, GPUTexture* src, u32 x, u32 y, u32 width, u32 height, u32 line_skip);
|
||||||
|
bool DeinterlaceSetTargetSize(u32 width, u32 height, bool preserve);
|
||||||
|
void DestroyDeinterlaceTextures();
|
||||||
|
bool ApplyChromaSmoothing(GPUTexture* src, u32 x, u32 y, u32 width, u32 height);
|
||||||
|
|
||||||
s32 m_display_width = 0;
|
s32 m_display_width = 0;
|
||||||
s32 m_display_height = 0;
|
s32 m_display_height = 0;
|
||||||
s32 m_display_active_left = 0;
|
s32 m_display_active_left = 0;
|
||||||
|
@ -586,6 +593,15 @@ protected:
|
||||||
s32 m_display_active_height = 0;
|
s32 m_display_active_height = 0;
|
||||||
float m_display_aspect_ratio = 1.0f;
|
float m_display_aspect_ratio = 1.0f;
|
||||||
|
|
||||||
|
u32 m_current_deinterlace_buffer = 0;
|
||||||
|
std::unique_ptr<GPUPipeline> m_deinterlace_pipeline;
|
||||||
|
std::unique_ptr<GPUPipeline> m_deinterlace_extract_pipeline;
|
||||||
|
std::array<std::unique_ptr<GPUTexture>, DEINTERLACE_BUFFER_COUNT> m_deinterlace_buffers;
|
||||||
|
std::unique_ptr<GPUTexture> m_deinterlace_texture;
|
||||||
|
|
||||||
|
std::unique_ptr<GPUPipeline> m_chroma_smoothing_pipeline;
|
||||||
|
std::unique_ptr<GPUTexture> m_chroma_smoothing_texture;
|
||||||
|
|
||||||
std::unique_ptr<GPUPipeline> m_display_pipeline;
|
std::unique_ptr<GPUPipeline> m_display_pipeline;
|
||||||
GPUTexture* m_display_texture = nullptr;
|
GPUTexture* m_display_texture = nullptr;
|
||||||
s32 m_display_texture_view_x = 0;
|
s32 m_display_texture_view_x = 0;
|
||||||
|
@ -619,7 +635,7 @@ protected:
|
||||||
Stats m_stats = {};
|
Stats m_stats = {};
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool CompileDisplayPipeline();
|
bool CompileDisplayPipelines(bool display, bool deinterlace, bool chroma_smoothing);
|
||||||
|
|
||||||
using GP0CommandHandler = bool (GPU::*)();
|
using GP0CommandHandler = bool (GPU::*)();
|
||||||
using GP0CommandHandlerTable = std::array<GP0CommandHandler, 256>;
|
using GP0CommandHandlerTable = std::array<GP0CommandHandler, 256>;
|
||||||
|
|
|
@ -203,7 +203,6 @@ bool GPU_HW::Initialize()
|
||||||
m_line_detect_mode = (m_resolution_scale > 1) ? g_settings.gpu_line_detect_mode : GPULineDetectMode::Disabled;
|
m_line_detect_mode = (m_resolution_scale > 1) ? g_settings.gpu_line_detect_mode : GPULineDetectMode::Disabled;
|
||||||
m_clamp_uvs = ShouldClampUVs();
|
m_clamp_uvs = ShouldClampUVs();
|
||||||
m_compute_uv_range = m_clamp_uvs;
|
m_compute_uv_range = m_clamp_uvs;
|
||||||
m_chroma_smoothing = g_settings.gpu_24bit_chroma_smoothing;
|
|
||||||
m_downsample_mode = GetDownsampleMode(m_resolution_scale);
|
m_downsample_mode = GetDownsampleMode(m_resolution_scale);
|
||||||
m_wireframe_mode = g_settings.gpu_wireframe_mode;
|
m_wireframe_mode = g_settings.gpu_wireframe_mode;
|
||||||
m_disable_color_perspective = features.noperspective_interpolation && ShouldDisableColorPerspective();
|
m_disable_color_perspective = features.noperspective_interpolation && ShouldDisableColorPerspective();
|
||||||
|
@ -336,7 +335,7 @@ void GPU_HW::UpdateSettings(const Settings& old_settings)
|
||||||
m_true_color != g_settings.gpu_true_color || m_debanding != g_settings.gpu_debanding ||
|
m_true_color != g_settings.gpu_true_color || m_debanding != g_settings.gpu_debanding ||
|
||||||
m_per_sample_shading != per_sample_shading || m_scaled_dithering != g_settings.gpu_scaled_dithering ||
|
m_per_sample_shading != per_sample_shading || m_scaled_dithering != g_settings.gpu_scaled_dithering ||
|
||||||
m_texture_filtering != g_settings.gpu_texture_filter || m_clamp_uvs != clamp_uvs ||
|
m_texture_filtering != g_settings.gpu_texture_filter || m_clamp_uvs != clamp_uvs ||
|
||||||
m_chroma_smoothing != g_settings.gpu_24bit_chroma_smoothing || m_downsample_mode != downsample_mode ||
|
m_downsample_mode != downsample_mode ||
|
||||||
(m_downsample_mode == GPUDownsampleMode::Box &&
|
(m_downsample_mode == GPUDownsampleMode::Box &&
|
||||||
g_settings.gpu_downsample_scale != old_settings.gpu_downsample_scale) ||
|
g_settings.gpu_downsample_scale != old_settings.gpu_downsample_scale) ||
|
||||||
m_wireframe_mode != wireframe_mode || m_pgxp_depth_buffer != g_settings.UsingPGXPDepthBuffer() ||
|
m_wireframe_mode != wireframe_mode || m_pgxp_depth_buffer != g_settings.UsingPGXPDepthBuffer() ||
|
||||||
|
@ -389,7 +388,6 @@ void GPU_HW::UpdateSettings(const Settings& old_settings)
|
||||||
m_line_detect_mode = (m_resolution_scale > 1) ? g_settings.gpu_line_detect_mode : GPULineDetectMode::Disabled;
|
m_line_detect_mode = (m_resolution_scale > 1) ? g_settings.gpu_line_detect_mode : GPULineDetectMode::Disabled;
|
||||||
m_clamp_uvs = clamp_uvs;
|
m_clamp_uvs = clamp_uvs;
|
||||||
m_compute_uv_range = m_clamp_uvs;
|
m_compute_uv_range = m_clamp_uvs;
|
||||||
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_wireframe_mode = wireframe_mode;
|
||||||
m_disable_color_perspective = disable_color_perspective;
|
m_disable_color_perspective = disable_color_perspective;
|
||||||
|
@ -701,10 +699,6 @@ void GPU_HW::ClearFramebuffer()
|
||||||
g_gpu_device->ClearRenderTarget(m_vram_texture.get(), 0);
|
g_gpu_device->ClearRenderTarget(m_vram_texture.get(), 0);
|
||||||
g_gpu_device->ClearDepth(m_vram_depth_texture.get(), m_pgxp_depth_buffer ? 1.0f : 0.0f);
|
g_gpu_device->ClearDepth(m_vram_depth_texture.get(), m_pgxp_depth_buffer ? 1.0f : 0.0f);
|
||||||
ClearVRAMDirtyRectangle();
|
ClearVRAMDirtyRectangle();
|
||||||
|
|
||||||
if (m_display_private_texture)
|
|
||||||
g_gpu_device->ClearRenderTarget(m_display_private_texture.get(), 0);
|
|
||||||
|
|
||||||
m_last_depth_z = 1.0f;
|
m_last_depth_z = 1.0f;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -719,11 +713,11 @@ void GPU_HW::DestroyBuffers()
|
||||||
m_vram_upload_buffer.reset();
|
m_vram_upload_buffer.reset();
|
||||||
m_vram_readback_download_texture.reset();
|
m_vram_readback_download_texture.reset();
|
||||||
g_gpu_device->RecycleTexture(std::move(m_downsample_texture));
|
g_gpu_device->RecycleTexture(std::move(m_downsample_texture));
|
||||||
|
g_gpu_device->RecycleTexture(std::move(m_vram_extract_texture));
|
||||||
g_gpu_device->RecycleTexture(std::move(m_vram_read_texture));
|
g_gpu_device->RecycleTexture(std::move(m_vram_read_texture));
|
||||||
g_gpu_device->RecycleTexture(std::move(m_vram_depth_texture));
|
g_gpu_device->RecycleTexture(std::move(m_vram_depth_texture));
|
||||||
g_gpu_device->RecycleTexture(std::move(m_vram_texture));
|
g_gpu_device->RecycleTexture(std::move(m_vram_texture));
|
||||||
g_gpu_device->RecycleTexture(std::move(m_vram_readback_texture));
|
g_gpu_device->RecycleTexture(std::move(m_vram_readback_texture));
|
||||||
g_gpu_device->RecycleTexture(std::move(m_display_private_texture));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool GPU_HW::CompilePipelines()
|
bool GPU_HW::CompilePipelines()
|
||||||
|
@ -1123,25 +1117,20 @@ bool GPU_HW::CompilePipelines()
|
||||||
// Display
|
// Display
|
||||||
{
|
{
|
||||||
for (u8 depth_24 = 0; depth_24 < 2; depth_24++)
|
for (u8 depth_24 = 0; depth_24 < 2; depth_24++)
|
||||||
{
|
|
||||||
for (u8 interlace_mode = 0; interlace_mode < 3; interlace_mode++)
|
|
||||||
{
|
{
|
||||||
std::unique_ptr<GPUShader> fs = g_gpu_device->CreateShader(
|
std::unique_ptr<GPUShader> fs = g_gpu_device->CreateShader(
|
||||||
GPUShaderStage::Fragment,
|
GPUShaderStage::Fragment, shadergen.GenerateVRAMExtractFragmentShader(ConvertToBoolUnchecked(depth_24)));
|
||||||
shadergen.GenerateDisplayFragmentShader(
|
|
||||||
ConvertToBoolUnchecked(depth_24), static_cast<InterlacedRenderMode>(interlace_mode), m_chroma_smoothing));
|
|
||||||
if (!fs)
|
if (!fs)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
plconfig.fragment_shader = fs.get();
|
plconfig.fragment_shader = fs.get();
|
||||||
|
|
||||||
if (!(m_display_pipelines[depth_24][interlace_mode] = g_gpu_device->CreatePipeline(plconfig)))
|
if (!(m_vram_extract_pipeline[depth_24] = g_gpu_device->CreatePipeline(plconfig)))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
progress.Increment();
|
progress.Increment();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (m_downsample_mode == GPUDownsampleMode::Adaptive)
|
if (m_downsample_mode == GPUDownsampleMode::Adaptive)
|
||||||
{
|
{
|
||||||
|
@ -1242,6 +1231,9 @@ void GPU_HW::DestroyPipelines()
|
||||||
for (std::unique_ptr<GPUPipeline>& p : m_vram_copy_pipelines)
|
for (std::unique_ptr<GPUPipeline>& p : m_vram_copy_pipelines)
|
||||||
destroy(p);
|
destroy(p);
|
||||||
|
|
||||||
|
for (std::unique_ptr<GPUPipeline>& p : m_vram_extract_pipeline)
|
||||||
|
destroy(p);
|
||||||
|
|
||||||
destroy(m_vram_readback_pipeline);
|
destroy(m_vram_readback_pipeline);
|
||||||
destroy(m_vram_update_depth_pipeline);
|
destroy(m_vram_update_depth_pipeline);
|
||||||
destroy(m_vram_write_replacement_pipeline);
|
destroy(m_vram_write_replacement_pipeline);
|
||||||
|
@ -1251,8 +1243,6 @@ void GPU_HW::DestroyPipelines()
|
||||||
destroy(m_downsample_blur_pass_pipeline);
|
destroy(m_downsample_blur_pass_pipeline);
|
||||||
destroy(m_downsample_composite_pass_pipeline);
|
destroy(m_downsample_composite_pass_pipeline);
|
||||||
m_downsample_composite_sampler.reset();
|
m_downsample_composite_sampler.reset();
|
||||||
|
|
||||||
m_display_pipelines.enumerate(destroy);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
GPU_HW::BatchRenderMode GPU_HW::BatchConfig::GetRenderMode() const
|
GPU_HW::BatchRenderMode GPU_HW::BatchConfig::GetRenderMode() const
|
||||||
|
@ -1407,14 +1397,6 @@ ALWAYS_INLINE_RELEASE void GPU_HW::DrawBatchVertices(BatchRenderMode render_mode
|
||||||
g_gpu_device->DrawIndexed(num_indices, base_index, base_vertex);
|
g_gpu_device->DrawIndexed(num_indices, base_index, base_vertex);
|
||||||
}
|
}
|
||||||
|
|
||||||
void GPU_HW::ClearDisplay()
|
|
||||||
{
|
|
||||||
ClearDisplayTexture();
|
|
||||||
|
|
||||||
if (m_display_private_texture)
|
|
||||||
g_gpu_device->ClearRenderTarget(m_display_private_texture.get(), 0xFF000000u);
|
|
||||||
}
|
|
||||||
|
|
||||||
ALWAYS_INLINE_RELEASE void GPU_HW::HandleFlippedQuadTextureCoordinates(BatchVertex* vertices)
|
ALWAYS_INLINE_RELEASE void GPU_HW::HandleFlippedQuadTextureCoordinates(BatchVertex* vertices)
|
||||||
{
|
{
|
||||||
// Taken from beetle-psx gpu_polygon.cpp
|
// Taken from beetle-psx gpu_polygon.cpp
|
||||||
|
@ -2417,19 +2399,6 @@ ALWAYS_INLINE bool GPU_HW::IsFlushed() const
|
||||||
return (m_batch_index_count == 0);
|
return (m_batch_index_count == 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
GPU_HW::InterlacedRenderMode GPU_HW::GetInterlacedRenderMode() const
|
|
||||||
{
|
|
||||||
if (IsInterlacedDisplayEnabled())
|
|
||||||
{
|
|
||||||
return m_GPUSTAT.vertical_resolution ? InterlacedRenderMode::InterleavedFields :
|
|
||||||
InterlacedRenderMode::SeparateFields;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return InterlacedRenderMode::None;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ALWAYS_INLINE_RELEASE bool GPU_HW::NeedsTwoPassRendering() const
|
ALWAYS_INLINE_RELEASE bool GPU_HW::NeedsTwoPassRendering() const
|
||||||
{
|
{
|
||||||
// We need two-pass rendering when using BG-FG blending and texturing, as the transparency can be enabled
|
// We need two-pass rendering when using BG-FG blending and texturing, as the transparency can be enabled
|
||||||
|
@ -3104,6 +3073,8 @@ void GPU_HW::UpdateDisplay()
|
||||||
{
|
{
|
||||||
FlushRender();
|
FlushRender();
|
||||||
|
|
||||||
|
GL_SCOPE("UpdateDisplay()");
|
||||||
|
|
||||||
if (g_settings.debugging.show_vram)
|
if (g_settings.debugging.show_vram)
|
||||||
{
|
{
|
||||||
if (IsUsingMultisampling())
|
if (IsUsingMultisampling())
|
||||||
|
@ -3119,90 +3090,107 @@ void GPU_HW::UpdateDisplay()
|
||||||
|
|
||||||
SetDisplayParameters(VRAM_WIDTH, VRAM_HEIGHT, 0, 0, VRAM_WIDTH, VRAM_HEIGHT,
|
SetDisplayParameters(VRAM_WIDTH, VRAM_HEIGHT, 0, 0, VRAM_WIDTH, VRAM_HEIGHT,
|
||||||
static_cast<float>(VRAM_WIDTH) / static_cast<float>(VRAM_HEIGHT));
|
static_cast<float>(VRAM_WIDTH) / static_cast<float>(VRAM_HEIGHT));
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
|
||||||
// TODO: use a dynamically sized texture
|
|
||||||
SetDisplayParameters(m_crtc_state.display_width, m_crtc_state.display_height, m_crtc_state.display_origin_left,
|
SetDisplayParameters(m_crtc_state.display_width, m_crtc_state.display_height, m_crtc_state.display_origin_left,
|
||||||
m_crtc_state.display_origin_top, m_crtc_state.display_vram_width,
|
m_crtc_state.display_origin_top, m_crtc_state.display_vram_width,
|
||||||
m_crtc_state.display_vram_height, ComputeDisplayAspectRatio());
|
m_crtc_state.display_vram_height, ComputeDisplayAspectRatio());
|
||||||
|
|
||||||
|
const bool interlaced = IsInterlacedDisplayEnabled();
|
||||||
|
const u32 interlaced_field = GetInterlacedDisplayField();
|
||||||
const u32 resolution_scale = m_GPUSTAT.display_area_color_depth_24 ? 1 : m_resolution_scale;
|
const u32 resolution_scale = m_GPUSTAT.display_area_color_depth_24 ? 1 : m_resolution_scale;
|
||||||
const u32 vram_offset_x = m_crtc_state.display_vram_left;
|
const u32 scaled_vram_offset_x = m_crtc_state.display_vram_left * resolution_scale;
|
||||||
const u32 vram_offset_y = m_crtc_state.display_vram_top;
|
const u32 scaled_vram_offset_y = (m_crtc_state.display_vram_top * resolution_scale) +
|
||||||
const u32 scaled_vram_offset_x = vram_offset_x * resolution_scale;
|
((interlaced && m_GPUSTAT.vertical_resolution) ? interlaced_field : 0);
|
||||||
const u32 scaled_vram_offset_y = vram_offset_y * resolution_scale;
|
const u32 scaled_display_width = m_crtc_state.display_vram_width * resolution_scale;
|
||||||
const u32 display_width = m_crtc_state.display_vram_width;
|
const u32 scaled_display_height = m_crtc_state.display_vram_height * resolution_scale;
|
||||||
const u32 display_height = m_crtc_state.display_vram_height;
|
const u32 read_height = interlaced ? (scaled_display_height / 2u) : scaled_display_height;
|
||||||
const u32 scaled_display_width = display_width * resolution_scale;
|
const u32 line_skip = m_GPUSTAT.vertical_resolution;
|
||||||
const u32 scaled_display_height = display_height * resolution_scale;
|
bool drew_anything = false;
|
||||||
const InterlacedRenderMode interlaced = GetInterlacedRenderMode();
|
|
||||||
|
|
||||||
if (IsDisplayDisabled())
|
if (IsDisplayDisabled())
|
||||||
{
|
{
|
||||||
ClearDisplayTexture();
|
ClearDisplayTexture();
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
else if (!m_GPUSTAT.display_area_color_depth_24 && interlaced == InterlacedRenderMode::None &&
|
else if (!m_GPUSTAT.display_area_color_depth_24 && !IsUsingMultisampling() &&
|
||||||
!IsUsingMultisampling() && (scaled_vram_offset_x + scaled_display_width) <= m_vram_texture->GetWidth() &&
|
(scaled_vram_offset_x + scaled_display_width) <= m_vram_texture->GetWidth() &&
|
||||||
(scaled_vram_offset_y + scaled_display_height) <= m_vram_texture->GetHeight())
|
(scaled_vram_offset_y + scaled_display_height) <= m_vram_texture->GetHeight())
|
||||||
{
|
{
|
||||||
|
// Fast path if no copies are needed.
|
||||||
if (IsUsingDownsampling())
|
if (interlaced)
|
||||||
{
|
{
|
||||||
DownsampleFramebuffer(m_vram_texture.get(), scaled_vram_offset_x, scaled_vram_offset_y, scaled_display_width,
|
GL_INS("Deinterlace fast path");
|
||||||
scaled_display_height);
|
drew_anything = true;
|
||||||
|
Deinterlace(m_vram_texture.get(), scaled_vram_offset_x, scaled_vram_offset_y, scaled_display_width, read_height,
|
||||||
|
interlaced_field, line_skip);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
GL_INS("Direct display");
|
||||||
SetDisplayTexture(m_vram_texture.get(), scaled_vram_offset_x, scaled_vram_offset_y, scaled_display_width,
|
SetDisplayTexture(m_vram_texture.get(), scaled_vram_offset_x, scaled_vram_offset_y, scaled_display_width,
|
||||||
scaled_display_height);
|
scaled_display_height);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (!m_display_private_texture || m_display_private_texture->GetWidth() != scaled_display_width ||
|
if (!m_vram_extract_texture || m_vram_extract_texture->GetWidth() != scaled_display_width ||
|
||||||
m_display_private_texture->GetHeight() != scaled_display_height)
|
m_vram_extract_texture->GetHeight() != read_height)
|
||||||
{
|
{
|
||||||
g_gpu_device->RecycleTexture(std::move(m_display_private_texture));
|
if (!g_gpu_device->ResizeTexture(&m_vram_extract_texture, scaled_display_width, read_height,
|
||||||
if (!(m_display_private_texture = g_gpu_device->FetchTexture(
|
GPUTexture::Type::RenderTarget, GPUTexture::Format::RGBA8)) [[unlikely]]
|
||||||
scaled_display_width, scaled_display_height, 1, 1, 1, GPUTexture::Type::RenderTarget, VRAM_RT_FORMAT)))
|
|
||||||
{
|
{
|
||||||
Log_ErrorFmt("Failed to create {}x{} display texture", scaled_display_width, scaled_display_height);
|
|
||||||
ClearDisplayTexture();
|
ClearDisplayTexture();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
GL_OBJECT_NAME(m_display_private_texture, "Display Texture");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: discard vs load for interlaced
|
g_gpu_device->InvalidateRenderTarget(m_vram_extract_texture.get());
|
||||||
if (interlaced == InterlacedRenderMode::None)
|
g_gpu_device->SetRenderTarget(m_vram_extract_texture.get());
|
||||||
g_gpu_device->InvalidateRenderTarget(m_display_private_texture.get());
|
g_gpu_device->SetPipeline(m_vram_extract_pipeline[BoolToUInt8(m_GPUSTAT.display_area_color_depth_24)].get());
|
||||||
|
|
||||||
g_gpu_device->SetRenderTarget(m_display_private_texture.get());
|
|
||||||
g_gpu_device->SetPipeline(
|
|
||||||
m_display_pipelines[BoolToUInt8(m_GPUSTAT.display_area_color_depth_24)][static_cast<u8>(interlaced)].get());
|
|
||||||
g_gpu_device->SetTextureSampler(0, m_vram_texture.get(), g_gpu_device->GetNearestSampler());
|
g_gpu_device->SetTextureSampler(0, m_vram_texture.get(), g_gpu_device->GetNearestSampler());
|
||||||
|
|
||||||
const u32 reinterpret_field_offset = (interlaced != InterlacedRenderMode::None) ? GetInterlacedDisplayField() : 0;
|
|
||||||
const u32 reinterpret_start_x = m_crtc_state.regs.X * resolution_scale;
|
const u32 reinterpret_start_x = m_crtc_state.regs.X * resolution_scale;
|
||||||
const u32 reinterpret_crop_left = (m_crtc_state.display_vram_left - m_crtc_state.regs.X) * resolution_scale;
|
const u32 skip_x = (m_crtc_state.display_vram_left - m_crtc_state.regs.X) * resolution_scale;
|
||||||
const u32 uniforms[4] = {reinterpret_start_x, scaled_vram_offset_y + reinterpret_field_offset,
|
GL_INS_FMT("Convert 16bpp to 24bpp, skip_x = {}, line_skip = {}", skip_x, line_skip);
|
||||||
reinterpret_crop_left, reinterpret_field_offset};
|
|
||||||
|
const u32 uniforms[4] = {reinterpret_start_x, scaled_vram_offset_y, skip_x, line_skip};
|
||||||
g_gpu_device->PushUniformBuffer(uniforms, sizeof(uniforms));
|
g_gpu_device->PushUniformBuffer(uniforms, sizeof(uniforms));
|
||||||
|
|
||||||
g_gpu_device->SetViewportAndScissor(0, 0, scaled_display_width, scaled_display_height);
|
g_gpu_device->SetViewportAndScissor(0, 0, scaled_display_width, read_height);
|
||||||
g_gpu_device->Draw(3, 0);
|
g_gpu_device->Draw(3, 0);
|
||||||
|
|
||||||
if (IsUsingDownsampling())
|
m_vram_extract_texture->MakeReadyForSampling();
|
||||||
DownsampleFramebuffer(m_display_private_texture.get(), 0, 0, scaled_display_width, scaled_display_height);
|
drew_anything = true;
|
||||||
else
|
|
||||||
SetDisplayTexture(m_display_private_texture.get(), 0, 0, scaled_display_width, scaled_display_height);
|
|
||||||
|
|
||||||
|
if (g_settings.gpu_24bit_chroma_smoothing)
|
||||||
|
{
|
||||||
|
if (ApplyChromaSmoothing(m_vram_extract_texture.get(), 0, 0, scaled_display_width, read_height))
|
||||||
|
{
|
||||||
|
if (interlaced)
|
||||||
|
Deinterlace(m_display_texture, 0, 0, scaled_display_width, read_height, interlaced_field, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (interlaced)
|
||||||
|
Deinterlace(m_vram_extract_texture.get(), 0, 0, scaled_display_width, read_height, interlaced_field, 0);
|
||||||
|
else
|
||||||
|
SetDisplayTexture(m_vram_extract_texture.get(), 0, 0, scaled_display_width, read_height);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_downsample_mode != GPUDownsampleMode::Disabled)
|
||||||
|
{
|
||||||
|
DebugAssert(m_display_texture);
|
||||||
|
DownsampleFramebuffer(m_display_texture, m_display_texture_view_x, m_display_texture_view_y,
|
||||||
|
m_display_texture_view_width, m_display_texture_view_height);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (drew_anything)
|
||||||
RestoreDeviceContext();
|
RestoreDeviceContext();
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void GPU_HW::DownsampleFramebuffer(GPUTexture* source, u32 left, u32 top, u32 width, u32 height)
|
void GPU_HW::DownsampleFramebuffer(GPUTexture* source, u32 left, u32 top, u32 width, u32 height)
|
||||||
{
|
{
|
||||||
|
|
|
@ -32,13 +32,6 @@ public:
|
||||||
OnlyTransparent
|
OnlyTransparent
|
||||||
};
|
};
|
||||||
|
|
||||||
enum class InterlacedRenderMode : u8
|
|
||||||
{
|
|
||||||
None,
|
|
||||||
InterleavedFields,
|
|
||||||
SeparateFields
|
|
||||||
};
|
|
||||||
|
|
||||||
GPU_HW();
|
GPU_HW();
|
||||||
~GPU_HW() override;
|
~GPU_HW() override;
|
||||||
|
|
||||||
|
@ -56,7 +49,6 @@ public:
|
||||||
std::tuple<u32, u32> GetEffectiveDisplayResolution(bool scaled = true) override final;
|
std::tuple<u32, u32> GetEffectiveDisplayResolution(bool scaled = true) override final;
|
||||||
std::tuple<u32, u32> GetFullDisplayResolution(bool scaled = true) override final;
|
std::tuple<u32, u32> GetFullDisplayResolution(bool scaled = true) override final;
|
||||||
|
|
||||||
void ClearDisplay() override;
|
|
||||||
void UpdateDisplay() override;
|
void UpdateDisplay() override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
@ -162,9 +154,6 @@ private:
|
||||||
/// Returns the value to be written to the depth buffer for the current operation for mask bit emulation.
|
/// Returns the value to be written to the depth buffer for the current operation for mask bit emulation.
|
||||||
float GetCurrentNormalizedVertexDepth() const;
|
float GetCurrentNormalizedVertexDepth() const;
|
||||||
|
|
||||||
/// Returns the interlaced mode to use when scanning out/displaying.
|
|
||||||
InterlacedRenderMode GetInterlacedRenderMode() const;
|
|
||||||
|
|
||||||
/// Returns if the draw needs to be broken into opaque/transparent passes.
|
/// Returns if the draw needs to be broken into opaque/transparent passes.
|
||||||
bool NeedsTwoPassRendering() const;
|
bool NeedsTwoPassRendering() const;
|
||||||
|
|
||||||
|
@ -212,7 +201,6 @@ private:
|
||||||
std::unique_ptr<GPUTexture> m_vram_readback_texture;
|
std::unique_ptr<GPUTexture> m_vram_readback_texture;
|
||||||
std::unique_ptr<GPUDownloadTexture> m_vram_readback_download_texture;
|
std::unique_ptr<GPUDownloadTexture> m_vram_readback_download_texture;
|
||||||
std::unique_ptr<GPUTexture> m_vram_replacement_texture;
|
std::unique_ptr<GPUTexture> m_vram_replacement_texture;
|
||||||
std::unique_ptr<GPUTexture> m_display_private_texture; // TODO: Move to base.
|
|
||||||
|
|
||||||
std::unique_ptr<GPUTextureBuffer> m_vram_upload_buffer;
|
std::unique_ptr<GPUTextureBuffer> m_vram_upload_buffer;
|
||||||
std::unique_ptr<GPUTexture> m_vram_write_texture;
|
std::unique_ptr<GPUTexture> m_vram_write_texture;
|
||||||
|
@ -237,7 +225,6 @@ private:
|
||||||
bool m_supports_framebuffer_fetch : 1 = false;
|
bool m_supports_framebuffer_fetch : 1 = false;
|
||||||
bool m_per_sample_shading : 1 = false;
|
bool m_per_sample_shading : 1 = false;
|
||||||
bool m_scaled_dithering : 1 = false;
|
bool m_scaled_dithering : 1 = false;
|
||||||
bool m_chroma_smoothing : 1 = false;
|
|
||||||
bool m_disable_color_perspective : 1 = false;
|
bool m_disable_color_perspective : 1 = false;
|
||||||
|
|
||||||
GPUTextureFilter m_texture_filtering = GPUTextureFilter::Nearest;
|
GPUTextureFilter m_texture_filtering = GPUTextureFilter::Nearest;
|
||||||
|
@ -275,12 +262,11 @@ private:
|
||||||
|
|
||||||
std::unique_ptr<GPUPipeline> m_vram_readback_pipeline;
|
std::unique_ptr<GPUPipeline> m_vram_readback_pipeline;
|
||||||
std::unique_ptr<GPUPipeline> m_vram_update_depth_pipeline;
|
std::unique_ptr<GPUPipeline> m_vram_update_depth_pipeline;
|
||||||
|
|
||||||
// [depth_24][interlace_mode]
|
|
||||||
DimensionalArray<std::unique_ptr<GPUPipeline>, 3, 2> m_display_pipelines{};
|
|
||||||
|
|
||||||
std::unique_ptr<GPUPipeline> m_vram_write_replacement_pipeline;
|
std::unique_ptr<GPUPipeline> m_vram_write_replacement_pipeline;
|
||||||
|
|
||||||
|
std::array<std::unique_ptr<GPUPipeline>, 2> m_vram_extract_pipeline; // [24bit]
|
||||||
|
std::unique_ptr<GPUTexture> m_vram_extract_texture;
|
||||||
|
|
||||||
std::unique_ptr<GPUTexture> m_downsample_texture;
|
std::unique_ptr<GPUTexture> m_downsample_texture;
|
||||||
std::unique_ptr<GPUPipeline> m_downsample_first_pass_pipeline;
|
std::unique_ptr<GPUPipeline> m_downsample_first_pass_pipeline;
|
||||||
std::unique_ptr<GPUPipeline> m_downsample_mid_pass_pipeline;
|
std::unique_ptr<GPUPipeline> m_downsample_mid_pass_pipeline;
|
||||||
|
|
|
@ -1019,36 +1019,18 @@ float3 ApplyDebanding(float2 frag_coord)
|
||||||
return ss.str();
|
return ss.str();
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string GPU_HW_ShaderGen::GenerateDisplayFragmentShader(bool depth_24bit,
|
std::string GPU_HW_ShaderGen::GenerateVRAMExtractFragmentShader(bool depth_24bit)
|
||||||
GPU_HW::InterlacedRenderMode interlace_mode,
|
|
||||||
bool smooth_chroma)
|
|
||||||
{
|
{
|
||||||
std::stringstream ss;
|
std::stringstream ss;
|
||||||
WriteHeader(ss);
|
WriteHeader(ss);
|
||||||
DefineMacro(ss, "DEPTH_24BIT", depth_24bit);
|
DefineMacro(ss, "DEPTH_24BIT", depth_24bit);
|
||||||
DefineMacro(ss, "INTERLACED", interlace_mode != GPU_HW::InterlacedRenderMode::None);
|
DefineMacro(ss, "MULTISAMPLED", UsingMSAA());
|
||||||
DefineMacro(ss, "INTERLEAVED", interlace_mode == GPU_HW::InterlacedRenderMode::InterleavedFields);
|
|
||||||
DefineMacro(ss, "SMOOTH_CHROMA", smooth_chroma);
|
|
||||||
|
|
||||||
WriteCommonFunctions(ss);
|
WriteCommonFunctions(ss);
|
||||||
DeclareUniformBuffer(ss, {"uint2 u_vram_offset", "uint u_crop_left", "uint u_field_offset"}, true);
|
DeclareUniformBuffer(ss, {"uint2 u_vram_offset", "uint u_skip_x", "uint u_line_skip"}, true);
|
||||||
DeclareTexture(ss, "samp0", 0, UsingMSAA());
|
DeclareTexture(ss, "samp0", 0, UsingMSAA());
|
||||||
|
|
||||||
ss << R"(
|
ss << R"(
|
||||||
float3 RGBToYUV(float3 rgb)
|
|
||||||
{
|
|
||||||
return float3(dot(rgb.rgb, float3(0.299f, 0.587f, 0.114f)),
|
|
||||||
dot(rgb.rgb, float3(-0.14713f, -0.28886f, 0.436f)),
|
|
||||||
dot(rgb.rgb, float3(0.615f, -0.51499f, -0.10001f)));
|
|
||||||
}
|
|
||||||
|
|
||||||
float3 YUVToRGB(float3 yuv)
|
|
||||||
{
|
|
||||||
return float3(dot(yuv, float3(1.0f, 0.0f, 1.13983f)),
|
|
||||||
dot(yuv, float3(1.0f, -0.39465f, -0.58060f)),
|
|
||||||
dot(yuv, float3(1.0f, 2.03211f, 0.0f)));
|
|
||||||
}
|
|
||||||
|
|
||||||
float4 LoadVRAM(int2 coords)
|
float4 LoadVRAM(int2 coords)
|
||||||
{
|
{
|
||||||
#if MULTISAMPLING
|
#if MULTISAMPLING
|
||||||
|
@ -1079,61 +1061,15 @@ float3 SampleVRAM24(uint2 icoords)
|
||||||
return float3(float(s1s0 & 0xFFu) / 255.0, float((s1s0 >> 8u) & 0xFFu) / 255.0,
|
return float3(float(s1s0 & 0xFFu) / 255.0, float((s1s0 >> 8u) & 0xFFu) / 255.0,
|
||||||
float((s1s0 >> 16u) & 0xFFu) / 255.0);
|
float((s1s0 >> 16u) & 0xFFu) / 255.0);
|
||||||
}
|
}
|
||||||
|
|
||||||
float3 SampleVRAMAverage2x2(uint2 icoords)
|
|
||||||
{
|
|
||||||
float3 value = SampleVRAM24(icoords);
|
|
||||||
value += SampleVRAM24(icoords + uint2(0, 1));
|
|
||||||
value += SampleVRAM24(icoords + uint2(1, 0));
|
|
||||||
value += SampleVRAM24(icoords + uint2(1, 1));
|
|
||||||
return value * 0.25;
|
|
||||||
}
|
|
||||||
|
|
||||||
float3 SampleVRAM24Smoothed(uint2 icoords)
|
|
||||||
{
|
|
||||||
int2 base = int2(icoords) - 1;
|
|
||||||
uint2 low = uint2(max(base & ~1, int2(0, 0)));
|
|
||||||
uint2 high = low + 2u;
|
|
||||||
float2 coeff = vec2(base & 1) * 0.5 + 0.25;
|
|
||||||
|
|
||||||
float3 p = SampleVRAM24(icoords);
|
|
||||||
float3 p00 = SampleVRAMAverage2x2(low);
|
|
||||||
float3 p01 = SampleVRAMAverage2x2(uint2(low.x, high.y));
|
|
||||||
float3 p10 = SampleVRAMAverage2x2(uint2(high.x, low.y));
|
|
||||||
float3 p11 = SampleVRAMAverage2x2(high);
|
|
||||||
|
|
||||||
float3 s = lerp(lerp(p00, p10, coeff.x),
|
|
||||||
lerp(p01, p11, coeff.x),
|
|
||||||
coeff.y);
|
|
||||||
|
|
||||||
float y = RGBToYUV(p).x;
|
|
||||||
float2 uv = RGBToYUV(s).yz;
|
|
||||||
return YUVToRGB(float3(y, uv));
|
|
||||||
}
|
|
||||||
)";
|
)";
|
||||||
|
|
||||||
DeclareFragmentEntryPoint(ss, 0, 1, {}, true, 1);
|
DeclareFragmentEntryPoint(ss, 0, 1, {}, true, 1);
|
||||||
ss << R"(
|
ss << R"(
|
||||||
{
|
{
|
||||||
uint2 icoords = uint2(v_pos.xy) + uint2(u_crop_left, 0u);
|
uint2 icoords = uint2(uint(v_pos.x) + u_skip_x, uint(v_pos.y) << u_line_skip);
|
||||||
|
|
||||||
#if INTERLACED
|
|
||||||
if ((icoords.y & 1u) != u_field_offset)
|
|
||||||
discard;
|
|
||||||
|
|
||||||
#if !INTERLEAVED
|
|
||||||
icoords.y /= 2u;
|
|
||||||
#else
|
|
||||||
icoords.y &= ~1u;
|
|
||||||
#endif
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if DEPTH_24BIT
|
#if DEPTH_24BIT
|
||||||
#if SMOOTH_CHROMA
|
|
||||||
o_col0 = float4(SampleVRAM24Smoothed(icoords), 1.0);
|
|
||||||
#else
|
|
||||||
o_col0 = float4(SampleVRAM24(icoords), 1.0);
|
o_col0 = float4(SampleVRAM24(icoords), 1.0);
|
||||||
#endif
|
|
||||||
#else
|
#else
|
||||||
o_col0 = float4(LoadVRAM(int2((icoords + u_vram_offset) % VRAM_SIZE)).rgb, 1.0);
|
o_col0 = float4(LoadVRAM(int2((icoords + u_vram_offset) % VRAM_SIZE)).rgb, 1.0);
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -17,8 +17,6 @@ public:
|
||||||
std::string GenerateBatchVertexShader(bool textured);
|
std::string GenerateBatchVertexShader(bool textured);
|
||||||
std::string GenerateBatchFragmentShader(GPU_HW::BatchRenderMode render_mode, GPUTransparencyMode transparency,
|
std::string GenerateBatchFragmentShader(GPU_HW::BatchRenderMode render_mode, GPUTransparencyMode transparency,
|
||||||
GPUTextureMode texture_mode, bool dithering, bool interlacing);
|
GPUTextureMode texture_mode, bool dithering, bool interlacing);
|
||||||
std::string GenerateDisplayFragmentShader(bool depth_24bit, GPU_HW::InterlacedRenderMode interlace_mode,
|
|
||||||
bool smooth_chroma);
|
|
||||||
std::string GenerateWireframeGeometryShader();
|
std::string GenerateWireframeGeometryShader();
|
||||||
std::string GenerateWireframeFragmentShader();
|
std::string GenerateWireframeFragmentShader();
|
||||||
std::string GenerateVRAMReadFragmentShader();
|
std::string GenerateVRAMReadFragmentShader();
|
||||||
|
@ -26,6 +24,7 @@ public:
|
||||||
std::string GenerateVRAMCopyFragmentShader();
|
std::string GenerateVRAMCopyFragmentShader();
|
||||||
std::string GenerateVRAMFillFragmentShader(bool wrapped, bool interlaced);
|
std::string GenerateVRAMFillFragmentShader(bool wrapped, bool interlaced);
|
||||||
std::string GenerateVRAMUpdateDepthFragmentShader();
|
std::string GenerateVRAMUpdateDepthFragmentShader();
|
||||||
|
std::string GenerateVRAMExtractFragmentShader(bool depth_24bit);
|
||||||
|
|
||||||
std::string GenerateAdaptiveDownsampleVertexShader();
|
std::string GenerateAdaptiveDownsampleVertexShader();
|
||||||
std::string GenerateAdaptiveDownsampleMipFragmentShader(bool first_pass);
|
std::string GenerateAdaptiveDownsampleMipFragmentShader(bool first_pass);
|
||||||
|
|
|
@ -83,3 +83,181 @@ std::string GPUShaderGen::GenerateDisplaySharpBilinearFragmentShader()
|
||||||
|
|
||||||
return ss.str();
|
return ss.str();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string GPUShaderGen::GenerateInterleavedFieldExtractFragmentShader()
|
||||||
|
{
|
||||||
|
std::stringstream ss;
|
||||||
|
WriteHeader(ss);
|
||||||
|
DeclareUniformBuffer(ss, {"uint2 u_src_offset", "uint u_line_skip"}, true);
|
||||||
|
DeclareTexture(ss, "samp0", 0, false);
|
||||||
|
|
||||||
|
DeclareFragmentEntryPoint(ss, 0, 1, {}, true);
|
||||||
|
ss << R"(
|
||||||
|
{
|
||||||
|
uint2 tcoord = u_src_offset + uint2(uint(v_pos.x), uint(v_pos.y) << u_line_skip);
|
||||||
|
o_col0 = LOAD_TEXTURE(samp0, int2(tcoord), 0);
|
||||||
|
}
|
||||||
|
)";
|
||||||
|
|
||||||
|
return ss.str();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string GPUShaderGen::GenerateDeinterlaceWeaveFragmentShader()
|
||||||
|
{
|
||||||
|
std::stringstream ss;
|
||||||
|
WriteHeader(ss);
|
||||||
|
DeclareUniformBuffer(ss, {"uint2 u_src_offset", "uint u_render_field", "uint u_line_skip"}, true);
|
||||||
|
DeclareTexture(ss, "samp0", 0, false);
|
||||||
|
|
||||||
|
DeclareFragmentEntryPoint(ss, 0, 1, {}, true);
|
||||||
|
ss << R"(
|
||||||
|
{
|
||||||
|
uint2 fcoord = uint2(v_pos.xy);
|
||||||
|
if ((fcoord.y & 1) != u_render_field)
|
||||||
|
discard;
|
||||||
|
|
||||||
|
uint2 tcoord = u_src_offset + uint2(fcoord.x, (fcoord.y / 2u) << u_line_skip);
|
||||||
|
o_col0 = LOAD_TEXTURE(samp0, int2(tcoord), 0);
|
||||||
|
})";
|
||||||
|
|
||||||
|
return ss.str();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string GPUShaderGen::GenerateDeinterlaceBlendFragmentShader()
|
||||||
|
{
|
||||||
|
std::stringstream ss;
|
||||||
|
WriteHeader(ss);
|
||||||
|
DeclareTexture(ss, "samp0", 0, false);
|
||||||
|
DeclareTexture(ss, "samp1", 1, false);
|
||||||
|
|
||||||
|
DeclareFragmentEntryPoint(ss, 0, 1, {}, true);
|
||||||
|
ss << R"(
|
||||||
|
{
|
||||||
|
uint2 uv = uint2(v_pos.xy);
|
||||||
|
float4 c0 = LOAD_TEXTURE(samp0, int2(uv), 0);
|
||||||
|
float4 c1 = LOAD_TEXTURE(samp1, int2(uv), 0);
|
||||||
|
o_col0 = (c0 + c1) * 0.5f;
|
||||||
|
}
|
||||||
|
)";
|
||||||
|
|
||||||
|
return ss.str();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string GPUShaderGen::GenerateFastMADReconstructFragmentShader()
|
||||||
|
{
|
||||||
|
std::stringstream ss;
|
||||||
|
WriteHeader(ss);
|
||||||
|
DeclareUniformBuffer(ss, {"uint u_current_field", "uint u_height"}, true);
|
||||||
|
DeclareTexture(ss, "samp0", 0, false);
|
||||||
|
DeclareTexture(ss, "samp1", 1, false);
|
||||||
|
DeclareTexture(ss, "samp2", 2, false);
|
||||||
|
DeclareTexture(ss, "samp3", 3, false);
|
||||||
|
|
||||||
|
ss << R"(
|
||||||
|
CONSTANT float3 SENSITIVITY = float3(0.08f, 0.08f, 0.08f);
|
||||||
|
)";
|
||||||
|
|
||||||
|
DeclareFragmentEntryPoint(ss, 0, 1, {}, true);
|
||||||
|
ss << R"(
|
||||||
|
{
|
||||||
|
int2 uv = int2(int(v_pos.x), int(v_pos.y) >> 1);
|
||||||
|
float3 cur = LOAD_TEXTURE(samp0, uv, 0).rgb;
|
||||||
|
|
||||||
|
float3 hn = LOAD_TEXTURE(samp0, uv + int2(0, -1), 0).rgb;
|
||||||
|
float3 cn = LOAD_TEXTURE(samp1, uv, 0).rgb;
|
||||||
|
float3 ln = LOAD_TEXTURE(samp0, uv + int2(0, 1), 0).rgb;
|
||||||
|
|
||||||
|
float3 ho = LOAD_TEXTURE(samp2, uv + int2(0, -1), 0).rgb;
|
||||||
|
float3 co = LOAD_TEXTURE(samp3, uv, 0).rgb;
|
||||||
|
float3 lo = LOAD_TEXTURE(samp2, uv + int2(0, 1), 0).rgb;
|
||||||
|
|
||||||
|
float3 mh = abs(hn.rgb - ho.rgb) - SENSITIVITY;
|
||||||
|
float3 mc = abs(cn.rgb - co.rgb) - SENSITIVITY;
|
||||||
|
float3 ml = abs(ln.rgb - lo.rgb) - SENSITIVITY;
|
||||||
|
float3 mmaxv = max(mh, max(mc, ml));
|
||||||
|
float mmax = max(mmaxv.r, max(mmaxv.g, mmaxv.b));
|
||||||
|
|
||||||
|
// Is pixel F [n][ x , y ] present in the Current Field f [n] ?
|
||||||
|
uint row = uint(v_pos.y);
|
||||||
|
if ((row & 1u) == u_current_field)
|
||||||
|
{
|
||||||
|
// Directly uses the pixel from the Current Field
|
||||||
|
o_col0.rgb = cur;
|
||||||
|
}
|
||||||
|
else if (row > 0 && row < u_height && mmax > 0.0f)
|
||||||
|
{
|
||||||
|
// Reconstructs the missing pixel as the average of the same pixel from the line above and the
|
||||||
|
// line below it in the Current Field.
|
||||||
|
o_col0.rgb = (hn + ln) / 2.0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Reconstructs the missing pixel as the same pixel from the Previous Field.
|
||||||
|
o_col0.rgb = cn;
|
||||||
|
}
|
||||||
|
o_col0.a = 1.0f;
|
||||||
|
}
|
||||||
|
)";
|
||||||
|
|
||||||
|
return ss.str();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string GPUShaderGen::GenerateChromaSmoothingFragmentShader()
|
||||||
|
{
|
||||||
|
std::stringstream ss;
|
||||||
|
WriteHeader(ss);
|
||||||
|
DeclareUniformBuffer(ss, {"uint2 u_sample_offset", "uint2 u_clamp_size"}, true);
|
||||||
|
DeclareTexture(ss, "samp0", 0);
|
||||||
|
|
||||||
|
ss << R"(
|
||||||
|
float3 RGBToYUV(float3 rgb)
|
||||||
|
{
|
||||||
|
return float3(dot(rgb.rgb, float3(0.299f, 0.587f, 0.114f)),
|
||||||
|
dot(rgb.rgb, float3(-0.14713f, -0.28886f, 0.436f)),
|
||||||
|
dot(rgb.rgb, float3(0.615f, -0.51499f, -0.10001f)));
|
||||||
|
}
|
||||||
|
|
||||||
|
float3 YUVToRGB(float3 yuv)
|
||||||
|
{
|
||||||
|
return float3(dot(yuv, float3(1.0f, 0.0f, 1.13983f)),
|
||||||
|
dot(yuv, float3(1.0f, -0.39465f, -0.58060f)),
|
||||||
|
dot(yuv, float3(1.0f, 2.03211f, 0.0f)));
|
||||||
|
}
|
||||||
|
|
||||||
|
float3 SampleVRAMAverage2x2(uint2 icoords)
|
||||||
|
{
|
||||||
|
float3 value = LOAD_TEXTURE(samp0, icoords, 0).rgb;
|
||||||
|
value += LOAD_TEXTURE(samp0, icoords + uint2(0, 1), 0).rgb;
|
||||||
|
value += LOAD_TEXTURE(samp0, icoords + uint2(1, 0), 0).rgb;
|
||||||
|
value += LOAD_TEXTURE(samp0, icoords + uint2(1, 1), 0).rgb;
|
||||||
|
return value * 0.25;
|
||||||
|
}
|
||||||
|
)";
|
||||||
|
|
||||||
|
DeclareFragmentEntryPoint(ss, 0, 1, {}, true, 1);
|
||||||
|
ss << R"(
|
||||||
|
{
|
||||||
|
uint2 icoords = uint2(v_pos.xy) + u_sample_offset;
|
||||||
|
int2 base = int2(icoords) - 1;
|
||||||
|
uint2 low = uint2(max(base & ~1, int2(0, 0)));
|
||||||
|
uint2 high = min(low + 2u, u_clamp_size);
|
||||||
|
float2 coeff = vec2(base & 1) * 0.5 + 0.25;
|
||||||
|
|
||||||
|
float3 p = LOAD_TEXTURE(samp0, icoords, 0);
|
||||||
|
float3 p00 = SampleVRAMAverage2x2(low);
|
||||||
|
float3 p01 = SampleVRAMAverage2x2(uint2(low.x, high.y));
|
||||||
|
float3 p10 = SampleVRAMAverage2x2(uint2(high.x, low.y));
|
||||||
|
float3 p11 = SampleVRAMAverage2x2(high);
|
||||||
|
|
||||||
|
float3 s = lerp(lerp(p00, p10, coeff.x),
|
||||||
|
lerp(p01, p11, coeff.x),
|
||||||
|
coeff.y);
|
||||||
|
|
||||||
|
float y = RGBToYUV(p).x;
|
||||||
|
float2 uv = RGBToYUV(s).yz;
|
||||||
|
o_col0 = float4(YUVToRGB(float3(y, uv)), 1.0);
|
||||||
|
}
|
||||||
|
)";
|
||||||
|
|
||||||
|
return ss.str();
|
||||||
|
}
|
||||||
|
|
|
@ -15,6 +15,13 @@ public:
|
||||||
std::string GenerateDisplayFragmentShader(bool clamp_uv);
|
std::string GenerateDisplayFragmentShader(bool clamp_uv);
|
||||||
std::string GenerateDisplaySharpBilinearFragmentShader();
|
std::string GenerateDisplaySharpBilinearFragmentShader();
|
||||||
|
|
||||||
|
std::string GenerateInterleavedFieldExtractFragmentShader();
|
||||||
|
std::string GenerateDeinterlaceWeaveFragmentShader();
|
||||||
|
std::string GenerateDeinterlaceBlendFragmentShader();
|
||||||
|
std::string GenerateFastMADReconstructFragmentShader();
|
||||||
|
|
||||||
|
std::string GenerateChromaSmoothingFragmentShader();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void WriteDisplayUniformBuffer(std::stringstream& ss);
|
void WriteDisplayUniformBuffer(std::stringstream& ss);
|
||||||
};
|
};
|
||||||
|
|
|
@ -28,7 +28,7 @@ GPU_SW::GPU_SW() = default;
|
||||||
|
|
||||||
GPU_SW::~GPU_SW()
|
GPU_SW::~GPU_SW()
|
||||||
{
|
{
|
||||||
g_gpu_device->RecycleTexture(std::move(m_private_display_texture));
|
g_gpu_device->RecycleTexture(std::move(m_upload_texture));
|
||||||
m_backend.Shutdown();
|
m_backend.Shutdown();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -92,18 +92,18 @@ void GPU_SW::UpdateSettings(const Settings& old_settings)
|
||||||
|
|
||||||
GPUTexture* GPU_SW::GetDisplayTexture(u32 width, u32 height, GPUTexture::Format format)
|
GPUTexture* GPU_SW::GetDisplayTexture(u32 width, u32 height, GPUTexture::Format format)
|
||||||
{
|
{
|
||||||
if (!m_private_display_texture || m_private_display_texture->GetWidth() != width ||
|
if (!m_upload_texture || m_upload_texture->GetWidth() != width || m_upload_texture->GetHeight() != height ||
|
||||||
m_private_display_texture->GetHeight() != height || m_private_display_texture->GetFormat() != format)
|
m_upload_texture->GetFormat() != format)
|
||||||
{
|
{
|
||||||
ClearDisplayTexture();
|
ClearDisplayTexture();
|
||||||
g_gpu_device->RecycleTexture(std::move(m_private_display_texture));
|
g_gpu_device->RecycleTexture(std::move(m_upload_texture));
|
||||||
m_private_display_texture =
|
m_upload_texture =
|
||||||
g_gpu_device->FetchTexture(width, height, 1, 1, 1, GPUTexture::Type::DynamicTexture, format, nullptr, 0);
|
g_gpu_device->FetchTexture(width, height, 1, 1, 1, GPUTexture::Type::DynamicTexture, format, nullptr, 0);
|
||||||
if (!m_private_display_texture)
|
if (!m_upload_texture)
|
||||||
Log_ErrorPrintf("Failed to create %ux%u %u texture", width, height, static_cast<u32>(format));
|
Log_ErrorPrintf("Failed to create %ux%u %u texture", width, height, static_cast<u32>(format));
|
||||||
}
|
}
|
||||||
|
|
||||||
return m_private_display_texture.get();
|
return m_upload_texture.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
template<GPUTexture::Format out_format, typename out_type>
|
template<GPUTexture::Format out_format, typename out_type>
|
||||||
|
@ -240,35 +240,26 @@ ALWAYS_INLINE void CopyOutRow16<GPUTexture::Format::BGRA8, u32>(const u16* src_p
|
||||||
}
|
}
|
||||||
|
|
||||||
template<GPUTexture::Format display_format>
|
template<GPUTexture::Format display_format>
|
||||||
void GPU_SW::CopyOut15Bit(u32 src_x, u32 src_y, u32 width, u32 height, u32 field, bool interlaced, bool interleaved)
|
ALWAYS_INLINE_RELEASE bool GPU_SW::CopyOut15Bit(u32 src_x, u32 src_y, u32 width, u32 height, u32 line_skip)
|
||||||
{
|
{
|
||||||
using OutputPixelType =
|
using OutputPixelType =
|
||||||
std::conditional_t<display_format == GPUTexture::Format::RGBA8 || display_format == GPUTexture::Format::BGRA8, u32,
|
std::conditional_t<display_format == GPUTexture::Format::RGBA8 || display_format == GPUTexture::Format::BGRA8, u32,
|
||||||
u16>;
|
u16>;
|
||||||
|
|
||||||
GPUTexture* texture = GetDisplayTexture(width, height, display_format);
|
GPUTexture* texture = GetDisplayTexture(width, height, display_format);
|
||||||
if (!texture)
|
if (!texture) [[unlikely]]
|
||||||
return;
|
return false;
|
||||||
|
|
||||||
u32 dst_stride = GPU_MAX_DISPLAY_WIDTH * sizeof(OutputPixelType);
|
u32 dst_stride = width * sizeof(OutputPixelType);
|
||||||
u8* dst_ptr = m_display_texture_buffer.data() + (interlaced ? (field != 0 ? dst_stride : 0) : 0);
|
u8* dst_ptr = m_upload_buffer.data();
|
||||||
|
const bool mapped = texture->Map(reinterpret_cast<void**>(&dst_ptr), &dst_stride, 0, 0, width, height);
|
||||||
const bool mapped =
|
|
||||||
(!interlaced && texture->Map(reinterpret_cast<void**>(&dst_ptr), &dst_stride, 0, 0, width, height));
|
|
||||||
|
|
||||||
const u32 output_stride = dst_stride;
|
|
||||||
const u8 interlaced_shift = BoolToUInt8(interlaced);
|
|
||||||
const u8 interleaved_shift = BoolToUInt8(interleaved);
|
|
||||||
|
|
||||||
// Fast path when not wrapping around.
|
// Fast path when not wrapping around.
|
||||||
if ((src_x + width) <= VRAM_WIDTH && (src_y + height) <= VRAM_HEIGHT)
|
if ((src_x + width) <= VRAM_WIDTH && (src_y + height) <= VRAM_HEIGHT)
|
||||||
{
|
{
|
||||||
const u32 rows = height >> interlaced_shift;
|
|
||||||
dst_stride <<= interlaced_shift;
|
|
||||||
|
|
||||||
const u16* src_ptr = &g_vram[src_y * VRAM_WIDTH + src_x];
|
const u16* src_ptr = &g_vram[src_y * VRAM_WIDTH + src_x];
|
||||||
const u32 src_step = VRAM_WIDTH << interleaved_shift;
|
const u32 src_step = VRAM_WIDTH << line_skip;
|
||||||
for (u32 row = 0; row < rows; row++)
|
for (u32 row = 0; row < height; row++)
|
||||||
{
|
{
|
||||||
CopyOutRow16<display_format>(src_ptr, reinterpret_cast<OutputPixelType*>(dst_ptr), width);
|
CopyOutRow16<display_format>(src_ptr, reinterpret_cast<OutputPixelType*>(dst_ptr), width);
|
||||||
src_ptr += src_step;
|
src_ptr += src_step;
|
||||||
|
@ -277,11 +268,9 @@ void GPU_SW::CopyOut15Bit(u32 src_x, u32 src_y, u32 width, u32 height, u32 field
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
const u32 rows = height >> interlaced_shift;
|
|
||||||
dst_stride <<= interlaced_shift;
|
|
||||||
|
|
||||||
const u32 end_x = src_x + width;
|
const u32 end_x = src_x + width;
|
||||||
for (u32 row = 0; row < rows; row++)
|
const u32 y_step = (1 << line_skip);
|
||||||
|
for (u32 row = 0; row < height; row++)
|
||||||
{
|
{
|
||||||
const u16* src_row_ptr = &g_vram[(src_y % VRAM_HEIGHT) * VRAM_WIDTH];
|
const u16* src_row_ptr = &g_vram[(src_y % VRAM_HEIGHT) * VRAM_WIDTH];
|
||||||
OutputPixelType* dst_row_ptr = reinterpret_cast<OutputPixelType*>(dst_ptr);
|
OutputPixelType* dst_row_ptr = reinterpret_cast<OutputPixelType*>(dst_ptr);
|
||||||
|
@ -289,7 +278,7 @@ void GPU_SW::CopyOut15Bit(u32 src_x, u32 src_y, u32 width, u32 height, u32 field
|
||||||
for (u32 col = src_x; col < end_x; col++)
|
for (u32 col = src_x; col < end_x; col++)
|
||||||
*(dst_row_ptr++) = VRAM16ToOutput<display_format, OutputPixelType>(src_row_ptr[col % VRAM_WIDTH]);
|
*(dst_row_ptr++) = VRAM16ToOutput<display_format, OutputPixelType>(src_row_ptr[col % VRAM_WIDTH]);
|
||||||
|
|
||||||
src_y += (1 << interleaved_shift);
|
src_y += y_step;
|
||||||
dst_ptr += dst_stride;
|
dst_ptr += dst_stride;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -297,61 +286,31 @@ void GPU_SW::CopyOut15Bit(u32 src_x, u32 src_y, u32 width, u32 height, u32 field
|
||||||
if (mapped)
|
if (mapped)
|
||||||
texture->Unmap();
|
texture->Unmap();
|
||||||
else
|
else
|
||||||
texture->Update(0, 0, width, height, m_display_texture_buffer.data(), output_stride);
|
texture->Update(0, 0, width, height, m_upload_buffer.data(), dst_stride);
|
||||||
|
|
||||||
SetDisplayTexture(texture, 0, 0, width, height);
|
return true;
|
||||||
}
|
|
||||||
|
|
||||||
void GPU_SW::CopyOut15Bit(GPUTexture::Format display_format, u32 src_x, u32 src_y, u32 width, u32 height, u32 field,
|
|
||||||
bool interlaced, bool interleaved)
|
|
||||||
{
|
|
||||||
switch (display_format)
|
|
||||||
{
|
|
||||||
case GPUTexture::Format::RGBA5551:
|
|
||||||
CopyOut15Bit<GPUTexture::Format::RGBA5551>(src_x, src_y, width, height, field, interlaced, interleaved);
|
|
||||||
break;
|
|
||||||
case GPUTexture::Format::RGB565:
|
|
||||||
CopyOut15Bit<GPUTexture::Format::RGB565>(src_x, src_y, width, height, field, interlaced, interleaved);
|
|
||||||
break;
|
|
||||||
case GPUTexture::Format::RGBA8:
|
|
||||||
CopyOut15Bit<GPUTexture::Format::RGBA8>(src_x, src_y, width, height, field, interlaced, interleaved);
|
|
||||||
break;
|
|
||||||
case GPUTexture::Format::BGRA8:
|
|
||||||
CopyOut15Bit<GPUTexture::Format::BGRA8>(src_x, src_y, width, height, field, interlaced, interleaved);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template<GPUTexture::Format display_format>
|
template<GPUTexture::Format display_format>
|
||||||
void GPU_SW::CopyOut24Bit(u32 src_x, u32 src_y, u32 skip_x, u32 width, u32 height, u32 field, bool interlaced,
|
ALWAYS_INLINE_RELEASE bool GPU_SW::CopyOut24Bit(u32 src_x, u32 src_y, u32 skip_x, u32 width, u32 height, u32 line_skip)
|
||||||
bool interleaved)
|
|
||||||
{
|
{
|
||||||
using OutputPixelType =
|
using OutputPixelType =
|
||||||
std::conditional_t<display_format == GPUTexture::Format::RGBA8 || display_format == GPUTexture::Format::BGRA8, u32,
|
std::conditional_t<display_format == GPUTexture::Format::RGBA8 || display_format == GPUTexture::Format::BGRA8, u32,
|
||||||
u16>;
|
u16>;
|
||||||
|
|
||||||
GPUTexture* texture = GetDisplayTexture(width, height, display_format);
|
GPUTexture* texture = GetDisplayTexture(width, height, display_format);
|
||||||
if (!texture)
|
if (!texture) [[unlikely]]
|
||||||
return;
|
return false;
|
||||||
|
|
||||||
u32 dst_stride = Common::AlignUpPow2<u32>(width * sizeof(OutputPixelType), 4);
|
u32 dst_stride = Common::AlignUpPow2<u32>(width * sizeof(OutputPixelType), 4);
|
||||||
u8* dst_ptr = m_display_texture_buffer.data() + (interlaced ? (field != 0 ? dst_stride : 0) : 0);
|
u8* dst_ptr = m_upload_buffer.data();
|
||||||
const bool mapped =
|
const bool mapped = texture->Map(reinterpret_cast<void**>(&dst_ptr), &dst_stride, 0, 0, width, height);
|
||||||
(!interlaced && texture->Map(reinterpret_cast<void**>(&dst_ptr), &dst_stride, 0, 0, width, height));
|
|
||||||
|
|
||||||
const u32 output_stride = dst_stride;
|
if ((src_x + width) <= VRAM_WIDTH && (src_y + (height << line_skip)) <= VRAM_HEIGHT)
|
||||||
const u8 interlaced_shift = BoolToUInt8(interlaced);
|
|
||||||
const u8 interleaved_shift = BoolToUInt8(interleaved);
|
|
||||||
const u32 rows = height >> interlaced_shift;
|
|
||||||
dst_stride <<= interlaced_shift;
|
|
||||||
|
|
||||||
if ((src_x + width) <= VRAM_WIDTH && (src_y + (rows << interleaved_shift)) <= VRAM_HEIGHT)
|
|
||||||
{
|
{
|
||||||
const u8* src_ptr = reinterpret_cast<const u8*>(&g_vram[src_y * VRAM_WIDTH + src_x]) + (skip_x * 3);
|
const u8* src_ptr = reinterpret_cast<const u8*>(&g_vram[src_y * VRAM_WIDTH + src_x]) + (skip_x * 3);
|
||||||
const u32 src_stride = (VRAM_WIDTH << interleaved_shift) * sizeof(u16);
|
const u32 src_stride = (VRAM_WIDTH << line_skip) * sizeof(u16);
|
||||||
for (u32 row = 0; row < rows; row++)
|
for (u32 row = 0; row < height; row++)
|
||||||
{
|
{
|
||||||
if constexpr (display_format == GPUTexture::Format::RGBA8)
|
if constexpr (display_format == GPUTexture::Format::RGBA8)
|
||||||
{
|
{
|
||||||
|
@ -407,7 +366,9 @@ void GPU_SW::CopyOut24Bit(u32 src_x, u32 src_y, u32 skip_x, u32 width, u32 heigh
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
for (u32 row = 0; row < rows; row++)
|
const u32 y_step = (1 << line_skip);
|
||||||
|
|
||||||
|
for (u32 row = 0; row < height; row++)
|
||||||
{
|
{
|
||||||
const u16* src_row_ptr = &g_vram[(src_y % VRAM_HEIGHT) * VRAM_WIDTH];
|
const u16* src_row_ptr = &g_vram[(src_y % VRAM_HEIGHT) * VRAM_WIDTH];
|
||||||
OutputPixelType* dst_row_ptr = reinterpret_cast<OutputPixelType*>(dst_ptr);
|
OutputPixelType* dst_row_ptr = reinterpret_cast<OutputPixelType*>(dst_ptr);
|
||||||
|
@ -438,7 +399,7 @@ void GPU_SW::CopyOut24Bit(u32 src_x, u32 src_y, u32 skip_x, u32 width, u32 heigh
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
src_y += (1 << interleaved_shift);
|
src_y += y_step;
|
||||||
dst_ptr += dst_stride;
|
dst_ptr += dst_stride;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -446,36 +407,55 @@ void GPU_SW::CopyOut24Bit(u32 src_x, u32 src_y, u32 skip_x, u32 width, u32 heigh
|
||||||
if (mapped)
|
if (mapped)
|
||||||
texture->Unmap();
|
texture->Unmap();
|
||||||
else
|
else
|
||||||
texture->Update(0, 0, width, height, m_display_texture_buffer.data(), output_stride);
|
texture->Update(0, 0, width, height, m_upload_buffer.data(), dst_stride);
|
||||||
|
|
||||||
SetDisplayTexture(texture, 0, 0, width, height);
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void GPU_SW::CopyOut24Bit(GPUTexture::Format display_format, u32 src_x, u32 src_y, u32 skip_x, u32 width, u32 height,
|
bool GPU_SW::CopyOut(u32 src_x, u32 src_y, u32 skip_x, u32 width, u32 height, u32 line_skip, bool is_24bit)
|
||||||
u32 field, bool interlaced, bool interleaved)
|
|
||||||
{
|
{
|
||||||
switch (display_format)
|
if (!is_24bit)
|
||||||
|
{
|
||||||
|
DebugAssert(skip_x == 0);
|
||||||
|
|
||||||
|
switch (m_16bit_display_format)
|
||||||
{
|
{
|
||||||
case GPUTexture::Format::RGBA5551:
|
case GPUTexture::Format::RGBA5551:
|
||||||
CopyOut24Bit<GPUTexture::Format::RGBA5551>(src_x, src_y, skip_x, width, height, field, interlaced, interleaved);
|
return CopyOut15Bit<GPUTexture::Format::RGBA5551>(src_x, src_y, width, height, line_skip);
|
||||||
break;
|
|
||||||
case GPUTexture::Format::RGB565:
|
|
||||||
CopyOut24Bit<GPUTexture::Format::RGB565>(src_x, src_y, skip_x, width, height, field, interlaced, interleaved);
|
|
||||||
break;
|
|
||||||
case GPUTexture::Format::RGBA8:
|
|
||||||
CopyOut24Bit<GPUTexture::Format::RGBA8>(src_x, src_y, skip_x, width, height, field, interlaced, interleaved);
|
|
||||||
break;
|
|
||||||
case GPUTexture::Format::BGRA8:
|
|
||||||
CopyOut24Bit<GPUTexture::Format::BGRA8>(src_x, src_y, skip_x, width, height, field, interlaced, interleaved);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void GPU_SW::ClearDisplay()
|
case GPUTexture::Format::RGB565:
|
||||||
|
return CopyOut15Bit<GPUTexture::Format::RGB565>(src_x, src_y, width, height, line_skip);
|
||||||
|
|
||||||
|
case GPUTexture::Format::RGBA8:
|
||||||
|
return CopyOut15Bit<GPUTexture::Format::RGBA8>(src_x, src_y, width, height, line_skip);
|
||||||
|
|
||||||
|
case GPUTexture::Format::BGRA8:
|
||||||
|
return CopyOut15Bit<GPUTexture::Format::BGRA8>(src_x, src_y, width, height, line_skip);
|
||||||
|
|
||||||
|
default:
|
||||||
|
UnreachableCode();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
{
|
{
|
||||||
std::memset(m_display_texture_buffer.data(), 0, m_display_texture_buffer.size());
|
switch (m_24bit_display_format)
|
||||||
|
{
|
||||||
|
case GPUTexture::Format::RGBA5551:
|
||||||
|
return CopyOut24Bit<GPUTexture::Format::RGBA5551>(src_x, src_y, skip_x, width, height, line_skip);
|
||||||
|
|
||||||
|
case GPUTexture::Format::RGB565:
|
||||||
|
return CopyOut24Bit<GPUTexture::Format::RGB565>(src_x, src_y, skip_x, width, height, line_skip);
|
||||||
|
|
||||||
|
case GPUTexture::Format::RGBA8:
|
||||||
|
return CopyOut24Bit<GPUTexture::Format::RGBA8>(src_x, src_y, skip_x, width, height, line_skip);
|
||||||
|
|
||||||
|
case GPUTexture::Format::BGRA8:
|
||||||
|
return CopyOut24Bit<GPUTexture::Format::BGRA8>(src_x, src_y, skip_x, width, height, line_skip);
|
||||||
|
|
||||||
|
default:
|
||||||
|
UnreachableCode();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void GPU_SW::UpdateDisplay()
|
void GPU_SW::UpdateDisplay()
|
||||||
|
@ -495,45 +475,49 @@ void GPU_SW::UpdateDisplay()
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const u32 vram_offset_y = m_crtc_state.display_vram_top;
|
const bool is_24bit = m_GPUSTAT.display_area_color_depth_24;
|
||||||
const u32 display_width = m_crtc_state.display_vram_width;
|
const bool interlaced = IsInterlacedDisplayEnabled();
|
||||||
const u32 display_height = m_crtc_state.display_vram_height;
|
const u32 field = GetInterlacedDisplayField();
|
||||||
|
const u32 vram_offset_x = is_24bit ? m_crtc_state.regs.X : m_crtc_state.display_vram_left;
|
||||||
|
const u32 vram_offset_y =
|
||||||
|
m_crtc_state.display_vram_top + ((interlaced && m_GPUSTAT.vertical_resolution) ? field : 0);
|
||||||
|
const u32 skip_x = is_24bit ? (m_crtc_state.display_vram_left - m_crtc_state.regs.X) : 0;
|
||||||
|
const u32 read_width = m_crtc_state.display_vram_width;
|
||||||
|
const u32 read_height = interlaced ? (m_crtc_state.display_vram_height / 2) : m_crtc_state.display_vram_height;
|
||||||
|
|
||||||
if (IsInterlacedDisplayEnabled())
|
if (IsInterlacedDisplayEnabled())
|
||||||
{
|
{
|
||||||
const u32 field = GetInterlacedDisplayField();
|
const u32 line_skip = m_GPUSTAT.vertical_resolution;
|
||||||
if (m_GPUSTAT.display_area_color_depth_24)
|
if (CopyOut(vram_offset_x, vram_offset_y, skip_x, read_width, read_height, line_skip, is_24bit))
|
||||||
{
|
{
|
||||||
CopyOut24Bit(m_24bit_display_format, m_crtc_state.regs.X, vram_offset_y + field,
|
if (is_24bit && g_settings.gpu_24bit_chroma_smoothing)
|
||||||
m_crtc_state.display_vram_left - m_crtc_state.regs.X, display_width, display_height, field, true,
|
{
|
||||||
m_GPUSTAT.vertical_resolution);
|
if (ApplyChromaSmoothing(m_upload_texture.get(), 0, 0, read_width, read_height))
|
||||||
|
Deinterlace(m_display_texture, 0, 0, read_width, read_height, field, 0);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
CopyOut15Bit(m_16bit_display_format, m_crtc_state.display_vram_left, vram_offset_y + field, display_width,
|
Deinterlace(m_upload_texture.get(), 0, 0, read_width, read_height, field, 0);
|
||||||
display_height, field, true, m_GPUSTAT.vertical_resolution);
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (m_GPUSTAT.display_area_color_depth_24)
|
if (CopyOut(vram_offset_x, vram_offset_y, skip_x, read_width, read_height, 0, is_24bit))
|
||||||
{
|
{
|
||||||
CopyOut24Bit(m_24bit_display_format, m_crtc_state.regs.X, vram_offset_y,
|
if (is_24bit && g_settings.gpu_24bit_chroma_smoothing)
|
||||||
m_crtc_state.display_vram_left - m_crtc_state.regs.X, display_width, display_height, 0, false,
|
ApplyChromaSmoothing(m_upload_texture.get(), 0, 0, read_width, read_height);
|
||||||
false);
|
else
|
||||||
}
|
SetDisplayTexture(m_upload_texture.get(), 0, 0, read_width, read_height);
|
||||||
else
|
|
||||||
{
|
|
||||||
CopyOut15Bit(m_16bit_display_format, m_crtc_state.display_vram_left, vram_offset_y, display_width,
|
|
||||||
display_height, 0, false, false);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
CopyOut15Bit(m_16bit_display_format, 0, 0, VRAM_WIDTH, VRAM_HEIGHT, 0, false, false);
|
|
||||||
SetDisplayParameters(VRAM_WIDTH, VRAM_HEIGHT, 0, 0, VRAM_WIDTH, VRAM_HEIGHT,
|
SetDisplayParameters(VRAM_WIDTH, VRAM_HEIGHT, 0, 0, VRAM_WIDTH, VRAM_HEIGHT,
|
||||||
static_cast<float>(VRAM_WIDTH) / static_cast<float>(VRAM_HEIGHT));
|
static_cast<float>(VRAM_WIDTH) / static_cast<float>(VRAM_HEIGHT));
|
||||||
|
if (CopyOut(0, 0, 0, VRAM_WIDTH, VRAM_HEIGHT, 0, false))
|
||||||
|
SetDisplayTexture(m_upload_texture.get(), 0, 0, VRAM_WIDTH, VRAM_HEIGHT);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -42,17 +42,13 @@ protected:
|
||||||
void CopyVRAM(u32 src_x, u32 src_y, u32 dst_x, u32 dst_y, u32 width, u32 height) override;
|
void CopyVRAM(u32 src_x, u32 src_y, u32 dst_x, u32 dst_y, u32 width, u32 height) override;
|
||||||
|
|
||||||
template<GPUTexture::Format display_format>
|
template<GPUTexture::Format display_format>
|
||||||
void CopyOut15Bit(u32 src_x, u32 src_y, u32 width, u32 height, u32 field, bool interlaced, bool interleaved);
|
bool CopyOut15Bit(u32 src_x, u32 src_y, u32 width, u32 height, u32 line_skip);
|
||||||
void CopyOut15Bit(GPUTexture::Format display_format, u32 src_x, u32 src_y, u32 width, u32 height, u32 field,
|
|
||||||
bool interlaced, bool interleaved);
|
|
||||||
|
|
||||||
template<GPUTexture::Format display_format>
|
template<GPUTexture::Format display_format>
|
||||||
void CopyOut24Bit(u32 src_x, u32 src_y, u32 skip_x, u32 width, u32 height, u32 field, bool interlaced,
|
bool CopyOut24Bit(u32 src_x, u32 src_y, u32 skip_x, u32 width, u32 height, u32 line_skip);
|
||||||
bool interleaved);
|
|
||||||
void CopyOut24Bit(GPUTexture::Format display_format, u32 src_x, u32 src_y, u32 skip_x, u32 width, u32 height,
|
bool CopyOut(u32 src_x, u32 src_y, u32 skip_x, u32 width, u32 height, u32 line_skip, bool is_24bit);
|
||||||
u32 field, bool interlaced, bool interleaved);
|
|
||||||
|
|
||||||
void ClearDisplay() override;
|
|
||||||
void UpdateDisplay() override;
|
void UpdateDisplay() override;
|
||||||
|
|
||||||
void DispatchRenderCommand() override;
|
void DispatchRenderCommand() override;
|
||||||
|
@ -62,10 +58,10 @@ protected:
|
||||||
|
|
||||||
GPUTexture* GetDisplayTexture(u32 width, u32 height, GPUTexture::Format format);
|
GPUTexture* GetDisplayTexture(u32 width, u32 height, GPUTexture::Format format);
|
||||||
|
|
||||||
FixedHeapArray<u8, GPU_MAX_DISPLAY_WIDTH * GPU_MAX_DISPLAY_HEIGHT * sizeof(u32)> m_display_texture_buffer;
|
FixedHeapArray<u8, GPU_MAX_DISPLAY_WIDTH * GPU_MAX_DISPLAY_HEIGHT * sizeof(u32)> m_upload_buffer;
|
||||||
GPUTexture::Format m_16bit_display_format = GPUTexture::Format::RGB565;
|
GPUTexture::Format m_16bit_display_format = GPUTexture::Format::RGB565;
|
||||||
GPUTexture::Format m_24bit_display_format = GPUTexture::Format::RGBA8;
|
GPUTexture::Format m_24bit_display_format = GPUTexture::Format::RGBA8;
|
||||||
std::unique_ptr<GPUTexture> m_private_display_texture; // TODO: Move to base.
|
std::unique_ptr<GPUTexture> m_upload_texture;
|
||||||
|
|
||||||
GPU_SW_Backend m_backend;
|
GPU_SW_Backend m_backend;
|
||||||
};
|
};
|
||||||
|
|
|
@ -224,6 +224,11 @@ void Settings::Load(SettingsInterface& si)
|
||||||
gpu_pgxp_depth_buffer = si.GetBoolValue("GPU", "PGXPDepthBuffer", false);
|
gpu_pgxp_depth_buffer = si.GetBoolValue("GPU", "PGXPDepthBuffer", false);
|
||||||
SetPGXPDepthClearThreshold(si.GetFloatValue("GPU", "PGXPDepthClearThreshold", DEFAULT_GPU_PGXP_DEPTH_THRESHOLD));
|
SetPGXPDepthClearThreshold(si.GetFloatValue("GPU", "PGXPDepthClearThreshold", DEFAULT_GPU_PGXP_DEPTH_THRESHOLD));
|
||||||
|
|
||||||
|
display_deinterlacing_mode =
|
||||||
|
ParseDisplayDeinterlacingMode(si.GetStringValue("Display", "DeinterlacingMode",
|
||||||
|
GetDisplayDeinterlacingModeName(DEFAULT_DISPLAY_DEINTERLACING_MODE))
|
||||||
|
.c_str())
|
||||||
|
.value_or(DEFAULT_DISPLAY_DEINTERLACING_MODE);
|
||||||
display_crop_mode =
|
display_crop_mode =
|
||||||
ParseDisplayCropMode(
|
ParseDisplayCropMode(
|
||||||
si.GetStringValue("Display", "CropMode", GetDisplayCropModeName(DEFAULT_DISPLAY_CROP_MODE)).c_str())
|
si.GetStringValue("Display", "CropMode", GetDisplayCropModeName(DEFAULT_DISPLAY_CROP_MODE)).c_str())
|
||||||
|
@ -498,6 +503,7 @@ void Settings::Save(SettingsInterface& si) const
|
||||||
si.SetBoolValue("GPU", "PGXPDepthBuffer", gpu_pgxp_depth_buffer);
|
si.SetBoolValue("GPU", "PGXPDepthBuffer", gpu_pgxp_depth_buffer);
|
||||||
si.SetFloatValue("GPU", "PGXPDepthClearThreshold", GetPGXPDepthClearThreshold());
|
si.SetFloatValue("GPU", "PGXPDepthClearThreshold", GetPGXPDepthClearThreshold());
|
||||||
|
|
||||||
|
si.SetStringValue("Display", "DeinterlacingMode", GetDisplayDeinterlacingModeName(display_deinterlacing_mode));
|
||||||
si.SetStringValue("Display", "CropMode", GetDisplayCropModeName(display_crop_mode));
|
si.SetStringValue("Display", "CropMode", GetDisplayCropModeName(display_crop_mode));
|
||||||
si.SetIntValue("Display", "ActiveStartOffset", display_active_start_offset);
|
si.SetIntValue("Display", "ActiveStartOffset", display_active_start_offset);
|
||||||
si.SetIntValue("Display", "ActiveEndOffset", display_active_end_offset);
|
si.SetIntValue("Display", "ActiveEndOffset", display_active_end_offset);
|
||||||
|
@ -1198,6 +1204,44 @@ const char* Settings::GetGPUWireframeModeDisplayName(GPUWireframeMode mode)
|
||||||
return Host::TranslateToCString("GPUWireframeMode", s_wireframe_mode_display_names[static_cast<int>(mode)]);
|
return Host::TranslateToCString("GPUWireframeMode", s_wireframe_mode_display_names[static_cast<int>(mode)]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static constexpr const std::array s_display_deinterlacing_mode_names = {
|
||||||
|
"Disabled",
|
||||||
|
"Weave",
|
||||||
|
"Blend",
|
||||||
|
"Adaptive",
|
||||||
|
};
|
||||||
|
static constexpr const std::array s_display_deinterlacing_mode_display_names = {
|
||||||
|
TRANSLATE_NOOP("DisplayDeinterlacingMode", "Disabled (Flickering)"),
|
||||||
|
TRANSLATE_NOOP("DisplayDeinterlacingMode", "Weave (Combing)"),
|
||||||
|
TRANSLATE_NOOP("DisplayDeinterlacingMode", "Blend (Blur)"),
|
||||||
|
TRANSLATE_NOOP("DisplayDeinterlacingMode", "Adaptive (FastMAD)"),
|
||||||
|
};
|
||||||
|
|
||||||
|
std::optional<DisplayDeinterlacingMode> Settings::ParseDisplayDeinterlacingMode(const char* str)
|
||||||
|
{
|
||||||
|
int index = 0;
|
||||||
|
for (const char* name : s_display_deinterlacing_mode_names)
|
||||||
|
{
|
||||||
|
if (StringUtil::Strcasecmp(name, str) == 0)
|
||||||
|
return static_cast<DisplayDeinterlacingMode>(index);
|
||||||
|
|
||||||
|
index++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* Settings::GetDisplayDeinterlacingModeName(DisplayDeinterlacingMode mode)
|
||||||
|
{
|
||||||
|
return s_display_deinterlacing_mode_names[static_cast<int>(mode)];
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* Settings::GetDisplayDeinterlacingModeDisplayName(DisplayDeinterlacingMode mode)
|
||||||
|
{
|
||||||
|
return Host::TranslateToCString("DisplayDeinterlacingMode",
|
||||||
|
s_display_deinterlacing_mode_display_names[static_cast<int>(mode)]);
|
||||||
|
}
|
||||||
|
|
||||||
static constexpr const std::array s_display_crop_mode_names = {"None", "Overscan", "Borders"};
|
static constexpr const std::array s_display_crop_mode_names = {"None", "Overscan", "Borders"};
|
||||||
static constexpr const std::array s_display_crop_mode_display_names = {
|
static constexpr const std::array 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"),
|
||||||
|
|
|
@ -129,6 +129,7 @@ struct Settings
|
||||||
bool gpu_pgxp_cpu : 1 = false;
|
bool gpu_pgxp_cpu : 1 = false;
|
||||||
bool gpu_pgxp_preserve_proj_fp : 1 = false;
|
bool gpu_pgxp_preserve_proj_fp : 1 = false;
|
||||||
bool gpu_pgxp_depth_buffer : 1 = false;
|
bool gpu_pgxp_depth_buffer : 1 = false;
|
||||||
|
DisplayDeinterlacingMode display_deinterlacing_mode = DEFAULT_DISPLAY_DEINTERLACING_MODE;
|
||||||
DisplayCropMode display_crop_mode = DEFAULT_DISPLAY_CROP_MODE;
|
DisplayCropMode display_crop_mode = DEFAULT_DISPLAY_CROP_MODE;
|
||||||
DisplayAspectRatio display_aspect_ratio = DEFAULT_DISPLAY_ASPECT_RATIO;
|
DisplayAspectRatio display_aspect_ratio = DEFAULT_DISPLAY_ASPECT_RATIO;
|
||||||
DisplayAlignment display_alignment = DEFAULT_DISPLAY_ALIGNMENT;
|
DisplayAlignment display_alignment = DEFAULT_DISPLAY_ALIGNMENT;
|
||||||
|
@ -394,6 +395,10 @@ struct Settings
|
||||||
static const char* GetGPUWireframeModeName(GPUWireframeMode mode);
|
static const char* GetGPUWireframeModeName(GPUWireframeMode mode);
|
||||||
static const char* GetGPUWireframeModeDisplayName(GPUWireframeMode mode);
|
static const char* GetGPUWireframeModeDisplayName(GPUWireframeMode mode);
|
||||||
|
|
||||||
|
static std::optional<DisplayDeinterlacingMode> ParseDisplayDeinterlacingMode(const char* str);
|
||||||
|
static const char* GetDisplayDeinterlacingModeName(DisplayDeinterlacingMode mode);
|
||||||
|
static const char* GetDisplayDeinterlacingModeDisplayName(DisplayDeinterlacingMode 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);
|
||||||
|
@ -483,6 +488,7 @@ struct Settings
|
||||||
static constexpr AudioBackend DEFAULT_AUDIO_BACKEND = AudioBackend::Null;
|
static constexpr AudioBackend DEFAULT_AUDIO_BACKEND = AudioBackend::Null;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
static constexpr DisplayDeinterlacingMode DEFAULT_DISPLAY_DEINTERLACING_MODE = DisplayDeinterlacingMode::Adaptive;
|
||||||
static constexpr DisplayCropMode DEFAULT_DISPLAY_CROP_MODE = DisplayCropMode::Overscan;
|
static constexpr DisplayCropMode DEFAULT_DISPLAY_CROP_MODE = DisplayCropMode::Overscan;
|
||||||
static constexpr DisplayAspectRatio DEFAULT_DISPLAY_ASPECT_RATIO = DisplayAspectRatio::Auto;
|
static constexpr DisplayAspectRatio DEFAULT_DISPLAY_ASPECT_RATIO = DisplayAspectRatio::Auto;
|
||||||
static constexpr DisplayAlignment DEFAULT_DISPLAY_ALIGNMENT = DisplayAlignment::Center;
|
static constexpr DisplayAlignment DEFAULT_DISPLAY_ALIGNMENT = DisplayAlignment::Center;
|
||||||
|
|
|
@ -3684,6 +3684,7 @@ void System::CheckForSettingsChanges(const Settings& old_settings)
|
||||||
g_settings.gpu_downsample_mode != old_settings.gpu_downsample_mode ||
|
g_settings.gpu_downsample_mode != old_settings.gpu_downsample_mode ||
|
||||||
g_settings.gpu_downsample_scale != old_settings.gpu_downsample_scale ||
|
g_settings.gpu_downsample_scale != old_settings.gpu_downsample_scale ||
|
||||||
g_settings.gpu_wireframe_mode != old_settings.gpu_wireframe_mode ||
|
g_settings.gpu_wireframe_mode != old_settings.gpu_wireframe_mode ||
|
||||||
|
g_settings.display_deinterlacing_mode != old_settings.display_deinterlacing_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 ||
|
||||||
|
|
|
@ -77,6 +77,15 @@ enum class GPURenderer : u8
|
||||||
Count
|
Count
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum class DisplayDeinterlacingMode : u8
|
||||||
|
{
|
||||||
|
Disabled,
|
||||||
|
Weave,
|
||||||
|
Blend,
|
||||||
|
Adaptive,
|
||||||
|
Count
|
||||||
|
};
|
||||||
|
|
||||||
enum class GPUTextureFilter : u8
|
enum class GPUTextureFilter : u8
|
||||||
{
|
{
|
||||||
Nearest,
|
Nearest,
|
||||||
|
|
|
@ -66,6 +66,9 @@ GraphicsSettingsWidget::GraphicsSettingsWidget(SettingsWindow* dialog, QWidget*
|
||||||
SettingWidgetBinder::BindWidgetToIntSetting(sif, m_ui.customAspectRatioDenominator, "Display",
|
SettingWidgetBinder::BindWidgetToIntSetting(sif, m_ui.customAspectRatioDenominator, "Display",
|
||||||
"CustomAspectRatioDenominator", 1);
|
"CustomAspectRatioDenominator", 1);
|
||||||
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.widescreenHack, "GPU", "WidescreenHack", false);
|
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.widescreenHack, "GPU", "WidescreenHack", false);
|
||||||
|
SettingWidgetBinder::BindWidgetToEnumSetting(
|
||||||
|
sif, m_ui.displayDeinterlacing, "Display", "DeinterlacingMode", &Settings::ParseDisplayDeinterlacingMode,
|
||||||
|
&Settings::GetDisplayDeinterlacingModeName, Settings::DEFAULT_DISPLAY_DEINTERLACING_MODE);
|
||||||
SettingWidgetBinder::BindWidgetToEnumSetting(sif, m_ui.displayCropMode, "Display", "CropMode",
|
SettingWidgetBinder::BindWidgetToEnumSetting(sif, m_ui.displayCropMode, "Display", "CropMode",
|
||||||
&Settings::ParseDisplayCropMode, &Settings::GetDisplayCropModeName,
|
&Settings::ParseDisplayCropMode, &Settings::GetDisplayCropModeName,
|
||||||
Settings::DEFAULT_DISPLAY_CROP_MODE);
|
Settings::DEFAULT_DISPLAY_CROP_MODE);
|
||||||
|
@ -251,7 +254,7 @@ GraphicsSettingsWidget::GraphicsSettingsWidget(SettingsWindow* dialog, QWidget*
|
||||||
dialog->registerWidgetHelp(
|
dialog->registerWidgetHelp(
|
||||||
m_ui.gpuDownsampleMode, tr("Down-Sampling"), tr("Disabled"),
|
m_ui.gpuDownsampleMode, tr("Down-Sampling"), tr("Disabled"),
|
||||||
tr("Downsamples the rendered image prior to displaying it. Can improve overall image quality in mixed 2D/3D games, "
|
tr("Downsamples the rendered image prior to displaying it. Can improve overall image quality in mixed 2D/3D games, "
|
||||||
"but should be disabled for pure 3D games. Only applies to the hardware renderers."));
|
"but should be disabled for pure 3D games."));
|
||||||
dialog->registerWidgetHelp(m_ui.gpuDownsampleScale, tr("Down-Sampling Display Scale"), tr("1x"),
|
dialog->registerWidgetHelp(m_ui.gpuDownsampleScale, tr("Down-Sampling Display Scale"), tr("1x"),
|
||||||
tr("Selects the resolution scale that will be applied to the final image. 1x will "
|
tr("Selects the resolution scale that will be applied to the final image. 1x will "
|
||||||
"downsample to the original console resolution."));
|
"downsample to the original console resolution."));
|
||||||
|
@ -259,15 +262,21 @@ GraphicsSettingsWidget::GraphicsSettingsWidget(SettingsWindow* dialog, QWidget*
|
||||||
m_ui.textureFiltering, tr("Texture Filtering"),
|
m_ui.textureFiltering, tr("Texture Filtering"),
|
||||||
QString::fromUtf8(Settings::GetTextureFilterDisplayName(Settings::DEFAULT_GPU_TEXTURE_FILTER)),
|
QString::fromUtf8(Settings::GetTextureFilterDisplayName(Settings::DEFAULT_GPU_TEXTURE_FILTER)),
|
||||||
tr("Smooths out the blockiness of magnified textures on 3D object by using filtering. <br>Will have a "
|
tr("Smooths out the blockiness of magnified textures on 3D object by using filtering. <br>Will have a "
|
||||||
"greater effect on higher resolution scales. Only applies to the hardware renderers. <br>The JINC2 and "
|
"greater effect on higher resolution scales. <br>The JINC2 and especially xBR filtering modes are very "
|
||||||
"especially xBR filtering modes are very demanding, and may not be worth the speed penalty."));
|
"demanding, and may not be worth the speed penalty."));
|
||||||
dialog->registerWidgetHelp(
|
dialog->registerWidgetHelp(
|
||||||
m_ui.displayAspectRatio, tr("Aspect Ratio"),
|
m_ui.displayAspectRatio, tr("Aspect Ratio"),
|
||||||
QString::fromUtf8(Settings::GetDisplayAspectRatioDisplayName(Settings::DEFAULT_DISPLAY_ASPECT_RATIO)),
|
QString::fromUtf8(Settings::GetDisplayAspectRatioDisplayName(Settings::DEFAULT_DISPLAY_ASPECT_RATIO)),
|
||||||
tr("Changes the aspect ratio used to display the console's output to the screen. The default is Auto (Game Native) "
|
tr("Changes the aspect ratio used to display the console's output to the screen. The default is Auto (Game Native) "
|
||||||
"which automatically adjusts the aspect ratio to match how a game would be shown on a typical TV of the era."));
|
"which automatically adjusts the aspect ratio to match how a game would be shown on a typical TV of the era."));
|
||||||
dialog->registerWidgetHelp(
|
dialog->registerWidgetHelp(
|
||||||
m_ui.displayCropMode, tr("Crop Mode"),
|
m_ui.displayCropMode, tr("Deinterlacing"),
|
||||||
|
QString::fromUtf8(Settings::GetDisplayDeinterlacingModeName(Settings::DEFAULT_DISPLAY_DEINTERLACING_MODE)),
|
||||||
|
tr("Determines which algorithm is used to convert interlaced frames to progressive for display on your system. "
|
||||||
|
"Generally, the \"Disable Interlacing\" enhancement provides better quality output, but some games require "
|
||||||
|
"interlaced rendering."));
|
||||||
|
dialog->registerWidgetHelp(
|
||||||
|
m_ui.displayCropMode, tr("Crop"),
|
||||||
QString::fromUtf8(Settings::GetDisplayCropModeDisplayName(Settings::DEFAULT_DISPLAY_CROP_MODE)),
|
QString::fromUtf8(Settings::GetDisplayCropModeDisplayName(Settings::DEFAULT_DISPLAY_CROP_MODE)),
|
||||||
tr("Determines how much of the area typically not visible on a consumer TV set to crop/hide. Some games display "
|
tr("Determines how much of the area typically not visible on a consumer TV set to crop/hide. Some games display "
|
||||||
"content in the overscan area, or use it for screen effects. May not display correctly with the \"All Borders\" "
|
"content in the overscan area, or use it for screen effects. May not display correctly with the \"All Borders\" "
|
||||||
|
@ -285,16 +294,15 @@ GraphicsSettingsWidget::GraphicsSettingsWidget(SettingsWindow* dialog, QWidget*
|
||||||
"channel. This produces nicer looking gradients at the cost of making some colours look slightly different. "
|
"channel. This produces nicer looking gradients at the cost of making some colours look slightly different. "
|
||||||
"Disabling the option also enables dithering, which makes the transition between colours less sharp by applying "
|
"Disabling the option also enables dithering, which makes the transition between colours less sharp by applying "
|
||||||
"a pattern around those pixels. Most games are compatible with this option, but there is a number which aren't "
|
"a pattern around those pixels. Most games are compatible with this option, but there is a number which aren't "
|
||||||
"and will have broken effects with it enabled. Only applies to the hardware renderers."));
|
"and will have broken effects with it enabled."));
|
||||||
dialog->registerWidgetHelp(
|
dialog->registerWidgetHelp(
|
||||||
m_ui.widescreenHack, tr("Widescreen Rendering"), tr("Unchecked"),
|
m_ui.widescreenHack, tr("Widescreen Rendering"), tr("Unchecked"),
|
||||||
tr("Scales vertex positions in screen-space to a widescreen aspect ratio, essentially "
|
tr("Scales vertex positions in screen-space to a widescreen aspect ratio, essentially "
|
||||||
"increasing the field of view from 4:3 to the chosen display aspect ratio in 3D games. <b><u>May not be "
|
"increasing the field of view from 4:3 to the chosen display aspect ratio in 3D games. <b><u>May not be "
|
||||||
"compatible with all games.</u></b>"));
|
"compatible with all games.</u></b>"));
|
||||||
dialog->registerWidgetHelp(
|
dialog->registerWidgetHelp(m_ui.pgxpEnable, tr("PGXP Geometry Correction"), tr("Unchecked"),
|
||||||
m_ui.pgxpEnable, tr("PGXP Geometry Correction"), tr("Unchecked"),
|
tr("Reduces \"wobbly\" polygons and \"warping\" textures that are common in PS1 games. "
|
||||||
tr("Reduces \"wobbly\" polygons and \"warping\" textures that are common in PS1 games. <br>Only "
|
"<strong>May not be compatible with all games.</strong>"));
|
||||||
"works with the hardware renderers. <b><u>May not be compatible with all games.</u></b>"));
|
|
||||||
dialog->registerWidgetHelp(
|
dialog->registerWidgetHelp(
|
||||||
m_ui.pgxpDepthBuffer, tr("PGXP Depth Buffer"), tr("Unchecked"),
|
m_ui.pgxpDepthBuffer, tr("PGXP Depth Buffer"), tr("Unchecked"),
|
||||||
tr("Attempts to reduce polygon Z-fighting by testing pixels against the depth values from PGXP. Low compatibility, "
|
tr("Attempts to reduce polygon Z-fighting by testing pixels against the depth values from PGXP. Low compatibility, "
|
||||||
|
@ -303,8 +311,7 @@ GraphicsSettingsWidget::GraphicsSettingsWidget(SettingsWindow* dialog, QWidget*
|
||||||
m_ui.force43For24Bit, tr("Force 4:3 For FMVs"), tr("Unchecked"),
|
m_ui.force43For24Bit, tr("Force 4:3 For FMVs"), tr("Unchecked"),
|
||||||
tr("Switches back to 4:3 display aspect ratio when displaying 24-bit content, usually FMVs."));
|
tr("Switches back to 4:3 display aspect ratio when displaying 24-bit content, usually FMVs."));
|
||||||
dialog->registerWidgetHelp(m_ui.chromaSmoothingFor24Bit, tr("FMV Chroma Smoothing"), tr("Unchecked"),
|
dialog->registerWidgetHelp(m_ui.chromaSmoothingFor24Bit, tr("FMV Chroma Smoothing"), tr("Unchecked"),
|
||||||
tr("Smooths out blockyness between colour transitions in 24-bit content, usually FMVs. "
|
tr("Smooths out blockyness between colour transitions in 24-bit content, usually FMVs."));
|
||||||
"Only applies to the hardware renderers."));
|
|
||||||
dialog->registerWidgetHelp(
|
dialog->registerWidgetHelp(
|
||||||
m_ui.disableInterlacing, tr("Disable Interlacing"), tr("Checked"),
|
m_ui.disableInterlacing, tr("Disable Interlacing"), tr("Checked"),
|
||||||
tr(
|
tr(
|
||||||
|
@ -364,7 +371,7 @@ GraphicsSettingsWidget::GraphicsSettingsWidget(SettingsWindow* dialog, QWidget*
|
||||||
dialog->registerWidgetHelp(
|
dialog->registerWidgetHelp(
|
||||||
m_ui.scaledDithering, tr("Scaled Dithering"), tr("Checked"),
|
m_ui.scaledDithering, tr("Scaled Dithering"), tr("Checked"),
|
||||||
tr("Scales the dither pattern to the resolution scale of the emulated GPU. This makes the dither pattern much less "
|
tr("Scales the dither pattern to the resolution scale of the emulated GPU. This makes the dither pattern much less "
|
||||||
"obvious at higher resolutions. <br>Usually safe to enable, and only supported by the hardware renderers."));
|
"obvious at higher resolutions. Usually safe to enable."));
|
||||||
dialog->registerWidgetHelp(
|
dialog->registerWidgetHelp(
|
||||||
m_ui.useSoftwareRendererForReadbacks, tr("Software Renderer Readbacks"), tr("Unchecked"),
|
m_ui.useSoftwareRendererForReadbacks, tr("Software Renderer Readbacks"), tr("Unchecked"),
|
||||||
tr("Runs the software renderer in parallel for VRAM readbacks. On some systems, this may result in greater "
|
tr("Runs the software renderer in parallel for VRAM readbacks. On some systems, this may result in greater "
|
||||||
|
@ -525,6 +532,12 @@ void GraphicsSettingsWidget::setupAdditionalUi()
|
||||||
QString::fromUtf8(Settings::GetDisplayAspectRatioDisplayName(static_cast<DisplayAspectRatio>(i))));
|
QString::fromUtf8(Settings::GetDisplayAspectRatioDisplayName(static_cast<DisplayAspectRatio>(i))));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (u32 i = 0; i < static_cast<u32>(DisplayDeinterlacingMode::Count); i++)
|
||||||
|
{
|
||||||
|
m_ui.displayDeinterlacing->addItem(
|
||||||
|
QString::fromUtf8(Settings::GetDisplayDeinterlacingModeDisplayName(static_cast<DisplayDeinterlacingMode>(i))));
|
||||||
|
}
|
||||||
|
|
||||||
for (u32 i = 0; i < static_cast<u32>(DisplayCropMode::Count); i++)
|
for (u32 i = 0; i < static_cast<u32>(DisplayCropMode::Count); i++)
|
||||||
{
|
{
|
||||||
m_ui.displayCropMode->addItem(
|
m_ui.displayCropMode->addItem(
|
||||||
|
@ -641,7 +654,6 @@ void GraphicsSettingsWidget::updateRendererDependentOptions()
|
||||||
m_ui.gpuDownsampleScale->setEnabled(is_hardware);
|
m_ui.gpuDownsampleScale->setEnabled(is_hardware);
|
||||||
m_ui.trueColor->setEnabled(is_hardware);
|
m_ui.trueColor->setEnabled(is_hardware);
|
||||||
m_ui.pgxpEnable->setEnabled(is_hardware);
|
m_ui.pgxpEnable->setEnabled(is_hardware);
|
||||||
m_ui.chromaSmoothingFor24Bit->setEnabled(is_hardware);
|
|
||||||
|
|
||||||
m_ui.gpuLineDetectMode->setEnabled(is_hardware);
|
m_ui.gpuLineDetectMode->setEnabled(is_hardware);
|
||||||
m_ui.gpuLineDetectModeLabel->setEnabled(is_hardware);
|
m_ui.gpuLineDetectModeLabel->setEnabled(is_hardware);
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
<x>0</x>
|
<x>0</x>
|
||||||
<y>0</y>
|
<y>0</y>
|
||||||
<width>584</width>
|
<width>584</width>
|
||||||
<height>434</height>
|
<height>450</height>
|
||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
<property name="windowTitle">
|
<property name="windowTitle">
|
||||||
|
@ -260,37 +260,37 @@
|
||||||
</item>
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</item>
|
</item>
|
||||||
<item row="4" column="0">
|
<item row="5" column="0">
|
||||||
<widget class="QLabel" name="label_7">
|
<widget class="QLabel" name="label_7">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Crop:</string>
|
<string>Crop:</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="4" column="1">
|
<item row="5" column="1">
|
||||||
<widget class="QComboBox" name="displayCropMode"/>
|
<widget class="QComboBox" name="displayCropMode"/>
|
||||||
</item>
|
</item>
|
||||||
<item row="5" column="0">
|
<item row="6" column="0">
|
||||||
<widget class="QLabel" name="label_8">
|
<widget class="QLabel" name="label_8">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Scaling:</string>
|
<string>Scaling:</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="5" column="1">
|
<item row="6" column="1">
|
||||||
<widget class="QComboBox" name="displayScaling"/>
|
<widget class="QComboBox" name="displayScaling"/>
|
||||||
</item>
|
</item>
|
||||||
<item row="6" column="0">
|
<item row="7" column="0">
|
||||||
<widget class="QLabel" name="label_2">
|
<widget class="QLabel" name="label_2">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>VSync:</string>
|
<string>VSync:</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="6" column="1">
|
<item row="7" column="1">
|
||||||
<widget class="QComboBox" name="displaySyncMode"/>
|
<widget class="QComboBox" name="displaySyncMode"/>
|
||||||
</item>
|
</item>
|
||||||
<item row="7" column="0" colspan="2">
|
<item row="8" column="0" colspan="2">
|
||||||
<layout class="QGridLayout" name="gridLayout_2">
|
<layout class="QGridLayout" name="gridLayout_2">
|
||||||
<item row="1" column="0">
|
<item row="1" column="0">
|
||||||
<widget class="QCheckBox" name="pgxpEnable">
|
<widget class="QCheckBox" name="pgxpEnable">
|
||||||
|
@ -350,6 +350,16 @@
|
||||||
</item>
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</item>
|
</item>
|
||||||
|
<item row="4" column="0">
|
||||||
|
<widget class="QLabel" name="label_3">
|
||||||
|
<property name="text">
|
||||||
|
<string>Deinterlacing:</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="4" column="1">
|
||||||
|
<widget class="QComboBox" name="displayDeinterlacing"/>
|
||||||
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
|
|
@ -959,6 +959,46 @@ void GPUDevice::SetDisplayMaxFPS(float max_fps)
|
||||||
m_display_frame_interval = (max_fps > 0.0f) ? (1.0f / max_fps) : 0.0f;
|
m_display_frame_interval = (max_fps > 0.0f) ? (1.0f / max_fps) : 0.0f;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool GPUDevice::ResizeTexture(std::unique_ptr<GPUTexture>* tex, u32 new_width, u32 new_height, GPUTexture::Type type,
|
||||||
|
GPUTexture::Format format, bool preserve /* = true */)
|
||||||
|
{
|
||||||
|
GPUTexture* old_tex = tex->get();
|
||||||
|
DebugAssert(!old_tex || (old_tex->GetLayers() == 1 && old_tex->GetLevels() == 1 && old_tex->GetSamples() == 1));
|
||||||
|
std::unique_ptr<GPUTexture> new_tex = FetchTexture(new_width, new_height, 1, 1, 1, type, format);
|
||||||
|
if (!new_tex) [[unlikely]]
|
||||||
|
{
|
||||||
|
Log_ErrorFmt("Failed to create new {}x{} texture", new_width, new_height);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (old_tex)
|
||||||
|
{
|
||||||
|
if (old_tex->GetState() == GPUTexture::State::Cleared)
|
||||||
|
{
|
||||||
|
if (type == GPUTexture::Type::RenderTarget)
|
||||||
|
ClearRenderTarget(new_tex.get(), old_tex->GetClearColor());
|
||||||
|
}
|
||||||
|
else if (old_tex->GetState() == GPUTexture::State::Dirty)
|
||||||
|
{
|
||||||
|
const u32 copy_width = std::min(new_width, old_tex->GetWidth());
|
||||||
|
const u32 copy_height = std::min(new_height, old_tex->GetHeight());
|
||||||
|
if (type == GPUTexture::Type::RenderTarget)
|
||||||
|
ClearRenderTarget(new_tex.get(), 0);
|
||||||
|
CopyTextureRegion(new_tex.get(), 0, 0, 0, 0, old_tex, 0, 0, 0, 0, copy_width, copy_height);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (preserve)
|
||||||
|
{
|
||||||
|
// If we're expecting data to be there, make sure to clear it.
|
||||||
|
if (type == GPUTexture::Type::RenderTarget)
|
||||||
|
ClearRenderTarget(new_tex.get(), 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
RecycleTexture(std::move(*tex));
|
||||||
|
*tex = std::move(new_tex);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
bool GPUDevice::ShouldSkipDisplayingFrame()
|
bool GPUDevice::ShouldSkipDisplayingFrame()
|
||||||
{
|
{
|
||||||
if (m_display_frame_interval == 0.0f)
|
if (m_display_frame_interval == 0.0f)
|
||||||
|
|
|
@ -650,6 +650,8 @@ public:
|
||||||
bool UsesLowerLeftOrigin() const;
|
bool UsesLowerLeftOrigin() const;
|
||||||
static Common::Rectangle<s32> FlipToLowerLeft(const Common::Rectangle<s32>& rc, s32 target_height);
|
static Common::Rectangle<s32> FlipToLowerLeft(const Common::Rectangle<s32>& rc, s32 target_height);
|
||||||
void SetDisplayMaxFPS(float max_fps);
|
void SetDisplayMaxFPS(float max_fps);
|
||||||
|
bool ResizeTexture(std::unique_ptr<GPUTexture>* tex, u32 new_width, u32 new_height, GPUTexture::Type type,
|
||||||
|
GPUTexture::Format format, bool preserve = true);
|
||||||
bool ShouldSkipDisplayingFrame();
|
bool ShouldSkipDisplayingFrame();
|
||||||
void ThrottlePresentation();
|
void ThrottlePresentation();
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue