diff --git a/src/xenia/gpu/texture_dump.cc b/src/xenia/gpu/texture_dump.cc index 2d666714a..e21ad34d2 100644 --- a/src/xenia/gpu/texture_dump.cc +++ b/src/xenia/gpu/texture_dump.cc @@ -9,6 +9,8 @@ #include +#include "xenia/base/logging.h" +#include "xenia/base/math.h" #include "xenia/gpu/texture_info.h" DEFINE_bool(texture_dump, false, "Dump textures to DDS"); @@ -48,11 +50,11 @@ void TextureDump(const TextureInfo& src, void* buffer, size_t length) { } else { dds_header.flags |= 0x8u; } - dds_header.height = src.height + 1; - dds_header.width = src.width + 1; + dds_header.height = std::max(1u, (src.height + 1) >> src.mip_min_level); + dds_header.width = std::max(1u, (src.width + 1) >> src.mip_min_level); dds_header.pitch_or_linear_size = src.GetMipExtent(0, false).block_pitch_h * src.format_info()->bytes_per_block(); - dds_header.mip_levels = src.mip_levels; + dds_header.mip_levels = src.mip_levels(); dds_header.pixel_format.size = sizeof(dds_header.pixel_format); switch (src.format) { @@ -84,6 +86,7 @@ void TextureDump(const TextureInfo& src, void* buffer, size_t length) { assert_unhandled_case(src.format); std::memset(&dds_header.pixel_format, 0xCD, sizeof(dds_header.pixel_format)); + XELOGW("Skipping %s for texture dump.", src.format_info()->name); return; } } diff --git a/src/xenia/gpu/texture_info.cc b/src/xenia/gpu/texture_info.cc index c154beecd..0695a1bf9 100644 --- a/src/xenia/gpu/texture_info.cc +++ b/src/xenia/gpu/texture_info.cc @@ -62,8 +62,9 @@ bool TextureInfo::Prepare(const xe_gpu_texture_fetch_t& fetch, break; } info.pitch = fetch.pitch << 5; - assert_true(fetch.mip_min_level == 0); - info.mip_levels = 1 + fetch.mip_max_level; + + info.mip_min_level = fetch.mip_min_level; + info.mip_max_level = std::max(fetch.mip_min_level, fetch.mip_max_level); info.is_tiled = fetch.tiled; info.has_packed_mips = fetch.packed_mips; @@ -79,11 +80,6 @@ bool TextureInfo::Prepare(const xe_gpu_texture_fetch_t& fetch, info.extent = TextureExtent::Calculate(out_info, true); info.SetupMemoryInfo(fetch.base_address << 12, fetch.mip_address << 12); - if (!info.memory.mip_address) { - // No mip data? One mip level, period. - info.mip_levels = 1; - } - return true; } @@ -107,7 +103,8 @@ bool TextureInfo::PrepareResolve(uint32_t physical_address, info.depth = depth - 1; info.pitch = pitch; - info.mip_levels = 1; + info.mip_min_level = 0; + info.mip_max_level = 0; info.is_tiled = true; info.has_packed_mips = false; @@ -132,8 +129,14 @@ const TextureExtent TextureInfo::GetMipExtent(uint32_t mip, if (mip == 0) { return extent; } - uint32_t mip_width = xe::next_pow2(width + 1) >> mip; - uint32_t mip_height = xe::next_pow2(height + 1) >> mip; + uint32_t mip_width, mip_height; + if (is_guest) { + mip_width = xe::next_pow2(width + 1) >> mip; + mip_height = xe::next_pow2(height + 1) >> mip; + } else { + mip_width = std::max(1u, (width + 1) >> mip); + mip_height = std::max(1u, (height + 1) >> mip); + } return TextureExtent::Calculate(format_info(), mip_width, mip_height, depth + 1, is_tiled, is_guest); } @@ -305,30 +308,50 @@ uint64_t TextureInfo::hash() const { void TextureInfo::SetupMemoryInfo(uint32_t base_address, uint32_t mip_address) { uint32_t bytes_per_block = format_info()->bytes_per_block(); - memory.base_address = base_address; - memory.base_size = GetMipExtent(0, true).visible_blocks() * bytes_per_block; + memory.base_address = 0; + memory.base_size = 0; memory.mip_address = 0; memory.mip_size = 0; - if (mip_levels <= 1 || !mip_address) { + if (mip_min_level == 0 && base_address) { + // There is a base mip level. + memory.base_address = base_address; + memory.base_size = GetMipExtent(0, true).visible_blocks() * bytes_per_block; + } + + if (mip_min_level == 0 && mip_max_level == 0) { // Sort circuit. Only one mip. return; } - if (base_address == mip_address) { + if (mip_min_level == 0 && base_address == mip_address) { // TODO(gibbed): This doesn't actually make any sense. Force only one mip. // Offending title issues: #26, #45 return; } + if (mip_min_level > 0) { + if ((base_address && !mip_address) || (base_address == mip_address)) { + // Mip data is actually at base address? + mip_address = base_address; + base_address = 0; + } else if (!base_address && mip_address) { + // Nothing needs to be done. + } else { + // WTF? + assert_always(); + } + } + memory.mip_address = mip_address; if (!has_packed_mips) { - for (uint32_t mip = 1; mip < mip_levels - 1; mip++) { + for (uint32_t mip = std::max(1u, mip_min_level); mip < mip_max_level; + mip++) { memory.mip_size += GetMipExtent(mip, true).all_blocks() * bytes_per_block; } memory.mip_size += - GetMipExtent(mip_levels - 1, true).visible_blocks() * bytes_per_block; + GetMipExtent(mip_max_level, true).visible_blocks() * bytes_per_block; return; } @@ -336,8 +359,8 @@ void TextureInfo::SetupMemoryInfo(uint32_t base_address, uint32_t mip_address) { uint32_t height_pow2 = xe::next_pow2(height + 1); // Walk forward to find the address of the mip. - uint32_t packed_mip_base = 1; - for (uint32_t mip = packed_mip_base; mip < mip_levels - 1; + uint32_t packed_mip_base = std::max(1u, mip_min_level); + for (uint32_t mip = packed_mip_base; mip < mip_max_level; mip++, packed_mip_base++) { uint32_t mip_width = std::max(width_pow2 >> mip, 1u); uint32_t mip_height = std::max(height_pow2 >> mip, 1u); diff --git a/src/xenia/gpu/texture_info.h b/src/xenia/gpu/texture_info.h index f201395b5..0fd1205d0 100644 --- a/src/xenia/gpu/texture_info.h +++ b/src/xenia/gpu/texture_info.h @@ -327,7 +327,8 @@ struct TextureInfo { uint32_t height; // height in pixels uint32_t depth; // depth in layers uint32_t pitch; // pitch in blocks - uint32_t mip_levels; + uint32_t mip_min_level; + uint32_t mip_max_level; bool is_tiled; bool has_packed_mips; @@ -342,6 +343,8 @@ struct TextureInfo { return format_info()->type == FormatType::kCompressed; } + uint32_t mip_levels() const { return 1 + (mip_max_level - mip_min_level); } + static bool Prepare(const xenos::xe_gpu_texture_fetch_t& fetch, TextureInfo* out_info); diff --git a/src/xenia/gpu/vulkan/texture_cache.cc b/src/xenia/gpu/vulkan/texture_cache.cc index 3b64f46e4..0afd4e911 100644 --- a/src/xenia/gpu/vulkan/texture_cache.cc +++ b/src/xenia/gpu/vulkan/texture_cache.cc @@ -21,8 +21,6 @@ #include "xenia/gpu/vulkan/vulkan_gpu_flags.h" #include "xenia/ui/vulkan/vulkan_mem_alloc.h" -DEFINE_bool(enable_mip_watches, false, "Enable mipmap watches"); - DECLARE_bool(texture_dump); namespace xe { @@ -252,7 +250,7 @@ TextureCache::Texture* TextureCache::AllocateTexture( image_info.extent.width = texture_info.width + 1; image_info.extent.height = texture_info.height + 1; image_info.extent.depth = !is_cube ? texture_info.depth + 1 : 1; - image_info.mipLevels = texture_info.mip_levels; + image_info.mipLevels = texture_info.mip_min_level + texture_info.mip_levels(); image_info.arrayLayers = !is_cube ? 1 : 6; image_info.samples = VK_SAMPLE_COUNT_1_BIT; image_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE; @@ -352,8 +350,10 @@ TextureCache::Texture* TextureCache::DemandResolveTexture( } // Tell the trace writer to "cache" this memory (but not read it) - trace_writer_->WriteMemoryReadCached(texture_info.memory.base_address, - texture_info.memory.base_size); + if (texture_info.memory.base_address) { + trace_writer_->WriteMemoryReadCached(texture_info.memory.base_address, + texture_info.memory.base_size); + } if (texture_info.memory.mip_address) { trace_writer_->WriteMemoryReadCached(texture_info.memory.mip_address, texture_info.memory.mip_size); @@ -390,34 +390,14 @@ TextureCache::Texture* TextureCache::DemandResolveTexture( get_dimension_name(texture_info.dimension))); // Setup an access watch. If this texture is touched, it is destroyed. - if (!FLAGS_enable_mip_watches || !texture_info.memory.mip_address) { + if (texture_info.memory.base_address && texture_info.memory.base_size) { texture->access_watch_handle = memory_->AddPhysicalAccessWatch( texture_info.memory.base_address, texture_info.memory.base_size, cpu::MMIOHandler::kWatchWrite, &WatchCallback, this, texture); - } else { - // TODO(gibbed): This makes the dangerous assumption that the base mip + - // following mips are near each other in memory. - uint32_t watch_address, watch_size; - if (texture_info.memory.base_address < texture_info.memory.mip_address) { - assert_true(texture_info.memory.base_address + - texture_info.memory.base_size <= - texture_info.memory.mip_address); - watch_address = texture_info.memory.base_address; - watch_size = - (texture_info.memory.mip_address + texture_info.memory.mip_size) - - texture_info.memory.base_address; - } else { - assert_true(texture_info.memory.mip_address + - texture_info.memory.mip_size <= - texture_info.memory.base_address); - watch_address = texture_info.memory.mip_address; - watch_size = - (texture_info.memory.base_address + texture_info.memory.base_size) - - texture_info.memory.mip_address; - } + } else if (texture_info.memory.mip_address && texture_info.memory.mip_size) { texture->access_watch_handle = memory_->AddPhysicalAccessWatch( - watch_address, watch_size, cpu::MMIOHandler::kWatchWrite, - &WatchCallback, this, texture); + texture_info.memory.mip_address, texture_info.memory.mip_size, + cpu::MMIOHandler::kWatchWrite, &WatchCallback, this, texture); } textures_[texture_hash] = texture; @@ -438,8 +418,10 @@ TextureCache::Texture* TextureCache::Demand(const TextureInfo& texture_info, break; } - trace_writer_->WriteMemoryReadCached(texture_info.memory.base_address, - texture_info.memory.base_size); + if (texture_info.memory.base_address) { + trace_writer_->WriteMemoryReadCached(texture_info.memory.base_address, + texture_info.memory.base_size); + } if (texture_info.memory.mip_address) { trace_writer_->WriteMemoryReadCached(texture_info.memory.mip_address, texture_info.memory.mip_size); @@ -474,8 +456,10 @@ TextureCache::Texture* TextureCache::Demand(const TextureInfo& texture_info, // assert_always(); } - trace_writer_->WriteMemoryReadCached(texture_info.memory.base_address, - texture_info.memory.base_size); + if (texture_info.memory.base_address) { + trace_writer_->WriteMemoryReadCached(texture_info.memory.base_address, + texture_info.memory.base_size); + } if (texture_info.memory.mip_address) { trace_writer_->WriteMemoryReadCached(texture_info.memory.mip_address, texture_info.memory.mip_size); @@ -501,34 +485,14 @@ TextureCache::Texture* TextureCache::Demand(const TextureInfo& texture_info, // Okay. Put a writewatch on it to tell us if it's been modified from the // guest. - if (!FLAGS_enable_mip_watches || !texture_info.memory.mip_address) { + if (texture_info.memory.base_address && texture_info.memory.base_size) { texture->access_watch_handle = memory_->AddPhysicalAccessWatch( texture_info.memory.base_address, texture_info.memory.base_size, cpu::MMIOHandler::kWatchWrite, &WatchCallback, this, texture); - } else { - // TODO(gibbed): This makes the dangerous assumption that the base mip + - // following mips are near each other in memory. - uint32_t watch_address, watch_size; - if (texture_info.memory.base_address < texture_info.memory.mip_address) { - assert_true(texture_info.memory.base_address + - texture_info.memory.base_size <= - texture_info.memory.mip_address); - watch_address = texture_info.memory.base_address; - watch_size = - (texture_info.memory.mip_address + texture_info.memory.mip_size) - - texture_info.memory.base_address; - } else { - assert_true(texture_info.memory.mip_address + - texture_info.memory.mip_size <= - texture_info.memory.base_address); - watch_address = texture_info.memory.mip_address; - watch_size = - (texture_info.memory.base_address + texture_info.memory.base_size) - - texture_info.memory.mip_address; - } + } else if (texture_info.memory.mip_address && texture_info.memory.mip_size) { texture->access_watch_handle = memory_->AddPhysicalAccessWatch( - watch_address, watch_size, cpu::MMIOHandler::kWatchWrite, - &WatchCallback, this, texture); + texture_info.memory.mip_address, texture_info.memory.mip_size, + cpu::MMIOHandler::kWatchWrite, &WatchCallback, this, texture); } return texture; @@ -602,8 +566,8 @@ TextureCache::TextureView* TextureCache::DemandView(Texture* texture, } else { view_info.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; } - view_info.subresourceRange.baseMipLevel = 0; - view_info.subresourceRange.levelCount = texture->texture_info.mip_levels; + view_info.subresourceRange.baseMipLevel = texture->texture_info.mip_min_level; + view_info.subresourceRange.levelCount = texture->texture_info.mip_levels(); view_info.subresourceRange.baseArrayLayer = 0; view_info.subresourceRange.layerCount = !is_cube ? 1 : 6; @@ -978,13 +942,13 @@ bool TextureCache::UploadTexture(VkCommandBuffer command_buffer, XELOGGPU( "Uploading texture @ 0x%.8X/0x%.8X (%ux%ux%u, format: %s, dim: %s, " - "levels: %u, pitch: %u, tiled: %s, packed mips: %s, unpack length: " - "0x%.8X)", + "levels: %u (%u-%u), pitch: %u, tiled: %s, packed mips: %s, unpack " + "length: 0x%.8X)", src.memory.base_address, src.memory.mip_address, src.width + 1, src.height + 1, src.depth + 1, src.format_info()->name, - get_dimension_name(src.dimension), src.mip_levels, src.pitch, - src.is_tiled ? "yes" : "no", src.has_packed_mips ? "yes" : "no", - unpack_length); + get_dimension_name(src.dimension), src.mip_levels(), src.mip_min_level, + src.mip_max_level, src.pitch, src.is_tiled ? "yes" : "no", + src.has_packed_mips ? "yes" : "no", unpack_length); XELOGGPU("Extent: %ux%ux%u %u,%u,%u", src.extent.pitch, src.extent.height, src.extent.depth, src.extent.block_pitch_h, src.extent.block_height, @@ -1038,13 +1002,14 @@ bool TextureCache::UploadTexture(VkCommandBuffer command_buffer, // TODO: If the GPU supports it, we can submit a compute batch to convert the // texture and copy it to its destination. Otherwise, fallback to conversion // on the CPU. - uint32_t copy_region_count = src.mip_levels; + uint32_t copy_region_count = src.mip_levels(); std::vector copy_regions(copy_region_count); // Upload all mips. auto unpack_buffer = reinterpret_cast(alloc->host_ptr); VkDeviceSize unpack_offset = 0; - for (uint32_t mip = 0, region = 0; mip < src.mip_levels; mip++, region++) { + for (uint32_t mip = src.mip_min_level, region = 0; mip <= src.mip_max_level; + mip++, region++) { if (!ConvertTexture(&unpack_buffer[unpack_offset], ©_regions[region], mip, src)) { XELOGW("Failed to convert texture mip %u!", mip); @@ -1086,8 +1051,8 @@ bool TextureCache::UploadTexture(VkCommandBuffer command_buffer, } else { barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; } - barrier.subresourceRange.baseMipLevel = 0; - barrier.subresourceRange.levelCount = src.mip_levels; + barrier.subresourceRange.baseMipLevel = src.mip_min_level; + barrier.subresourceRange.levelCount = src.mip_levels(); barrier.subresourceRange.baseArrayLayer = copy_regions[0].imageSubresource.baseArrayLayer; barrier.subresourceRange.layerCount = @@ -1103,7 +1068,7 @@ bool TextureCache::UploadTexture(VkCommandBuffer command_buffer, dest->format == VK_FORMAT_D32_SFLOAT_S8_UINT) { // Do just a depth upload (for now). // This assumes depth buffers don't have mips (hopefully they don't) - assert_true(src.mip_levels == 1); + assert_true(src.mip_levels() == 1); copy_regions[0].imageSubresource.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT; } @@ -1197,7 +1162,7 @@ uint32_t TextureCache::ComputeTextureStorage(const TextureInfo& src) { uint32_t height = src.height + 1; uint32_t depth = src.depth + 1; uint32_t length = 0; - for (uint32_t mip = 0; mip < src.mip_levels; ++mip) { + for (uint32_t mip = src.mip_min_level; mip <= src.mip_max_level; ++mip) { if (mip == 0 && !src.memory.base_address) { continue; } else if (mip > 0 && !src.memory.mip_address) {