[Vulkan] Implement tiled_shared_memory for resolution scaling
Adds support for >3x resolution scaling to vulkan by implementing sparse scaled resolve similar to the d3d12 implementation.
This commit is contained in:
@@ -851,8 +851,11 @@ bool D3D12CommandProcessor::SetupContext() {
|
|||||||
TextureCache::GetConfigDrawResolutionScale(draw_resolution_scale_x,
|
TextureCache::GetConfigDrawResolutionScale(draw_resolution_scale_x,
|
||||||
draw_resolution_scale_y);
|
draw_resolution_scale_y);
|
||||||
|
|
||||||
if (!D3D12TextureCache::ClampDrawResolutionScaleToMaxSupported(
|
bool has_tiled_resources =
|
||||||
draw_resolution_scale_x, draw_resolution_scale_y, provider)) {
|
provider.GetTiledResourcesTier() >= D3D12_TILED_RESOURCES_TIER_1;
|
||||||
|
if (!TextureCache::ClampDrawResolutionScaleToMaxSupported(
|
||||||
|
draw_resolution_scale_x, draw_resolution_scale_y, has_tiled_resources,
|
||||||
|
provider.GetVirtualAddressBitsPerResource())) {
|
||||||
draw_resolution_scale_not_clamped = false;
|
draw_resolution_scale_not_clamped = false;
|
||||||
}
|
}
|
||||||
if (!draw_resolution_scale_not_clamped) {
|
if (!draw_resolution_scale_not_clamped) {
|
||||||
|
|||||||
@@ -18,14 +18,8 @@
|
|||||||
#include "xenia/gpu/d3d12/d3d12_command_processor.h"
|
#include "xenia/gpu/d3d12/d3d12_command_processor.h"
|
||||||
#include "xenia/ui/d3d12/d3d12_util.h"
|
#include "xenia/ui/d3d12/d3d12_util.h"
|
||||||
|
|
||||||
DEFINE_bool(d3d12_tiled_shared_memory, true,
|
|
||||||
"Enable tiled resources for shared memory emulation. Disabling "
|
|
||||||
"them increases video memory usage - a 512 MB buffer is created - "
|
|
||||||
"but allows graphics debuggers that don't support tiled resources "
|
|
||||||
"to work.",
|
|
||||||
"D3D12");
|
|
||||||
|
|
||||||
DECLARE_bool(gpu_allow_invalid_upload_range);
|
DECLARE_bool(gpu_allow_invalid_upload_range);
|
||||||
|
DECLARE_bool(tiled_shared_memory);
|
||||||
|
|
||||||
namespace xe {
|
namespace xe {
|
||||||
namespace gpu {
|
namespace gpu {
|
||||||
@@ -50,7 +44,7 @@ bool D3D12SharedMemory::Initialize() {
|
|||||||
ui::d3d12::util::FillBufferResourceDesc(
|
ui::d3d12::util::FillBufferResourceDesc(
|
||||||
buffer_desc, kBufferSize, D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS);
|
buffer_desc, kBufferSize, D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS);
|
||||||
buffer_state_ = D3D12_RESOURCE_STATE_COPY_DEST;
|
buffer_state_ = D3D12_RESOURCE_STATE_COPY_DEST;
|
||||||
if (cvars::d3d12_tiled_shared_memory &&
|
if (cvars::tiled_shared_memory &&
|
||||||
provider.GetTiledResourcesTier() !=
|
provider.GetTiledResourcesTier() !=
|
||||||
D3D12_TILED_RESOURCES_TIER_NOT_SUPPORTED &&
|
D3D12_TILED_RESOURCES_TIER_NOT_SUPPORTED &&
|
||||||
!provider.GetGraphicsAnalysis()) {
|
!provider.GetGraphicsAnalysis()) {
|
||||||
|
|||||||
@@ -846,40 +846,6 @@ void D3D12TextureCache::WriteSampler(SamplerParameters parameters,
|
|||||||
device->CreateSampler(&desc, handle);
|
device->CreateSampler(&desc, handle);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool D3D12TextureCache::ClampDrawResolutionScaleToMaxSupported(
|
|
||||||
uint32_t& scale_x, uint32_t& scale_y,
|
|
||||||
const ui::d3d12::D3D12Provider& provider) {
|
|
||||||
bool was_clamped;
|
|
||||||
if (provider.GetTiledResourcesTier() < D3D12_TILED_RESOURCES_TIER_1) {
|
|
||||||
was_clamped = scale_x > 1 || scale_y > 1;
|
|
||||||
scale_x = 1;
|
|
||||||
scale_y = 1;
|
|
||||||
return !was_clamped;
|
|
||||||
}
|
|
||||||
// Limit to the virtual address space available for a resource.
|
|
||||||
was_clamped = false;
|
|
||||||
uint32_t virtual_address_bits_per_resource =
|
|
||||||
provider.GetVirtualAddressBitsPerResource();
|
|
||||||
while (scale_x > 1 || scale_y > 1) {
|
|
||||||
uint64_t highest_scaled_address =
|
|
||||||
uint64_t(SharedMemory::kBufferSize) * (scale_x * scale_y) - 1;
|
|
||||||
if (uint32_t(64) - xe::lzcnt(highest_scaled_address) <=
|
|
||||||
virtual_address_bits_per_resource) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
// When reducing from a square size, prefer decreasing the horizontal
|
|
||||||
// resolution as vertical resolution difference is visible more clearly in
|
|
||||||
// perspective.
|
|
||||||
was_clamped = true;
|
|
||||||
if (scale_x >= scale_y) {
|
|
||||||
--scale_x;
|
|
||||||
} else {
|
|
||||||
--scale_y;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return !was_clamped;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool D3D12TextureCache::EnsureScaledResolveMemoryCommitted(
|
bool D3D12TextureCache::EnsureScaledResolveMemoryCommitted(
|
||||||
uint32_t start_unscaled, uint32_t length_unscaled,
|
uint32_t start_unscaled, uint32_t length_unscaled,
|
||||||
uint32_t length_scaled_alignment_log2) {
|
uint32_t length_scaled_alignment_log2) {
|
||||||
|
|||||||
@@ -126,10 +126,6 @@ class D3D12TextureCache final : public TextureCache {
|
|||||||
void WriteSampler(SamplerParameters parameters,
|
void WriteSampler(SamplerParameters parameters,
|
||||||
D3D12_CPU_DESCRIPTOR_HANDLE handle) const;
|
D3D12_CPU_DESCRIPTOR_HANDLE handle) const;
|
||||||
|
|
||||||
// Returns whether the actual scale is not smaller than the requested one.
|
|
||||||
static bool ClampDrawResolutionScaleToMaxSupported(
|
|
||||||
uint32_t& scale_x, uint32_t& scale_y,
|
|
||||||
const ui::d3d12::D3D12Provider& provider);
|
|
||||||
// Ensures the tiles backing the range in the buffers are allocated.
|
// Ensures the tiles backing the range in the buffers are allocated.
|
||||||
bool EnsureScaledResolveMemoryCommitted(
|
bool EnsureScaledResolveMemoryCommitted(
|
||||||
uint32_t start_unscaled, uint32_t length_unscaled,
|
uint32_t start_unscaled, uint32_t length_unscaled,
|
||||||
|
|||||||
@@ -12,17 +12,19 @@
|
|||||||
#include "xenia/base/clock.h"
|
#include "xenia/base/clock.h"
|
||||||
#include "xenia/base/cvar.h"
|
#include "xenia/base/cvar.h"
|
||||||
#include "xenia/base/logging.h"
|
#include "xenia/base/logging.h"
|
||||||
|
#include "xenia/base/math.h"
|
||||||
#include "xenia/base/profiling.h"
|
#include "xenia/base/profiling.h"
|
||||||
#include "xenia/gpu/gpu_flags.h"
|
#include "xenia/gpu/gpu_flags.h"
|
||||||
|
#include "xenia/gpu/shared_memory.h"
|
||||||
|
|
||||||
DEFINE_int32(
|
DEFINE_int32(
|
||||||
draw_resolution_scale_x, 1,
|
draw_resolution_scale_x, 1,
|
||||||
"Integer pixel width scale used for scaling the rendering resolution "
|
"Integer pixel width scale used for scaling the rendering resolution "
|
||||||
"opaquely to the game.\n"
|
"opaquely to the game.\n"
|
||||||
"1, 2 and 3 may be supported, but support of anything above 1 depends on "
|
"Values from 1 to 7 may be supported, depending on device capabilities. "
|
||||||
"the device properties, such as whether it supports sparse binding / tiled "
|
"Requires sparse binding (Vulkan) or tiled resources (D3D12) for scales "
|
||||||
"resources, the number of virtual address bits per resource, and other "
|
"above 1x1. The emulator will automatically clamp to the maximum supported "
|
||||||
"factors.\n"
|
"scale if the requested value exceeds device limits.\n"
|
||||||
"Various effects and parts of game rendering pipelines may work "
|
"Various effects and parts of game rendering pipelines may work "
|
||||||
"incorrectly as pixels become ambiguous from the game's perspective and "
|
"incorrectly as pixels become ambiguous from the game's perspective and "
|
||||||
"because half-pixel offset (which normally doesn't affect coverage when "
|
"because half-pixel offset (which normally doesn't affect coverage when "
|
||||||
@@ -59,6 +61,10 @@ DEFINE_uint32(
|
|||||||
"textures - so with 2x2 resolution scaling, the soft limit will be 360 + "
|
"textures - so with 2x2 resolution scaling, the soft limit will be 360 + "
|
||||||
"96 MB, and with 3x3, it will be 360 + 216 MB.",
|
"96 MB, and with 3x3, it will be 360 + 216 MB.",
|
||||||
"GPU");
|
"GPU");
|
||||||
|
DEFINE_bool(tiled_shared_memory, true,
|
||||||
|
"Enable tiled/sparse resources for efficient large address space "
|
||||||
|
"support. Disable for graphics debugger compatibility.",
|
||||||
|
"GPU");
|
||||||
|
|
||||||
namespace xe {
|
namespace xe {
|
||||||
namespace gpu {
|
namespace gpu {
|
||||||
@@ -187,6 +193,43 @@ bool TextureCache::GetConfigDrawResolutionScale(uint32_t& x_out,
|
|||||||
return clamped_x == config_x && clamped_y == config_y;
|
return clamped_x == config_x && clamped_y == config_y;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool TextureCache::ClampDrawResolutionScaleToMaxSupported(
|
||||||
|
uint32_t& scale_x, uint32_t& scale_y, bool sparse_bind_supported,
|
||||||
|
uint32_t virtual_address_bits_per_resource) {
|
||||||
|
// Without sparse/tiled resource support, resolution scaling is not possible
|
||||||
|
// because the scaled address space exceeds what simple buffers can handle.
|
||||||
|
if (!sparse_bind_supported) {
|
||||||
|
bool was_clamped = scale_x > 1 || scale_y > 1;
|
||||||
|
scale_x = 1;
|
||||||
|
scale_y = 1;
|
||||||
|
return !was_clamped;
|
||||||
|
}
|
||||||
|
|
||||||
|
// With sparse binding, limit based on virtual address space if specified.
|
||||||
|
bool was_clamped = false;
|
||||||
|
if (virtual_address_bits_per_resource > 0) {
|
||||||
|
while (scale_x > 1 || scale_y > 1) {
|
||||||
|
uint64_t highest_scaled_address =
|
||||||
|
uint64_t(SharedMemory::kBufferSize) * (scale_x * scale_y) - 1;
|
||||||
|
if (uint32_t(64) - xe::lzcnt(highest_scaled_address) <=
|
||||||
|
virtual_address_bits_per_resource) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// When reducing from a square size, prefer decreasing the horizontal
|
||||||
|
// resolution as vertical resolution difference is visible more clearly in
|
||||||
|
// perspective.
|
||||||
|
was_clamped = true;
|
||||||
|
if (scale_x >= scale_y) {
|
||||||
|
--scale_x;
|
||||||
|
} else {
|
||||||
|
--scale_y;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return !was_clamped;
|
||||||
|
}
|
||||||
|
|
||||||
void TextureCache::ClearCache() { DestroyAllTextures(); }
|
void TextureCache::ClearCache() { DestroyAllTextures(); }
|
||||||
|
|
||||||
void TextureCache::CompletedSubmissionUpdated(
|
void TextureCache::CompletedSubmissionUpdated(
|
||||||
|
|||||||
@@ -69,6 +69,14 @@ class TextureCache {
|
|||||||
|
|
||||||
// Returns whether the actual scale is not smaller than the requested one.
|
// Returns whether the actual scale is not smaller than the requested one.
|
||||||
static bool GetConfigDrawResolutionScale(uint32_t& x_out, uint32_t& y_out);
|
static bool GetConfigDrawResolutionScale(uint32_t& x_out, uint32_t& y_out);
|
||||||
|
|
||||||
|
// Clamps the resolution scale based on device capabilities.
|
||||||
|
// sparse_bind_supported: whether the device supports sparse/tiled resources
|
||||||
|
// virtual_address_bits: max bits for virtual address per resource (0 = no
|
||||||
|
// limit) Returns true if scale was not clamped.
|
||||||
|
static bool ClampDrawResolutionScaleToMaxSupported(
|
||||||
|
uint32_t& scale_x, uint32_t& scale_y, bool sparse_bind_supported,
|
||||||
|
uint32_t virtual_address_bits_per_resource = 0);
|
||||||
uint32_t draw_resolution_scale_x() const { return draw_resolution_scale_x_; }
|
uint32_t draw_resolution_scale_x() const { return draw_resolution_scale_x_; }
|
||||||
uint32_t draw_resolution_scale_y() const { return draw_resolution_scale_y_; }
|
uint32_t draw_resolution_scale_y() const { return draw_resolution_scale_y_; }
|
||||||
|
|
||||||
|
|||||||
@@ -276,10 +276,26 @@ bool VulkanCommandProcessor::SetupContext() {
|
|||||||
<< shared_memory_binding_count_log2;
|
<< shared_memory_binding_count_log2;
|
||||||
|
|
||||||
// Requires the transient descriptor set layouts.
|
// Requires the transient descriptor set layouts.
|
||||||
// Get draw resolution scale using the same method as D3D12
|
// Get draw resolution scale and clamp based on device capabilities
|
||||||
uint32_t draw_resolution_scale_x, draw_resolution_scale_y;
|
uint32_t draw_resolution_scale_x, draw_resolution_scale_y;
|
||||||
TextureCache::GetConfigDrawResolutionScale(draw_resolution_scale_x,
|
bool draw_resolution_scale_not_clamped =
|
||||||
draw_resolution_scale_y);
|
TextureCache::GetConfigDrawResolutionScale(draw_resolution_scale_x,
|
||||||
|
draw_resolution_scale_y);
|
||||||
|
// Check if sparse binding is supported for resolution scaling
|
||||||
|
bool has_sparse_binding = device_properties.sparseBinding &&
|
||||||
|
device_properties.sparseResidencyBuffer;
|
||||||
|
if (!TextureCache::ClampDrawResolutionScaleToMaxSupported(
|
||||||
|
draw_resolution_scale_x, draw_resolution_scale_y, has_sparse_binding,
|
||||||
|
0)) {
|
||||||
|
draw_resolution_scale_not_clamped = false;
|
||||||
|
}
|
||||||
|
if (!draw_resolution_scale_not_clamped) {
|
||||||
|
XELOGW(
|
||||||
|
"The requested draw resolution scale is not supported by the device or "
|
||||||
|
"the emulator, reducing to {}x{}",
|
||||||
|
draw_resolution_scale_x, draw_resolution_scale_y);
|
||||||
|
}
|
||||||
|
|
||||||
render_target_cache_ = std::make_unique<VulkanRenderTargetCache>(
|
render_target_cache_ = std::make_unique<VulkanRenderTargetCache>(
|
||||||
*register_file_, *memory_, trace_writer_, draw_resolution_scale_x,
|
*register_file_, *memory_, trace_writer_, draw_resolution_scale_x,
|
||||||
draw_resolution_scale_y, *this);
|
draw_resolution_scale_y, *this);
|
||||||
|
|||||||
@@ -1156,17 +1156,9 @@ bool VulkanRenderTargetCache::Resolve(const Memory& memory,
|
|||||||
draw_resolution_scale_x() * draw_resolution_scale_y();
|
draw_resolution_scale_x() * draw_resolution_scale_y();
|
||||||
uint64_t scaled_offset =
|
uint64_t scaled_offset =
|
||||||
uint64_t(dest_address) * draw_resolution_scale_area;
|
uint64_t(dest_address) * draw_resolution_scale_area;
|
||||||
|
uint64_t buffer_relative_offset =
|
||||||
// Get the buffer's base offset to calculate relative offset
|
scaled_offset -
|
||||||
uint64_t buffer_relative_offset = 0;
|
texture_cache.GetCurrentScaledResolveBufferBaseOffset();
|
||||||
size_t buffer_index =
|
|
||||||
texture_cache.GetScaledResolveCurrentBufferIndex();
|
|
||||||
auto* buffer_info =
|
|
||||||
texture_cache.GetScaledResolveBufferInfo(buffer_index);
|
|
||||||
if (buffer_info) {
|
|
||||||
buffer_relative_offset =
|
|
||||||
scaled_offset - buffer_info->range_start_scaled;
|
|
||||||
}
|
|
||||||
|
|
||||||
write_descriptor_set_dest_buffer_info.buffer = scaled_buffer;
|
write_descriptor_set_dest_buffer_info.buffer = scaled_buffer;
|
||||||
write_descriptor_set_dest_buffer_info.offset =
|
write_descriptor_set_dest_buffer_info.offset =
|
||||||
|
|||||||
@@ -15,6 +15,7 @@
|
|||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
#include "xenia/base/assert.h"
|
#include "xenia/base/assert.h"
|
||||||
|
#include "xenia/base/cvar.h"
|
||||||
#include "xenia/base/logging.h"
|
#include "xenia/base/logging.h"
|
||||||
#include "xenia/base/math.h"
|
#include "xenia/base/math.h"
|
||||||
#include "xenia/base/profiling.h"
|
#include "xenia/base/profiling.h"
|
||||||
@@ -27,6 +28,8 @@
|
|||||||
#include "xenia/ui/vulkan/vulkan_mem_alloc.h"
|
#include "xenia/ui/vulkan/vulkan_mem_alloc.h"
|
||||||
#include "xenia/ui/vulkan/vulkan_util.h"
|
#include "xenia/ui/vulkan/vulkan_util.h"
|
||||||
|
|
||||||
|
DECLARE_bool(tiled_shared_memory);
|
||||||
|
|
||||||
namespace xe {
|
namespace xe {
|
||||||
namespace gpu {
|
namespace gpu {
|
||||||
namespace vulkan {
|
namespace vulkan {
|
||||||
@@ -477,7 +480,10 @@ VulkanTextureCache::~VulkanTextureCache() {
|
|||||||
// textures before destroying VMA.
|
// textures before destroying VMA.
|
||||||
DestroyAllTextures(true);
|
DestroyAllTextures(true);
|
||||||
|
|
||||||
// Clean up scaled resolve buffers before destroying VMA
|
// Clean up sparse scaled resolve resources (heaps and sparse buffers)
|
||||||
|
ShutdownSparseScaledResolve();
|
||||||
|
|
||||||
|
// Clean up simple scaled resolve buffers before destroying VMA
|
||||||
// The command processor should ensure all GPU operations are complete
|
// The command processor should ensure all GPU operations are complete
|
||||||
// before the texture cache is destroyed
|
// before the texture cache is destroyed
|
||||||
for (ScaledResolveBuffer& buffer : scaled_resolve_buffers_) {
|
for (ScaledResolveBuffer& buffer : scaled_resolve_buffers_) {
|
||||||
@@ -1323,15 +1329,8 @@ bool VulkanTextureCache::LoadTextureDataFromResidentMemoryImpl(Texture& texture,
|
|||||||
draw_resolution_scale_x() * draw_resolution_scale_y();
|
draw_resolution_scale_x() * draw_resolution_scale_y();
|
||||||
uint64_t scaled_offset =
|
uint64_t scaled_offset =
|
||||||
uint64_t(guest_address) * draw_resolution_scale_area;
|
uint64_t(guest_address) * draw_resolution_scale_area;
|
||||||
|
uint64_t buffer_relative_offset =
|
||||||
uint64_t buffer_relative_offset = 0;
|
scaled_offset - GetCurrentScaledResolveBufferBaseOffset();
|
||||||
if (scaled_resolve_current_buffer_index_ <
|
|
||||||
scaled_resolve_buffers_.size()) {
|
|
||||||
const ScaledResolveBuffer& current_buffer =
|
|
||||||
scaled_resolve_buffers_[scaled_resolve_current_buffer_index_];
|
|
||||||
buffer_relative_offset =
|
|
||||||
scaled_offset - current_buffer.range_start_scaled;
|
|
||||||
}
|
|
||||||
|
|
||||||
write_descriptor_set_source_base_buffer_info.buffer = scaled_buffer;
|
write_descriptor_set_source_base_buffer_info.buffer = scaled_buffer;
|
||||||
write_descriptor_set_source_base_buffer_info.offset =
|
write_descriptor_set_source_base_buffer_info.offset =
|
||||||
@@ -2668,6 +2667,12 @@ bool VulkanTextureCache::Initialize() {
|
|||||||
max_anisotropy_ = xenos::AnisoFilter::kDisabled;
|
max_anisotropy_ = xenos::AnisoFilter::kDisabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Initialize sparse scaled resolve buffers if draw resolution scaling is
|
||||||
|
// enabled and sparse binding is supported.
|
||||||
|
if (!InitializeSparseScaledResolve()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2743,6 +2748,13 @@ bool VulkanTextureCache::EnsureScaledResolveMemoryCommitted(
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Dispatch to sparse implementation if supported
|
||||||
|
if (sparse_scaled_resolve_supported_) {
|
||||||
|
return EnsureScaledResolveMemoryCommittedSparse(
|
||||||
|
start_unscaled, length_unscaled, length_scaled_alignment_log2);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Simple non-overlapping buffer implementation (fallback)
|
||||||
if (length_unscaled == 0) {
|
if (length_unscaled == 0) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -2859,6 +2871,13 @@ bool VulkanTextureCache::MakeScaledResolveRangeCurrent(
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Dispatch to sparse implementation if supported
|
||||||
|
if (sparse_scaled_resolve_supported_) {
|
||||||
|
return MakeScaledResolveRangeCurrentSparse(start_unscaled, length_unscaled,
|
||||||
|
length_scaled_alignment_log2);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Simple non-overlapping buffer implementation (fallback)
|
||||||
// First ensure the memory is committed (creates buffers if needed)
|
// First ensure the memory is committed (creates buffers if needed)
|
||||||
if (!EnsureScaledResolveMemoryCommitted(start_unscaled, length_unscaled,
|
if (!EnsureScaledResolveMemoryCommitted(start_unscaled, length_unscaled,
|
||||||
length_scaled_alignment_log2)) {
|
length_scaled_alignment_log2)) {
|
||||||
@@ -2893,12 +2912,340 @@ bool VulkanTextureCache::MakeScaledResolveRangeCurrent(
|
|||||||
}
|
}
|
||||||
|
|
||||||
VkBuffer VulkanTextureCache::GetCurrentScaledResolveBuffer() const {
|
VkBuffer VulkanTextureCache::GetCurrentScaledResolveBuffer() const {
|
||||||
|
if (sparse_scaled_resolve_supported_) {
|
||||||
|
if (scaled_resolve_current_buffer_index_ < kMaxScaledResolveSparseBuffers &&
|
||||||
|
scaled_resolve_sparse_buffers_[scaled_resolve_current_buffer_index_]) {
|
||||||
|
return scaled_resolve_sparse_buffers_
|
||||||
|
[scaled_resolve_current_buffer_index_]
|
||||||
|
->buffer();
|
||||||
|
}
|
||||||
|
return VK_NULL_HANDLE;
|
||||||
|
}
|
||||||
if (scaled_resolve_current_buffer_index_ >= scaled_resolve_buffers_.size()) {
|
if (scaled_resolve_current_buffer_index_ >= scaled_resolve_buffers_.size()) {
|
||||||
return VK_NULL_HANDLE;
|
return VK_NULL_HANDLE;
|
||||||
}
|
}
|
||||||
return scaled_resolve_buffers_[scaled_resolve_current_buffer_index_].buffer;
|
return scaled_resolve_buffers_[scaled_resolve_current_buffer_index_].buffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Sparse scaled resolve helper functions
|
||||||
|
|
||||||
|
size_t VulkanTextureCache::GetScaledResolveSparseBufferCount() const {
|
||||||
|
// Each buffer is 2GB, and buffer N covers [N GB .. (N+2) GB)
|
||||||
|
// So for an address space of X bytes, we need ceil((X-1) / 1GB) buffers
|
||||||
|
// (or 0 if X <= 2GB, but we handle that with max)
|
||||||
|
uint64_t address_space = uint64_t(SharedMemory::kBufferSize) *
|
||||||
|
draw_resolution_scale_x() *
|
||||||
|
draw_resolution_scale_y();
|
||||||
|
// Number of buffers needed: ceil((address_space - 1) / 1GB)
|
||||||
|
// For 3x3 (4.5GB): (4.5GB - 1) / 1GB = 3.5 -> 4 buffers
|
||||||
|
// For 2x2 (2GB): (2GB - 1) / 1GB = 0.99 -> 1 buffer
|
||||||
|
// For 1x1 (512MB): (512MB - 1) / 1GB = 0 -> but we need at least 1
|
||||||
|
if (address_space <= kScaledResolveSparseBufferSize) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return size_t((address_space - 1) >> 30);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::array<size_t, 2> VulkanTextureCache::GetPossibleScaledResolveBufferIndices(
|
||||||
|
uint64_t address_scaled) const {
|
||||||
|
// Given an address, find which buffers could contain it.
|
||||||
|
// Buffer N covers [N GB .. (N+2) GB), so address A could be in:
|
||||||
|
// - Buffer floor(A/1GB) if it exists (address is in [N GB .. (N+1) GB) part)
|
||||||
|
// - Buffer floor(A/1GB) - 1 if it exists (address is in [(N-1)+1 GB .. N+1
|
||||||
|
// GB) part)
|
||||||
|
size_t gb_index = size_t(address_scaled >> 30);
|
||||||
|
size_t buffer_count = GetScaledResolveSparseBufferCount();
|
||||||
|
size_t max_buffer_index = buffer_count > 0 ? buffer_count - 1 : 0;
|
||||||
|
|
||||||
|
// First possible buffer: the one starting at this GB
|
||||||
|
size_t buffer_a = std::min(gb_index, max_buffer_index);
|
||||||
|
// Second possible buffer: the one starting 1GB earlier (if it exists)
|
||||||
|
size_t buffer_b = gb_index > 0 ? std::min(gb_index - 1, max_buffer_index)
|
||||||
|
: max_buffer_index + 1; // Invalid
|
||||||
|
|
||||||
|
return {buffer_a, buffer_b};
|
||||||
|
}
|
||||||
|
|
||||||
|
bool VulkanTextureCache::InitializeSparseScaledResolve() {
|
||||||
|
if (!IsDrawResolutionScaled()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!cvars::tiled_shared_memory) {
|
||||||
|
XELOGI("VulkanTextureCache: Sparse scaled resolve disabled by CVAR");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
const ui::vulkan::VulkanDevice* vulkan_device =
|
||||||
|
command_processor_.GetVulkanDevice();
|
||||||
|
const ui::vulkan::VulkanDevice::Properties& device_properties =
|
||||||
|
vulkan_device->properties();
|
||||||
|
|
||||||
|
if (!device_properties.sparseBinding ||
|
||||||
|
!device_properties.sparseResidencyBuffer) {
|
||||||
|
XELOGI(
|
||||||
|
"VulkanTextureCache: Sparse binding not supported, using simple "
|
||||||
|
"buffers");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
const ui::vulkan::VulkanDevice::Functions& dfn = vulkan_device->functions();
|
||||||
|
VkDevice device = vulkan_device->device();
|
||||||
|
|
||||||
|
size_t buffer_count = GetScaledResolveSparseBufferCount();
|
||||||
|
XELOGI(
|
||||||
|
"VulkanTextureCache: Creating {} sparse scaled resolve buffers (2GB "
|
||||||
|
"each)",
|
||||||
|
buffer_count);
|
||||||
|
|
||||||
|
// Create the sparse buffers
|
||||||
|
for (size_t i = 0; i < buffer_count; ++i) {
|
||||||
|
VkBufferCreateInfo buffer_create_info = {};
|
||||||
|
buffer_create_info.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
|
||||||
|
buffer_create_info.flags = VK_BUFFER_CREATE_SPARSE_BINDING_BIT |
|
||||||
|
VK_BUFFER_CREATE_SPARSE_RESIDENCY_BIT;
|
||||||
|
buffer_create_info.size = kScaledResolveSparseBufferSize;
|
||||||
|
buffer_create_info.usage = VK_BUFFER_USAGE_STORAGE_BUFFER_BIT;
|
||||||
|
buffer_create_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
|
||||||
|
|
||||||
|
VkBuffer buffer;
|
||||||
|
VkResult result =
|
||||||
|
dfn.vkCreateBuffer(device, &buffer_create_info, nullptr, &buffer);
|
||||||
|
if (result != VK_SUCCESS) {
|
||||||
|
XELOGE("VulkanTextureCache: Failed to create sparse buffer {}: error {}",
|
||||||
|
i, static_cast<int>(result));
|
||||||
|
ShutdownSparseScaledResolve();
|
||||||
|
return true; // Fall back to simple buffers
|
||||||
|
}
|
||||||
|
|
||||||
|
scaled_resolve_sparse_buffers_[i] =
|
||||||
|
std::make_unique<ScaledResolveSparseBuffer>(buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get memory requirements for the sparse buffers
|
||||||
|
VkMemoryRequirements memory_requirements;
|
||||||
|
dfn.vkGetBufferMemoryRequirements(device,
|
||||||
|
scaled_resolve_sparse_buffers_[0]->buffer(),
|
||||||
|
&memory_requirements);
|
||||||
|
|
||||||
|
// Find a suitable device-local memory type
|
||||||
|
if (!xe::bit_scan_forward(memory_requirements.memoryTypeBits &
|
||||||
|
vulkan_device->memory_types().device_local,
|
||||||
|
&scaled_resolve_memory_type_)) {
|
||||||
|
XELOGE(
|
||||||
|
"VulkanTextureCache: Failed to find suitable memory type for sparse "
|
||||||
|
"buffers");
|
||||||
|
ShutdownSparseScaledResolve();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculate the number of heaps we might need
|
||||||
|
uint64_t address_space = uint64_t(SharedMemory::kBufferSize) *
|
||||||
|
draw_resolution_scale_x() *
|
||||||
|
draw_resolution_scale_y();
|
||||||
|
uint32_t max_heap_count =
|
||||||
|
uint32_t((address_space + kScaledResolveHeapSize - 1) >>
|
||||||
|
kScaledResolveHeapSizeLog2);
|
||||||
|
scaled_resolve_heaps_.resize(max_heap_count, VK_NULL_HANDLE);
|
||||||
|
scaled_resolve_heap_count_ = 0;
|
||||||
|
|
||||||
|
sparse_scaled_resolve_supported_ = true;
|
||||||
|
XELOGI(
|
||||||
|
"VulkanTextureCache: Sparse scaled resolve initialized with {} buffers, "
|
||||||
|
"max {} heaps",
|
||||||
|
buffer_count, max_heap_count);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void VulkanTextureCache::ShutdownSparseScaledResolve() {
|
||||||
|
const ui::vulkan::VulkanDevice* vulkan_device =
|
||||||
|
command_processor_.GetVulkanDevice();
|
||||||
|
const ui::vulkan::VulkanDevice::Functions& dfn = vulkan_device->functions();
|
||||||
|
VkDevice device = vulkan_device->device();
|
||||||
|
|
||||||
|
// Free all heaps
|
||||||
|
for (VkDeviceMemory heap : scaled_resolve_heaps_) {
|
||||||
|
if (heap != VK_NULL_HANDLE) {
|
||||||
|
dfn.vkFreeMemory(device, heap, nullptr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
scaled_resolve_heaps_.clear();
|
||||||
|
scaled_resolve_heap_count_ = 0;
|
||||||
|
|
||||||
|
// Destroy sparse buffers
|
||||||
|
for (auto& buffer : scaled_resolve_sparse_buffers_) {
|
||||||
|
if (buffer) {
|
||||||
|
dfn.vkDestroyBuffer(device, buffer->buffer(), nullptr);
|
||||||
|
buffer.reset();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
scaled_resolve_memory_type_ = UINT32_MAX;
|
||||||
|
sparse_scaled_resolve_supported_ = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void VulkanTextureCache::BindHeapToOverlappingBuffers(uint32_t heap_index,
|
||||||
|
VkDeviceMemory heap) {
|
||||||
|
// Calculate the address this heap covers
|
||||||
|
uint64_t heap_address = uint64_t(heap_index) << kScaledResolveHeapSizeLog2;
|
||||||
|
|
||||||
|
// Find which buffers this heap should be bound to
|
||||||
|
auto [buffer_a, buffer_b] =
|
||||||
|
GetPossibleScaledResolveBufferIndices(heap_address);
|
||||||
|
|
||||||
|
size_t buffer_count = GetScaledResolveSparseBufferCount();
|
||||||
|
|
||||||
|
// Bind to the first possible buffer
|
||||||
|
if (buffer_a < buffer_count && scaled_resolve_sparse_buffers_[buffer_a]) {
|
||||||
|
uint64_t offset_in_buffer =
|
||||||
|
heap_address - (uint64_t(buffer_a) << 30); // buffer_a * 1GB
|
||||||
|
|
||||||
|
VkSparseMemoryBind bind = {};
|
||||||
|
bind.resourceOffset = offset_in_buffer;
|
||||||
|
bind.size = kScaledResolveHeapSize;
|
||||||
|
bind.memory = heap;
|
||||||
|
bind.memoryOffset = 0;
|
||||||
|
bind.flags = 0;
|
||||||
|
|
||||||
|
command_processor_.SparseBindBuffer(
|
||||||
|
scaled_resolve_sparse_buffers_[buffer_a]->buffer(), 1, &bind,
|
||||||
|
VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bind to the second possible buffer (if different and valid)
|
||||||
|
if (buffer_b < buffer_count && buffer_b != buffer_a &&
|
||||||
|
scaled_resolve_sparse_buffers_[buffer_b]) {
|
||||||
|
uint64_t offset_in_buffer =
|
||||||
|
heap_address - (uint64_t(buffer_b) << 30); // buffer_b * 1GB
|
||||||
|
|
||||||
|
VkSparseMemoryBind bind = {};
|
||||||
|
bind.resourceOffset = offset_in_buffer;
|
||||||
|
bind.size = kScaledResolveHeapSize;
|
||||||
|
bind.memory = heap;
|
||||||
|
bind.memoryOffset = 0;
|
||||||
|
bind.flags = 0;
|
||||||
|
|
||||||
|
command_processor_.SparseBindBuffer(
|
||||||
|
scaled_resolve_sparse_buffers_[buffer_b]->buffer(), 1, &bind,
|
||||||
|
VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool VulkanTextureCache::EnsureScaledResolveMemoryCommittedSparse(
|
||||||
|
uint32_t start_unscaled, uint32_t length_unscaled,
|
||||||
|
uint32_t length_scaled_alignment_log2) {
|
||||||
|
if (length_unscaled == 0) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (start_unscaled > SharedMemory::kBufferSize ||
|
||||||
|
(SharedMemory::kBufferSize - start_unscaled) < length_unscaled) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t draw_resolution_scale_area =
|
||||||
|
draw_resolution_scale_x() * draw_resolution_scale_y();
|
||||||
|
uint64_t start_scaled = uint64_t(start_unscaled) * draw_resolution_scale_area;
|
||||||
|
uint64_t length_scaled_alignment_bits =
|
||||||
|
(UINT64_C(1) << length_scaled_alignment_log2) - 1;
|
||||||
|
uint64_t length_scaled =
|
||||||
|
(uint64_t(length_unscaled) * draw_resolution_scale_area +
|
||||||
|
length_scaled_alignment_bits) &
|
||||||
|
~length_scaled_alignment_bits;
|
||||||
|
uint64_t end_scaled = start_scaled + length_scaled;
|
||||||
|
|
||||||
|
// Calculate which heaps we need
|
||||||
|
uint32_t heap_first = uint32_t(start_scaled >> kScaledResolveHeapSizeLog2);
|
||||||
|
uint32_t heap_last = uint32_t((end_scaled - 1) >> kScaledResolveHeapSizeLog2);
|
||||||
|
|
||||||
|
const ui::vulkan::VulkanDevice* vulkan_device =
|
||||||
|
command_processor_.GetVulkanDevice();
|
||||||
|
const ui::vulkan::VulkanDevice::Functions& dfn = vulkan_device->functions();
|
||||||
|
VkDevice device = vulkan_device->device();
|
||||||
|
|
||||||
|
// Allocate and bind any heaps we need
|
||||||
|
for (uint32_t heap_index = heap_first; heap_index <= heap_last;
|
||||||
|
++heap_index) {
|
||||||
|
if (heap_index >= scaled_resolve_heaps_.size()) {
|
||||||
|
XELOGE("VulkanTextureCache: Heap index {} out of range (max {})",
|
||||||
|
heap_index, scaled_resolve_heaps_.size());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (scaled_resolve_heaps_[heap_index] == VK_NULL_HANDLE) {
|
||||||
|
// Allocate a new heap
|
||||||
|
VkMemoryAllocateInfo allocate_info = {};
|
||||||
|
allocate_info.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
|
||||||
|
allocate_info.allocationSize = kScaledResolveHeapSize;
|
||||||
|
allocate_info.memoryTypeIndex = scaled_resolve_memory_type_;
|
||||||
|
|
||||||
|
VkDeviceMemory heap;
|
||||||
|
VkResult result =
|
||||||
|
dfn.vkAllocateMemory(device, &allocate_info, nullptr, &heap);
|
||||||
|
if (result != VK_SUCCESS) {
|
||||||
|
XELOGE("VulkanTextureCache: Failed to allocate heap {}: error {}",
|
||||||
|
heap_index, static_cast<int>(result));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
scaled_resolve_heaps_[heap_index] = heap;
|
||||||
|
++scaled_resolve_heap_count_;
|
||||||
|
|
||||||
|
// Bind this heap to all overlapping buffers
|
||||||
|
BindHeapToOverlappingBuffers(heap_index, heap);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool VulkanTextureCache::MakeScaledResolveRangeCurrentSparse(
|
||||||
|
uint32_t start_unscaled, uint32_t length_unscaled,
|
||||||
|
uint32_t length_scaled_alignment_log2) {
|
||||||
|
// First ensure the memory is committed
|
||||||
|
if (!EnsureScaledResolveMemoryCommittedSparse(start_unscaled, length_unscaled,
|
||||||
|
length_scaled_alignment_log2)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t draw_resolution_scale_area =
|
||||||
|
draw_resolution_scale_x() * draw_resolution_scale_y();
|
||||||
|
uint64_t start_scaled = uint64_t(start_unscaled) * draw_resolution_scale_area;
|
||||||
|
uint64_t length_scaled_alignment_bits =
|
||||||
|
(UINT64_C(1) << length_scaled_alignment_log2) - 1;
|
||||||
|
uint64_t length_scaled =
|
||||||
|
(uint64_t(length_unscaled) * draw_resolution_scale_area +
|
||||||
|
length_scaled_alignment_bits) &
|
||||||
|
~length_scaled_alignment_bits;
|
||||||
|
uint64_t end_scaled = start_scaled + length_scaled;
|
||||||
|
|
||||||
|
// Find a buffer that contains the entire range
|
||||||
|
// Each buffer is 2GB and covers [buffer_index GB .. (buffer_index + 2) GB)
|
||||||
|
auto [buffer_a, buffer_b] =
|
||||||
|
GetPossibleScaledResolveBufferIndices(start_scaled);
|
||||||
|
auto [end_buffer_a, end_buffer_b] = GetPossibleScaledResolveBufferIndices(
|
||||||
|
end_scaled > 0 ? end_scaled - 1 : 0);
|
||||||
|
|
||||||
|
// Check if buffer_a can contain both start and end
|
||||||
|
size_t chosen_buffer = SIZE_MAX;
|
||||||
|
if (buffer_a == end_buffer_a || buffer_a == end_buffer_b) {
|
||||||
|
chosen_buffer = buffer_a;
|
||||||
|
} else if (buffer_b == end_buffer_a || buffer_b == end_buffer_b) {
|
||||||
|
chosen_buffer = buffer_b;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (chosen_buffer == SIZE_MAX ||
|
||||||
|
chosen_buffer >= GetScaledResolveSparseBufferCount()) {
|
||||||
|
XELOGE("VulkanTextureCache: No buffer can contain range [{:X}, {:X})",
|
||||||
|
start_scaled, end_scaled);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
scaled_resolve_current_buffer_index_ = chosen_buffer;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace vulkan
|
} // namespace vulkan
|
||||||
} // namespace gpu
|
} // namespace gpu
|
||||||
} // namespace xe
|
} // namespace xe
|
||||||
|
|||||||
@@ -122,6 +122,7 @@ class VulkanTextureCache final : public TextureCache {
|
|||||||
xenos::TextureFormat& format_out);
|
xenos::TextureFormat& format_out);
|
||||||
|
|
||||||
// Scaled resolve buffer management (for use by VulkanRenderTargetCache)
|
// Scaled resolve buffer management (for use by VulkanRenderTargetCache)
|
||||||
|
// Simple non-overlapping buffer (fallback when sparse binding unavailable)
|
||||||
struct ScaledResolveBuffer {
|
struct ScaledResolveBuffer {
|
||||||
VkBuffer buffer = VK_NULL_HANDLE;
|
VkBuffer buffer = VK_NULL_HANDLE;
|
||||||
VmaAllocation allocation = VK_NULL_HANDLE;
|
VmaAllocation allocation = VK_NULL_HANDLE;
|
||||||
@@ -130,6 +131,24 @@ class VulkanTextureCache final : public TextureCache {
|
|||||||
uint64_t range_length_scaled = 0;
|
uint64_t range_length_scaled = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Sparse buffer wrapper for overlapping 2GB windows
|
||||||
|
class ScaledResolveSparseBuffer {
|
||||||
|
public:
|
||||||
|
explicit ScaledResolveSparseBuffer(VkBuffer buffer) : buffer_(buffer) {}
|
||||||
|
|
||||||
|
VkBuffer buffer() const { return buffer_; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
VkBuffer buffer_ = VK_NULL_HANDLE;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Constants for sparse scaled resolve
|
||||||
|
static constexpr uint32_t kScaledResolveHeapSizeLog2 = 24; // 16MB heaps
|
||||||
|
static constexpr uint32_t kScaledResolveHeapSize =
|
||||||
|
uint32_t(1) << kScaledResolveHeapSizeLog2;
|
||||||
|
static constexpr uint64_t kScaledResolveSparseBufferSize =
|
||||||
|
uint64_t(2) << 30; // 2GB per buffer
|
||||||
|
|
||||||
// Public scaled resolve buffer methods for use by VulkanRenderTargetCache
|
// Public scaled resolve buffer methods for use by VulkanRenderTargetCache
|
||||||
bool EnsureScaledResolveMemoryCommittedPublic(
|
bool EnsureScaledResolveMemoryCommittedPublic(
|
||||||
uint32_t start_unscaled, uint32_t length_unscaled,
|
uint32_t start_unscaled, uint32_t length_unscaled,
|
||||||
@@ -144,6 +163,20 @@ class VulkanTextureCache final : public TextureCache {
|
|||||||
|
|
||||||
VkBuffer GetCurrentScaledResolveBuffer() const;
|
VkBuffer GetCurrentScaledResolveBuffer() const;
|
||||||
|
|
||||||
|
// Returns the base scaled address that the current buffer starts at.
|
||||||
|
// For sparse buffers: buffer N starts at N GB (N << 30)
|
||||||
|
// For simple buffers: returns the buffer's range_start_scaled
|
||||||
|
uint64_t GetCurrentScaledResolveBufferBaseOffset() const {
|
||||||
|
if (sparse_scaled_resolve_supported_) {
|
||||||
|
return uint64_t(scaled_resolve_current_buffer_index_) << 30;
|
||||||
|
}
|
||||||
|
if (scaled_resolve_current_buffer_index_ < scaled_resolve_buffers_.size()) {
|
||||||
|
return scaled_resolve_buffers_[scaled_resolve_current_buffer_index_]
|
||||||
|
.range_start_scaled;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
size_t GetScaledResolveCurrentBufferIndex() const {
|
size_t GetScaledResolveCurrentBufferIndex() const {
|
||||||
return scaled_resolve_current_buffer_index_;
|
return scaled_resolve_current_buffer_index_;
|
||||||
}
|
}
|
||||||
@@ -352,6 +385,20 @@ class VulkanTextureCache final : public TextureCache {
|
|||||||
|
|
||||||
xenos::ClampMode NormalizeClampMode(xenos::ClampMode clamp_mode) const;
|
xenos::ClampMode NormalizeClampMode(xenos::ClampMode clamp_mode) const;
|
||||||
|
|
||||||
|
// Sparse scaled resolve helper functions
|
||||||
|
bool InitializeSparseScaledResolve();
|
||||||
|
void ShutdownSparseScaledResolve();
|
||||||
|
size_t GetScaledResolveSparseBufferCount() const;
|
||||||
|
std::array<size_t, 2> GetPossibleScaledResolveBufferIndices(
|
||||||
|
uint64_t address_scaled) const;
|
||||||
|
bool EnsureScaledResolveMemoryCommittedSparse(
|
||||||
|
uint32_t start_unscaled, uint32_t length_unscaled,
|
||||||
|
uint32_t length_scaled_alignment_log2);
|
||||||
|
bool MakeScaledResolveRangeCurrentSparse(
|
||||||
|
uint32_t start_unscaled, uint32_t length_unscaled,
|
||||||
|
uint32_t length_scaled_alignment_log2);
|
||||||
|
void BindHeapToOverlappingBuffers(uint32_t heap_index, VkDeviceMemory heap);
|
||||||
|
|
||||||
VulkanCommandProcessor& command_processor_;
|
VulkanCommandProcessor& command_processor_;
|
||||||
VkPipelineStageFlags guest_shader_pipeline_stages_;
|
VkPipelineStageFlags guest_shader_pipeline_stages_;
|
||||||
|
|
||||||
@@ -392,12 +439,31 @@ class VulkanTextureCache final : public TextureCache {
|
|||||||
std::pair<const SamplerParameters, Sampler>* sampler_used_first_ = nullptr;
|
std::pair<const SamplerParameters, Sampler>* sampler_used_first_ = nullptr;
|
||||||
std::pair<const SamplerParameters, Sampler>* sampler_used_last_ = nullptr;
|
std::pair<const SamplerParameters, Sampler>* sampler_used_last_ = nullptr;
|
||||||
|
|
||||||
// Scaled resolve buffer storage
|
// Scaled resolve buffer storage (simple non-overlapping, fallback path)
|
||||||
std::vector<ScaledResolveBuffer> scaled_resolve_buffers_;
|
std::vector<ScaledResolveBuffer> scaled_resolve_buffers_;
|
||||||
// Current scaled resolve range tracking
|
// Current scaled resolve range tracking
|
||||||
uint64_t scaled_resolve_current_range_start_scaled_ = 0;
|
uint64_t scaled_resolve_current_range_start_scaled_ = 0;
|
||||||
uint64_t scaled_resolve_current_range_length_scaled_ = 0;
|
uint64_t scaled_resolve_current_range_length_scaled_ = 0;
|
||||||
size_t scaled_resolve_current_buffer_index_ = SIZE_MAX;
|
size_t scaled_resolve_current_buffer_index_ = SIZE_MAX;
|
||||||
|
|
||||||
|
// Sparse scaled resolve (overlapping 2GB windows)
|
||||||
|
bool sparse_scaled_resolve_supported_ = false;
|
||||||
|
// 2GB overlapping sparse buffers - buffer N covers [N GB ... (N+2) GB)
|
||||||
|
// For 3x3 scale (4.5GB), need 4 buffers: 0:[0-2GB), 1:[1-3GB), 2:[2-4GB),
|
||||||
|
// 3:[3-4.5GB)
|
||||||
|
static constexpr size_t kMaxScaledResolveSparseBuffers =
|
||||||
|
(uint64_t(SharedMemory::kBufferSize) * kMaxDrawResolutionScaleAlongAxis *
|
||||||
|
kMaxDrawResolutionScaleAlongAxis -
|
||||||
|
1) >>
|
||||||
|
30;
|
||||||
|
std::array<std::unique_ptr<ScaledResolveSparseBuffer>,
|
||||||
|
kMaxScaledResolveSparseBuffers>
|
||||||
|
scaled_resolve_sparse_buffers_;
|
||||||
|
// 16MB heaps that can be mapped to multiple buffer regions
|
||||||
|
std::vector<VkDeviceMemory> scaled_resolve_heaps_;
|
||||||
|
uint32_t scaled_resolve_heap_count_ = 0;
|
||||||
|
// Memory type for sparse allocations
|
||||||
|
uint32_t scaled_resolve_memory_type_ = UINT32_MAX;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace vulkan
|
} // namespace vulkan
|
||||||
|
|||||||
Reference in New Issue
Block a user