From aa3eeaa4171e7ca26febfe44357af0fc54d428cc Mon Sep 17 00:00:00 2001 From: kd-11 Date: Mon, 28 Oct 2019 21:06:15 +0300 Subject: [PATCH] rsx: Separate subresource_layout:dim_in_block and subresource_layout::dim_in_texel - These two are not always linked when working with compressed textures. The actual texels extend past the actual size of the image if the size is not aligned. e.g if height is 1, the real height is 4, but its not possible to determine this from the aligned size. It could be 1, 2, 3 or 4 for example. - Fixes image out-of-bounds writes when uploading from CPU --- rpcs3/Emu/RSX/Common/TextureUtils.cpp | 45 +++++++++++++++++---------- rpcs3/Emu/RSX/Common/TextureUtils.h | 4 +++ rpcs3/Emu/RSX/Common/texture_cache.h | 8 ++--- rpcs3/Emu/RSX/VK/VKRenderTargets.h | 4 +-- rpcs3/Emu/RSX/VK/VKTexture.cpp | 11 +++---- rpcs3/Emu/RSX/rsx_utils.h | 7 +++++ 6 files changed, 49 insertions(+), 30 deletions(-) diff --git a/rpcs3/Emu/RSX/Common/TextureUtils.cpp b/rpcs3/Emu/RSX/Common/TextureUtils.cpp index 382d762a52..f8bb47bc70 100644 --- a/rpcs3/Emu/RSX/Common/TextureUtils.cpp +++ b/rpcs3/Emu/RSX/Common/TextureUtils.cpp @@ -338,10 +338,6 @@ namespace std::vector result; size_t offset_in_src = 0; - // Always lower than width/height so fits in u16 - u16 texture_height_in_block = (height_in_texel + block_edge_in_texel - 1) / block_edge_in_texel; - u16 texture_width_in_block = (width_in_texel + block_edge_in_texel - 1) / block_edge_in_texel; - u8 border_size = border ? (padded_row ? 1 : 4) : 0; u32 src_pitch_in_block; u32 full_height_in_block; @@ -349,43 +345,58 @@ namespace for (unsigned layer = 0; layer < layer_count; layer++) { - u16 miplevel_height_in_block = texture_height_in_block, miplevel_width_in_block = texture_width_in_block; + u16 miplevel_width_in_texel = width_in_texel, miplevel_height_in_texel = height_in_texel; for (unsigned mip_level = 0; mip_level < mipmap_count; mip_level++) { - rsx_subresource_layout current_subresource_layout = {}; - // Since <= width/height, fits on 16 bits - current_subresource_layout.height_in_block = miplevel_height_in_block; - current_subresource_layout.width_in_block = miplevel_width_in_block; + result.push_back({}); + rsx_subresource_layout& current_subresource_layout = result.back(); + + current_subresource_layout.width_in_texel = miplevel_width_in_texel; + current_subresource_layout.height_in_texel = miplevel_height_in_texel; + current_subresource_layout.level = mip_level; + current_subresource_layout.layer = layer; current_subresource_layout.depth = depth; current_subresource_layout.border = border_size; + if constexpr (block_edge_in_texel == 1) + { + current_subresource_layout.width_in_block = miplevel_width_in_texel; + current_subresource_layout.height_in_block = miplevel_height_in_texel; + } + else + { + current_subresource_layout.width_in_block = rsx::aligned_div(miplevel_width_in_texel, block_edge_in_texel); + current_subresource_layout.height_in_block = rsx::aligned_div(miplevel_height_in_texel, block_edge_in_texel); + } + if (padded_row) { src_pitch_in_block = suggested_pitch_in_bytes / block_size_in_bytes; - full_height_in_block = miplevel_height_in_block + border_size + border_size; + full_height_in_block = current_subresource_layout.height_in_block + (border_size + border_size); } else if (!border) { - src_pitch_in_block = miplevel_width_in_block + border_size + border_size; - full_height_in_block = miplevel_height_in_block; + src_pitch_in_block = current_subresource_layout.width_in_block; + full_height_in_block = current_subresource_layout.height_in_block; } else { - src_pitch_in_block = rsx::next_pow2(miplevel_width_in_block + border_size + border_size); - full_height_in_block = rsx::next_pow2(miplevel_height_in_block + border_size + border_size); + src_pitch_in_block = rsx::next_pow2(current_subresource_layout.width_in_block + border_size + border_size); + full_height_in_block = rsx::next_pow2(current_subresource_layout.height_in_block + border_size + border_size); } slice_sz = src_pitch_in_block * block_size_in_bytes * full_height_in_block * depth; current_subresource_layout.pitch_in_block = src_pitch_in_block; current_subresource_layout.data = gsl::span(texture_data_pointer + offset_in_src, slice_sz); - result.push_back(current_subresource_layout); offset_in_src += slice_sz; - miplevel_height_in_block = std::max(miplevel_height_in_block / 2, 1); - miplevel_width_in_block = std::max(miplevel_width_in_block / 2, 1); + miplevel_width_in_texel = std::max(miplevel_width_in_texel / 2, 1); + miplevel_height_in_texel = std::max(miplevel_height_in_texel / 2, 1); } + offset_in_src = align(offset_in_src, 128); } + return result; } } diff --git a/rpcs3/Emu/RSX/Common/TextureUtils.h b/rpcs3/Emu/RSX/Common/TextureUtils.h index aeca9b0c86..0f59fe73d0 100644 --- a/rpcs3/Emu/RSX/Common/TextureUtils.h +++ b/rpcs3/Emu/RSX/Common/TextureUtils.h @@ -89,9 +89,13 @@ namespace rsx struct rsx_subresource_layout { gsl::span data; + u16 width_in_texel; + u16 height_in_texel; u16 width_in_block; u16 height_in_block; u16 depth; + u16 level; + u16 layer; u8 border; u8 reserved; u32 pitch_in_block; diff --git a/rpcs3/Emu/RSX/Common/texture_cache.h b/rpcs3/Emu/RSX/Common/texture_cache.h index 988082522c..86a3f050a1 100644 --- a/rpcs3/Emu/RSX/Common/texture_cache.h +++ b/rpcs3/Emu/RSX/Common/texture_cache.h @@ -2401,8 +2401,8 @@ namespace rsx std::vector subresource_layout; rsx_subresource_layout subres = {}; - subres.width_in_block = image_width; - subres.height_in_block = image_height; + subres.width_in_block = subres.width_in_texel = image_width; + subres.height_in_block = subres.height_in_texel = image_height; subres.pitch_in_block = full_width; subres.depth = 1; subres.data = { vm::_ptr(image_base), src.pitch * image_height }; @@ -2533,8 +2533,8 @@ namespace rsx const u16 pitch_in_block = dst.pitch / dst_bpp; std::vector subresource_layout; rsx_subresource_layout subres = {}; - subres.width_in_block = dst_dimensions.width; - subres.height_in_block = dst_dimensions.height; + subres.width_in_block = subres.width_in_texel = dst_dimensions.width; + subres.height_in_block = subres.height_in_texel = dst_dimensions.height; subres.pitch_in_block = pitch_in_block; subres.depth = 1; subres.data = { vm::get_super_ptr(dst.rsx_address), dst.pitch * dst_dimensions.height }; diff --git a/rpcs3/Emu/RSX/VK/VKRenderTargets.h b/rpcs3/Emu/RSX/VK/VKRenderTargets.h index 29a3d7e86b..58308b5927 100644 --- a/rpcs3/Emu/RSX/VK/VKRenderTargets.h +++ b/rpcs3/Emu/RSX/VK/VKRenderTargets.h @@ -241,8 +241,8 @@ namespace vk } rsx_subresource_layout subres{}; - subres.width_in_block = surface_width * samples_x; - subres.height_in_block = surface_height * samples_y; + subres.width_in_block = subres.width_in_texel = surface_width * samples_x; + subres.height_in_block = subres.height_in_texel = surface_height * samples_y; subres.pitch_in_block = rsx_pitch / get_bpp(); subres.depth = 1; subres.data = { (const gsl::byte*)vm::get_super_ptr(base_addr), s32(rsx_pitch * surface_height * samples_y) }; diff --git a/rpcs3/Emu/RSX/VK/VKTexture.cpp b/rpcs3/Emu/RSX/VK/VKTexture.cpp index 94f827f953..0b74c792b8 100644 --- a/rpcs3/Emu/RSX/VK/VKTexture.cpp +++ b/rpcs3/Emu/RSX/VK/VKTexture.cpp @@ -542,7 +542,6 @@ namespace vk const std::vector& subresource_layout, int format, bool is_swizzled, u16 mipmap_count, VkImageAspectFlags flags, vk::data_heap &upload_heap, u32 heap_align) { - u32 mipmap_level = 0; u32 block_in_pixel = get_format_block_size_in_texel(format); u8 block_size_in_bytes = get_format_block_size_in_bytes(format); @@ -591,13 +590,13 @@ namespace vk copy_regions.push_back({}); auto& copy_info = copy_regions.back(); copy_info.bufferOffset = offset_in_buffer; - copy_info.imageExtent.height = layout.height_in_block * block_in_pixel; - copy_info.imageExtent.width = std::min(layout.width_in_block, layout.pitch_in_block) * block_in_pixel; + copy_info.imageExtent.height = layout.height_in_texel; + copy_info.imageExtent.width = layout.width_in_texel; copy_info.imageExtent.depth = layout.depth; copy_info.imageSubresource.aspectMask = flags; copy_info.imageSubresource.layerCount = 1; - copy_info.imageSubresource.baseArrayLayer = mipmap_level / mipmap_count; - copy_info.imageSubresource.mipLevel = mipmap_level % mipmap_count; + copy_info.imageSubresource.baseArrayLayer = layout.layer; + copy_info.imageSubresource.mipLevel = layout.level; copy_info.bufferRowLength = block_in_pixel * row_pitch / block_size_in_bytes; if (opt.require_swap || dst_image->aspect() & VK_IMAGE_ASPECT_STENCIL_BIT) @@ -621,8 +620,6 @@ namespace vk scratch_offset += image_linear_size; verify("Out of scratch memory" HERE), (scratch_offset + image_linear_size) <= scratch_buf->size(); } - - mipmap_level++; } if (opt.require_swap || dst_image->aspect() & VK_IMAGE_ASPECT_STENCIL_BIT) diff --git a/rpcs3/Emu/RSX/rsx_utils.h b/rpcs3/Emu/RSX/rsx_utils.h index 8e20f1666a..053fb22577 100644 --- a/rpcs3/Emu/RSX/rsx_utils.h +++ b/rpcs3/Emu/RSX/rsx_utils.h @@ -277,6 +277,13 @@ namespace rsx return ((value + alignment - 1) / alignment) * alignment; } + // General purpose aligned division, the result is rounded up not truncated + template + static inline T aligned_div(T value, U alignment) + { + return (value + alignment - 1) / alignment; + } + // Copy memory in inverse direction from source // Used to scale negatively x axis while transfering image data template