[GPU] Load guest-resolved mips from scaled resolve memory
Mips were always regenerated by blitting down from base, even when guest resolved real data into scaled memory. The upload footprint is the guest mip reduced then scaled while the subresource is the base scaled then reduced, so the deepest mips of scaled textures can disagree by a row or column per axis. Compressed formats round up to the block and absorb it, uncompressed copies now clamp per axis so they never overrun the host image. Matches D3D12 fix for the same problem. Co-authored-by: Herman S. <429230+has207@users.noreply.github.com>
This commit is contained in:
committed by
Radosław Gliński
parent
7101021150
commit
f25003c0ea
@@ -1732,7 +1732,36 @@ bool D3D12TextureCache::LoadTextureDataFromResidentMemoryImpl(Texture& texture,
|
||||
source_box.front + std::max(depth >> level, uint32_t(1));
|
||||
source_box_ptr = &source_box;
|
||||
} else {
|
||||
// Non-packed level: copy the whole footprint. The footprint is sized as
|
||||
// the guest mip reduced then scaled, while the host mip subresource is
|
||||
// the base scaled then reduced, so for the deepest mips of scaled
|
||||
// textures the footprint can be a row or column larger. Compressed dests
|
||||
// round up to the block and absorb it. For uncompressed dests clamp the
|
||||
// copy to the exact subresource with an explicit source box to avoid
|
||||
// overrunning. Clamp per axis to min(footprint, subresource) since a
|
||||
// non-power-of-two axis can be smaller than the subresource while another
|
||||
// axis is larger, and the box must not exceed the source footprint
|
||||
// either.
|
||||
source_box_ptr = nullptr;
|
||||
if (!host_block_compressed) {
|
||||
const D3D12_SUBRESOURCE_FOOTPRINT& footprint =
|
||||
location_source.PlacedFootprint.Footprint;
|
||||
uint32_t dst_width = std::max(
|
||||
(width * texture_resolution_scale_x) >> level, uint32_t(1));
|
||||
uint32_t dst_height = std::max(
|
||||
(height * texture_resolution_scale_y) >> level, uint32_t(1));
|
||||
uint32_t dst_depth = std::max(depth >> level, uint32_t(1));
|
||||
if (footprint.Width > dst_width || footprint.Height > dst_height ||
|
||||
footprint.Depth > dst_depth) {
|
||||
source_box.left = 0;
|
||||
source_box.top = 0;
|
||||
source_box.front = 0;
|
||||
source_box.right = std::min(footprint.Width, dst_width);
|
||||
source_box.bottom = std::min(footprint.Height, dst_height);
|
||||
source_box.back = std::min(footprint.Depth, dst_depth);
|
||||
source_box_ptr = &source_box;
|
||||
}
|
||||
}
|
||||
}
|
||||
for (uint32_t slice = 0; slice < array_size; ++slice) {
|
||||
command_list.D3DCopyTextureRegion(&location_dest, 0, 0, 0,
|
||||
|
||||
@@ -638,6 +638,19 @@ class TextureCache {
|
||||
// implementation to update the internal dependencies of the binding.
|
||||
virtual void UpdateTextureBindingsImpl(uint32_t fetch_constant_mask) {}
|
||||
|
||||
// Checks if there are any pages that contain scaled resolve data within the
|
||||
// range.
|
||||
bool IsRangeScaledResolved(uint32_t start_unscaled, uint32_t length_unscaled);
|
||||
|
||||
// Whether the mips of a scaled resolve texture must be generated on the host
|
||||
// (the guest did not resolve them into scaled memory itself).
|
||||
bool ScaledResolveMipsNeedGeneration(const Texture& texture) {
|
||||
const TextureKey& key = texture.key();
|
||||
return key.scaled_resolve && key.mip_max_level != 0 &&
|
||||
!IsRangeScaledResolved(key.mip_page << 12,
|
||||
texture.GetGuestMipsSize());
|
||||
}
|
||||
|
||||
private:
|
||||
void UpdateTexturesTotalHostMemoryUsage(uint64_t add, uint64_t subtract);
|
||||
|
||||
@@ -646,9 +659,6 @@ class TextureCache {
|
||||
void* context, void* data, uint64_t argument,
|
||||
bool invalidated_by_gpu);
|
||||
|
||||
// Checks if there are any pages that contain scaled resolve data within the
|
||||
// range.
|
||||
bool IsRangeScaledResolved(uint32_t start_unscaled, uint32_t length_unscaled);
|
||||
// Global shared memory invalidation callback for invalidating scaled resolved
|
||||
// texture data.
|
||||
static void ScaledResolveGlobalWatchCallbackThunk(
|
||||
|
||||
@@ -1233,10 +1233,9 @@ bool VulkanTextureCache::LoadTextureDataFromResidentMemoryImpl(Texture& texture,
|
||||
uint32_t bytes_per_block = guest_format_info->bytes_per_block();
|
||||
uint32_t level_first = load_base ? 0 : 1;
|
||||
uint32_t level_last = load_mips ? texture_key.mip_max_level : 0;
|
||||
// For scaled resolve textures, we only load level 0 from the scaled buffer -
|
||||
// mips will be generated via blit.
|
||||
// Load the guest's resolved mips from the scaled buffer, else generate them.
|
||||
uint32_t level_last_for_blit_gen = 0;
|
||||
if (texture_key.scaled_resolve && level_last > 0) {
|
||||
if (level_last > 0 && ScaledResolveMipsNeedGeneration(texture)) {
|
||||
level_last_for_blit_gen = level_last;
|
||||
level_last = 0; // Only load base level from buffer
|
||||
}
|
||||
@@ -1755,10 +1754,17 @@ bool VulkanTextureCache::LoadTextureDataFromResidentMemoryImpl(Texture& texture,
|
||||
copy_region.imageOffset.x = 0;
|
||||
copy_region.imageOffset.y = 0;
|
||||
copy_region.imageOffset.z = 0;
|
||||
copy_region.imageExtent.width =
|
||||
std::max((width * texture_resolution_scale_x) >> level, UINT32_C(1));
|
||||
copy_region.imageExtent.height =
|
||||
std::max((height * texture_resolution_scale_y) >> level, UINT32_C(1));
|
||||
// The image mip is scale-then-reduce (max((dim*scale)>>level,1)) while the
|
||||
// buffer footprint (bufferRowLength/bufferImageHeight) is
|
||||
// reduce-then-scale, so for the deepest mips of scaled textures the mip can
|
||||
// be a row or column larger than the buffer holds. Clamp the copy extent to
|
||||
// the footprint so the GPU never reads past the buffer.
|
||||
copy_region.imageExtent.width = std::min(
|
||||
std::max((width * texture_resolution_scale_x) >> level, UINT32_C(1)),
|
||||
copy_region.bufferRowLength);
|
||||
copy_region.imageExtent.height = std::min(
|
||||
std::max((height * texture_resolution_scale_y) >> level, UINT32_C(1)),
|
||||
copy_region.bufferImageHeight);
|
||||
copy_region.imageExtent.depth = std::max(depth >> level, UINT32_C(1));
|
||||
}
|
||||
|
||||
@@ -2054,6 +2060,12 @@ VkImageView VulkanTextureCache::VulkanTexture::GetOrCreate3DAs2DImageView(
|
||||
image_create_info.format = format;
|
||||
image_create_info.extent.width = key().GetWidth();
|
||||
image_create_info.extent.height = key().GetHeight();
|
||||
if (key().scaled_resolve) {
|
||||
image_create_info.extent.width *=
|
||||
vulkan_texture_cache.draw_resolution_scale_x();
|
||||
image_create_info.extent.height *=
|
||||
vulkan_texture_cache.draw_resolution_scale_y();
|
||||
}
|
||||
image_create_info.extent.depth = 1;
|
||||
image_create_info.mipLevels = 1;
|
||||
image_create_info.arrayLayers = 1;
|
||||
|
||||
Reference in New Issue
Block a user