[Vulkan] Add logging to certain error conditions.
[Vulkan] Track both texture block height and texture block vertical pitch. [Vulkan] Only convert the necessary visible height on texture uploads. [Vulkan] Fix write watch on texture data so it properly only covers mip 0 data.
This commit is contained in:
parent
7068d0a5a9
commit
bbebfd49c8
|
@ -83,7 +83,8 @@ bool TextureInfo::Prepare(const xe_gpu_texture_fetch_t& fetch,
|
||||||
bool TextureInfo::PrepareResolve(uint32_t physical_address,
|
bool TextureInfo::PrepareResolve(uint32_t physical_address,
|
||||||
TextureFormat format, Endian endian,
|
TextureFormat format, Endian endian,
|
||||||
uint32_t pitch, uint32_t width,
|
uint32_t pitch, uint32_t width,
|
||||||
uint32_t height, TextureInfo* out_info) {
|
uint32_t height, uint32_t depth,
|
||||||
|
TextureInfo* out_info) {
|
||||||
assert_true(width > 0);
|
assert_true(width > 0);
|
||||||
assert_true(height > 0);
|
assert_true(height > 0);
|
||||||
|
|
||||||
|
@ -95,7 +96,7 @@ bool TextureInfo::PrepareResolve(uint32_t physical_address,
|
||||||
info.width = width - 1;
|
info.width = width - 1;
|
||||||
info.height = height - 1;
|
info.height = height - 1;
|
||||||
info.mip_levels = 1;
|
info.mip_levels = 1;
|
||||||
info.depth = 0;
|
info.depth = depth - 1;
|
||||||
info.pitch = pitch;
|
info.pitch = pitch;
|
||||||
|
|
||||||
info.endianness = endian;
|
info.endianness = endian;
|
||||||
|
@ -156,11 +157,20 @@ uint32_t TextureInfo::GetMipLocation(uint32_t mip, uint32_t* offset_x,
|
||||||
return guest_address;
|
return guest_address;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uint32_t address_base, address_offset;
|
||||||
|
|
||||||
// If the texture is <= 16 pixels w/h, the mips are packed with the base
|
// If the texture is <= 16 pixels w/h, the mips are packed with the base
|
||||||
// texture. Otherwise, they're stored beginning from mip_address.
|
// texture. Otherwise, they're stored beginning from mip_address.
|
||||||
uint32_t address_base =
|
if (std::min(width, height) < 16) {
|
||||||
std::min(width, height) < 16 ? guest_address : mip_address;
|
address_base = guest_address;
|
||||||
uint32_t address_offset = 0;
|
address_offset = 0;
|
||||||
|
} else if (guest_address == mip_address) {
|
||||||
|
address_base = guest_address;
|
||||||
|
address_offset = GetMipByteSize(0, is_guest);
|
||||||
|
} else {
|
||||||
|
address_base = mip_address;
|
||||||
|
address_offset = 0;
|
||||||
|
}
|
||||||
|
|
||||||
if (!has_packed_mips) {
|
if (!has_packed_mips) {
|
||||||
for (uint32_t i = 1; i < mip; i++) {
|
for (uint32_t i = 1; i < mip; i++) {
|
||||||
|
@ -183,7 +193,6 @@ uint32_t TextureInfo::GetMipLocation(uint32_t mip, uint32_t* offset_x,
|
||||||
// We've reached the point where the mips are packed into a single tile.
|
// We've reached the point where the mips are packed into a single tile.
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
address_offset += GetMipByteSize(i, is_guest);
|
address_offset += GetMipByteSize(i, is_guest);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -196,7 +205,13 @@ uint32_t TextureInfo::GetMipLocation(uint32_t mip, uint32_t* offset_x,
|
||||||
uint32_t TextureInfo::GetMipByteSize(uint32_t mip, bool is_guest) const {
|
uint32_t TextureInfo::GetMipByteSize(uint32_t mip, bool is_guest) const {
|
||||||
uint32_t bytes_per_block = format_info()->bytes_per_block();
|
uint32_t bytes_per_block = format_info()->bytes_per_block();
|
||||||
auto mip_usage = GetMipMemoryUsage(mip, is_guest);
|
auto mip_usage = GetMipMemoryUsage(mip, is_guest);
|
||||||
return mip_usage.blocks() * bytes_per_block;
|
return mip_usage.all_blocks() * bytes_per_block;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t TextureInfo::GetMipVisibleByteSize(uint32_t mip, bool is_guest) const {
|
||||||
|
uint32_t bytes_per_block = format_info()->bytes_per_block();
|
||||||
|
auto mip_usage = GetMipMemoryUsage(mip, is_guest);
|
||||||
|
return mip_usage.visible_blocks() * bytes_per_block;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t TextureInfo::GetByteSize(bool is_guest) const {
|
uint32_t TextureInfo::GetByteSize(bool is_guest) const {
|
||||||
|
|
|
@ -293,11 +293,15 @@ struct TextureInfo;
|
||||||
struct TextureMemoryUsage {
|
struct TextureMemoryUsage {
|
||||||
uint32_t pitch; // texel pitch
|
uint32_t pitch; // texel pitch
|
||||||
uint32_t height; // texel height
|
uint32_t height; // texel height
|
||||||
uint32_t block_pitch; // # of horizontal pitch blocks
|
|
||||||
uint32_t block_height; // # of vertical blocks
|
uint32_t block_height; // # of vertical blocks
|
||||||
|
uint32_t block_pitch_h; // # of horizontal pitch blocks
|
||||||
|
uint32_t block_pitch_v; // # of vertical pitch blocks
|
||||||
uint32_t depth;
|
uint32_t depth;
|
||||||
|
|
||||||
uint32_t blocks() const { return block_pitch * block_height * depth; }
|
uint32_t all_blocks() const { return block_pitch_h * block_pitch_v * depth; }
|
||||||
|
uint32_t visible_blocks() const {
|
||||||
|
return block_pitch_h * block_height * depth;
|
||||||
|
}
|
||||||
|
|
||||||
static TextureMemoryUsage Calculate(const FormatInfo* format_info,
|
static TextureMemoryUsage Calculate(const FormatInfo* format_info,
|
||||||
uint32_t pitch, uint32_t height,
|
uint32_t pitch, uint32_t height,
|
||||||
|
@ -339,7 +343,7 @@ struct TextureInfo {
|
||||||
static bool PrepareResolve(uint32_t physical_address,
|
static bool PrepareResolve(uint32_t physical_address,
|
||||||
TextureFormat texture_format, Endian endian,
|
TextureFormat texture_format, Endian endian,
|
||||||
uint32_t pitch, uint32_t width, uint32_t height,
|
uint32_t pitch, uint32_t width, uint32_t height,
|
||||||
TextureInfo* out_info);
|
uint32_t depth, TextureInfo* out_info);
|
||||||
|
|
||||||
uint32_t GetMaxMipLevels() const;
|
uint32_t GetMaxMipLevels() const;
|
||||||
|
|
||||||
|
@ -352,6 +356,7 @@ struct TextureInfo {
|
||||||
bool is_guest) const;
|
bool is_guest) const;
|
||||||
|
|
||||||
uint32_t GetMipByteSize(uint32_t mip, bool is_guest) const;
|
uint32_t GetMipByteSize(uint32_t mip, bool is_guest) const;
|
||||||
|
uint32_t GetMipVisibleByteSize(uint32_t mip, bool is_guest) const;
|
||||||
|
|
||||||
uint32_t GetByteSize(bool is_guest) const;
|
uint32_t GetByteSize(bool is_guest) const;
|
||||||
|
|
||||||
|
|
|
@ -27,29 +27,30 @@ static TextureMemoryUsage CalculateMemoryUsage(const FormatInfo* format_info,
|
||||||
|
|
||||||
usage.pitch = pitch;
|
usage.pitch = pitch;
|
||||||
usage.height = height;
|
usage.height = height;
|
||||||
usage.block_pitch = xe::round_up(usage.pitch, format_info->block_width) /
|
usage.block_pitch_h = xe::round_up(usage.pitch, format_info->block_width) /
|
||||||
format_info->block_width;
|
format_info->block_width;
|
||||||
usage.block_height = xe::round_up(usage.height, format_info->block_height) /
|
usage.block_height = xe::round_up(usage.height, format_info->block_height) /
|
||||||
format_info->block_height;
|
format_info->block_height;
|
||||||
|
usage.block_pitch_v = usage.block_height;
|
||||||
usage.depth = depth;
|
usage.depth = depth;
|
||||||
|
|
||||||
if (is_guest) {
|
if (is_guest) {
|
||||||
// Texture dimensions must be a multiple of tile
|
// Texture dimensions must be a multiple of tile
|
||||||
// dimensions (32x32 blocks).
|
// dimensions (32x32 blocks).
|
||||||
usage.block_pitch = xe::round_up(usage.block_pitch, 32);
|
usage.block_pitch_h = xe::round_up(usage.block_pitch_h, 32);
|
||||||
usage.block_height = xe::round_up(usage.block_height, 32);
|
usage.block_pitch_v = xe::round_up(usage.block_pitch_v, 32);
|
||||||
|
|
||||||
usage.pitch = usage.block_pitch * format_info->block_width;
|
usage.pitch = usage.block_pitch_h * format_info->block_width;
|
||||||
usage.height = usage.block_height * format_info->block_height;
|
usage.height = usage.block_pitch_v * format_info->block_height;
|
||||||
|
|
||||||
uint32_t bytes_per_block = format_info->bytes_per_block();
|
uint32_t bytes_per_block = format_info->bytes_per_block();
|
||||||
uint32_t byte_pitch = usage.block_pitch * bytes_per_block;
|
uint32_t byte_pitch = usage.block_pitch_h * bytes_per_block;
|
||||||
|
|
||||||
if (!is_tiled) {
|
if (!is_tiled) {
|
||||||
// Each row must be a multiple of 256 bytes in linear textures.
|
// Each row must be a multiple of 256 bytes in linear textures.
|
||||||
byte_pitch = xe::round_up(byte_pitch, 256);
|
byte_pitch = xe::round_up(byte_pitch, 256);
|
||||||
usage.block_pitch = byte_pitch / bytes_per_block;
|
usage.block_pitch_h = byte_pitch / bytes_per_block;
|
||||||
usage.pitch = usage.block_pitch * format_info->block_width;
|
usage.pitch = usage.block_pitch_h * format_info->block_width;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Is depth special?
|
// Is depth special?
|
||||||
|
|
|
@ -425,6 +425,9 @@ void TextureCache::WatchCallback(void* context_ptr, void* data_ptr,
|
||||||
touched_texture->access_watch_handle = 0;
|
touched_texture->access_watch_handle = 0;
|
||||||
touched_texture->pending_invalidation = true;
|
touched_texture->pending_invalidation = true;
|
||||||
|
|
||||||
|
/*XELOGI("Invalidating texture @ 0x%.8X!",
|
||||||
|
touched_texture->texture_info.guest_address);*/
|
||||||
|
|
||||||
// Add to pending list so Scavenge will clean it up.
|
// Add to pending list so Scavenge will clean it up.
|
||||||
self->invalidated_textures_mutex_.lock();
|
self->invalidated_textures_mutex_.lock();
|
||||||
self->invalidated_textures_->insert(touched_texture);
|
self->invalidated_textures_->insert(touched_texture);
|
||||||
|
@ -470,15 +473,17 @@ TextureCache::Texture* TextureCache::DemandResolveTexture(
|
||||||
device_->DbgSetObjectName(
|
device_->DbgSetObjectName(
|
||||||
reinterpret_cast<uint64_t>(texture->image),
|
reinterpret_cast<uint64_t>(texture->image),
|
||||||
VK_DEBUG_REPORT_OBJECT_TYPE_IMAGE_EXT,
|
VK_DEBUG_REPORT_OBJECT_TYPE_IMAGE_EXT,
|
||||||
xe::format_string(
|
xe::format_string("RT: 0x%.8X - 0x%.8X (%s, %s)",
|
||||||
"RT: 0x%.8X - 0x%.8X (%s, %s)", texture_info.guest_address,
|
texture_info.guest_address,
|
||||||
texture_info.guest_address + texture_info.GetByteSize(true),
|
texture_info.guest_address +
|
||||||
|
texture_info.GetMipVisibleByteSize(0, true),
|
||||||
texture_info.format_info()->name,
|
texture_info.format_info()->name,
|
||||||
get_dimension_name(texture_info.dimension)));
|
get_dimension_name(texture_info.dimension)));
|
||||||
|
|
||||||
// Setup an access watch. If this texture is touched, it is destroyed.
|
// Setup an access watch. If this texture is touched, it is destroyed.
|
||||||
|
// TODO(gibbed): Setup access watch for mipmap data.
|
||||||
texture->access_watch_handle = memory_->AddPhysicalAccessWatch(
|
texture->access_watch_handle = memory_->AddPhysicalAccessWatch(
|
||||||
texture_info.guest_address, texture_info.GetByteSize(true),
|
texture_info.guest_address, texture_info.GetMipVisibleByteSize(0, true),
|
||||||
cpu::MMIOHandler::kWatchWrite, &WatchCallback, this, texture);
|
cpu::MMIOHandler::kWatchWrite, &WatchCallback, this, texture);
|
||||||
|
|
||||||
textures_[texture_hash] = texture;
|
textures_[texture_hash] = texture;
|
||||||
|
@ -536,8 +541,9 @@ TextureCache::Texture* TextureCache::Demand(const TextureInfo& texture_info,
|
||||||
|
|
||||||
// Okay. Put a writewatch on it to tell us if it's been modified from the
|
// Okay. Put a writewatch on it to tell us if it's been modified from the
|
||||||
// guest.
|
// guest.
|
||||||
|
// TODO(gibbed): Setup access watch for mipmap data.
|
||||||
texture->access_watch_handle = memory_->AddPhysicalAccessWatch(
|
texture->access_watch_handle = memory_->AddPhysicalAccessWatch(
|
||||||
texture_info.guest_address, texture_info.GetByteSize(true),
|
texture_info.guest_address, texture_info.GetMipVisibleByteSize(0, true),
|
||||||
cpu::MMIOHandler::kWatchWrite, &WatchCallback, this, texture);
|
cpu::MMIOHandler::kWatchWrite, &WatchCallback, this, texture);
|
||||||
|
|
||||||
if (!UploadTexture(command_buffer, completion_fence, texture, texture_info)) {
|
if (!UploadTexture(command_buffer, completion_fence, texture, texture_info)) {
|
||||||
|
@ -856,8 +862,8 @@ TextureCache::Texture* TextureCache::LookupAddress(uint32_t guest_address,
|
||||||
for (auto it = textures_.begin(); it != textures_.end(); ++it) {
|
for (auto it = textures_.begin(); it != textures_.end(); ++it) {
|
||||||
const auto& texture_info = it->second->texture_info;
|
const auto& texture_info = it->second->texture_info;
|
||||||
if (guest_address >= texture_info.guest_address &&
|
if (guest_address >= texture_info.guest_address &&
|
||||||
guest_address <
|
guest_address < texture_info.guest_address +
|
||||||
texture_info.guest_address + texture_info.GetByteSize(true) &&
|
texture_info.GetMipVisibleByteSize(0, true) &&
|
||||||
texture_info.pitch >= width && texture_info.height >= height &&
|
texture_info.pitch >= width && texture_info.height >= height &&
|
||||||
out_offset) {
|
out_offset) {
|
||||||
auto offset_bytes = guest_address - texture_info.guest_address;
|
auto offset_bytes = guest_address - texture_info.guest_address;
|
||||||
|
@ -939,9 +945,9 @@ bool TextureCache::ConvertTexture(uint8_t* dest, VkBufferImageCopy* copy_region,
|
||||||
auto dst_usage = GetMipMemoryUsage(src, mip);
|
auto dst_usage = GetMipMemoryUsage(src, mip);
|
||||||
|
|
||||||
uint32_t src_pitch =
|
uint32_t src_pitch =
|
||||||
src_usage.block_pitch * src.format_info()->bytes_per_block();
|
src_usage.block_pitch_h * src.format_info()->bytes_per_block();
|
||||||
uint32_t dst_pitch =
|
uint32_t dst_pitch =
|
||||||
dst_usage.block_pitch * GetFormatInfo(src.format)->bytes_per_block();
|
dst_usage.block_pitch_h * GetFormatInfo(src.format)->bytes_per_block();
|
||||||
|
|
||||||
auto copy_block = GetFormatCopyBlock(src.format);
|
auto copy_block = GetFormatCopyBlock(src.format);
|
||||||
|
|
||||||
|
@ -954,8 +960,8 @@ bool TextureCache::ConvertTexture(uint8_t* dest, VkBufferImageCopy* copy_region,
|
||||||
copy_block(src.endianness, dest + y * dst_pitch,
|
copy_block(src.endianness, dest + y * dst_pitch,
|
||||||
src_mem + y * src_pitch, dst_pitch);
|
src_mem + y * src_pitch, dst_pitch);
|
||||||
}
|
}
|
||||||
src_mem += src_pitch * src_usage.block_height;
|
src_mem += src_pitch * src_usage.block_pitch_v;
|
||||||
dest += dst_pitch * dst_usage.block_height;
|
dest += dst_pitch * dst_usage.block_pitch_v;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Untile image.
|
// Untile image.
|
||||||
|
@ -965,18 +971,18 @@ bool TextureCache::ConvertTexture(uint8_t* dest, VkBufferImageCopy* copy_region,
|
||||||
std::memset(&untile_info, 0, sizeof(untile_info));
|
std::memset(&untile_info, 0, sizeof(untile_info));
|
||||||
untile_info.offset_x = offset_x;
|
untile_info.offset_x = offset_x;
|
||||||
untile_info.offset_y = offset_y;
|
untile_info.offset_y = offset_y;
|
||||||
untile_info.width = dst_usage.block_pitch;
|
untile_info.width = dst_usage.block_pitch_h;
|
||||||
untile_info.height = dst_usage.block_height;
|
untile_info.height = dst_usage.block_height;
|
||||||
untile_info.input_pitch = src_usage.block_pitch;
|
untile_info.input_pitch = src_usage.block_pitch_h;
|
||||||
untile_info.output_pitch = dst_usage.block_pitch;
|
untile_info.output_pitch = dst_usage.block_pitch_h;
|
||||||
untile_info.input_format_info = src.format_info();
|
untile_info.input_format_info = src.format_info();
|
||||||
untile_info.output_format_info = GetFormatInfo(src.format);
|
untile_info.output_format_info = GetFormatInfo(src.format);
|
||||||
untile_info.copy_callback = [=](auto o, auto i, auto l) {
|
untile_info.copy_callback = [=](auto o, auto i, auto l) {
|
||||||
copy_block(src.endianness, o, i, l);
|
copy_block(src.endianness, o, i, l);
|
||||||
};
|
};
|
||||||
texture_conversion::Untile(dest, src_mem, &untile_info);
|
texture_conversion::Untile(dest, src_mem, &untile_info);
|
||||||
src_mem += src_pitch * src_usage.block_height;
|
src_mem += src_pitch * src_usage.block_pitch_v;
|
||||||
dest += dst_pitch * dst_usage.block_height;
|
dest += dst_pitch * dst_usage.block_pitch_v;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1195,7 +1201,7 @@ uint32_t TextureCache::ComputeMipStorage(const FormatInfo* format_info,
|
||||||
depth, false, false);
|
depth, false, false);
|
||||||
}
|
}
|
||||||
uint32_t bytes_per_block = format_info->bytes_per_block();
|
uint32_t bytes_per_block = format_info->bytes_per_block();
|
||||||
return usage.blocks() * bytes_per_block;
|
return usage.all_blocks() * bytes_per_block;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t TextureCache::ComputeMipStorage(const TextureInfo& src, uint32_t mip) {
|
uint32_t TextureCache::ComputeMipStorage(const TextureInfo& src, uint32_t mip) {
|
||||||
|
|
|
@ -822,6 +822,10 @@ bool VulkanCommandProcessor::PopulateVertexBuffers(
|
||||||
assert_true(vertex_bindings.size() <= 32);
|
assert_true(vertex_bindings.size() <= 32);
|
||||||
auto descriptor_set = buffer_cache_->PrepareVertexSet(
|
auto descriptor_set = buffer_cache_->PrepareVertexSet(
|
||||||
setup_buffer, current_batch_fence_, vertex_bindings);
|
setup_buffer, current_batch_fence_, vertex_bindings);
|
||||||
|
if (!descriptor_set) {
|
||||||
|
XELOGW("Failed to prepare vertex set!");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
vkCmdBindDescriptorSets(command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS,
|
vkCmdBindDescriptorSets(command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS,
|
||||||
pipeline_cache_->pipeline_layout(), 2, 1,
|
pipeline_cache_->pipeline_layout(), 2, 1,
|
||||||
|
@ -843,6 +847,7 @@ bool VulkanCommandProcessor::PopulateSamplers(VkCommandBuffer command_buffer,
|
||||||
pixel_shader ? pixel_shader->texture_bindings() : dummy_bindings);
|
pixel_shader ? pixel_shader->texture_bindings() : dummy_bindings);
|
||||||
if (!descriptor_set) {
|
if (!descriptor_set) {
|
||||||
// Unable to bind set.
|
// Unable to bind set.
|
||||||
|
XELOGW("Failed to prepare texture set!");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1023,13 +1028,14 @@ bool VulkanCommandProcessor::IssueCopy() {
|
||||||
|
|
||||||
// Demand a resolve texture from the texture cache.
|
// Demand a resolve texture from the texture cache.
|
||||||
TextureInfo texture_info;
|
TextureInfo texture_info;
|
||||||
TextureInfo::PrepareResolve(copy_dest_base, copy_dest_format, resolve_endian,
|
TextureInfo::PrepareResolve(
|
||||||
copy_dest_pitch, dest_logical_width,
|
copy_dest_base, copy_dest_format, resolve_endian, copy_dest_pitch,
|
||||||
std::max(1u, dest_logical_height), &texture_info);
|
dest_logical_width, std::max(1u, dest_logical_height), 1, &texture_info);
|
||||||
|
|
||||||
auto texture = texture_cache_->DemandResolveTexture(texture_info);
|
auto texture = texture_cache_->DemandResolveTexture(texture_info);
|
||||||
if (!texture) {
|
if (!texture) {
|
||||||
// Out of memory.
|
// Out of memory.
|
||||||
|
XELOGD("Failed to demand resolve texture!");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1137,6 +1143,7 @@ bool VulkanCommandProcessor::IssueCopy() {
|
||||||
auto view = render_cache_->FindTileView(
|
auto view = render_cache_->FindTileView(
|
||||||
edram_base, surface_pitch, surface_msaa, is_color_source, src_format);
|
edram_base, surface_pitch, surface_msaa, is_color_source, src_format);
|
||||||
if (!view) {
|
if (!view) {
|
||||||
|
XELOGGPU("Failed to find tile view!");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue