graphics: name game Vulkan resources in debug dumps

This commit is contained in:
nmzik
2026-08-21 04:48:43 +02:00
parent 7022afa86e
commit 342fae06a6
5 changed files with 145 additions and 49 deletions
+4
View File
@@ -9,6 +9,7 @@
#include "graphics/host_gpu/renderer/commandScheduler.h"
#include "graphics/host_gpu/renderer/render.h"
#include "graphics/host_gpu/renderer/renderContext.h"
#include "graphics/host_gpu/vulkanCommon.h"
#include "kernel/memory.h"
#include <algorithm>
@@ -435,6 +436,9 @@ BufferCache::CachedBuffer& BufferCache::GetOrCreateBuffer(CommandBuffer& command
cached->tick_accessed_last = m_gc_tick;
cached->buffer = std::make_shared<Buffer>(m_graphics, m_scheduler, MemoryUsage::DeviceLocal,
merged.address, AllFlags, merged.size);
SetVulkanObjectNameF(m_graphics.device, cached->buffer->Handle(),
"Kyty.GameBuffer[guest=0x{:016x} size=0x{:x}]", cached->vaddr,
cached->size);
for (const auto overlap: overlaps) {
const auto& old = *overlap->second;
cached->buffer->CopyFrom(command, *old.buffer, 0, old.vaddr - cached->vaddr, old.size);
+35 -2
View File
@@ -34,6 +34,36 @@ constexpr uint64_t NumFramesBeforeRemoval = 32;
thread_local const TextureCache* g_locked_cache = nullptr;
[[nodiscard]] const char* BindingTypeName(TextureCache::BindingType type) {
switch (type) {
case TextureCache::BindingType::Texture: return "Texture";
case TextureCache::BindingType::Storage: return "StorageTexture";
case TextureCache::BindingType::RenderTarget: return "ColorTarget";
case TextureCache::BindingType::DepthTarget: return "DepthTarget";
case TextureCache::BindingType::VideoOut: return "VideoOut";
}
return "Image";
}
void NameImageBinding(GraphicContext& graphics, Image& image, vk::ImageView view,
TextureCache::BindingType type, const ImageViewInfo& view_info) {
const auto* role = BindingTypeName(type);
SetVulkanObjectNameF(
graphics.device, image.backing.image,
"Kyty.{}.Image[guest=0x{:016x} size=0x{:x} extent={}x{}x{} format={} mips={} layers={} "
"samples={}]",
role, image.info.data.address, image.info.data.size, image.info.extent.width,
image.info.extent.height, image.info.extent.depth,
static_cast<uint32_t>(image.info.pixel_format), image.info.resources.levels,
image.info.resources.layers, image.info.samples);
SetVulkanObjectNameF(
graphics.device, view,
"Kyty.{}.View[guest=0x{:016x} format={} aspect=0x{:x} mip={}+{} layer={}+{}]", role,
image.info.data.address, static_cast<uint32_t>(view_info.format),
static_cast<vk::ImageAspectFlags::MaskType>(view_info.aspect), view_info.base_level,
view_info.level_count, view_info.base_layer, view_info.layer_count);
}
class CacheLock final {
public:
CacheLock(const TextureCache& owner, TrackingSpinLock& lock): m_lock(lock) {
@@ -714,7 +744,7 @@ ImageId TextureCache::ResolveDepthOverlap(const ImageInfo& requested, BindingTyp
cached.info.data.size / cached.info.resources.layers &&
!requested.HasStencil() && !cached.info.HasStencil() && !requested.HasMetadata() &&
!cached.info.HasMetadata();
bool recreate = cached.info.resources < requested.resources;
bool recreate = cached.info.resources < requested.resources;
switch (binding) {
case BindingType::Texture:
recreate |= requested.IsDepth() && !cached.info.IsDepth();
@@ -1359,6 +1389,7 @@ vk::ImageView TextureCache::FindTexture(ImageId id, const ImageDesc& desc) {
default: EXIT("TextureCache: invalid texture binding\n");
}
const auto view = image.FindView(desc.view_info);
NameImageBinding(m_graphics, image, view, desc.type, desc.view_info);
RetainImage(m_scheduler.Current(), id);
return view;
}
@@ -1392,6 +1423,7 @@ vk::ImageView TextureCache::FindRenderTarget(ImageId id, const ImageDesc& desc)
CommitGpuWrite(image);
TrackImageDownloadLocked(id, image);
const auto view = image.FindView(desc.view_info);
NameImageBinding(m_graphics, image, view, desc.type, desc.view_info);
RetainImage(m_scheduler.Current(), id);
return view;
}
@@ -1431,6 +1463,7 @@ vk::ImageView TextureCache::FindDepthTarget(ImageId id, const ImageDesc& desc) {
AssociateStencilLocked(id, desc.info.stencil);
}
const auto view = image.FindView(desc.view_info);
NameImageBinding(m_graphics, image, view, desc.type, desc.view_info);
RetainImage(m_scheduler.Current(), id);
return view;
}
@@ -1957,7 +1990,7 @@ bool TextureCache::TryConsumeDccFill(uint64_t address, uint64_t size, uint32_t f
// This dispatch may precede color-target discovery. Retain it in the shared
// metadata map, but PendingDcc remains invisible to IsMeta until registration. Returning
// false lets the guest dispatch execute and initialize memory while the type is uncertain.
m_surface_metas.emplace(address, MetaDataInfo {.type = MetaDataInfo::Type::PendingDcc,
m_surface_metas.emplace(address, MetaDataInfo {.type = MetaDataInfo::Type::PendingDcc,
.clear_mask = dcc_clear_mask,
.fill_value = fill_value,
.fill_size = size});
@@ -45,6 +45,16 @@
namespace Libs::Graphics {
static const char* ShaderStageResourceName(DescriptorCache::Stage stage) {
switch (stage) {
case DescriptorCache::Stage::Vertex: return "Vertex";
case DescriptorCache::Stage::Pixel: return "Pixel";
case DescriptorCache::Stage::Compute: return "Compute";
case DescriptorCache::Stage::Unknown: return "Unknown";
}
return "Unknown";
}
static void BindNullStorageBuffer(RenderContext& context, BufferView& dst) {
auto owner = context.GetBufferCache().ObtainNullBuffer();
dst.buffer = owner->Handle();
@@ -82,7 +92,8 @@ static bool IsMultisampledTexture(Prospero::ImageType type) {
static BufferView NativeStorageBuffer(RenderContext& context, CommandBuffer& command_buffer,
const ShaderBufferResource& descriptor,
const ShaderRecompiler::IR::BufferResource& resource,
uint32_t& buffer_offset) {
DescriptorCache::Stage stage, uint32_t slot,
uint32_t& buffer_offset) {
BufferView result;
buffer_offset = 0;
@@ -117,6 +128,11 @@ static BufferView NativeStorageBuffer(RenderContext& context, CommandBuffer& com
result.buffer = binding.buffer;
result.offset = aligned_offset;
result.range = static_cast<vk::DeviceSize>(size + adjustment);
SetVulkanObjectNameF(
graphics.device, result.buffer,
"Kyty.{}.StorageBuffer[slot={} guest=0x{:016x} size=0x{:x} access={} formatted={}]",
ShaderStageResourceName(stage), slot, address, size,
resource.written ? (resource.read ? "ReadWrite" : "Write") : "Read", resource.formatted);
return result;
}
@@ -124,7 +140,7 @@ static BufferView
NativeAddressBuffer(RenderContext& context, CommandBuffer& command_buffer,
const ShaderRecompiler::IR::AddressResource& resource,
const ShaderRecompiler::IR::ResourceSnapshot::Address& address,
uint32_t& address_offset) {
DescriptorCache::Stage stage, uint32_t slot, uint32_t& address_offset) {
BufferView result;
address_offset = 0;
if (address.binding_base == 0) {
@@ -147,17 +163,20 @@ NativeAddressBuffer(RenderContext& context, CommandBuffer& command_buffer,
}
const auto& graphics = context.GetGraphics();
const auto alignment = graphics.StorageMinAlignment();
auto binding =
auto binding =
context.GetBufferCache().ObtainBuffer(command_buffer, address.binding_base, size);
const auto aligned_offset = binding.offset - binding.offset % alignment;
const auto adjustment = binding.offset - aligned_offset;
const auto max_range = graphics.GetPhysicalDeviceProperties().limits.maxStorageBufferRange;
const auto visible_size = std::min(size, static_cast<uint64_t>(max_range - adjustment));
address_offset = static_cast<uint32_t>(adjustment);
result.owner = std::move(binding.owner);
result.buffer = binding.buffer;
result.offset = aligned_offset;
result.range = static_cast<vk::DeviceSize>(visible_size + adjustment);
const auto visible_size = std::min(size, static_cast<uint64_t>(max_range - adjustment));
address_offset = static_cast<uint32_t>(adjustment);
result.owner = std::move(binding.owner);
result.buffer = binding.buffer;
result.offset = aligned_offset;
result.range = static_cast<vk::DeviceSize>(visible_size + adjustment);
SetVulkanObjectNameF(graphics.device, result.buffer,
"Kyty.{}.AddressBuffer[slot={} guest=0x{:016x} size=0x{:x}]",
ShaderStageResourceName(stage), slot, address.binding_base, visible_size);
return result;
}
@@ -458,10 +477,9 @@ void ValidateStorageTexture(const ShaderRecompiler::IR::ImageResource& resource,
const bool raw_sint_storage = format == Prospero::BufferFormat::k32SInt && uint_resource &&
resource.written && !resource.read && !resource.atomic;
const bool format_ok =
raw_sint_storage ||
(Prospero::IsSampledTextureFormat(format) &&
uint_resource == Prospero::IsUintTextureFormat(format) &&
(!resource.atomic || format == Prospero::BufferFormat::k32UInt));
raw_sint_storage || (Prospero::IsSampledTextureFormat(format) &&
uint_resource == Prospero::IsUintTextureFormat(format) &&
(!resource.atomic || format == Prospero::BufferFormat::k32UInt));
if (resource_ok && descriptor_ok && encoding_ok && format_ok && size != 0) {
return;
}
@@ -568,8 +586,8 @@ static void PopulateTextureMipLayout(ImageInfo& info) {
static ImageViewInfo TextureViewInfo(const ShaderRecompiler::IR::ImageResource& resource,
const ShaderTextureResource& descriptor, vk::Format format,
bool shader_conversion, bool storage,
uint32_t view_levels, uint32_t image_layers) {
bool shader_conversion, bool storage, uint32_t view_levels,
uint32_t image_layers) {
ImageViewInfo view {};
view.format = format;
view.aspect = vk::ImageAspectFlagBits::eColor;
@@ -684,10 +702,9 @@ RenderExecutor::ResolveTexture(const ShaderRecompiler::IR::ImageResource& reso
const bool shader_conversion =
surface_format.conversion_format != Prospero::BufferFormat::kInvalid;
const bool sampled_numeric_class =
storage ||
(Prospero::IsSampledTextureFormat(format) &&
(resource.kind == ShaderRecompiler::IR::ResourceKind::ImageUint) ==
Prospero::IsUintTextureFormat(format));
storage || (Prospero::IsSampledTextureFormat(format) &&
(resource.kind == ShaderRecompiler::IR::ResourceKind::ImageUint) ==
Prospero::IsUintTextureFormat(format));
if (!storage &&
(resource.kind == ShaderRecompiler::IR::ResourceKind::Image ||
resource.kind == ShaderRecompiler::IR::ResourceKind::ImageUint) &&
@@ -752,7 +769,7 @@ RenderExecutor::ResolveTexture(const ShaderRecompiler::IR::ImageResource& reso
view_levels, desc.info.resources.layers);
desc.type = storage ? TextureCache::BindingType::Storage : TextureCache::BindingType::Texture;
auto id = texture_cache.FindImage(desc, shader_conversion);
auto id = texture_cache.FindImage(desc, shader_conversion);
auto* image = &texture_cache.GetImage(id);
const bool stencil_association = static_cast<bool>(image->depth_id);
if (stencil_association) {
@@ -910,17 +927,18 @@ void RenderExecutor::RebindBuffers(CommandBuffer& buffer,
CopyNativeDescriptor(snapshot.buffers[i], descriptor.fields);
uint32_t buffer_offset = 0;
resources.buffers.push_back(NativeStorageBuffer(m_context, buffer, descriptor,
program.info.buffers[i], buffer_offset));
program.info.buffers[i], prepared.stage, i,
buffer_offset));
pack_memory_offset(i, buffer_offset);
}
resources.addresses.clear();
resources.addresses.reserve(program.info.addresses.size());
for (uint32_t i = 0; i < program.info.addresses.size(); i++) {
uint32_t address_offset = 0;
resources.addresses.push_back(NativeAddressBuffer(
m_context, buffer, program.info.addresses[i], snapshot.addresses[i], address_offset));
pack_memory_offset(static_cast<uint32_t>(program.info.buffers.size()) + i,
address_offset);
resources.addresses.push_back(
NativeAddressBuffer(m_context, buffer, program.info.addresses[i], snapshot.addresses[i],
prepared.stage, i, address_offset));
pack_memory_offset(static_cast<uint32_t>(program.info.buffers.size()) + i, address_offset);
}
}
+42 -22
View File
@@ -142,18 +142,6 @@ static void LogMrtState(const char* draw_name, const RenderCommandBuffer& buffer
(bc0.separate_alpha_blend && (IsDualSourceBlendFactor(bc0.alpha_srcblend) ||
IsDualSourceBlendFactor(bc0.alpha_destblend)));
for (uint32_t i = 1; i < 8; i++) {
const auto& rt = ctx.GetRenderTarget(i);
if (rt.base.addr != 0 || ps_input_info.target_output_mode[i] != 0 ||
((rt_mask >> (i * 4u)) & 0x0fu) != 0 || ((cb_shader_mask >> (i * 4u)) & 0x0fu) != 0) {
interesting = true;
}
}
if (!interesting) {
return;
}
auto log_id = g_mrt_state_log_count.fetch_add(1);
if (log_id >= 32) {
return;
@@ -553,8 +541,7 @@ struct DrawCallInfo {
};
static bool ResolveDccAttachmentClear(TextureCache& cache, const RenderColorInfo& target,
const ImageViewInfo& view,
vk::ClearColorValue& clear_value) {
const ImageViewInfo& view, vk::ClearColorValue& clear_value) {
if (target.desc.info.metadata.kind != ImageMetadataKind::Dcc) {
return false;
}
@@ -641,6 +628,15 @@ RenderState RenderExecutor::AcquireRenderTargets(CommandBuffer& buffer, RenderCo
}
target.image_view = cache.FindRenderTarget(target.image_id, target.desc);
auto& image = cache.GetImage(target.image_id);
SetVulkanObjectNameF(m_context.GetGraphics().device, image.backing.image,
"Kyty.MRT{}.Image[guest=0x{:016x} size=0x{:x} format={}]",
target.target_slot, image.info.data.address, image.info.data.size,
static_cast<uint32_t>(image.info.pixel_format));
SetVulkanObjectNameF(m_context.GetGraphics().device, target.image_view,
"Kyty.MRT{}.View[guest=0x{:016x} mip={} layer={}+{}]",
target.target_slot, image.info.data.address,
target.desc.view_info.base_level, target.desc.view_info.base_layer,
target.desc.view_info.layer_count);
EXIT_IF(image.backing.samples != target.samples || target.image_view == nullptr);
if (attachment_samples == 0) {
attachment_samples = target.samples;
@@ -657,15 +653,15 @@ RenderState RenderExecutor::AcquireRenderTargets(CommandBuffer& buffer, RenderCo
ImageSubresourceRange {view.base_level, view.level_count, view.base_layer,
view.layer_count},
buffer.Handle());
state.width = std::min(state.width, target.extent.width);
state.height = std::min(state.height, target.extent.height);
state.num_layers = std::min(state.num_layers, view.layer_count);
auto& attachment = state.color_attachments[i];
attachment.image_view = target.image_view;
attachment.image_layout = layout;
attachment.clear_value = target.color_clear_value.uint32;
state.width = std::min(state.width, target.extent.width);
state.height = std::min(state.height, target.extent.height);
state.num_layers = std::min(state.num_layers, view.layer_count);
auto& attachment = state.color_attachments[i];
attachment.image_view = target.image_view;
attachment.image_layout = layout;
attachment.clear_value = target.color_clear_value.uint32;
vk::ClearColorValue metadata_clear_value {};
const bool metadata_clear =
const bool metadata_clear =
ResolveDccAttachmentClear(cache, target, view, metadata_clear_value);
if (metadata_clear) {
attachment.clear_value = metadata_clear_value.uint32;
@@ -690,6 +686,14 @@ RenderState RenderExecutor::AcquireRenderTargets(CommandBuffer& buffer, RenderCo
EXIT("failed to consume HTile clear state\n");
}
auto& image = cache.GetImage(depth.image_id);
SetVulkanObjectNameF(m_context.GetGraphics().device, image.backing.image,
"Kyty.DepthTarget.Image[guest=0x{:016x} size=0x{:x} format={}]",
image.info.data.address, image.info.data.size,
static_cast<uint32_t>(image.info.pixel_format));
SetVulkanObjectNameF(m_context.GetGraphics().device, depth.image_view,
"Kyty.DepthTarget.View[guest=0x{:016x} layer={}+{}]",
image.info.data.address, depth.desc.view_info.base_layer,
depth.desc.view_info.layer_count);
EXIT_IF(depth.image_view == nullptr || image.backing.samples != depth.samples);
if (attachment_samples == 0) {
attachment_samples = depth.samples;
@@ -862,6 +866,9 @@ static PreparedVertexBuffers AcquireVertexBuffers(RenderCommandBuffer& b
Libs::LibKernel::Memory::ClampRangeSize(range.base_address, range.RequestedSize());
range.acquired_end = range.base_address + size;
range.binding = cache.ObtainBuffer(buffer, range.base_address, size);
SetVulkanObjectNameF(buffer.GetContext().GetGraphics().device, range.binding.buffer,
"Kyty.VertexBufferRange[guest=0x{:016x} size=0x{:x}]",
range.base_address, size);
}
// Rebuild slot bindings, offsetting non-empty slots into their acquired merged range.
@@ -894,6 +901,10 @@ static PreparedVertexBuffers AcquireVertexBuffers(RenderCommandBuffer& b
prepared.buffers[i] = range->binding.buffer;
prepared.offsets[i] = range->binding.offset + vertex.addr - range->base_address;
SetVulkanObjectNameF(
buffer.GetContext().GetGraphics().device, prepared.buffers[i],
"Kyty.VertexBuffer[slot={} guest=0x{:016x} size=0x{:x} stride={} records={}]", i,
vertex.addr, size, vertex.stride, vertex.num_records);
}
if (null_owner != nullptr) {
@@ -1104,6 +1115,15 @@ static PreparedIndexBuffer PrepareIndexBuffer(RenderCommandBuffer& buffe
prepared.buffer = binding.buffer;
prepared.offset = binding.offset;
}
if (source.host_data != nullptr) {
SetVulkanObjectNameF(buffer.GetContext().GetGraphics().device, prepared.buffer,
"Kyty.IndexBuffer[guest=transient size=0x{:x} type={}]", source.size,
static_cast<uint32_t>(source.type));
} else {
SetVulkanObjectNameF(buffer.GetContext().GetGraphics().device, prepared.buffer,
"Kyty.IndexBuffer[guest=0x{:016x} size=0x{:x} type={}]",
source.address, source.size, static_cast<uint32_t>(source.type));
}
return prepared;
}
+21
View File
@@ -10,10 +10,13 @@
#define VMA_STATIC_VULKAN_FUNCTIONS 0
#define VMA_DYNAMIC_VULKAN_FUNCTIONS 1
#include "common/emulatorConfig.h"
#include "graphics/guest_gpu/gpu_defs.h"
#include <cstdint>
#include <fmt/format.h>
#include <string>
#include <utility>
#include <vector>
#include <vulkan/vulkan.hpp>
#include <vulkan/vulkan_hash.hpp>
@@ -29,6 +32,24 @@ std::string VulkanToString(vk::QueueFlags value);
vk::Format VulkanFormat(Prospero::BufferFormat guest_format);
void RequireVulkanSuccess(vk::Result result, const char* operation);
template <typename Handle, typename... Args>
void SetVulkanObjectNameF(vk::Device device, Handle handle, fmt::format_string<Args...> format,
Args&&... args) {
if (!Config::GraphicsDebugDumpEnabled() || device == nullptr || handle == nullptr ||
VULKAN_HPP_DEFAULT_DISPATCHER.vkSetDebugUtilsObjectNameEXT == nullptr) {
return;
}
const auto name = fmt::format(format, std::forward<Args>(args)...);
vk::DebugUtilsObjectNameInfoEXT info {};
info.sType = vk::StructureType::eDebugUtilsObjectNameInfoEXT;
info.objectType = Handle::objectType;
info.objectHandle = static_cast<uint64_t>(
reinterpret_cast<uintptr_t>(static_cast<typename Handle::CType>(handle)));
info.pObjectName = name.c_str();
(void)device.setDebugUtilsObjectNameEXT(&info);
}
template <typename T, typename Enumerator>
[[nodiscard]] std::vector<T> EnumerateVulkan(const char* operation, Enumerator&& enumerate) {
for (;;) {