GPU: Add adaptive and box downsampling modes
This commit is contained in:
parent
5236583544
commit
3cb2cd8235
|
@ -450,4 +450,14 @@
|
|||
<item>light</item>
|
||||
<item>dark</item>
|
||||
</string-array>
|
||||
<string-array name="settings_downsample_mode_entries">
|
||||
<item>Disabled</item>
|
||||
<item>Box (Downsample 3D/Smooth All)</item>
|
||||
<item>Adaptive (Preserve 3D/Smooth 2D)</item>
|
||||
</string-array>
|
||||
<string-array name="settings_downsample_mode_values">
|
||||
<item>Disabled</item>
|
||||
<item>Box</item>
|
||||
<item>Adaptive</item>
|
||||
</string-array>
|
||||
</resources>
|
||||
|
|
|
@ -182,4 +182,5 @@
|
|||
<string name="settings_summary_use_analog_sticks_for_dpad">Allows you to use the analog sticks to control the d-pad in digital mode, as well as the buttons.</string>
|
||||
<string name="settings_disable_all_enhancements">Disable All Enhancements</string>
|
||||
<string name="settings_summary_disable_all_enhancements">Temporarily disables all enhancements, which can be useful when debugging issues.</string>
|
||||
<string name="settings_downsample_mode">Downsampling</string>
|
||||
</resources>
|
||||
|
|
|
@ -34,6 +34,15 @@
|
|||
app:useSimpleSummaryProvider="true"
|
||||
app:iconSpaceReserved="false" />
|
||||
|
||||
<ListPreference
|
||||
app:key="GPU/DownsampleMode"
|
||||
app:title="@string/settings_downsample_mode"
|
||||
app:entries="@array/settings_downsample_mode_entries"
|
||||
app:entryValues="@array/settings_downsample_mode_values"
|
||||
app:defaultValue="Disabled"
|
||||
app:useSimpleSummaryProvider="true"
|
||||
app:iconSpaceReserved="false" />
|
||||
|
||||
<SwitchPreferenceCompat
|
||||
app:key="Display/LinearFiltering"
|
||||
app:title="@string/settings_linear_upscaling"
|
||||
|
|
|
@ -55,6 +55,7 @@ bool GPU_HW::Initialize(HostDisplay* host_display)
|
|||
m_texture_filtering = g_settings.gpu_texture_filter;
|
||||
m_using_uv_limits = ShouldUseUVLimits();
|
||||
m_chroma_smoothing = g_settings.gpu_24bit_chroma_smoothing;
|
||||
m_downsample_mode = GetDownsampleMode(m_resolution_scale);
|
||||
|
||||
if (m_multisamples != g_settings.gpu_multisamples)
|
||||
{
|
||||
|
@ -70,10 +71,20 @@ bool GPU_HW::Initialize(HostDisplay* host_display)
|
|||
if (!m_supports_dual_source_blend && TextureFilterRequiresDualSourceBlend(m_texture_filtering))
|
||||
{
|
||||
g_host_interface->AddFormattedOSDMessage(
|
||||
20.0f, g_host_interface->TranslateString("OSDMessage", "Texture filter '%s' is not supported on your device."),
|
||||
20.0f,
|
||||
g_host_interface->TranslateString("OSDMessage",
|
||||
"Texture filter '%s' is not supported with the current renderer."),
|
||||
Settings::GetTextureFilterDisplayName(m_texture_filtering));
|
||||
m_texture_filtering = GPUTextureFilter::Nearest;
|
||||
}
|
||||
if (!m_supports_adaptive_downsampling && g_settings.gpu_resolution_scale > 1 &&
|
||||
g_settings.gpu_downsample_mode == GPUDownsampleMode::Adaptive)
|
||||
{
|
||||
g_host_interface->AddOSDMessage(
|
||||
g_host_interface->TranslateStdString(
|
||||
"OSDMessage", "Adaptive downsampling is not supported with the current renderer, using box filter instead."),
|
||||
20.0f);
|
||||
}
|
||||
|
||||
m_pgxp_depth_buffer = g_settings.gpu_pgxp_depth_buffer;
|
||||
PrintSettingsToLog();
|
||||
|
@ -117,15 +128,17 @@ void GPU_HW::UpdateHWSettings(bool* framebuffer_changed, bool* shaders_changed)
|
|||
const u32 resolution_scale = CalculateResolutionScale();
|
||||
const u32 multisamples = std::min(m_max_multisamples, g_settings.gpu_multisamples);
|
||||
const bool per_sample_shading = g_settings.gpu_per_sample_shading && m_supports_per_sample_shading;
|
||||
const GPUDownsampleMode downsample_mode = GetDownsampleMode(resolution_scale);
|
||||
const bool use_uv_limits = ShouldUseUVLimits();
|
||||
|
||||
*framebuffer_changed = (m_resolution_scale != resolution_scale || m_multisamples != multisamples);
|
||||
*framebuffer_changed =
|
||||
(m_resolution_scale != resolution_scale || m_multisamples != multisamples || m_downsample_mode != downsample_mode);
|
||||
*shaders_changed =
|
||||
(m_resolution_scale != resolution_scale || m_multisamples != multisamples ||
|
||||
m_true_color != g_settings.gpu_true_color || m_per_sample_shading != per_sample_shading ||
|
||||
m_scaled_dithering != g_settings.gpu_scaled_dithering || m_texture_filtering != g_settings.gpu_texture_filter ||
|
||||
m_using_uv_limits != use_uv_limits || m_chroma_smoothing != g_settings.gpu_24bit_chroma_smoothing ||
|
||||
m_pgxp_depth_buffer != g_settings.UsingPGXPDepthBuffer());
|
||||
m_downsample_mode != downsample_mode || m_pgxp_depth_buffer != g_settings.UsingPGXPDepthBuffer());
|
||||
|
||||
if (m_resolution_scale != resolution_scale)
|
||||
{
|
||||
|
@ -159,6 +172,7 @@ void GPU_HW::UpdateHWSettings(bool* framebuffer_changed, bool* shaders_changed)
|
|||
m_texture_filtering = g_settings.gpu_texture_filter;
|
||||
m_using_uv_limits = use_uv_limits;
|
||||
m_chroma_smoothing = g_settings.gpu_24bit_chroma_smoothing;
|
||||
m_downsample_mode = downsample_mode;
|
||||
|
||||
if (!m_supports_dual_source_blend && TextureFilterRequiresDualSourceBlend(m_texture_filtering))
|
||||
m_texture_filtering = GPUTextureFilter::Nearest;
|
||||
|
@ -176,16 +190,30 @@ void GPU_HW::UpdateHWSettings(bool* framebuffer_changed, bool* shaders_changed)
|
|||
|
||||
u32 GPU_HW::CalculateResolutionScale() const
|
||||
{
|
||||
u32 scale;
|
||||
if (g_settings.gpu_resolution_scale != 0)
|
||||
return std::clamp<u32>(g_settings.gpu_resolution_scale, 1, m_max_resolution_scale);
|
||||
{
|
||||
scale = std::clamp<u32>(g_settings.gpu_resolution_scale, 1, m_max_resolution_scale);
|
||||
}
|
||||
else
|
||||
{
|
||||
// auto scaling
|
||||
const s32 height = (m_crtc_state.display_height != 0) ? static_cast<s32>(m_crtc_state.display_height) : 480;
|
||||
const s32 preferred_scale =
|
||||
static_cast<s32>(std::ceil(static_cast<float>(m_host_display->GetWindowHeight()) / height));
|
||||
Log_InfoPrintf("Height = %d, preferred scale = %d", height, preferred_scale);
|
||||
|
||||
// auto scaling
|
||||
const s32 height = (m_crtc_state.display_height != 0) ? static_cast<s32>(m_crtc_state.display_height) : 480;
|
||||
const s32 preferred_scale =
|
||||
static_cast<s32>(std::ceil(static_cast<float>(m_host_display->GetWindowHeight()) / height));
|
||||
Log_InfoPrintf("Height = %d, preferred scale = %d", height, preferred_scale);
|
||||
scale = static_cast<u32>(std::clamp<s32>(preferred_scale, 1, m_max_resolution_scale));
|
||||
}
|
||||
|
||||
return static_cast<u32>(std::clamp<s32>(preferred_scale, 1, m_max_resolution_scale));
|
||||
if (g_settings.gpu_downsample_mode == GPUDownsampleMode::Adaptive && m_supports_adaptive_downsampling && scale > 1 &&
|
||||
(scale % 2) != 0)
|
||||
{
|
||||
Log_InfoPrintf("Resolution scale %u not supported for adaptive smoothing, using %u", scale, (scale - 1));
|
||||
scale--;
|
||||
}
|
||||
|
||||
return scale;
|
||||
}
|
||||
|
||||
void GPU_HW::UpdateResolutionScale()
|
||||
|
@ -196,6 +224,17 @@ void GPU_HW::UpdateResolutionScale()
|
|||
UpdateSettings();
|
||||
}
|
||||
|
||||
GPUDownsampleMode GPU_HW::GetDownsampleMode(u32 resolution_scale) const
|
||||
{
|
||||
if (resolution_scale == 1)
|
||||
return GPUDownsampleMode::Disabled;
|
||||
|
||||
if (g_settings.gpu_downsample_mode == GPUDownsampleMode::Adaptive)
|
||||
return m_supports_adaptive_downsampling ? GPUDownsampleMode::Adaptive : GPUDownsampleMode::Box;
|
||||
|
||||
return g_settings.gpu_downsample_mode;
|
||||
}
|
||||
|
||||
std::tuple<u32, u32> GPU_HW::GetEffectiveDisplayResolution()
|
||||
{
|
||||
return std::make_tuple(m_crtc_state.display_vram_width * m_resolution_scale,
|
||||
|
@ -213,6 +252,7 @@ void GPU_HW::PrintSettingsToLog()
|
|||
Log_InfoPrintf("Dual-source blending: %s", m_supports_dual_source_blend ? "Supported" : "Not supported");
|
||||
Log_InfoPrintf("Using UV limits: %s", m_using_uv_limits ? "YES" : "NO");
|
||||
Log_InfoPrintf("Depth buffer: %s", m_pgxp_depth_buffer ? "YES" : "NO");
|
||||
Log_InfoPrintf("Downsampling: %s", Settings::GetDownsampleModeDisplayName(m_downsample_mode));
|
||||
}
|
||||
|
||||
void GPU_HW::UpdateVRAMReadTexture()
|
||||
|
@ -368,6 +408,36 @@ void GPU_HW::CheckForDepthClear(const BatchVertex* vertices, u32 num_vertices)
|
|||
m_last_depth_z = average_z;
|
||||
}
|
||||
|
||||
u32 GPU_HW::GetAdaptiveDownsamplingMipLevels() const
|
||||
{
|
||||
u32 levels = 0;
|
||||
u32 current_width = VRAM_WIDTH * m_resolution_scale;
|
||||
while (current_width >= VRAM_WIDTH)
|
||||
{
|
||||
levels++;
|
||||
current_width /= 2;
|
||||
}
|
||||
|
||||
return levels;
|
||||
}
|
||||
|
||||
GPU_HW::SmoothingUBOData GPU_HW::GetSmoothingUBO(u32 level, u32 left, u32 top, u32 width, u32 height, u32 tex_width,
|
||||
u32 tex_height) const
|
||||
{
|
||||
const float rcp_width = 1.0f / static_cast<float>(tex_width >> level);
|
||||
const float rcp_height = 1.0f / static_cast<float>(tex_height >> level);
|
||||
|
||||
SmoothingUBOData data;
|
||||
data.min_uv[0] = static_cast<float>(left >> level) * rcp_width;
|
||||
data.min_uv[1] = static_cast<float>(top >> level) * rcp_height;
|
||||
data.max_uv[0] = static_cast<float>((left + width) >> level) * rcp_width;
|
||||
data.max_uv[1] = static_cast<float>((top + height) >> level) * rcp_height;
|
||||
data.rcp_size[0] = rcp_width;
|
||||
data.rcp_size[1] = rcp_height;
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
void GPU_HW::DrawLine(float x0, float y0, u32 col0, float x1, float y1, u32 col1, float depth)
|
||||
{
|
||||
const float dx = x1 - x0;
|
||||
|
|
|
@ -188,8 +188,10 @@ protected:
|
|||
virtual void DrawBatchVertices(BatchRenderMode render_mode, u32 base_vertex, u32 num_vertices) = 0;
|
||||
|
||||
u32 CalculateResolutionScale() const;
|
||||
GPUDownsampleMode GetDownsampleMode(u32 resolution_scale) const;
|
||||
|
||||
ALWAYS_INLINE bool IsUsingMultisampling() const { return m_multisamples > 1; }
|
||||
ALWAYS_INLINE bool IsUsingDownsampling() const { return (m_downsample_mode != GPUDownsampleMode::Disabled); }
|
||||
|
||||
void SetFullVRAMDirtyRectangle()
|
||||
{
|
||||
|
@ -287,6 +289,21 @@ protected:
|
|||
void SetBatchDepthBuffer(bool enabled);
|
||||
void CheckForDepthClear(const BatchVertex* vertices, u32 num_vertices);
|
||||
|
||||
/// UBO data for adaptive smoothing.
|
||||
struct SmoothingUBOData
|
||||
{
|
||||
float min_uv[2];
|
||||
float max_uv[2];
|
||||
float rcp_size[2];
|
||||
};
|
||||
|
||||
/// Returns the number of mipmap levels used for adaptive smoothing.
|
||||
u32 GetAdaptiveDownsamplingMipLevels() const;
|
||||
|
||||
/// Returns the UBO data for an adaptive smoothing pass.
|
||||
SmoothingUBOData GetSmoothingUBO(u32 level, u32 left, u32 top, u32 width, u32 height, u32 tex_width,
|
||||
u32 tex_height) const;
|
||||
|
||||
HeapArray<u16, VRAM_WIDTH * VRAM_HEIGHT> m_vram_shadow;
|
||||
|
||||
BatchVertex* m_batch_start_vertex_ptr = nullptr;
|
||||
|
@ -301,13 +318,22 @@ protected:
|
|||
u32 m_max_resolution_scale = 1;
|
||||
u32 m_max_multisamples = 1;
|
||||
HostDisplay::RenderAPI m_render_api = HostDisplay::RenderAPI::None;
|
||||
bool m_per_sample_shading = false;
|
||||
bool m_true_color = true;
|
||||
bool m_scaled_dithering = false;
|
||||
|
||||
union
|
||||
{
|
||||
BitField<u8, bool, 0, 1> m_supports_per_sample_shading;
|
||||
BitField<u8, bool, 1, 1> m_supports_dual_source_blend;
|
||||
BitField<u8, bool, 2, 1> m_supports_adaptive_downsampling;
|
||||
BitField<u8, bool, 3, 1> m_per_sample_shading;
|
||||
BitField<u8, bool, 4, 1> m_scaled_dithering;
|
||||
BitField<u8, bool, 5, 1> m_chroma_smoothing;
|
||||
|
||||
u8 bits = 0;
|
||||
};
|
||||
|
||||
GPUTextureFilter m_texture_filtering = GPUTextureFilter::Nearest;
|
||||
bool m_chroma_smoothing = false;
|
||||
bool m_supports_per_sample_shading = false;
|
||||
bool m_supports_dual_source_blend = false;
|
||||
GPUDownsampleMode m_downsample_mode = GPUDownsampleMode::Disabled;
|
||||
bool m_using_uv_limits = false;
|
||||
bool m_pgxp_depth_buffer = false;
|
||||
|
||||
|
|
|
@ -179,6 +179,7 @@ void GPU_HW_D3D11::SetCapabilities()
|
|||
m_max_resolution_scale = max_texture_scale;
|
||||
m_supports_dual_source_blend = true;
|
||||
m_supports_per_sample_shading = (m_device->GetFeatureLevel() >= D3D_FEATURE_LEVEL_10_1);
|
||||
m_supports_adaptive_downsampling = true;
|
||||
|
||||
m_max_multisamples = 1;
|
||||
for (u32 multisamples = 2; multisamples < D3D11_MAX_MULTISAMPLE_SAMPLE_COUNT; multisamples++)
|
||||
|
@ -226,6 +227,47 @@ bool GPU_HW_D3D11::CreateFramebuffer()
|
|||
if (FAILED(hr))
|
||||
return false;
|
||||
|
||||
if (m_downsample_mode == GPUDownsampleMode::Adaptive)
|
||||
{
|
||||
const u32 levels = GetAdaptiveDownsamplingMipLevels();
|
||||
|
||||
if (!m_downsample_texture.Create(m_device.Get(), texture_width, texture_height, static_cast<u16>(levels), 1,
|
||||
texture_format, D3D11_BIND_SHADER_RESOURCE | D3D11_BIND_RENDER_TARGET) ||
|
||||
!m_downsample_weight_texture.Create(m_device.Get(), texture_width >> (levels - 1),
|
||||
texture_height >> (levels - 1), 1, 1, DXGI_FORMAT_R8_UNORM,
|
||||
D3D11_BIND_SHADER_RESOURCE | D3D11_BIND_RENDER_TARGET))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
m_downsample_mip_views.resize(levels);
|
||||
for (u32 i = 0; i < levels; i++)
|
||||
{
|
||||
const CD3D11_SHADER_RESOURCE_VIEW_DESC srv_desc(m_downsample_texture, D3D11_SRV_DIMENSION_TEXTURE2D,
|
||||
texture_format, i, 1);
|
||||
const CD3D11_RENDER_TARGET_VIEW_DESC rtv_desc(m_downsample_texture, D3D11_RTV_DIMENSION_TEXTURE2D, texture_format,
|
||||
i, 1);
|
||||
|
||||
hr = m_device->CreateShaderResourceView(m_downsample_texture, &srv_desc,
|
||||
m_downsample_mip_views[i].first.GetAddressOf());
|
||||
if (FAILED(hr))
|
||||
return false;
|
||||
|
||||
hr = m_device->CreateRenderTargetView(m_downsample_texture, &rtv_desc,
|
||||
m_downsample_mip_views[i].second.GetAddressOf());
|
||||
if (FAILED(hr))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if (m_downsample_mode == GPUDownsampleMode::Box)
|
||||
{
|
||||
if (!m_downsample_texture.Create(m_device.Get(), VRAM_WIDTH, VRAM_HEIGHT, 1, 1, texture_format,
|
||||
D3D11_BIND_SHADER_RESOURCE | D3D11_BIND_RENDER_TARGET))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
m_context->OMSetRenderTargets(1, m_vram_texture.GetD3DRTVArray(), nullptr);
|
||||
SetFullVRAMDirtyRectangle();
|
||||
return true;
|
||||
|
@ -243,6 +285,10 @@ void GPU_HW_D3D11::ClearFramebuffer()
|
|||
|
||||
void GPU_HW_D3D11::DestroyFramebuffer()
|
||||
{
|
||||
m_downsample_mip_views.clear();
|
||||
m_downsample_weight_texture.Destroy();
|
||||
m_downsample_texture.Destroy();
|
||||
|
||||
m_vram_read_texture.Destroy();
|
||||
m_vram_depth_view.Reset();
|
||||
m_vram_depth_texture.Destroy();
|
||||
|
@ -351,6 +397,11 @@ bool GPU_HW_D3D11::CreateStateObjects()
|
|||
if (FAILED(hr))
|
||||
return false;
|
||||
|
||||
sampler_desc.Filter = D3D11_FILTER_MIN_MAG_MIP_LINEAR;
|
||||
hr = m_device->CreateSamplerState(&sampler_desc, m_trilinear_sampler_state.ReleaseAndGetAddressOf());
|
||||
if (FAILED(hr))
|
||||
return false;
|
||||
|
||||
for (u8 transparency_mode = 0; transparency_mode < 5; transparency_mode++)
|
||||
{
|
||||
bl_desc = CD3D11_BLEND_DESC(CD3D11_DEFAULT());
|
||||
|
@ -382,6 +433,7 @@ void GPU_HW_D3D11::DestroyStateObjects()
|
|||
m_batch_blend_states = {};
|
||||
m_linear_sampler_state.Reset();
|
||||
m_point_sampler_state.Reset();
|
||||
m_trilinear_sampler_state.Reset();
|
||||
m_blend_no_color_writes_state.Reset();
|
||||
m_blend_disabled_state.Reset();
|
||||
m_depth_test_greater_state.Reset();
|
||||
|
@ -403,7 +455,7 @@ bool GPU_HW_D3D11::CompileShaders()
|
|||
m_pgxp_depth_buffer, m_supports_dual_source_blend);
|
||||
|
||||
Common::Timer compile_time;
|
||||
const int progress_total = 1 + 1 + 2 + (4 * 9 * 2 * 2) + 7 + (2 * 3);
|
||||
const int progress_total = 1 + 1 + 2 + (4 * 9 * 2 * 2) + 7 + (2 * 3) + 1;
|
||||
int progress_value = 0;
|
||||
#define UPDATE_PROGRESS() \
|
||||
do \
|
||||
|
@ -446,7 +498,8 @@ bool GPU_HW_D3D11::CompileShaders()
|
|||
|
||||
m_screen_quad_vertex_shader =
|
||||
shader_cache.GetVertexShader(m_device.Get(), shadergen.GenerateScreenQuadVertexShader());
|
||||
if (!m_screen_quad_vertex_shader)
|
||||
m_uv_quad_vertex_shader = shader_cache.GetVertexShader(m_device.Get(), shadergen.GenerateUVQuadVertexShader());
|
||||
if (!m_screen_quad_vertex_shader || !m_uv_quad_vertex_shader)
|
||||
return false;
|
||||
|
||||
UPDATE_PROGRESS();
|
||||
|
@ -546,6 +599,33 @@ bool GPU_HW_D3D11::CompileShaders()
|
|||
|
||||
UPDATE_PROGRESS();
|
||||
|
||||
if (m_downsample_mode == GPUDownsampleMode::Adaptive)
|
||||
{
|
||||
m_downsample_first_pass_pixel_shader =
|
||||
shader_cache.GetPixelShader(m_device.Get(), shadergen.GenerateAdaptiveDownsampleMipFragmentShader(true));
|
||||
m_downsample_mid_pass_pixel_shader =
|
||||
shader_cache.GetPixelShader(m_device.Get(), shadergen.GenerateAdaptiveDownsampleMipFragmentShader(false));
|
||||
m_downsample_blur_pass_pixel_shader =
|
||||
shader_cache.GetPixelShader(m_device.Get(), shadergen.GenerateAdaptiveDownsampleBlurFragmentShader());
|
||||
m_downsample_composite_pixel_shader =
|
||||
shader_cache.GetPixelShader(m_device.Get(), shadergen.GenerateAdaptiveDownsampleCompositeFragmentShader());
|
||||
|
||||
if (!m_downsample_first_pass_pixel_shader || !m_downsample_mid_pass_pixel_shader ||
|
||||
!m_downsample_blur_pass_pixel_shader || !m_downsample_composite_pixel_shader)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if (m_downsample_mode == GPUDownsampleMode::Box)
|
||||
{
|
||||
m_downsample_first_pass_pixel_shader =
|
||||
shader_cache.GetPixelShader(m_device.Get(), shadergen.GenerateBoxSampleDownsampleFragmentShader());
|
||||
if (!m_downsample_first_pass_pixel_shader)
|
||||
return false;
|
||||
}
|
||||
|
||||
UPDATE_PROGRESS();
|
||||
|
||||
#undef UPDATE_PROGRESS
|
||||
|
||||
return true;
|
||||
|
@ -553,6 +633,10 @@ bool GPU_HW_D3D11::CompileShaders()
|
|||
|
||||
void GPU_HW_D3D11::DestroyShaders()
|
||||
{
|
||||
m_downsample_composite_pixel_shader.Reset();
|
||||
m_downsample_blur_pass_pixel_shader.Reset();
|
||||
m_downsample_mid_pass_pixel_shader.Reset();
|
||||
m_downsample_first_pass_pixel_shader.Reset();
|
||||
m_display_pixel_shaders = {};
|
||||
m_vram_update_depth_pixel_shader.Reset();
|
||||
m_vram_copy_pixel_shader.Reset();
|
||||
|
@ -561,6 +645,7 @@ void GPU_HW_D3D11::DestroyShaders()
|
|||
m_vram_interlaced_fill_pixel_shader.Reset();
|
||||
m_vram_fill_pixel_shader.Reset();
|
||||
m_copy_pixel_shader.Reset();
|
||||
m_uv_quad_vertex_shader.Reset();
|
||||
m_screen_quad_vertex_shader.Reset();
|
||||
m_batch_pixel_shaders = {};
|
||||
m_batch_vertex_shaders = {};
|
||||
|
@ -747,9 +832,18 @@ void GPU_HW_D3D11::UpdateDisplay()
|
|||
!IsUsingMultisampling() && (scaled_vram_offset_x + scaled_display_width) <= m_vram_texture.GetWidth() &&
|
||||
(scaled_vram_offset_y + scaled_display_height) <= m_vram_texture.GetHeight())
|
||||
{
|
||||
m_host_display->SetDisplayTexture(m_vram_texture.GetD3DSRV(), HostDisplayPixelFormat::RGBA8,
|
||||
m_vram_texture.GetWidth(), m_vram_texture.GetHeight(), scaled_vram_offset_x,
|
||||
scaled_vram_offset_y, scaled_display_width, scaled_display_height);
|
||||
|
||||
if (IsUsingDownsampling())
|
||||
{
|
||||
DownsampleFramebuffer(m_vram_texture, scaled_vram_offset_x, scaled_vram_offset_y, scaled_display_width,
|
||||
scaled_display_height);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_host_display->SetDisplayTexture(m_vram_texture.GetD3DSRV(), HostDisplayPixelFormat::RGBA8,
|
||||
m_vram_texture.GetWidth(), m_vram_texture.GetHeight(), scaled_vram_offset_x,
|
||||
scaled_vram_offset_y, scaled_display_width, scaled_display_height);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -769,9 +863,16 @@ void GPU_HW_D3D11::UpdateDisplay()
|
|||
SetViewportAndScissor(0, 0, scaled_display_width, scaled_display_height);
|
||||
DrawUtilityShader(display_pixel_shader, uniforms, sizeof(uniforms));
|
||||
|
||||
m_host_display->SetDisplayTexture(m_display_texture.GetD3DSRV(), HostDisplayPixelFormat::RGBA8,
|
||||
m_display_texture.GetWidth(), m_display_texture.GetHeight(), 0, 0,
|
||||
scaled_display_width, scaled_display_height);
|
||||
if (IsUsingDownsampling())
|
||||
{
|
||||
DownsampleFramebuffer(m_display_texture, 0, 0, scaled_display_width, scaled_display_height);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_host_display->SetDisplayTexture(m_display_texture.GetD3DSRV(), HostDisplayPixelFormat::RGBA8,
|
||||
m_display_texture.GetWidth(), m_display_texture.GetHeight(), 0, 0,
|
||||
scaled_display_width, scaled_display_height);
|
||||
}
|
||||
|
||||
RestoreGraphicsAPIState();
|
||||
}
|
||||
|
@ -972,6 +1073,103 @@ void GPU_HW_D3D11::ClearDepthBuffer()
|
|||
m_last_depth_z = 1.0f;
|
||||
}
|
||||
|
||||
void GPU_HW_D3D11::DownsampleFramebuffer(D3D11::Texture& source, u32 left, u32 top, u32 width, u32 height)
|
||||
{
|
||||
if (m_downsample_mode == GPUDownsampleMode::Adaptive)
|
||||
DownsampleFramebufferAdaptive(source, left, top, width, height);
|
||||
else
|
||||
DownsampleFramebufferBoxFilter(source, left, top, width, height);
|
||||
}
|
||||
|
||||
void GPU_HW_D3D11::DownsampleFramebufferAdaptive(D3D11::Texture& source, u32 left, u32 top, u32 width, u32 height)
|
||||
{
|
||||
CD3D11_BOX src_box(left, top, 0, left + width, top + height, 1);
|
||||
m_context->OMSetDepthStencilState(m_depth_disabled_state.Get(), 0);
|
||||
m_context->OMSetBlendState(m_blend_disabled_state.Get(), nullptr, 0xFFFFFFFFu);
|
||||
m_context->CopySubresourceRegion(m_downsample_texture, 0, left, top, 0, source, 0, &src_box);
|
||||
m_context->PSSetSamplers(0, 1, m_point_sampler_state.GetAddressOf());
|
||||
m_context->VSSetShader(m_uv_quad_vertex_shader.Get(), nullptr, 0);
|
||||
|
||||
// create mip chain
|
||||
const u32 levels = m_downsample_texture.GetLevels();
|
||||
for (u32 level = 1; level < levels; level++)
|
||||
{
|
||||
SetViewportAndScissor(left >> level, top >> level, width >> level, height >> level);
|
||||
m_context->OMSetRenderTargets(1, m_downsample_mip_views[level].second.GetAddressOf(), nullptr);
|
||||
m_context->PSSetShaderResources(0, 1, m_downsample_mip_views[level - 1].first.GetAddressOf());
|
||||
|
||||
const SmoothingUBOData ubo = GetSmoothingUBO(level, left, top, width, height, m_downsample_texture.GetWidth(),
|
||||
m_downsample_texture.GetHeight());
|
||||
m_context->PSSetShader(
|
||||
(level == 1) ? m_downsample_first_pass_pixel_shader.Get() : m_downsample_mid_pass_pixel_shader.Get(), nullptr, 0);
|
||||
UploadUniformBuffer(&ubo, sizeof(ubo));
|
||||
m_context->Draw(3, 0);
|
||||
}
|
||||
|
||||
// blur pass at lowest level
|
||||
{
|
||||
const u32 last_level = levels - 1;
|
||||
|
||||
SetViewportAndScissor(left >> last_level, top >> last_level, width >> last_level, height >> last_level);
|
||||
m_context->OMSetRenderTargets(1, m_downsample_weight_texture.GetD3DRTVArray(), nullptr);
|
||||
m_context->PSSetShaderResources(0, 1, m_downsample_mip_views.back().first.GetAddressOf());
|
||||
m_context->PSSetShader(m_downsample_blur_pass_pixel_shader.Get(), nullptr, 0);
|
||||
|
||||
const SmoothingUBOData ubo = GetSmoothingUBO(last_level, left, top, width, height, m_downsample_texture.GetWidth(),
|
||||
m_downsample_texture.GetHeight());
|
||||
m_context->PSSetShader(m_downsample_blur_pass_pixel_shader.Get(), nullptr, 0);
|
||||
UploadUniformBuffer(&ubo, sizeof(ubo));
|
||||
m_context->Draw(3, 0);
|
||||
}
|
||||
|
||||
// composite downsampled and upsampled images together
|
||||
{
|
||||
SetViewportAndScissor(left, top, width, height);
|
||||
m_context->OMSetRenderTargets(1, m_display_texture.GetD3DRTVArray(), nullptr);
|
||||
|
||||
ID3D11ShaderResourceView* const srvs[2] = {m_downsample_texture.GetD3DSRV(),
|
||||
m_downsample_weight_texture.GetD3DSRV()};
|
||||
ID3D11SamplerState* const samplers[2] = {m_trilinear_sampler_state.Get(), m_linear_sampler_state.Get()};
|
||||
m_context->PSSetShaderResources(0, countof(srvs), srvs);
|
||||
m_context->PSSetSamplers(0, countof(samplers), samplers);
|
||||
m_context->PSSetShader(m_downsample_composite_pixel_shader.Get(), nullptr, 0);
|
||||
m_context->Draw(3, 0);
|
||||
}
|
||||
|
||||
ID3D11ShaderResourceView* const null_srvs[2] = {};
|
||||
m_context->PSSetShaderResources(0, countof(null_srvs), null_srvs);
|
||||
m_batch_ubo_dirty = true;
|
||||
|
||||
RestoreGraphicsAPIState();
|
||||
|
||||
m_host_display->SetDisplayTexture(m_display_texture.GetD3DSRV(), HostDisplayPixelFormat::RGBA8,
|
||||
m_display_texture.GetWidth(), m_display_texture.GetHeight(), left, top, width,
|
||||
height);
|
||||
}
|
||||
|
||||
void GPU_HW_D3D11::DownsampleFramebufferBoxFilter(D3D11::Texture& source, u32 left, u32 top, u32 width, u32 height)
|
||||
{
|
||||
const u32 ds_left = left / m_resolution_scale;
|
||||
const u32 ds_top = top / m_resolution_scale;
|
||||
const u32 ds_width = width / m_resolution_scale;
|
||||
const u32 ds_height = height / m_resolution_scale;
|
||||
|
||||
m_context->OMSetDepthStencilState(m_depth_disabled_state.Get(), 0);
|
||||
m_context->OMSetRenderTargets(1, m_downsample_texture.GetD3DRTVArray(), nullptr);
|
||||
m_context->OMSetBlendState(m_blend_disabled_state.Get(), nullptr, 0xFFFFFFFFu);
|
||||
m_context->VSSetShader(m_screen_quad_vertex_shader.Get(), nullptr, 0);
|
||||
m_context->PSSetShader(m_downsample_first_pass_pixel_shader.Get(), nullptr, 0);
|
||||
m_context->PSSetShaderResources(0, 1, source.GetD3DSRVArray());
|
||||
SetViewportAndScissor(ds_left, ds_top, ds_width, ds_height);
|
||||
m_context->Draw(3, 0);
|
||||
|
||||
RestoreGraphicsAPIState();
|
||||
|
||||
m_host_display->SetDisplayTexture(m_downsample_texture.GetD3DSRV(), HostDisplayPixelFormat::RGBA8,
|
||||
m_downsample_texture.GetWidth(), m_downsample_texture.GetHeight(), ds_left, ds_top,
|
||||
ds_width, ds_height);
|
||||
}
|
||||
|
||||
std::unique_ptr<GPU> GPU::CreateHardwareD3D11Renderer()
|
||||
{
|
||||
return std::make_unique<GPU_HW_D3D11>();
|
||||
|
|
|
@ -71,6 +71,10 @@ private:
|
|||
|
||||
bool BlitVRAMReplacementTexture(const TextureReplacementTexture* tex, u32 dst_x, u32 dst_y, u32 width, u32 height);
|
||||
|
||||
void DownsampleFramebuffer(D3D11::Texture& source, u32 left, u32 top, u32 width, u32 height);
|
||||
void DownsampleFramebufferAdaptive(D3D11::Texture& source, u32 left, u32 top, u32 width, u32 height);
|
||||
void DownsampleFramebufferBoxFilter(D3D11::Texture& source, u32 left, u32 top, u32 width, u32 height);
|
||||
|
||||
ComPtr<ID3D11Device> m_device;
|
||||
ComPtr<ID3D11DeviceContext> m_context;
|
||||
|
||||
|
@ -105,6 +109,7 @@ private:
|
|||
|
||||
ComPtr<ID3D11SamplerState> m_point_sampler_state;
|
||||
ComPtr<ID3D11SamplerState> m_linear_sampler_state;
|
||||
ComPtr<ID3D11SamplerState> m_trilinear_sampler_state;
|
||||
|
||||
std::array<ComPtr<ID3D11BlendState>, 5> m_batch_blend_states; // [transparency_mode]
|
||||
ComPtr<ID3D11InputLayout> m_batch_input_layout;
|
||||
|
@ -113,6 +118,7 @@ private:
|
|||
m_batch_pixel_shaders; // [render_mode][texture_mode][dithering][interlacing]
|
||||
|
||||
ComPtr<ID3D11VertexShader> m_screen_quad_vertex_shader;
|
||||
ComPtr<ID3D11VertexShader> m_uv_quad_vertex_shader;
|
||||
ComPtr<ID3D11PixelShader> m_copy_pixel_shader;
|
||||
ComPtr<ID3D11PixelShader> m_vram_fill_pixel_shader;
|
||||
ComPtr<ID3D11PixelShader> m_vram_interlaced_fill_pixel_shader;
|
||||
|
@ -123,4 +129,13 @@ private:
|
|||
std::array<std::array<ComPtr<ID3D11PixelShader>, 3>, 2> m_display_pixel_shaders; // [depth_24][interlaced]
|
||||
|
||||
D3D11::Texture m_vram_replacement_texture;
|
||||
|
||||
// downsampling
|
||||
ComPtr<ID3D11PixelShader> m_downsample_first_pass_pixel_shader;
|
||||
ComPtr<ID3D11PixelShader> m_downsample_mid_pass_pixel_shader;
|
||||
ComPtr<ID3D11PixelShader> m_downsample_blur_pass_pixel_shader;
|
||||
ComPtr<ID3D11PixelShader> m_downsample_composite_pixel_shader;
|
||||
D3D11::Texture m_downsample_texture;
|
||||
D3D11::Texture m_downsample_weight_texture;
|
||||
std::vector<std::pair<ComPtr<ID3D11ShaderResourceView>, ComPtr<ID3D11RenderTargetView>>> m_downsample_mip_views;
|
||||
};
|
||||
|
|
|
@ -273,6 +273,9 @@ void GPU_HW_OpenGL::SetCapabilities(HostDisplay* host_display)
|
|||
|
||||
m_max_resolution_scale = std::min<int>(m_max_resolution_scale, line_width_range[1]);
|
||||
}
|
||||
|
||||
// adaptive smoothing would require texture views, which aren't in GLES.
|
||||
m_supports_adaptive_downsampling = false;
|
||||
}
|
||||
|
||||
bool GPU_HW_OpenGL::CreateFramebuffer()
|
||||
|
@ -307,6 +310,15 @@ bool GPU_HW_OpenGL::CreateFramebuffer()
|
|||
m_vram_depth_texture.GetGLId(), 0);
|
||||
Assert(glCheckFramebufferStatus(GL_DRAW_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE);
|
||||
|
||||
if (m_downsample_mode == GPUDownsampleMode::Box)
|
||||
{
|
||||
if (!m_downsample_texture.Create(VRAM_WIDTH, VRAM_HEIGHT, 1, GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE) ||
|
||||
!m_downsample_texture.CreateFramebuffer())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
SetFullVRAMDirtyRectangle();
|
||||
return true;
|
||||
}
|
||||
|
@ -394,7 +406,7 @@ bool GPU_HW_OpenGL::CompilePrograms()
|
|||
m_pgxp_depth_buffer, m_supports_dual_source_blend);
|
||||
|
||||
Common::Timer compile_time;
|
||||
const int progress_total = (4 * 9 * 2 * 2) + (2 * 3) + 5;
|
||||
const int progress_total = (4 * 9 * 2 * 2) + (2 * 3) + 6;
|
||||
int progress_value = 0;
|
||||
#define UPDATE_PROGRESS() \
|
||||
do \
|
||||
|
@ -579,6 +591,28 @@ bool GPU_HW_OpenGL::CompilePrograms()
|
|||
}
|
||||
|
||||
UPDATE_PROGRESS();
|
||||
|
||||
if (m_downsample_mode == GPUDownsampleMode::Box)
|
||||
{
|
||||
prog = shader_cache.GetProgram(shadergen.GenerateScreenQuadVertexShader(), {},
|
||||
shadergen.GenerateBoxSampleDownsampleFragmentShader(),
|
||||
[this, use_binding_layout](GL::Program& prog) {
|
||||
if (!IsGLES() && !use_binding_layout)
|
||||
prog.BindFragData(0, "o_col0");
|
||||
});
|
||||
if (!prog)
|
||||
return false;
|
||||
|
||||
if (!use_binding_layout)
|
||||
{
|
||||
prog->Bind();
|
||||
prog->Uniform1i("samp0", 0);
|
||||
}
|
||||
|
||||
m_downsample_program = std::move(*prog);
|
||||
}
|
||||
|
||||
UPDATE_PROGRESS();
|
||||
#undef UPDATE_PROGRESS
|
||||
|
||||
return true;
|
||||
|
@ -748,11 +782,19 @@ void GPU_HW_OpenGL::UpdateDisplay()
|
|||
!IsUsingMultisampling() && (scaled_vram_offset_x + scaled_display_width) <= m_vram_texture.GetWidth() &&
|
||||
(scaled_vram_offset_y + scaled_display_height) <= m_vram_texture.GetHeight())
|
||||
{
|
||||
m_host_display->SetDisplayTexture(reinterpret_cast<void*>(static_cast<uintptr_t>(m_vram_texture.GetGLId())),
|
||||
HostDisplayPixelFormat::RGBA8, m_vram_texture.GetWidth(),
|
||||
m_vram_texture.GetHeight(), scaled_vram_offset_x,
|
||||
m_vram_texture.GetHeight() - scaled_vram_offset_y, scaled_display_width,
|
||||
-static_cast<s32>(scaled_display_height));
|
||||
if (IsUsingDownsampling())
|
||||
{
|
||||
DownsampleFramebuffer(m_vram_texture, scaled_vram_offset_x, scaled_vram_offset_y, scaled_display_width,
|
||||
scaled_display_height);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_host_display->SetDisplayTexture(reinterpret_cast<void*>(static_cast<uintptr_t>(m_vram_texture.GetGLId())),
|
||||
HostDisplayPixelFormat::RGBA8, m_vram_texture.GetWidth(),
|
||||
m_vram_texture.GetHeight(), scaled_vram_offset_x,
|
||||
m_vram_texture.GetHeight() - scaled_vram_offset_y, scaled_display_width,
|
||||
-static_cast<s32>(scaled_display_height));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -779,10 +821,17 @@ void GPU_HW_OpenGL::UpdateDisplay()
|
|||
glBindVertexArray(m_attributeless_vao_id);
|
||||
glDrawArrays(GL_TRIANGLES, 0, 3);
|
||||
|
||||
m_host_display->SetDisplayTexture(reinterpret_cast<void*>(static_cast<uintptr_t>(m_display_texture.GetGLId())),
|
||||
HostDisplayPixelFormat::RGBA8, m_display_texture.GetWidth(),
|
||||
m_display_texture.GetHeight(), 0, scaled_display_height, scaled_display_width,
|
||||
-static_cast<s32>(scaled_display_height));
|
||||
if (IsUsingDownsampling())
|
||||
{
|
||||
DownsampleFramebuffer(m_display_texture, 0, 0, scaled_display_width, scaled_display_height);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_host_display->SetDisplayTexture(reinterpret_cast<void*>(static_cast<uintptr_t>(m_display_texture.GetGLId())),
|
||||
HostDisplayPixelFormat::RGBA8, m_display_texture.GetWidth(),
|
||||
m_display_texture.GetHeight(), 0, scaled_display_height, scaled_display_width,
|
||||
-static_cast<s32>(scaled_display_height));
|
||||
}
|
||||
|
||||
// restore state
|
||||
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, m_vram_fbo_id);
|
||||
|
@ -1136,6 +1185,37 @@ void GPU_HW_OpenGL::ClearDepthBuffer()
|
|||
m_last_depth_z = 1.0f;
|
||||
}
|
||||
|
||||
void GPU_HW_OpenGL::DownsampleFramebuffer(GL::Texture& source, u32 left, u32 top, u32 width, u32 height)
|
||||
{
|
||||
DebugAssert(m_downsample_mode != GPUDownsampleMode::Adaptive);
|
||||
DownsampleFramebufferBoxFilter(source, left, top, width, height);
|
||||
}
|
||||
|
||||
void GPU_HW_OpenGL::DownsampleFramebufferBoxFilter(GL::Texture& source, u32 left, u32 top, u32 width, u32 height)
|
||||
{
|
||||
const u32 ds_left = left / m_resolution_scale;
|
||||
const u32 ds_top = top / m_resolution_scale;
|
||||
const u32 ds_width = width / m_resolution_scale;
|
||||
const u32 ds_height = height / m_resolution_scale;
|
||||
|
||||
glDisable(GL_BLEND);
|
||||
glDisable(GL_DEPTH_TEST);
|
||||
glDisable(GL_SCISSOR_TEST);
|
||||
glViewport(ds_left, m_downsample_texture.GetHeight() - ds_top - ds_height, ds_width, ds_height);
|
||||
glBindVertexArray(m_attributeless_vao_id);
|
||||
source.Bind();
|
||||
m_downsample_texture.BindFramebuffer(GL_DRAW_FRAMEBUFFER);
|
||||
m_downsample_program.Bind();
|
||||
glDrawArrays(GL_TRIANGLES, 0, 3);
|
||||
|
||||
RestoreGraphicsAPIState();
|
||||
|
||||
m_host_display->SetDisplayTexture(reinterpret_cast<void*>(static_cast<uintptr_t>(m_downsample_texture.GetGLId())),
|
||||
HostDisplayPixelFormat::RGBA8, m_downsample_texture.GetWidth(),
|
||||
m_downsample_texture.GetHeight(), ds_left,
|
||||
m_downsample_texture.GetHeight() - ds_top, ds_width, -static_cast<s32>(ds_height));
|
||||
}
|
||||
|
||||
std::unique_ptr<GPU> GPU::CreateHardwareOpenGLRenderer()
|
||||
{
|
||||
return std::make_unique<GPU_HW_OpenGL>();
|
||||
|
|
|
@ -69,6 +69,8 @@ private:
|
|||
void SetBlendMode();
|
||||
|
||||
bool BlitVRAMReplacementTexture(const TextureReplacementTexture* tex, u32 dst_x, u32 dst_y, u32 width, u32 height);
|
||||
void DownsampleFramebuffer(GL::Texture& source, u32 left, u32 top, u32 width, u32 height);
|
||||
void DownsampleFramebufferBoxFilter(GL::Texture& source, u32 left, u32 top, u32 width, u32 height);
|
||||
|
||||
// downsample texture - used for readbacks at >1xIR.
|
||||
GL::Texture m_vram_texture;
|
||||
|
@ -107,4 +109,7 @@ private:
|
|||
GLenum m_current_depth_test = 0;
|
||||
GPUTransparencyMode m_current_transparency_mode = GPUTransparencyMode::Disabled;
|
||||
BatchRenderMode m_current_render_mode = BatchRenderMode::TransparencyDisabled;
|
||||
|
||||
GL::Texture m_downsample_texture;
|
||||
GL::Program m_downsample_program;
|
||||
};
|
||||
|
|
|
@ -1340,3 +1340,143 @@ std::string GPU_HW_ShaderGen::GenerateVRAMUpdateDepthFragmentShader()
|
|||
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
std::string GPU_HW_ShaderGen::GenerateAdaptiveDownsampleMipFragmentShader(bool first_pass)
|
||||
{
|
||||
std::stringstream ss;
|
||||
WriteHeader(ss);
|
||||
WriteCommonFunctions(ss);
|
||||
DeclareTexture(ss, "samp0", 0, false);
|
||||
DeclareUniformBuffer(ss, { "float2 u_uv_min", "float2 u_uv_max", "float2 u_rcp_resolution" }, true);
|
||||
DefineMacro(ss, "FIRST_PASS", first_pass);
|
||||
|
||||
// mipmap_energy.glsl ported from parallel-rsx.
|
||||
ss << R"(
|
||||
|
||||
float4 get_bias(float3 c00, float3 c01, float3 c10, float3 c11)
|
||||
{
|
||||
// Measure the "energy" (variance) in the pixels.
|
||||
// If the pixels are all the same (2D content), use maximum bias, otherwise, taper off quickly back to 0 (edges)
|
||||
float3 avg = 0.25 * (c00 + c01 + c10 + c11);
|
||||
float s00 = dot(c00 - avg, c00 - avg);
|
||||
float s01 = dot(c01 - avg, c01 - avg);
|
||||
float s10 = dot(c10 - avg, c10 - avg);
|
||||
float s11 = dot(c11 - avg, c11 - avg);
|
||||
return float4(avg, 1.0 - log2(1000.0 * (s00 + s01 + s10 + s11) + 1.0));
|
||||
}
|
||||
|
||||
float4 get_bias(float4 c00, float4 c01, float4 c10, float4 c11)
|
||||
{
|
||||
// Measure the "energy" (variance) in the pixels.
|
||||
// If the pixels are all the same (2D content), use maximum bias, otherwise, taper off quickly back to 0 (edges)
|
||||
float avg = 0.25 * (c00.a + c01.a + c10.a + c11.a);
|
||||
float4 bias = get_bias(c00.rgb, c01.rgb, c10.rgb, c11.rgb);
|
||||
bias.a *= avg;
|
||||
return bias;
|
||||
}
|
||||
|
||||
)";
|
||||
|
||||
DeclareFragmentEntryPoint(ss, 0, 1, {}, false, 1, false, false, false, false);
|
||||
ss << R"(
|
||||
{
|
||||
float2 uv = v_tex0 - (u_rcp_resolution * 0.25);
|
||||
#ifdef FIRST_PASS
|
||||
vec3 c00 = SAMPLE_TEXTURE_OFFSET(samp0, uv, int2(0, 0)).rgb;
|
||||
vec3 c01 = SAMPLE_TEXTURE_OFFSET(samp0, uv, int2(0, 1)).rgb;
|
||||
vec3 c10 = SAMPLE_TEXTURE_OFFSET(samp0, uv, int2(1, 0)).rgb;
|
||||
vec3 c11 = SAMPLE_TEXTURE_OFFSET(samp0, uv, int2(1, 1)).rgb;
|
||||
o_col0 = get_bias(c00, c01, c10, c11);
|
||||
#else
|
||||
vec4 c00 = SAMPLE_TEXTURE_OFFSET(samp0, uv, int2(0, 0));
|
||||
vec4 c01 = SAMPLE_TEXTURE_OFFSET(samp0, uv, int2(0, 1));
|
||||
vec4 c10 = SAMPLE_TEXTURE_OFFSET(samp0, uv, int2(1, 0));
|
||||
vec4 c11 = SAMPLE_TEXTURE_OFFSET(samp0, uv, int2(1, 1));
|
||||
o_col0 = get_bias(c00, c01, c10, c11);
|
||||
#endif
|
||||
}
|
||||
)";
|
||||
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
std::string GPU_HW_ShaderGen::GenerateAdaptiveDownsampleBlurFragmentShader()
|
||||
{
|
||||
std::stringstream ss;
|
||||
WriteHeader(ss);
|
||||
WriteCommonFunctions(ss);
|
||||
DeclareTexture(ss, "samp0", 0, false);
|
||||
DeclareUniformBuffer(ss, {"float2 u_uv_min", "float2 u_uv_max", "float2 u_rcp_resolution", "float sample_level"}, true);
|
||||
|
||||
// mipmap_blur.glsl ported from parallel-rsx.
|
||||
DeclareFragmentEntryPoint(ss, 0, 1, {}, false, 1, false, false, false, false);
|
||||
ss << R"(
|
||||
{
|
||||
float bias = 0.0;
|
||||
const float w0 = 0.25;
|
||||
const float w1 = 0.125;
|
||||
const float w2 = 0.0625;
|
||||
#define UV(x, y) clamp((v_tex0 + float2(x, y) * u_rcp_resolution), u_uv_min, u_uv_max)
|
||||
bias += w2 * SAMPLE_TEXTURE(samp0, UV(-1.0, -1.0)).a;
|
||||
bias += w2 * SAMPLE_TEXTURE(samp0, UV(+1.0, -1.0)).a;
|
||||
bias += w2 * SAMPLE_TEXTURE(samp0, UV(-1.0, +1.0)).a;
|
||||
bias += w2 * SAMPLE_TEXTURE(samp0, UV(+1.0, +1.0)).a;
|
||||
bias += w1 * SAMPLE_TEXTURE(samp0, UV( 0.0, -1.0)).a;
|
||||
bias += w1 * SAMPLE_TEXTURE(samp0, UV(-1.0, 0.0)).a;
|
||||
bias += w1 * SAMPLE_TEXTURE(samp0, UV(+1.0, 0.0)).a;
|
||||
bias += w1 * SAMPLE_TEXTURE(samp0, UV( 0.0, +1.0)).a;
|
||||
bias += w0 * SAMPLE_TEXTURE(samp0, UV( 0.0, 0.0)).a;
|
||||
o_col0 = float4(bias, bias, bias, bias);
|
||||
}
|
||||
)";
|
||||
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
std::string GPU_HW_ShaderGen::GenerateAdaptiveDownsampleCompositeFragmentShader()
|
||||
{
|
||||
std::stringstream ss;
|
||||
WriteHeader(ss);
|
||||
WriteCommonFunctions(ss);
|
||||
DeclareTexture(ss, "samp0", 0, false);
|
||||
DeclareTexture(ss, "samp1", 1, false);
|
||||
|
||||
// mipmap_resolve.glsl ported from parallel-rsx.
|
||||
DeclareFragmentEntryPoint(ss, 0, 1, {}, true, 1, false, false, false, false);
|
||||
ss << R"(
|
||||
{
|
||||
float2 uv = v_pos.xy * RCP_VRAM_SIZE;
|
||||
float bias = SAMPLE_TEXTURE(samp1, uv).r;
|
||||
float mip = float(RESOLUTION_SCALE - 1u) * bias;
|
||||
float3 color = SAMPLE_TEXTURE_LEVEL(samp0, uv, mip).rgb;
|
||||
o_col0 = float4(color, 1.0);
|
||||
}
|
||||
)";
|
||||
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
std::string GPU_HW_ShaderGen::GenerateBoxSampleDownsampleFragmentShader()
|
||||
{
|
||||
std::stringstream ss;
|
||||
WriteHeader(ss);
|
||||
WriteCommonFunctions(ss);
|
||||
DeclareTexture(ss, "samp0", 0, false);
|
||||
|
||||
DeclareFragmentEntryPoint(ss, 0, 1, {}, true, 1, false, false, false, false);
|
||||
ss << R"(
|
||||
{
|
||||
float3 color = float3(0.0, 0.0, 0.0);
|
||||
uint2 base_coords = uint2(v_pos.xy) * uint2(RESOLUTION_SCALE, RESOLUTION_SCALE);
|
||||
for (uint offset_x = 0u; offset_x < RESOLUTION_SCALE; offset_x++)
|
||||
{
|
||||
for (uint offset_y = 0u; offset_y < RESOLUTION_SCALE; offset_y++)
|
||||
color += LOAD_TEXTURE(samp0, int2(base_coords + uint2(offset_x, offset_y)), 0).rgb;
|
||||
}
|
||||
color /= float(RESOLUTION_SCALE * RESOLUTION_SCALE);
|
||||
o_col0 = float4(color, 1.0);
|
||||
}
|
||||
)";
|
||||
|
||||
return ss.str();
|
||||
}
|
||||
|
|
|
@ -21,6 +21,11 @@ public:
|
|||
std::string GenerateVRAMCopyFragmentShader();
|
||||
std::string GenerateVRAMUpdateDepthFragmentShader();
|
||||
|
||||
std::string GenerateAdaptiveDownsampleMipFragmentShader(bool first_pass);
|
||||
std::string GenerateAdaptiveDownsampleBlurFragmentShader();
|
||||
std::string GenerateAdaptiveDownsampleCompositeFragmentShader();
|
||||
std::string GenerateBoxSampleDownsampleFragmentShader();
|
||||
|
||||
private:
|
||||
ALWAYS_INLINE bool UsingMSAA() const { return m_multisamples > 1; }
|
||||
ALWAYS_INLINE bool UsingPerSampleShading() const { return m_multisamples > 1 && m_per_sample_shading; }
|
||||
|
|
|
@ -243,6 +243,8 @@ void GPU_HW_Vulkan::SetCapabilities()
|
|||
|
||||
m_supports_dual_source_blend = g_vulkan_context->GetDeviceFeatures().dualSrcBlend;
|
||||
m_supports_per_sample_shading = g_vulkan_context->GetDeviceFeatures().sampleRateShading;
|
||||
m_supports_adaptive_downsampling = true;
|
||||
|
||||
Log_InfoPrintf("Dual-source blend: %s", m_supports_dual_source_blend ? "supported" : "not supported");
|
||||
Log_InfoPrintf("Per-sample shading: %s", m_supports_per_sample_shading ? "supported" : "not supported");
|
||||
Log_InfoPrintf("Max multisamples: %u", m_max_multisamples);
|
||||
|
@ -270,6 +272,10 @@ void GPU_HW_Vulkan::DestroyResources()
|
|||
DestroyFramebuffer();
|
||||
DestroyPipelines();
|
||||
|
||||
Vulkan::Util::SafeDestroyPipelineLayout(m_downsample_pipeline_layout);
|
||||
Vulkan::Util::SafeDestroyDescriptorSetLayout(m_downsample_composite_descriptor_set_layout);
|
||||
Vulkan::Util::SafeDestroyPipelineLayout(m_downsample_composite_pipeline_layout);
|
||||
|
||||
Vulkan::Util::SafeFreeGlobalDescriptorSet(m_vram_write_descriptor_set);
|
||||
Vulkan::Util::SafeDestroyBufferView(m_texture_stream_buffer_view);
|
||||
|
||||
|
@ -286,6 +292,7 @@ void GPU_HW_Vulkan::DestroyResources()
|
|||
Vulkan::Util::SafeDestroyDescriptorSetLayout(m_batch_descriptor_set_layout);
|
||||
Vulkan::Util::SafeDestroySampler(m_point_sampler);
|
||||
Vulkan::Util::SafeDestroySampler(m_linear_sampler);
|
||||
Vulkan::Util::SafeDestroySampler(m_trilinear_sampler);
|
||||
}
|
||||
|
||||
void GPU_HW_Vulkan::BeginRenderPass(VkRenderPass render_pass, VkFramebuffer framebuffer, u32 x, u32 y, u32 width,
|
||||
|
@ -371,6 +378,24 @@ bool GPU_HW_Vulkan::CreatePipelineLayouts()
|
|||
if (m_vram_write_pipeline_layout == VK_NULL_HANDLE)
|
||||
return false;
|
||||
|
||||
plbuilder.AddDescriptorSet(m_single_sampler_descriptor_set_layout);
|
||||
plbuilder.AddPushConstants(VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT, 0, MAX_PUSH_CONSTANTS_SIZE);
|
||||
m_downsample_pipeline_layout = plbuilder.Create(device);
|
||||
if (m_downsample_pipeline_layout == VK_NULL_HANDLE)
|
||||
return false;
|
||||
|
||||
dslbuilder.AddBinding(1, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1, VK_SHADER_STAGE_FRAGMENT_BIT);
|
||||
dslbuilder.AddBinding(2, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1, VK_SHADER_STAGE_FRAGMENT_BIT);
|
||||
m_downsample_composite_descriptor_set_layout = dslbuilder.Create(device);
|
||||
if (m_downsample_composite_descriptor_set_layout == VK_NULL_HANDLE)
|
||||
return false;
|
||||
|
||||
plbuilder.AddDescriptorSet(m_downsample_composite_descriptor_set_layout);
|
||||
plbuilder.AddPushConstants(VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT, 0, MAX_PUSH_CONSTANTS_SIZE);
|
||||
m_downsample_composite_pipeline_layout = plbuilder.Create(device);
|
||||
if (m_downsample_composite_pipeline_layout == VK_NULL_HANDLE)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -393,6 +418,11 @@ bool GPU_HW_Vulkan::CreateSamplers()
|
|||
if (m_linear_sampler == VK_NULL_HANDLE)
|
||||
return false;
|
||||
|
||||
sbuilder.SetLinearSampler(true, VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER);
|
||||
m_trilinear_sampler = sbuilder.Create(device);
|
||||
if (m_trilinear_sampler == VK_NULL_HANDLE)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -447,16 +477,14 @@ bool GPU_HW_Vulkan::CreateFramebuffer()
|
|||
}
|
||||
|
||||
// vram framebuffer has both colour and depth
|
||||
{
|
||||
Vulkan::FramebufferBuilder fbb;
|
||||
fbb.AddAttachment(m_vram_texture.GetView());
|
||||
fbb.AddAttachment(m_vram_depth_texture.GetView());
|
||||
fbb.SetRenderPass(m_vram_render_pass);
|
||||
fbb.SetSize(m_vram_texture.GetWidth(), m_vram_texture.GetHeight(), m_vram_texture.GetLayers());
|
||||
m_vram_framebuffer = fbb.Create(g_vulkan_context->GetDevice());
|
||||
if (m_vram_framebuffer == VK_NULL_HANDLE)
|
||||
return false;
|
||||
}
|
||||
Vulkan::FramebufferBuilder fbb;
|
||||
fbb.AddAttachment(m_vram_texture.GetView());
|
||||
fbb.AddAttachment(m_vram_depth_texture.GetView());
|
||||
fbb.SetRenderPass(m_vram_render_pass);
|
||||
fbb.SetSize(m_vram_texture.GetWidth(), m_vram_texture.GetHeight(), m_vram_texture.GetLayers());
|
||||
m_vram_framebuffer = fbb.Create(g_vulkan_context->GetDevice());
|
||||
if (m_vram_framebuffer == VK_NULL_HANDLE)
|
||||
return false;
|
||||
|
||||
m_vram_update_depth_framebuffer = m_vram_depth_texture.CreateFramebuffer(m_vram_update_depth_render_pass);
|
||||
m_vram_readback_framebuffer = m_vram_readback_texture.CreateFramebuffer(m_vram_readback_render_pass);
|
||||
|
@ -477,8 +505,9 @@ bool GPU_HW_Vulkan::CreateFramebuffer()
|
|||
m_batch_descriptor_set = g_vulkan_context->AllocateGlobalDescriptorSet(m_batch_descriptor_set_layout);
|
||||
m_vram_copy_descriptor_set = g_vulkan_context->AllocateGlobalDescriptorSet(m_single_sampler_descriptor_set_layout);
|
||||
m_vram_read_descriptor_set = g_vulkan_context->AllocateGlobalDescriptorSet(m_single_sampler_descriptor_set_layout);
|
||||
m_display_descriptor_set = g_vulkan_context->AllocateGlobalDescriptorSet(m_single_sampler_descriptor_set_layout);
|
||||
if (m_batch_descriptor_set == VK_NULL_HANDLE || m_vram_copy_descriptor_set == VK_NULL_HANDLE ||
|
||||
m_vram_read_descriptor_set == VK_NULL_HANDLE)
|
||||
m_vram_read_descriptor_set == VK_NULL_HANDLE || m_display_descriptor_set == VK_NULL_HANDLE)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
@ -491,8 +520,109 @@ bool GPU_HW_Vulkan::CreateFramebuffer()
|
|||
m_point_sampler, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
|
||||
dsubuilder.AddCombinedImageSamplerDescriptorWrite(m_vram_read_descriptor_set, 1, m_vram_texture.GetView(),
|
||||
m_point_sampler, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
|
||||
dsubuilder.AddCombinedImageSamplerDescriptorWrite(m_display_descriptor_set, 1, m_display_texture.GetView(),
|
||||
m_point_sampler, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
|
||||
dsubuilder.Update(g_vulkan_context->GetDevice());
|
||||
|
||||
if (m_downsample_mode == GPUDownsampleMode::Adaptive)
|
||||
{
|
||||
const u32 levels = GetAdaptiveDownsamplingMipLevels();
|
||||
|
||||
if (!m_downsample_texture.Create(texture_width, texture_height, levels, 1, texture_format, VK_SAMPLE_COUNT_1_BIT,
|
||||
VK_IMAGE_VIEW_TYPE_2D, VK_IMAGE_TILING_OPTIMAL,
|
||||
VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_SAMPLED_BIT |
|
||||
VK_IMAGE_USAGE_TRANSFER_DST_BIT) ||
|
||||
!m_downsample_weight_texture.Create(VRAM_WIDTH, VRAM_HEIGHT, 1, 1, VK_FORMAT_R8_UNORM, VK_SAMPLE_COUNT_1_BIT,
|
||||
VK_IMAGE_VIEW_TYPE_2D, VK_IMAGE_TILING_OPTIMAL,
|
||||
VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_SAMPLED_BIT))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
m_downsample_texture.TransitionToLayout(cmdbuf, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
|
||||
|
||||
m_downsample_render_pass = g_vulkan_context->GetRenderPass(m_downsample_texture.GetFormat(), VK_FORMAT_UNDEFINED,
|
||||
VK_SAMPLE_COUNT_1_BIT, VK_ATTACHMENT_LOAD_OP_DONT_CARE);
|
||||
m_downsample_weight_render_pass =
|
||||
g_vulkan_context->GetRenderPass(m_downsample_weight_texture.GetFormat(), VK_FORMAT_UNDEFINED,
|
||||
VK_SAMPLE_COUNT_1_BIT, VK_ATTACHMENT_LOAD_OP_DONT_CARE);
|
||||
if (m_downsample_render_pass == VK_NULL_HANDLE || m_downsample_weight_render_pass == VK_NULL_HANDLE)
|
||||
return false;
|
||||
|
||||
m_downsample_weight_framebuffer = m_downsample_weight_texture.CreateFramebuffer(m_downsample_weight_render_pass);
|
||||
if (m_downsample_weight_framebuffer == VK_NULL_HANDLE)
|
||||
return false;
|
||||
|
||||
m_downsample_mip_views.resize(levels);
|
||||
for (u32 i = 0; i < levels; i++)
|
||||
{
|
||||
SmoothMipView& mv = m_downsample_mip_views[i];
|
||||
|
||||
const VkImageViewCreateInfo vci = {VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
|
||||
nullptr,
|
||||
0,
|
||||
m_downsample_texture.GetImage(),
|
||||
VK_IMAGE_VIEW_TYPE_2D,
|
||||
m_downsample_texture.GetFormat(),
|
||||
{VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY,
|
||||
VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY},
|
||||
{VK_IMAGE_ASPECT_COLOR_BIT, i, 1u, 0u, 1u}};
|
||||
VkResult res = vkCreateImageView(g_vulkan_context->GetDevice(), &vci, nullptr, &mv.image_view);
|
||||
if (res != VK_SUCCESS)
|
||||
{
|
||||
LOG_VULKAN_ERROR(res, "vkCreateImageView() for smooth mip failed: ");
|
||||
return false;
|
||||
}
|
||||
|
||||
mv.descriptor_set = g_vulkan_context->AllocateGlobalDescriptorSet(m_single_sampler_descriptor_set_layout);
|
||||
if (mv.descriptor_set == VK_NULL_HANDLE)
|
||||
return false;
|
||||
|
||||
dsubuilder.AddCombinedImageSamplerDescriptorWrite(m_downsample_mip_views[i].descriptor_set, 1,
|
||||
m_downsample_mip_views[i].image_view, m_point_sampler,
|
||||
VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
|
||||
|
||||
fbb.AddAttachment(mv.image_view);
|
||||
fbb.SetRenderPass(m_downsample_render_pass);
|
||||
fbb.SetSize(texture_width >> i, texture_height >> i, 1);
|
||||
mv.framebuffer = fbb.Create(g_vulkan_context->GetDevice());
|
||||
if (mv.framebuffer == VK_NULL_HANDLE)
|
||||
return false;
|
||||
}
|
||||
|
||||
m_downsample_composite_descriptor_set =
|
||||
g_vulkan_context->AllocateGlobalDescriptorSet(m_downsample_composite_descriptor_set_layout);
|
||||
if (m_downsample_composite_descriptor_set_layout == VK_NULL_HANDLE)
|
||||
return false;
|
||||
|
||||
dsubuilder.AddCombinedImageSamplerDescriptorWrite(m_downsample_composite_descriptor_set, 1,
|
||||
m_downsample_texture.GetView(), m_trilinear_sampler,
|
||||
VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
|
||||
dsubuilder.AddCombinedImageSamplerDescriptorWrite(m_downsample_composite_descriptor_set, 2,
|
||||
m_downsample_weight_texture.GetView(), m_linear_sampler,
|
||||
VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
|
||||
|
||||
dsubuilder.Update(g_vulkan_context->GetDevice());
|
||||
}
|
||||
else if (m_downsample_mode == GPUDownsampleMode::Box)
|
||||
{
|
||||
if (!m_downsample_texture.Create(VRAM_WIDTH, VRAM_HEIGHT, 1, 1, texture_format, VK_SAMPLE_COUNT_1_BIT,
|
||||
VK_IMAGE_VIEW_TYPE_2D, VK_IMAGE_TILING_OPTIMAL,
|
||||
VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_SAMPLED_BIT |
|
||||
VK_IMAGE_USAGE_TRANSFER_SRC_BIT))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
m_downsample_render_pass = g_vulkan_context->GetRenderPass(m_downsample_texture.GetFormat(), VK_FORMAT_UNDEFINED,
|
||||
VK_SAMPLE_COUNT_1_BIT, VK_ATTACHMENT_LOAD_OP_DONT_CARE);
|
||||
|
||||
m_downsample_mip_views.resize(1);
|
||||
m_downsample_mip_views[0].framebuffer = m_downsample_texture.CreateFramebuffer(m_downsample_render_pass);
|
||||
if (m_downsample_mip_views[0].framebuffer == VK_NULL_HANDLE)
|
||||
return false;
|
||||
}
|
||||
|
||||
ClearDisplay();
|
||||
SetFullVRAMDirtyRectangle();
|
||||
return true;
|
||||
|
@ -522,9 +652,23 @@ void GPU_HW_Vulkan::ClearFramebuffer()
|
|||
|
||||
void GPU_HW_Vulkan::DestroyFramebuffer()
|
||||
{
|
||||
Vulkan::Util::SafeFreeGlobalDescriptorSet(m_downsample_composite_descriptor_set);
|
||||
|
||||
for (SmoothMipView& mv : m_downsample_mip_views)
|
||||
{
|
||||
Vulkan::Util::SafeFreeGlobalDescriptorSet(mv.descriptor_set);
|
||||
Vulkan::Util::SafeDestroyImageView(mv.image_view);
|
||||
Vulkan::Util::SafeDestroyFramebuffer(mv.framebuffer);
|
||||
}
|
||||
m_downsample_mip_views.clear();
|
||||
m_downsample_texture.Destroy(false);
|
||||
Vulkan::Util::SafeDestroyFramebuffer(m_downsample_weight_framebuffer);
|
||||
m_downsample_weight_texture.Destroy(false);
|
||||
|
||||
Vulkan::Util::SafeFreeGlobalDescriptorSet(m_batch_descriptor_set);
|
||||
Vulkan::Util::SafeFreeGlobalDescriptorSet(m_vram_copy_descriptor_set);
|
||||
Vulkan::Util::SafeFreeGlobalDescriptorSet(m_vram_read_descriptor_set);
|
||||
Vulkan::Util::SafeFreeGlobalDescriptorSet(m_display_descriptor_set);
|
||||
|
||||
Vulkan::Util::SafeDestroyFramebuffer(m_vram_framebuffer);
|
||||
Vulkan::Util::SafeDestroyFramebuffer(m_vram_update_depth_framebuffer);
|
||||
|
@ -601,7 +745,7 @@ bool GPU_HW_Vulkan::CompilePipelines()
|
|||
m_pgxp_depth_buffer, m_supports_dual_source_blend);
|
||||
|
||||
Common::Timer compile_time;
|
||||
const int progress_total = 2 + (4 * 9 * 2 * 2) + (2 * 4 * 5 * 9 * 2 * 2) + 1 + 2 + 2 + 2 + 2 + (2 * 3);
|
||||
const int progress_total = 2 + (4 * 9 * 2 * 2) + (2 * 4 * 5 * 9 * 2 * 2) + 1 + 2 + 2 + 2 + 2 + (2 * 3) + 1;
|
||||
int progress_value = 0;
|
||||
#define UPDATE_PROGRESS() \
|
||||
do \
|
||||
|
@ -738,11 +882,18 @@ bool GPU_HW_Vulkan::CompilePipelines()
|
|||
g_vulkan_shader_cache->GetVertexShader(shadergen.GenerateScreenQuadVertexShader());
|
||||
if (fullscreen_quad_vertex_shader == VK_NULL_HANDLE)
|
||||
return false;
|
||||
VkShaderModule uv_quad_vertex_shader = g_vulkan_shader_cache->GetVertexShader(shadergen.GenerateUVQuadVertexShader());
|
||||
if (uv_quad_vertex_shader == VK_NULL_HANDLE)
|
||||
{
|
||||
vkDestroyShaderModule(g_vulkan_context->GetDevice(), uv_quad_vertex_shader, nullptr);
|
||||
return false;
|
||||
}
|
||||
|
||||
UPDATE_PROGRESS();
|
||||
|
||||
Common::ScopeGuard fullscreen_quad_vertex_shader_guard([&fullscreen_quad_vertex_shader]() {
|
||||
Common::ScopeGuard fullscreen_quad_vertex_shader_guard([&fullscreen_quad_vertex_shader, &uv_quad_vertex_shader]() {
|
||||
vkDestroyShaderModule(g_vulkan_context->GetDevice(), fullscreen_quad_vertex_shader, nullptr);
|
||||
vkDestroyShaderModule(g_vulkan_context->GetDevice(), uv_quad_vertex_shader, nullptr);
|
||||
});
|
||||
|
||||
// common state
|
||||
|
@ -907,6 +1058,87 @@ bool GPU_HW_Vulkan::CompilePipelines()
|
|||
}
|
||||
}
|
||||
|
||||
UPDATE_PROGRESS();
|
||||
|
||||
if (m_downsample_mode == GPUDownsampleMode::Adaptive)
|
||||
{
|
||||
gpbuilder.Clear();
|
||||
gpbuilder.SetRenderPass(m_downsample_render_pass, 0);
|
||||
gpbuilder.SetPipelineLayout(m_downsample_pipeline_layout);
|
||||
gpbuilder.SetVertexShader(uv_quad_vertex_shader);
|
||||
gpbuilder.SetNoCullRasterizationState();
|
||||
gpbuilder.SetNoDepthTestState();
|
||||
gpbuilder.SetNoBlendingState();
|
||||
gpbuilder.SetDynamicViewportAndScissorState();
|
||||
|
||||
VkShaderModule fs =
|
||||
g_vulkan_shader_cache->GetFragmentShader(shadergen.GenerateAdaptiveDownsampleMipFragmentShader(true));
|
||||
if (fs == VK_NULL_HANDLE)
|
||||
return false;
|
||||
|
||||
gpbuilder.SetFragmentShader(fs);
|
||||
m_downsample_first_pass_pipeline = gpbuilder.Create(device, pipeline_cache, false);
|
||||
vkDestroyShaderModule(g_vulkan_context->GetDevice(), fs, nullptr);
|
||||
if (m_downsample_first_pass_pipeline == VK_NULL_HANDLE)
|
||||
return false;
|
||||
|
||||
fs = g_vulkan_shader_cache->GetFragmentShader(shadergen.GenerateAdaptiveDownsampleMipFragmentShader(false));
|
||||
if (fs == VK_NULL_HANDLE)
|
||||
return false;
|
||||
|
||||
gpbuilder.SetFragmentShader(fs);
|
||||
m_downsample_mid_pass_pipeline = gpbuilder.Create(device, pipeline_cache, false);
|
||||
vkDestroyShaderModule(g_vulkan_context->GetDevice(), fs, nullptr);
|
||||
if (m_downsample_mid_pass_pipeline == VK_NULL_HANDLE)
|
||||
return false;
|
||||
|
||||
fs = g_vulkan_shader_cache->GetFragmentShader(shadergen.GenerateAdaptiveDownsampleBlurFragmentShader());
|
||||
if (fs == VK_NULL_HANDLE)
|
||||
return false;
|
||||
|
||||
gpbuilder.SetFragmentShader(fs);
|
||||
gpbuilder.SetRenderPass(m_downsample_weight_render_pass, 0);
|
||||
m_downsample_blur_pass_pipeline = gpbuilder.Create(device, pipeline_cache, false);
|
||||
vkDestroyShaderModule(g_vulkan_context->GetDevice(), fs, nullptr);
|
||||
if (m_downsample_blur_pass_pipeline == VK_NULL_HANDLE)
|
||||
return false;
|
||||
|
||||
fs = g_vulkan_shader_cache->GetFragmentShader(shadergen.GenerateAdaptiveDownsampleCompositeFragmentShader());
|
||||
if (fs == VK_NULL_HANDLE)
|
||||
return false;
|
||||
|
||||
gpbuilder.SetFragmentShader(fs);
|
||||
gpbuilder.SetPipelineLayout(m_downsample_composite_pipeline_layout);
|
||||
gpbuilder.SetRenderPass(m_display_render_pass, 0);
|
||||
m_downsample_composite_pass_pipeline = gpbuilder.Create(device, pipeline_cache, false);
|
||||
vkDestroyShaderModule(g_vulkan_context->GetDevice(), fs, nullptr);
|
||||
if (m_downsample_composite_pass_pipeline == VK_NULL_HANDLE)
|
||||
return false;
|
||||
}
|
||||
else if (m_downsample_mode == GPUDownsampleMode::Box)
|
||||
{
|
||||
gpbuilder.Clear();
|
||||
gpbuilder.SetRenderPass(m_downsample_render_pass, 0);
|
||||
gpbuilder.SetPipelineLayout(m_single_sampler_pipeline_layout);
|
||||
gpbuilder.SetVertexShader(fullscreen_quad_vertex_shader);
|
||||
gpbuilder.SetNoCullRasterizationState();
|
||||
gpbuilder.SetNoDepthTestState();
|
||||
gpbuilder.SetNoBlendingState();
|
||||
gpbuilder.SetDynamicViewportAndScissorState();
|
||||
|
||||
VkShaderModule fs = g_vulkan_shader_cache->GetFragmentShader(shadergen.GenerateBoxSampleDownsampleFragmentShader());
|
||||
if (fs == VK_NULL_HANDLE)
|
||||
return false;
|
||||
|
||||
gpbuilder.SetFragmentShader(fs);
|
||||
m_downsample_first_pass_pipeline = gpbuilder.Create(device, pipeline_cache, false);
|
||||
vkDestroyShaderModule(g_vulkan_context->GetDevice(), fs, nullptr);
|
||||
if (m_downsample_first_pass_pipeline == VK_NULL_HANDLE)
|
||||
return false;
|
||||
}
|
||||
|
||||
UPDATE_PROGRESS();
|
||||
|
||||
#undef UPDATE_PROGRESS
|
||||
|
||||
return true;
|
||||
|
@ -928,6 +1160,11 @@ void GPU_HW_Vulkan::DestroyPipelines()
|
|||
Vulkan::Util::SafeDestroyPipeline(m_vram_readback_pipeline);
|
||||
Vulkan::Util::SafeDestroyPipeline(m_vram_update_depth_pipeline);
|
||||
|
||||
Vulkan::Util::SafeDestroyPipeline(m_downsample_first_pass_pipeline);
|
||||
Vulkan::Util::SafeDestroyPipeline(m_downsample_mid_pass_pipeline);
|
||||
Vulkan::Util::SafeDestroyPipeline(m_downsample_blur_pass_pipeline);
|
||||
Vulkan::Util::SafeDestroyPipeline(m_downsample_composite_pass_pipeline);
|
||||
|
||||
m_display_pipelines.enumerate(Vulkan::Util::SafeDestroyPipeline);
|
||||
}
|
||||
|
||||
|
@ -1014,11 +1251,19 @@ void GPU_HW_Vulkan::UpdateDisplay()
|
|||
!IsUsingMultisampling() && (scaled_vram_offset_x + scaled_display_width) <= m_vram_texture.GetWidth() &&
|
||||
(scaled_vram_offset_y + scaled_display_height) <= m_vram_texture.GetHeight())
|
||||
{
|
||||
m_vram_texture.TransitionToLayout(g_vulkan_context->GetCurrentCommandBuffer(),
|
||||
VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
|
||||
m_host_display->SetDisplayTexture(&m_vram_texture, HostDisplayPixelFormat::RGBA8, m_vram_texture.GetWidth(),
|
||||
m_vram_texture.GetHeight(), scaled_vram_offset_x, scaled_vram_offset_y,
|
||||
scaled_display_width, scaled_display_height);
|
||||
if (IsUsingDownsampling())
|
||||
{
|
||||
DownsampleFramebuffer(m_vram_texture, scaled_vram_offset_x, scaled_vram_offset_y, scaled_display_width,
|
||||
scaled_display_height);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_vram_texture.TransitionToLayout(g_vulkan_context->GetCurrentCommandBuffer(),
|
||||
VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
|
||||
m_host_display->SetDisplayTexture(&m_vram_texture, HostDisplayPixelFormat::RGBA8, m_vram_texture.GetWidth(),
|
||||
m_vram_texture.GetHeight(), scaled_vram_offset_x, scaled_vram_offset_y,
|
||||
scaled_display_width, scaled_display_height);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -1051,11 +1296,17 @@ void GPU_HW_Vulkan::UpdateDisplay()
|
|||
m_vram_texture.TransitionToLayout(cmdbuf, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL);
|
||||
m_display_texture.TransitionToLayout(cmdbuf, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
|
||||
|
||||
m_host_display->SetDisplayTexture(&m_display_texture, HostDisplayPixelFormat::RGBA8, m_display_texture.GetWidth(),
|
||||
m_display_texture.GetHeight(), 0, 0, scaled_display_width,
|
||||
scaled_display_height);
|
||||
|
||||
RestoreGraphicsAPIState();
|
||||
if (IsUsingDownsampling())
|
||||
{
|
||||
DownsampleFramebuffer(m_display_texture, 0, 0, scaled_display_width, scaled_display_height);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_host_display->SetDisplayTexture(&m_display_texture, HostDisplayPixelFormat::RGBA8,
|
||||
m_display_texture.GetWidth(), m_display_texture.GetHeight(), 0, 0,
|
||||
scaled_display_width, scaled_display_height);
|
||||
RestoreGraphicsAPIState();
|
||||
}
|
||||
}
|
||||
|
||||
m_host_display->SetDisplayParameters(m_crtc_state.display_width, m_crtc_state.display_height,
|
||||
|
@ -1410,6 +1661,138 @@ bool GPU_HW_Vulkan::BlitVRAMReplacementTexture(const TextureReplacementTexture*
|
|||
return true;
|
||||
}
|
||||
|
||||
void GPU_HW_Vulkan::DownsampleFramebuffer(Vulkan::Texture& source, u32 left, u32 top, u32 width, u32 height)
|
||||
{
|
||||
if (m_downsample_mode == GPUDownsampleMode::Adaptive)
|
||||
DownsampleFramebufferAdaptive(source, left, top, width, height);
|
||||
else
|
||||
DownsampleFramebufferBoxFilter(source, left, top, width, height);
|
||||
}
|
||||
|
||||
void GPU_HW_Vulkan::DownsampleFramebufferBoxFilter(Vulkan::Texture& source, u32 left, u32 top, u32 width, u32 height)
|
||||
{
|
||||
VkCommandBuffer cmdbuf = g_vulkan_context->GetCurrentCommandBuffer();
|
||||
source.TransitionToLayout(cmdbuf, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
|
||||
m_downsample_texture.TransitionToLayout(cmdbuf, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL);
|
||||
|
||||
Assert(&source == &m_vram_texture || &source == &m_display_texture);
|
||||
VkDescriptorSet ds = (&source == &m_vram_texture) ? m_vram_copy_descriptor_set : m_display_descriptor_set;
|
||||
|
||||
const u32 ds_left = left / m_resolution_scale;
|
||||
const u32 ds_top = top / m_resolution_scale;
|
||||
const u32 ds_width = width / m_resolution_scale;
|
||||
const u32 ds_height = height / m_resolution_scale;
|
||||
|
||||
BeginRenderPass(m_downsample_render_pass, m_downsample_mip_views[0].framebuffer, ds_left, ds_top, ds_width,
|
||||
ds_height);
|
||||
Vulkan::Util::SetViewportAndScissor(cmdbuf, ds_left, ds_top, ds_width, ds_height);
|
||||
vkCmdBindPipeline(cmdbuf, VK_PIPELINE_BIND_POINT_GRAPHICS, m_downsample_first_pass_pipeline);
|
||||
vkCmdBindDescriptorSets(cmdbuf, VK_PIPELINE_BIND_POINT_GRAPHICS, m_single_sampler_pipeline_layout, 0, 1, &ds, 0,
|
||||
nullptr);
|
||||
vkCmdDraw(cmdbuf, 3, 1, 0, 0);
|
||||
EndRenderPass();
|
||||
|
||||
m_downsample_texture.TransitionToLayout(cmdbuf, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
|
||||
|
||||
RestoreGraphicsAPIState();
|
||||
|
||||
m_host_display->SetDisplayTexture(&m_downsample_texture, HostDisplayPixelFormat::RGBA8,
|
||||
m_downsample_texture.GetWidth(), m_downsample_texture.GetHeight(), ds_left, ds_top,
|
||||
ds_width, ds_height);
|
||||
}
|
||||
|
||||
void GPU_HW_Vulkan::DownsampleFramebufferAdaptive(Vulkan::Texture& source, u32 left, u32 top, u32 width, u32 height)
|
||||
{
|
||||
const VkImageCopy copy{{VK_IMAGE_ASPECT_COLOR_BIT, 0u, 0u, 1u},
|
||||
{static_cast<s32>(left), static_cast<s32>(top), 0},
|
||||
{VK_IMAGE_ASPECT_COLOR_BIT, 0u, 0u, 1u},
|
||||
{static_cast<s32>(left), static_cast<s32>(top), 0},
|
||||
{width, height, 1u}};
|
||||
|
||||
VkCommandBuffer cmdbuf = g_vulkan_context->GetCurrentCommandBuffer();
|
||||
source.TransitionToLayout(cmdbuf, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL);
|
||||
m_downsample_texture.TransitionSubresourcesToLayout(cmdbuf, 0, 1, 0, 1, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
|
||||
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL);
|
||||
vkCmdCopyImage(cmdbuf, source.GetImage(), source.GetLayout(), m_downsample_texture.GetImage(),
|
||||
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, ©);
|
||||
|
||||
m_downsample_texture.TransitionSubresourcesToLayout(cmdbuf, 0, 1, 0, 1, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
|
||||
VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
|
||||
|
||||
// creating mip chain
|
||||
const u32 levels = m_downsample_texture.GetLevels();
|
||||
for (u32 level = 1; level < levels; level++)
|
||||
{
|
||||
m_downsample_texture.TransitionSubresourcesToLayout(
|
||||
cmdbuf, level, 1, 0, 1, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL);
|
||||
|
||||
BeginRenderPass(m_downsample_render_pass, m_downsample_mip_views[level].framebuffer, left >> level, top >> level,
|
||||
width >> level, height >> level);
|
||||
Vulkan::Util::SetViewportAndScissor(cmdbuf, left >> level, top >> level, width >> level, height >> level);
|
||||
vkCmdBindPipeline(cmdbuf, VK_PIPELINE_BIND_POINT_GRAPHICS,
|
||||
(level == 1) ? m_downsample_first_pass_pipeline : m_downsample_mid_pass_pipeline);
|
||||
vkCmdBindDescriptorSets(cmdbuf, VK_PIPELINE_BIND_POINT_GRAPHICS, m_downsample_pipeline_layout, 0, 1,
|
||||
&m_downsample_mip_views[level - 1].descriptor_set, 0, nullptr);
|
||||
|
||||
const SmoothingUBOData ubo = GetSmoothingUBO(level, left, top, width, height, m_downsample_texture.GetWidth(),
|
||||
m_downsample_texture.GetHeight());
|
||||
vkCmdPushConstants(cmdbuf, m_downsample_pipeline_layout, VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT,
|
||||
0, sizeof(ubo), &ubo);
|
||||
|
||||
vkCmdDraw(cmdbuf, 3, 1, 0, 0);
|
||||
|
||||
EndRenderPass();
|
||||
|
||||
m_downsample_texture.TransitionSubresourcesToLayout(
|
||||
cmdbuf, level, 1, 0, 1, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
|
||||
}
|
||||
|
||||
// blur pass at lowest resolution
|
||||
{
|
||||
const u32 last_level = levels - 1;
|
||||
|
||||
m_downsample_weight_texture.TransitionToLayout(cmdbuf, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL);
|
||||
|
||||
BeginRenderPass(m_downsample_weight_render_pass, m_downsample_weight_framebuffer, left >> last_level,
|
||||
top >> last_level, width >> last_level, height >> last_level);
|
||||
Vulkan::Util::SetViewportAndScissor(cmdbuf, left >> last_level, top >> last_level, width >> last_level,
|
||||
height >> last_level);
|
||||
vkCmdBindPipeline(cmdbuf, VK_PIPELINE_BIND_POINT_GRAPHICS, m_downsample_blur_pass_pipeline);
|
||||
vkCmdBindDescriptorSets(cmdbuf, VK_PIPELINE_BIND_POINT_GRAPHICS, m_downsample_pipeline_layout, 0, 1,
|
||||
&m_downsample_mip_views[last_level].descriptor_set, 0, nullptr);
|
||||
|
||||
const SmoothingUBOData ubo = GetSmoothingUBO(last_level, left, top, width, height, m_downsample_texture.GetWidth(),
|
||||
m_downsample_texture.GetHeight());
|
||||
vkCmdPushConstants(cmdbuf, m_downsample_pipeline_layout, VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT,
|
||||
0, sizeof(ubo), &ubo);
|
||||
|
||||
vkCmdDraw(cmdbuf, 3, 1, 0, 0);
|
||||
EndRenderPass();
|
||||
|
||||
m_downsample_weight_texture.TransitionToLayout(cmdbuf, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
|
||||
}
|
||||
|
||||
// resolve pass
|
||||
{
|
||||
m_display_texture.TransitionToLayout(cmdbuf, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL);
|
||||
|
||||
BeginRenderPass(m_display_render_pass, m_display_framebuffer, left, top, width, height);
|
||||
Vulkan::Util::SetViewportAndScissor(cmdbuf, left, top, width, height);
|
||||
vkCmdBindPipeline(cmdbuf, VK_PIPELINE_BIND_POINT_GRAPHICS, m_downsample_composite_pass_pipeline);
|
||||
vkCmdBindDescriptorSets(cmdbuf, VK_PIPELINE_BIND_POINT_GRAPHICS, m_downsample_composite_pipeline_layout, 0, 1,
|
||||
&m_downsample_composite_descriptor_set, 0, nullptr);
|
||||
vkCmdDraw(cmdbuf, 3, 1, 0, 0);
|
||||
EndRenderPass();
|
||||
|
||||
m_display_texture.TransitionToLayout(cmdbuf, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
|
||||
}
|
||||
|
||||
RestoreGraphicsAPIState();
|
||||
|
||||
m_host_display->SetDisplayTexture(&m_display_texture, HostDisplayPixelFormat::RGBA8, m_display_texture.GetWidth(),
|
||||
m_display_texture.GetHeight(), left, top, width, height);
|
||||
}
|
||||
|
||||
std::unique_ptr<GPU> GPU::CreateHardwareVulkanRenderer()
|
||||
{
|
||||
return std::make_unique<GPU_HW_Vulkan>();
|
||||
|
|
|
@ -70,6 +70,10 @@ private:
|
|||
|
||||
bool BlitVRAMReplacementTexture(const TextureReplacementTexture* tex, u32 dst_x, u32 dst_y, u32 width, u32 height);
|
||||
|
||||
void DownsampleFramebuffer(Vulkan::Texture& source, u32 left, u32 top, u32 width, u32 height);
|
||||
void DownsampleFramebufferBoxFilter(Vulkan::Texture& source, u32 left, u32 top, u32 width, u32 height);
|
||||
void DownsampleFramebufferAdaptive(Vulkan::Texture& source, u32 left, u32 top, u32 width, u32 height);
|
||||
|
||||
VkRenderPass m_current_render_pass = VK_NULL_HANDLE;
|
||||
|
||||
VkRenderPass m_vram_render_pass = VK_NULL_HANDLE;
|
||||
|
@ -101,11 +105,13 @@ private:
|
|||
|
||||
VkSampler m_point_sampler = VK_NULL_HANDLE;
|
||||
VkSampler m_linear_sampler = VK_NULL_HANDLE;
|
||||
VkSampler m_trilinear_sampler = VK_NULL_HANDLE;
|
||||
|
||||
VkDescriptorSet m_batch_descriptor_set = VK_NULL_HANDLE;
|
||||
VkDescriptorSet m_vram_copy_descriptor_set = VK_NULL_HANDLE;
|
||||
VkDescriptorSet m_vram_read_descriptor_set = VK_NULL_HANDLE;
|
||||
VkDescriptorSet m_vram_write_descriptor_set = VK_NULL_HANDLE;
|
||||
VkDescriptorSet m_display_descriptor_set = VK_NULL_HANDLE;
|
||||
|
||||
Vulkan::StreamBuffer m_vertex_stream_buffer;
|
||||
Vulkan::StreamBuffer m_uniform_stream_buffer;
|
||||
|
@ -133,4 +139,28 @@ private:
|
|||
// texture replacements
|
||||
Vulkan::Texture m_vram_write_replacement_texture;
|
||||
Vulkan::StreamBuffer m_texture_replacment_stream_buffer;
|
||||
|
||||
// downsampling
|
||||
Vulkan::Texture m_downsample_texture;
|
||||
VkRenderPass m_downsample_render_pass = VK_NULL_HANDLE;
|
||||
Vulkan::Texture m_downsample_weight_texture;
|
||||
VkRenderPass m_downsample_weight_render_pass = VK_NULL_HANDLE;
|
||||
VkFramebuffer m_downsample_weight_framebuffer = VK_NULL_HANDLE;
|
||||
|
||||
struct SmoothMipView
|
||||
{
|
||||
VkImageView image_view = VK_NULL_HANDLE;
|
||||
VkDescriptorSet descriptor_set = VK_NULL_HANDLE;
|
||||
VkFramebuffer framebuffer = VK_NULL_HANDLE;
|
||||
};
|
||||
std::vector<SmoothMipView> m_downsample_mip_views;
|
||||
|
||||
VkPipelineLayout m_downsample_pipeline_layout = VK_NULL_HANDLE;
|
||||
VkDescriptorSetLayout m_downsample_composite_descriptor_set_layout = VK_NULL_HANDLE;
|
||||
VkPipelineLayout m_downsample_composite_pipeline_layout = VK_NULL_HANDLE;
|
||||
VkDescriptorSet m_downsample_composite_descriptor_set = VK_NULL_HANDLE;
|
||||
VkPipeline m_downsample_first_pass_pipeline = VK_NULL_HANDLE;
|
||||
VkPipeline m_downsample_mid_pass_pipeline = VK_NULL_HANDLE;
|
||||
VkPipeline m_downsample_blur_pass_pipeline = VK_NULL_HANDLE;
|
||||
VkPipeline m_downsample_composite_pass_pipeline = VK_NULL_HANDLE;
|
||||
};
|
||||
|
|
|
@ -479,6 +479,7 @@ void HostInterface::SetDefaultSettings(SettingsInterface& si)
|
|||
si.SetBoolValue("GPU", "TrueColor", false);
|
||||
si.SetBoolValue("GPU", "ScaledDithering", true);
|
||||
si.SetStringValue("GPU", "TextureFilter", Settings::GetTextureFilterName(Settings::DEFAULT_GPU_TEXTURE_FILTER));
|
||||
si.SetStringValue("GPU", "DownsampleMode", Settings::GetDownsampleModeName(Settings::DEFAULT_GPU_DOWNSAMPLE_MODE));
|
||||
si.SetBoolValue("GPU", "DisableInterlacing", false);
|
||||
si.SetBoolValue("GPU", "ForceNTSCTimings", false);
|
||||
si.SetBoolValue("GPU", "WidescreenHack", false);
|
||||
|
@ -719,6 +720,7 @@ void HostInterface::CheckForSettingsChanges(const Settings& old_settings)
|
|||
g_settings.gpu_disable_interlacing != old_settings.gpu_disable_interlacing ||
|
||||
g_settings.gpu_force_ntsc_timings != old_settings.gpu_force_ntsc_timings ||
|
||||
g_settings.gpu_24bit_chroma_smoothing != old_settings.gpu_24bit_chroma_smoothing ||
|
||||
g_settings.gpu_downsample_mode != old_settings.gpu_downsample_mode ||
|
||||
g_settings.display_crop_mode != old_settings.display_crop_mode ||
|
||||
g_settings.display_aspect_ratio != old_settings.display_aspect_ratio ||
|
||||
g_settings.gpu_pgxp_enable != old_settings.gpu_pgxp_enable ||
|
||||
|
|
|
@ -151,6 +151,10 @@ void Settings::Load(SettingsInterface& si)
|
|||
ParseTextureFilterName(
|
||||
si.GetStringValue("GPU", "TextureFilter", GetTextureFilterName(DEFAULT_GPU_TEXTURE_FILTER)).c_str())
|
||||
.value_or(DEFAULT_GPU_TEXTURE_FILTER);
|
||||
gpu_downsample_mode =
|
||||
ParseDownsampleModeName(
|
||||
si.GetStringValue("GPU", "DownsampleMode", GetDownsampleModeName(DEFAULT_GPU_DOWNSAMPLE_MODE)).c_str())
|
||||
.value_or(DEFAULT_GPU_DOWNSAMPLE_MODE);
|
||||
gpu_disable_interlacing = si.GetBoolValue("GPU", "DisableInterlacing", false);
|
||||
gpu_force_ntsc_timings = si.GetBoolValue("GPU", "ForceNTSCTimings", false);
|
||||
gpu_widescreen_hack = si.GetBoolValue("GPU", "WidescreenHack", false);
|
||||
|
@ -306,6 +310,7 @@ void Settings::Save(SettingsInterface& si) const
|
|||
si.SetBoolValue("GPU", "TrueColor", gpu_true_color);
|
||||
si.SetBoolValue("GPU", "ScaledDithering", gpu_scaled_dithering);
|
||||
si.SetStringValue("GPU", "TextureFilter", GetTextureFilterName(gpu_texture_filter));
|
||||
si.SetStringValue("GPU", "DownsampleMode", GetDownsampleModeName(gpu_downsample_mode));
|
||||
si.SetBoolValue("GPU", "DisableInterlacing", gpu_disable_interlacing);
|
||||
si.SetBoolValue("GPU", "ForceNTSCTimings", gpu_force_ntsc_timings);
|
||||
si.SetBoolValue("GPU", "WidescreenHack", gpu_widescreen_hack);
|
||||
|
@ -628,6 +633,35 @@ const char* Settings::GetTextureFilterDisplayName(GPUTextureFilter filter)
|
|||
return s_texture_filter_display_names[static_cast<int>(filter)];
|
||||
}
|
||||
|
||||
static constexpr auto s_downsample_mode_names = make_array("Disabled", "Box", "Adaptive");
|
||||
static constexpr auto s_downsample_mode_display_names = make_array(
|
||||
TRANSLATABLE("GPUDownsampleMode", "Disabled"), TRANSLATABLE("GPUDownsampleMode", "Box (Downsample 3D/Smooth All)"),
|
||||
TRANSLATABLE("GPUDownsampleMode", "Adaptive (Preserve 3D/Smooth 2D)"));
|
||||
|
||||
std::optional<GPUDownsampleMode> Settings::ParseDownsampleModeName(const char* str)
|
||||
{
|
||||
int index = 0;
|
||||
for (const char* name : s_downsample_mode_names)
|
||||
{
|
||||
if (StringUtil::Strcasecmp(name, str) == 0)
|
||||
return static_cast<GPUDownsampleMode>(index);
|
||||
|
||||
index++;
|
||||
}
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
const char* Settings::GetDownsampleModeName(GPUDownsampleMode mode)
|
||||
{
|
||||
return s_downsample_mode_names[static_cast<int>(mode)];
|
||||
}
|
||||
|
||||
const char* Settings::GetDownsampleModeDisplayName(GPUDownsampleMode mode)
|
||||
{
|
||||
return s_downsample_mode_display_names[static_cast<int>(mode)];
|
||||
}
|
||||
|
||||
static std::array<const char*, 3> s_display_crop_mode_names = {{"None", "Overscan", "Borders"}};
|
||||
static std::array<const char*, 3> s_display_crop_mode_display_names = {
|
||||
{TRANSLATABLE("DisplayCropMode", "None"), TRANSLATABLE("DisplayCropMode", "Only Overscan Area"),
|
||||
|
|
|
@ -104,6 +104,7 @@ struct Settings
|
|||
bool gpu_true_color = true;
|
||||
bool gpu_scaled_dithering = false;
|
||||
GPUTextureFilter gpu_texture_filter = GPUTextureFilter::Nearest;
|
||||
GPUDownsampleMode gpu_downsample_mode = GPUDownsampleMode::Disabled;
|
||||
bool gpu_disable_interlacing = false;
|
||||
bool gpu_force_ntsc_timings = false;
|
||||
bool gpu_widescreen_hack = false;
|
||||
|
@ -285,6 +286,10 @@ struct Settings
|
|||
static const char* GetTextureFilterName(GPUTextureFilter filter);
|
||||
static const char* GetTextureFilterDisplayName(GPUTextureFilter filter);
|
||||
|
||||
static std::optional<GPUDownsampleMode> ParseDownsampleModeName(const char* str);
|
||||
static const char* GetDownsampleModeName(GPUDownsampleMode mode);
|
||||
static const char* GetDownsampleModeDisplayName(GPUDownsampleMode mode);
|
||||
|
||||
static std::optional<DisplayCropMode> ParseDisplayCropMode(const char* str);
|
||||
static const char* GetDisplayCropModeName(DisplayCropMode crop_mode);
|
||||
static const char* GetDisplayCropModeDisplayName(DisplayCropMode crop_mode);
|
||||
|
@ -312,6 +317,7 @@ struct Settings
|
|||
static constexpr GPURenderer DEFAULT_GPU_RENDERER = GPURenderer::HardwareOpenGL;
|
||||
#endif
|
||||
static constexpr GPUTextureFilter DEFAULT_GPU_TEXTURE_FILTER = GPUTextureFilter::Nearest;
|
||||
static constexpr GPUDownsampleMode DEFAULT_GPU_DOWNSAMPLE_MODE = GPUDownsampleMode::Disabled;
|
||||
static constexpr ConsoleRegion DEFAULT_CONSOLE_REGION = ConsoleRegion::Auto;
|
||||
static constexpr float DEFAULT_GPU_PGXP_DEPTH_THRESHOLD = 300.0f;
|
||||
|
||||
|
|
|
@ -164,6 +164,9 @@ void ShaderGen::WriteHeader(std::stringstream& ss)
|
|||
ss << "#define VECTOR_COMP_EQ(a, b) equal((a), (b))\n";
|
||||
ss << "#define VECTOR_COMP_NEQ(a, b) notEqual((a), (b))\n";
|
||||
ss << "#define SAMPLE_TEXTURE(name, coords) texture(name, coords)\n";
|
||||
ss << "#define SAMPLE_TEXTURE_OFFSET(name, coords, offset) textureOffset(name, coords, offset)\n";
|
||||
ss << "#define SAMPLE_TEXTURE_LEVEL(name, coords, level) textureLod(name, coords, level)\n";
|
||||
ss << "#define SAMPLE_TEXTURE_LEVEL_OFFSET(name, coords, level, offset) textureLod(name, coords, level, offset)\n";
|
||||
ss << "#define LOAD_TEXTURE(name, coords, mip) texelFetch(name, coords, mip)\n";
|
||||
ss << "#define LOAD_TEXTURE_MS(name, coords, sample) texelFetch(name, coords, int(sample))\n";
|
||||
ss << "#define LOAD_TEXTURE_OFFSET(name, coords, mip, offset) texelFetchOffset(name, coords, mip, offset)\n";
|
||||
|
@ -202,6 +205,10 @@ void ShaderGen::WriteHeader(std::stringstream& ss)
|
|||
ss << "#define VECTOR_COMP_EQ(a, b) ((a) == (b))\n";
|
||||
ss << "#define VECTOR_COMP_NEQ(a, b) ((a) != (b))\n";
|
||||
ss << "#define SAMPLE_TEXTURE(name, coords) name.Sample(name##_ss, coords)\n";
|
||||
ss << "#define SAMPLE_TEXTURE_OFFSET(name, coords, offset) name.Sample(name##_ss, coords, offset)\n";
|
||||
ss << "#define SAMPLE_TEXTURE_LEVEL(name, coords, level) name.SampleLevel(name##_ss, coords, level)\n";
|
||||
ss << "#define SAMPLE_TEXTURE_LEVEL_OFFSET(name, coords, level, offset) name.SampleLevel(name##_ss, coords, level, "
|
||||
"offset)\n";
|
||||
ss << "#define LOAD_TEXTURE(name, coords, mip) name.Load(int3(coords, mip))\n";
|
||||
ss << "#define LOAD_TEXTURE_MS(name, coords, sample) name.Load(coords, sample)\n";
|
||||
ss << "#define LOAD_TEXTURE_OFFSET(name, coords, mip, offset) name.Load(int3(coords, mip), offset)\n";
|
||||
|
@ -547,6 +554,26 @@ std::string ShaderGen::GenerateScreenQuadVertexShader()
|
|||
return ss.str();
|
||||
}
|
||||
|
||||
std::string ShaderGen::GenerateUVQuadVertexShader()
|
||||
{
|
||||
std::stringstream ss;
|
||||
WriteHeader(ss);
|
||||
DeclareUniformBuffer(ss, {"float2 u_uv_min", "float2 u_uv_max"}, true);
|
||||
DeclareVertexEntryPoint(ss, {}, 0, 1, {}, true);
|
||||
ss << R"(
|
||||
{
|
||||
v_tex0 = float2(float((v_id << 1) & 2u), float(v_id & 2u));
|
||||
v_pos = float4(v_tex0 * float2(2.0f, -2.0f) + float2(-1.0f, 1.0f), 0.0f, 1.0f);
|
||||
v_tex0 = u_uv_min + (u_uv_max - u_uv_min) * v_tex0;
|
||||
#if API_OPENGL || API_OPENGL_ES || API_VULKAN
|
||||
v_pos.y = -v_pos.y;
|
||||
#endif
|
||||
}
|
||||
)";
|
||||
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
std::string ShaderGen::GenerateFillFragmentShader()
|
||||
{
|
||||
std::stringstream ss;
|
||||
|
|
|
@ -13,6 +13,7 @@ public:
|
|||
static bool UseGLSLBindingLayout();
|
||||
|
||||
std::string GenerateScreenQuadVertexShader();
|
||||
std::string GenerateUVQuadVertexShader();
|
||||
std::string GenerateFillFragmentShader();
|
||||
std::string GenerateCopyFragmentShader();
|
||||
|
||||
|
|
|
@ -75,6 +75,14 @@ enum class GPUTextureFilter : u8
|
|||
Count
|
||||
};
|
||||
|
||||
enum GPUDownsampleMode : u8
|
||||
{
|
||||
Disabled,
|
||||
Box,
|
||||
Adaptive,
|
||||
Count
|
||||
};
|
||||
|
||||
enum class DisplayCropMode : u8
|
||||
{
|
||||
None,
|
||||
|
|
|
@ -489,7 +489,7 @@ void LibretroHostInterface::OnSystemDestroyed()
|
|||
m_using_hardware_renderer = false;
|
||||
}
|
||||
|
||||
static std::array<retro_core_option_definition, 51> s_option_definitions = {{
|
||||
static std::array<retro_core_option_definition, 52> s_option_definitions = {{
|
||||
{"duckstation_Console.Region",
|
||||
"Console Region",
|
||||
"Determines which region/hardware to emulate. Auto-Detect will use the region of the disc inserted.",
|
||||
|
@ -701,12 +701,6 @@ static std::array<retro_core_option_definition, 51> s_option_definitions = {{
|
|||
"Very slow, and incompatible with the recompiler.",
|
||||
{{"true", "Enabled"}, {"false", "Disabled"}},
|
||||
"false"},
|
||||
{"duckstation_Display.CropMode",
|
||||
"Crop Mode",
|
||||
"Changes how much of the image is cropped. Some games display garbage in the overscan area which is typically "
|
||||
"hidden.",
|
||||
{{"None", "None"}, {"Overscan", "Only Overscan Area"}, {"Borders", "All Borders"}},
|
||||
"Overscan"},
|
||||
{"duckstation_Display.AspectRatio",
|
||||
"Aspect Ratio",
|
||||
"Sets the core-provided aspect ratio.",
|
||||
|
@ -724,6 +718,20 @@ static std::array<retro_core_option_definition, 51> s_option_definitions = {{
|
|||
{"1:1", "1:1"},
|
||||
{"PAR 1:1", "PAR 1:1"}},
|
||||
"Auto"},
|
||||
{"duckstation_Display.CropMode",
|
||||
"Crop Mode",
|
||||
"Changes how much of the image is cropped. Some games display garbage in the overscan area which is typically "
|
||||
"hidden.",
|
||||
{{"None", "None"}, {"Overscan", "Only Overscan Area"}, {"Borders", "All Borders"}},
|
||||
"Overscan"},
|
||||
{"duckstation_GPU.DownsampleMode",
|
||||
"Downsampling",
|
||||
"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.",
|
||||
{{"Disabled", "Disabled"},
|
||||
{"Box", "Box (Downsample 3D/Smooth All)"},
|
||||
{"Adaptive", "Adaptive (Preserve 3D/Smooth 2D)"}},
|
||||
"Disabled"},
|
||||
{"duckstation_Main.LoadDevicesFromSaveStates",
|
||||
"Load Devices From Save States",
|
||||
"Sets whether the contents of devices and memory cards will be loaded when a save state is loaded.",
|
||||
|
|
|
@ -28,6 +28,9 @@ DisplaySettingsWidget::DisplaySettingsWidget(QtHostInterface* host_interface, QW
|
|||
SettingWidgetBinder::BindWidgetToEnumSetting(m_host_interface, m_ui.displayCropMode, "Display", "CropMode",
|
||||
&Settings::ParseDisplayCropMode, &Settings::GetDisplayCropModeName,
|
||||
Settings::DEFAULT_DISPLAY_CROP_MODE);
|
||||
SettingWidgetBinder::BindWidgetToEnumSetting(m_host_interface, m_ui.gpuDownsampleMode, "GPU", "DownsampleMode",
|
||||
&Settings::ParseDownsampleModeName, &Settings::GetDownsampleModeName,
|
||||
Settings::DEFAULT_GPU_DOWNSAMPLE_MODE);
|
||||
SettingWidgetBinder::BindWidgetToBoolSetting(m_host_interface, m_ui.displayLinearFiltering, "Display",
|
||||
"LinearFiltering");
|
||||
SettingWidgetBinder::BindWidgetToBoolSetting(m_host_interface, m_ui.displayIntegerScaling, "Display",
|
||||
|
@ -73,6 +76,10 @@ DisplaySettingsWidget::DisplaySettingsWidget(QtHostInterface* host_interface, QW
|
|||
"Some games display content in the overscan area, or use it for screen effects. <br>May "
|
||||
"not display correctly with the \"All Borders\" setting. \"Only Overscan\" offers a good "
|
||||
"compromise between stability and hiding black borders."));
|
||||
dialog->registerWidgetHelp(
|
||||
m_ui.gpuDownsampleMode, tr("Downsampling"), tr("Disabled"),
|
||||
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."));
|
||||
dialog->registerWidgetHelp(m_ui.displayLinearFiltering, tr("Linear Upscaling"), tr("Checked"),
|
||||
tr("Uses bilinear texture filtering when displaying the console's framebuffer to the "
|
||||
"screen. <br>Disabling filtering "
|
||||
|
@ -139,6 +146,12 @@ void DisplaySettingsWidget::setupAdditionalUi()
|
|||
m_ui.displayCropMode->addItem(
|
||||
qApp->translate("DisplayCropMode", Settings::GetDisplayCropModeDisplayName(static_cast<DisplayCropMode>(i))));
|
||||
}
|
||||
|
||||
for (u32 i = 0; i < static_cast<u32>(GPUDownsampleMode::Count); i++)
|
||||
{
|
||||
m_ui.gpuDownsampleMode->addItem(
|
||||
qApp->translate("GPUDownsampleMode", Settings::GetDownsampleModeDisplayName(static_cast<GPUDownsampleMode>(i))));
|
||||
}
|
||||
}
|
||||
|
||||
void DisplaySettingsWidget::populateGPUAdaptersAndResolutions()
|
||||
|
|
|
@ -116,7 +116,17 @@
|
|||
<item row="1" column="1">
|
||||
<widget class="QComboBox" name="displayCropMode"/>
|
||||
</item>
|
||||
<item row="2" column="0" colspan="2">
|
||||
<item row="2" column="0">
|
||||
<widget class="QLabel" name="label_6">
|
||||
<property name="text">
|
||||
<string>Downsampling:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<widget class="QComboBox" name="gpuDownsampleMode"/>
|
||||
</item>
|
||||
<item row="3" column="0" colspan="2">
|
||||
<layout class="QGridLayout" name="gridLayout_2">
|
||||
<item row="0" column="0">
|
||||
<widget class="QCheckBox" name="displayLinearFiltering">
|
||||
|
|
|
@ -1080,6 +1080,21 @@ void SDLHostInterface::DrawQuickSettingsMenu()
|
|||
settings_changed |= ImGui::MenuItem("Display Linear Filtering", nullptr, &m_settings_copy.display_linear_filtering);
|
||||
settings_changed |= ImGui::MenuItem("Display Integer Scaling", nullptr, &m_settings_copy.display_integer_scaling);
|
||||
|
||||
if (ImGui::BeginMenu("Aspect Ratio"))
|
||||
{
|
||||
for (u32 i = 0; i < static_cast<u32>(DisplayAspectRatio::Count); i++)
|
||||
{
|
||||
if (ImGui::MenuItem(Settings::GetDisplayAspectRatioName(static_cast<DisplayAspectRatio>(i)), nullptr,
|
||||
m_settings_copy.display_aspect_ratio == static_cast<DisplayAspectRatio>(i)))
|
||||
{
|
||||
m_settings_copy.display_aspect_ratio = static_cast<DisplayAspectRatio>(i);
|
||||
settings_changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
|
||||
if (ImGui::BeginMenu("Crop Mode"))
|
||||
{
|
||||
for (u32 i = 0; i < static_cast<u32>(DisplayCropMode::Count); i++)
|
||||
|
@ -1095,14 +1110,14 @@ void SDLHostInterface::DrawQuickSettingsMenu()
|
|||
ImGui::EndMenu();
|
||||
}
|
||||
|
||||
if (ImGui::BeginMenu("Aspect Ratio"))
|
||||
if (ImGui::BeginMenu("Downsample Mode"))
|
||||
{
|
||||
for (u32 i = 0; i < static_cast<u32>(DisplayAspectRatio::Count); i++)
|
||||
for (u32 i = 0; i < static_cast<u32>(GPUDownsampleMode::Count); i++)
|
||||
{
|
||||
if (ImGui::MenuItem(Settings::GetDisplayAspectRatioName(static_cast<DisplayAspectRatio>(i)), nullptr,
|
||||
m_settings_copy.display_aspect_ratio == static_cast<DisplayAspectRatio>(i)))
|
||||
if (ImGui::MenuItem(Settings::GetDownsampleModeDisplayName(static_cast<GPUDownsampleMode>(i)), nullptr,
|
||||
m_settings_copy.gpu_downsample_mode == static_cast<GPUDownsampleMode>(i)))
|
||||
{
|
||||
m_settings_copy.display_aspect_ratio = static_cast<DisplayAspectRatio>(i);
|
||||
m_settings_copy.gpu_downsample_mode = static_cast<GPUDownsampleMode>(i);
|
||||
settings_changed = true;
|
||||
}
|
||||
}
|
||||
|
@ -1541,6 +1556,21 @@ void SDLHostInterface::DrawSettingsWindow()
|
|||
settings_changed = true;
|
||||
}
|
||||
|
||||
ImGui::Text("Downsample Mode:");
|
||||
ImGui::SameLine(indent);
|
||||
int gpu_downsample_mode = static_cast<int>(m_settings_copy.gpu_downsample_mode);
|
||||
if (ImGui::Combo(
|
||||
"##gpu_downsample_mode", &gpu_downsample_mode,
|
||||
[](void*, int index, const char** out_text) {
|
||||
*out_text = Settings::GetDownsampleModeDisplayName(static_cast<GPUDownsampleMode>(index));
|
||||
return true;
|
||||
},
|
||||
nullptr, static_cast<int>(GPUDownsampleMode::Count)))
|
||||
{
|
||||
m_settings_copy.gpu_downsample_mode = static_cast<GPUDownsampleMode>(gpu_downsample_mode);
|
||||
settings_changed = true;
|
||||
}
|
||||
|
||||
settings_changed |= ImGui::Checkbox("Use Debug Device", &m_settings_copy.gpu_use_debug_device);
|
||||
settings_changed |= ImGui::Checkbox("Linear Filtering", &m_settings_copy.display_linear_filtering);
|
||||
settings_changed |= ImGui::Checkbox("Integer Scaling", &m_settings_copy.display_integer_scaling);
|
||||
|
|
Loading…
Reference in New Issue