From 2b66a1b68fca2be2a6827f69f9774f7b88287c5a Mon Sep 17 00:00:00 2001 From: DrChat Date: Tue, 17 Apr 2018 15:35:24 -0500 Subject: [PATCH] [Vulkan] Emulate Xenos behavior with copy dest at a negative offset It appears the Xenos starts copying pixel data at the first encountered visible destination pixel, rather than texture mapping the source texture onto the destination polygon --- .../gpu/vulkan/vulkan_command_processor.cc | 57 ++++++++++++------- 1 file changed, 36 insertions(+), 21 deletions(-) diff --git a/src/xenia/gpu/vulkan/vulkan_command_processor.cc b/src/xenia/gpu/vulkan/vulkan_command_processor.cc index 52f5ba7fc..2dbed49b5 100644 --- a/src/xenia/gpu/vulkan/vulkan_command_processor.cc +++ b/src/xenia/gpu/vulkan/vulkan_command_processor.cc @@ -1179,35 +1179,50 @@ bool VulkanCommandProcessor::IssueCopy() { resolve_extent, }; - // By offsetting the destination texture by the window offset, we've - // already handled it and need to subtract the window offset from the - // destination rectangle. VkRect2D dst_rect = { - {resolve_offset.x + window_offset_x, - resolve_offset.y + window_offset_y}, + {resolve_offset.x, resolve_offset.y}, resolve_extent, }; + // If the destination rectangle lies outside the window, make it start + // inside. The Xenos does not copy pixel data at any offset in screen + // coordinates. + int32_t dst_adj_x = + std::max(dst_rect.offset.x, -window_offset_x) - dst_rect.offset.x; + int32_t dst_adj_y = + std::max(dst_rect.offset.y, -window_offset_y) - dst_rect.offset.y; + + if (uint32_t(dst_adj_x) > dst_rect.extent.width || + uint32_t(dst_adj_y) > dst_rect.extent.height) { + // No-op? + break; + } + + dst_rect.offset.x += dst_adj_x; + dst_rect.offset.y += dst_adj_y; + dst_rect.extent.width -= dst_adj_x; + dst_rect.extent.height -= dst_adj_y; + src_rect.extent.width -= dst_adj_x; + src_rect.extent.height -= dst_adj_y; + VkViewport viewport = { - float(-window_offset_x), - float(-window_offset_y), - float(copy_dest_pitch), - float(copy_dest_height), - 0.f, - 1.f, + 0.f, 0.f, float(copy_dest_pitch), float(copy_dest_height), 0.f, 1.f, }; + uint32_t scissor_tl_x = window_regs->window_scissor_tl.tl_x; + uint32_t scissor_br_x = window_regs->window_scissor_br.br_x; + uint32_t scissor_tl_y = window_regs->window_scissor_tl.tl_y; + uint32_t scissor_br_y = window_regs->window_scissor_br.br_y; + + // Clamp the values to destination dimensions. + scissor_tl_x = std::min(scissor_tl_x, copy_dest_pitch); + scissor_br_x = std::min(scissor_br_x, copy_dest_pitch); + scissor_tl_y = std::min(scissor_tl_y, copy_dest_height); + scissor_br_y = std::min(scissor_br_y, copy_dest_height); + VkRect2D scissor = { - { - int32_t(window_regs->window_scissor_tl.tl_x.value()), - int32_t(window_regs->window_scissor_tl.tl_y.value()), - }, - { - window_regs->window_scissor_br.br_x.value() - - window_regs->window_scissor_tl.tl_x.value(), - window_regs->window_scissor_br.br_y.value() - - window_regs->window_scissor_tl.tl_y.value(), - }, + {int32_t(scissor_tl_x), int32_t(scissor_tl_y)}, + {scissor_br_x - scissor_tl_x, scissor_br_y - scissor_tl_y}, }; blitter_->BlitTexture2D(