[Vulkan] Add resolution scaling support to Vulkan backend.

Largely similar to D3D12 implementation but more simple buffer
management and no mips scaling. Includes both FBO and FSI
rendering paths.

Tested on Linux with 2x2 and 3x3 running smoothly.
This commit is contained in:
Herman S.
2025-08-27 20:44:15 +09:00
committed by Radosław Gliński
parent 2de1b0b2dd
commit d430342d93
11 changed files with 563 additions and 53 deletions

View File

@@ -288,10 +288,13 @@ bool VulkanCommandProcessor::SetupContext() {
<< shared_memory_binding_count_log2;
// Requires the transient descriptor set layouts.
// TODO(Triang3l): Get the actual draw resolution scale when the texture cache
// supports resolution scaling.
// Get draw resolution scale using the same method as D3D12
uint32_t draw_resolution_scale_x, draw_resolution_scale_y;
TextureCache::GetConfigDrawResolutionScale(draw_resolution_scale_x,
draw_resolution_scale_y);
render_target_cache_ = std::make_unique<VulkanRenderTargetCache>(
*register_file_, *memory_, trace_writer_, 1, 1, *this);
*register_file_, *memory_, trace_writer_, draw_resolution_scale_x,
draw_resolution_scale_y, *this);
if (!render_target_cache_->Initialize(shared_memory_binding_count)) {
XELOGE("Failed to initialize the render target cache");
return false;
@@ -354,10 +357,10 @@ bool VulkanCommandProcessor::SetupContext() {
}
// Requires the transient descriptor set layouts.
// TODO(Triang3l): Actual draw resolution scale.
texture_cache_ =
VulkanTextureCache::Create(*register_file_, *shared_memory_, 1, 1, *this,
guest_shader_pipeline_stages_);
// Use the same draw resolution scale as render target cache
texture_cache_ = VulkanTextureCache::Create(
*register_file_, *shared_memory_, draw_resolution_scale_x,
draw_resolution_scale_y, *this, guest_shader_pipeline_stages_);
if (!texture_cache_) {
XELOGE("Failed to initialize the texture cache");
return false;
@@ -2468,8 +2471,12 @@ bool VulkanCommandProcessor::IssueDraw(xenos::PrimitiveType prim_type,
// life. Or even disregard the viewport bounds range in the fragment shader
// interlocks case completely - apply the viewport and the scissor offset
// directly to pixel address and to things like ps_param_gen.
uint32_t draw_resolution_scale_x = texture_cache_->draw_resolution_scale_x();
uint32_t draw_resolution_scale_y = texture_cache_->draw_resolution_scale_y();
draw_util::GetViewportInfoArgs gviargs{};
gviargs.Setup(1, 1, divisors::MagicDiv{1}, divisors::MagicDiv{1}, false,
gviargs.Setup(draw_resolution_scale_x, draw_resolution_scale_y,
texture_cache_->draw_resolution_scale_x_divisor(),
texture_cache_->draw_resolution_scale_y_divisor(), false,
device_properties.maxViewportDimensions[0],
device_properties.maxViewportDimensions[1], true,
normalized_depth_control, false, host_render_targets_used,
@@ -2479,7 +2486,8 @@ bool VulkanCommandProcessor::IssueDraw(xenos::PrimitiveType prim_type,
draw_util::GetHostViewportInfo(&gviargs, viewport_info);
// Update dynamic graphics pipeline state.
UpdateDynamicState(viewport_info, primitive_polygonal,
normalized_depth_control);
normalized_depth_control, draw_resolution_scale_x,
draw_resolution_scale_y);
auto vgt_draw_initiator = regs.Get<reg::VGT_DRAW_INITIATOR>();
@@ -3276,7 +3284,8 @@ void VulkanCommandProcessor::DestroyScratchBuffer() {
void VulkanCommandProcessor::UpdateDynamicState(
const draw_util::ViewportInfo& viewport_info, bool primitive_polygonal,
reg::RB_DEPTHCONTROL normalized_depth_control) {
reg::RB_DEPTHCONTROL normalized_depth_control,
uint32_t draw_resolution_scale_x, uint32_t draw_resolution_scale_y) {
#if XE_GPU_FINE_GRAINED_DRAW_SCOPES
SCOPE_profile_cpu_f("gpu");
#endif // XE_GPU_FINE_GRAINED_DRAW_SCOPES
@@ -3312,6 +3321,11 @@ void VulkanCommandProcessor::UpdateDynamicState(
// Scissor.
draw_util::Scissor scissor;
draw_util::GetScissor(regs, scissor);
// Scale the scissor to match the render target resolution scale
scissor.offset[0] *= draw_resolution_scale_x;
scissor.offset[1] *= draw_resolution_scale_y;
scissor.extent[0] *= draw_resolution_scale_x;
scissor.extent[1] *= draw_resolution_scale_y;
VkRect2D scissor_rect;
scissor_rect.offset.x = int32_t(scissor.offset[0]);
scissor_rect.offset.y = int32_t(scissor.offset[1]);