[GPU/Vulkan] Maybe fix minimum mip level. Probably breaks everything.

This commit is contained in:
gibbed 2018-06-04 10:13:28 -05:00
parent b5a88d1a7d
commit 2c7043bd98
4 changed files with 86 additions and 92 deletions

View File

@ -9,6 +9,8 @@
#include <gflags/gflags.h> #include <gflags/gflags.h>
#include "xenia/base/logging.h"
#include "xenia/base/math.h"
#include "xenia/gpu/texture_info.h" #include "xenia/gpu/texture_info.h"
DEFINE_bool(texture_dump, false, "Dump textures to DDS"); DEFINE_bool(texture_dump, false, "Dump textures to DDS");
@ -48,11 +50,11 @@ void TextureDump(const TextureInfo& src, void* buffer, size_t length) {
} else { } else {
dds_header.flags |= 0x8u; dds_header.flags |= 0x8u;
} }
dds_header.height = src.height + 1; dds_header.height = std::max(1u, (src.height + 1) >> src.mip_min_level);
dds_header.width = src.width + 1; 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 * dds_header.pitch_or_linear_size = src.GetMipExtent(0, false).block_pitch_h *
src.format_info()->bytes_per_block(); 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); dds_header.pixel_format.size = sizeof(dds_header.pixel_format);
switch (src.format) { switch (src.format) {
@ -84,6 +86,7 @@ void TextureDump(const TextureInfo& src, void* buffer, size_t length) {
assert_unhandled_case(src.format); assert_unhandled_case(src.format);
std::memset(&dds_header.pixel_format, 0xCD, std::memset(&dds_header.pixel_format, 0xCD,
sizeof(dds_header.pixel_format)); sizeof(dds_header.pixel_format));
XELOGW("Skipping %s for texture dump.", src.format_info()->name);
return; return;
} }
} }

View File

@ -62,8 +62,9 @@ bool TextureInfo::Prepare(const xe_gpu_texture_fetch_t& fetch,
break; break;
} }
info.pitch = fetch.pitch << 5; 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.is_tiled = fetch.tiled;
info.has_packed_mips = fetch.packed_mips; 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.extent = TextureExtent::Calculate(out_info, true);
info.SetupMemoryInfo(fetch.base_address << 12, fetch.mip_address << 12); 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; return true;
} }
@ -107,7 +103,8 @@ bool TextureInfo::PrepareResolve(uint32_t physical_address,
info.depth = depth - 1; info.depth = depth - 1;
info.pitch = pitch; info.pitch = pitch;
info.mip_levels = 1; info.mip_min_level = 0;
info.mip_max_level = 0;
info.is_tiled = true; info.is_tiled = true;
info.has_packed_mips = false; info.has_packed_mips = false;
@ -132,8 +129,14 @@ const TextureExtent TextureInfo::GetMipExtent(uint32_t mip,
if (mip == 0) { if (mip == 0) {
return extent; return extent;
} }
uint32_t mip_width = xe::next_pow2(width + 1) >> mip; uint32_t mip_width, mip_height;
uint32_t mip_height = xe::next_pow2(height + 1) >> mip; 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, return TextureExtent::Calculate(format_info(), mip_width, mip_height,
depth + 1, is_tiled, is_guest); 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) { void TextureInfo::SetupMemoryInfo(uint32_t base_address, uint32_t mip_address) {
uint32_t bytes_per_block = format_info()->bytes_per_block(); uint32_t bytes_per_block = format_info()->bytes_per_block();
memory.base_address = base_address; memory.base_address = 0;
memory.base_size = GetMipExtent(0, true).visible_blocks() * bytes_per_block; memory.base_size = 0;
memory.mip_address = 0; memory.mip_address = 0;
memory.mip_size = 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. // Sort circuit. Only one mip.
return; 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. // TODO(gibbed): This doesn't actually make any sense. Force only one mip.
// Offending title issues: #26, #45 // Offending title issues: #26, #45
return; 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; memory.mip_address = mip_address;
if (!has_packed_mips) { 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, true).all_blocks() * bytes_per_block;
} }
memory.mip_size += memory.mip_size +=
GetMipExtent(mip_levels - 1, true).visible_blocks() * bytes_per_block; GetMipExtent(mip_max_level, true).visible_blocks() * bytes_per_block;
return; 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); uint32_t height_pow2 = xe::next_pow2(height + 1);
// Walk forward to find the address of the mip. // Walk forward to find the address of the mip.
uint32_t packed_mip_base = 1; uint32_t packed_mip_base = std::max(1u, mip_min_level);
for (uint32_t mip = packed_mip_base; mip < mip_levels - 1; for (uint32_t mip = packed_mip_base; mip < mip_max_level;
mip++, packed_mip_base++) { mip++, packed_mip_base++) {
uint32_t mip_width = std::max(width_pow2 >> mip, 1u); uint32_t mip_width = std::max(width_pow2 >> mip, 1u);
uint32_t mip_height = std::max(height_pow2 >> mip, 1u); uint32_t mip_height = std::max(height_pow2 >> mip, 1u);

View File

@ -327,7 +327,8 @@ struct TextureInfo {
uint32_t height; // height in pixels uint32_t height; // height in pixels
uint32_t depth; // depth in layers uint32_t depth; // depth in layers
uint32_t pitch; // pitch in blocks uint32_t pitch; // pitch in blocks
uint32_t mip_levels; uint32_t mip_min_level;
uint32_t mip_max_level;
bool is_tiled; bool is_tiled;
bool has_packed_mips; bool has_packed_mips;
@ -342,6 +343,8 @@ struct TextureInfo {
return format_info()->type == FormatType::kCompressed; 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, static bool Prepare(const xenos::xe_gpu_texture_fetch_t& fetch,
TextureInfo* out_info); TextureInfo* out_info);

View File

@ -21,8 +21,6 @@
#include "xenia/gpu/vulkan/vulkan_gpu_flags.h" #include "xenia/gpu/vulkan/vulkan_gpu_flags.h"
#include "xenia/ui/vulkan/vulkan_mem_alloc.h" #include "xenia/ui/vulkan/vulkan_mem_alloc.h"
DEFINE_bool(enable_mip_watches, false, "Enable mipmap watches");
DECLARE_bool(texture_dump); DECLARE_bool(texture_dump);
namespace xe { namespace xe {
@ -252,7 +250,7 @@ TextureCache::Texture* TextureCache::AllocateTexture(
image_info.extent.width = texture_info.width + 1; image_info.extent.width = texture_info.width + 1;
image_info.extent.height = texture_info.height + 1; image_info.extent.height = texture_info.height + 1;
image_info.extent.depth = !is_cube ? texture_info.depth + 1 : 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.arrayLayers = !is_cube ? 1 : 6;
image_info.samples = VK_SAMPLE_COUNT_1_BIT; image_info.samples = VK_SAMPLE_COUNT_1_BIT;
image_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE; 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) // Tell the trace writer to "cache" this memory (but not read it)
if (texture_info.memory.base_address) {
trace_writer_->WriteMemoryReadCached(texture_info.memory.base_address, trace_writer_->WriteMemoryReadCached(texture_info.memory.base_address,
texture_info.memory.base_size); texture_info.memory.base_size);
}
if (texture_info.memory.mip_address) { if (texture_info.memory.mip_address) {
trace_writer_->WriteMemoryReadCached(texture_info.memory.mip_address, trace_writer_->WriteMemoryReadCached(texture_info.memory.mip_address,
texture_info.memory.mip_size); texture_info.memory.mip_size);
@ -390,34 +390,14 @@ TextureCache::Texture* TextureCache::DemandResolveTexture(
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.
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->access_watch_handle = memory_->AddPhysicalAccessWatch(
texture_info.memory.base_address, texture_info.memory.base_size, texture_info.memory.base_address, texture_info.memory.base_size,
cpu::MMIOHandler::kWatchWrite, &WatchCallback, this, texture); cpu::MMIOHandler::kWatchWrite, &WatchCallback, this, texture);
} else { } else if (texture_info.memory.mip_address && texture_info.memory.mip_size) {
// 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;
}
texture->access_watch_handle = memory_->AddPhysicalAccessWatch( texture->access_watch_handle = memory_->AddPhysicalAccessWatch(
watch_address, watch_size, cpu::MMIOHandler::kWatchWrite, texture_info.memory.mip_address, texture_info.memory.mip_size,
&WatchCallback, this, texture); cpu::MMIOHandler::kWatchWrite, &WatchCallback, this, texture);
} }
textures_[texture_hash] = texture; textures_[texture_hash] = texture;
@ -438,8 +418,10 @@ TextureCache::Texture* TextureCache::Demand(const TextureInfo& texture_info,
break; break;
} }
if (texture_info.memory.base_address) {
trace_writer_->WriteMemoryReadCached(texture_info.memory.base_address, trace_writer_->WriteMemoryReadCached(texture_info.memory.base_address,
texture_info.memory.base_size); texture_info.memory.base_size);
}
if (texture_info.memory.mip_address) { if (texture_info.memory.mip_address) {
trace_writer_->WriteMemoryReadCached(texture_info.memory.mip_address, trace_writer_->WriteMemoryReadCached(texture_info.memory.mip_address,
texture_info.memory.mip_size); texture_info.memory.mip_size);
@ -474,8 +456,10 @@ TextureCache::Texture* TextureCache::Demand(const TextureInfo& texture_info,
// assert_always(); // assert_always();
} }
if (texture_info.memory.base_address) {
trace_writer_->WriteMemoryReadCached(texture_info.memory.base_address, trace_writer_->WriteMemoryReadCached(texture_info.memory.base_address,
texture_info.memory.base_size); texture_info.memory.base_size);
}
if (texture_info.memory.mip_address) { if (texture_info.memory.mip_address) {
trace_writer_->WriteMemoryReadCached(texture_info.memory.mip_address, trace_writer_->WriteMemoryReadCached(texture_info.memory.mip_address,
texture_info.memory.mip_size); 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 // Okay. Put a writewatch on it to tell us if it's been modified from the
// guest. // 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->access_watch_handle = memory_->AddPhysicalAccessWatch(
texture_info.memory.base_address, texture_info.memory.base_size, texture_info.memory.base_address, texture_info.memory.base_size,
cpu::MMIOHandler::kWatchWrite, &WatchCallback, this, texture); cpu::MMIOHandler::kWatchWrite, &WatchCallback, this, texture);
} else { } else if (texture_info.memory.mip_address && texture_info.memory.mip_size) {
// 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;
}
texture->access_watch_handle = memory_->AddPhysicalAccessWatch( texture->access_watch_handle = memory_->AddPhysicalAccessWatch(
watch_address, watch_size, cpu::MMIOHandler::kWatchWrite, texture_info.memory.mip_address, texture_info.memory.mip_size,
&WatchCallback, this, texture); cpu::MMIOHandler::kWatchWrite, &WatchCallback, this, texture);
} }
return texture; return texture;
@ -602,8 +566,8 @@ TextureCache::TextureView* TextureCache::DemandView(Texture* texture,
} else { } else {
view_info.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; view_info.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
} }
view_info.subresourceRange.baseMipLevel = 0; view_info.subresourceRange.baseMipLevel = texture->texture_info.mip_min_level;
view_info.subresourceRange.levelCount = texture->texture_info.mip_levels; view_info.subresourceRange.levelCount = texture->texture_info.mip_levels();
view_info.subresourceRange.baseArrayLayer = 0; view_info.subresourceRange.baseArrayLayer = 0;
view_info.subresourceRange.layerCount = !is_cube ? 1 : 6; view_info.subresourceRange.layerCount = !is_cube ? 1 : 6;
@ -978,13 +942,13 @@ bool TextureCache::UploadTexture(VkCommandBuffer command_buffer,
XELOGGPU( XELOGGPU(
"Uploading texture @ 0x%.8X/0x%.8X (%ux%ux%u, format: %s, dim: %s, " "Uploading texture @ 0x%.8X/0x%.8X (%ux%ux%u, format: %s, dim: %s, "
"levels: %u, pitch: %u, tiled: %s, packed mips: %s, unpack length: " "levels: %u (%u-%u), pitch: %u, tiled: %s, packed mips: %s, unpack "
"0x%.8X)", "length: 0x%.8X)",
src.memory.base_address, src.memory.mip_address, src.width + 1, src.memory.base_address, src.memory.mip_address, src.width + 1,
src.height + 1, src.depth + 1, src.format_info()->name, src.height + 1, src.depth + 1, src.format_info()->name,
get_dimension_name(src.dimension), src.mip_levels, src.pitch, get_dimension_name(src.dimension), src.mip_levels(), src.mip_min_level,
src.is_tiled ? "yes" : "no", src.has_packed_mips ? "yes" : "no", src.mip_max_level, src.pitch, src.is_tiled ? "yes" : "no",
unpack_length); src.has_packed_mips ? "yes" : "no", unpack_length);
XELOGGPU("Extent: %ux%ux%u %u,%u,%u", src.extent.pitch, src.extent.height, 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, 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 // 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 // texture and copy it to its destination. Otherwise, fallback to conversion
// on the CPU. // on the CPU.
uint32_t copy_region_count = src.mip_levels; uint32_t copy_region_count = src.mip_levels();
std::vector<VkBufferImageCopy> copy_regions(copy_region_count); std::vector<VkBufferImageCopy> copy_regions(copy_region_count);
// Upload all mips. // Upload all mips.
auto unpack_buffer = reinterpret_cast<uint8_t*>(alloc->host_ptr); auto unpack_buffer = reinterpret_cast<uint8_t*>(alloc->host_ptr);
VkDeviceSize unpack_offset = 0; 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], &copy_regions[region], if (!ConvertTexture(&unpack_buffer[unpack_offset], &copy_regions[region],
mip, src)) { mip, src)) {
XELOGW("Failed to convert texture mip %u!", mip); XELOGW("Failed to convert texture mip %u!", mip);
@ -1086,8 +1051,8 @@ bool TextureCache::UploadTexture(VkCommandBuffer command_buffer,
} else { } else {
barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
} }
barrier.subresourceRange.baseMipLevel = 0; barrier.subresourceRange.baseMipLevel = src.mip_min_level;
barrier.subresourceRange.levelCount = src.mip_levels; barrier.subresourceRange.levelCount = src.mip_levels();
barrier.subresourceRange.baseArrayLayer = barrier.subresourceRange.baseArrayLayer =
copy_regions[0].imageSubresource.baseArrayLayer; copy_regions[0].imageSubresource.baseArrayLayer;
barrier.subresourceRange.layerCount = barrier.subresourceRange.layerCount =
@ -1103,7 +1068,7 @@ bool TextureCache::UploadTexture(VkCommandBuffer command_buffer,
dest->format == VK_FORMAT_D32_SFLOAT_S8_UINT) { dest->format == VK_FORMAT_D32_SFLOAT_S8_UINT) {
// Do just a depth upload (for now). // Do just a depth upload (for now).
// This assumes depth buffers don't have mips (hopefully they don't) // 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; 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 height = src.height + 1;
uint32_t depth = src.depth + 1; uint32_t depth = src.depth + 1;
uint32_t length = 0; 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) { if (mip == 0 && !src.memory.base_address) {
continue; continue;
} else if (mip > 0 && !src.memory.mip_address) { } else if (mip > 0 && !src.memory.mip_address) {