vulkan: Handle incompatible depth format using null binding. (#2892)

Co-authored-by: kalaposfos13 <153381648+kalaposfos13@users.noreply.github.com>
This commit is contained in:
squidbus 2025-05-09 08:43:20 -07:00 committed by GitHub
parent 8e7c5a4d99
commit b130fe6ed5
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 55 additions and 17 deletions

View file

@ -281,6 +281,11 @@ constexpr AmdGpu::Image ImageResource::GetSharp(const Info& info) const noexcept
// Fall back to null image if unbound.
return AmdGpu::Image::Null();
}
const auto data_fmt = image.GetDataFmt();
if (is_depth && data_fmt != AmdGpu::DataFormat::Format16 &&
data_fmt != AmdGpu::DataFormat::Format32) {
return AmdGpu::Image::NullDepth();
}
return image;
}

View file

@ -363,6 +363,12 @@ void PatchImageSharp(IR::Block& block, IR::Inst& inst, Info& info, Descriptors&
LOG_ERROR(Render_Vulkan, "Shader compiled with unbound image!");
image = AmdGpu::Image::Null();
}
const auto data_fmt = image.GetDataFmt();
if (inst_info.is_depth && data_fmt != AmdGpu::DataFormat::Format16 &&
data_fmt != AmdGpu::DataFormat::Format32) {
LOG_ERROR(Render_Vulkan, "Shader compiled using non-depth image with depth instruction!");
image = AmdGpu::Image::NullDepth();
}
ASSERT(image.GetType() != AmdGpu::ImageType::Invalid);
const bool is_written = inst.GetOpcode() == IR::Opcode::ImageWrite;

View file

@ -219,6 +219,19 @@ struct Image {
return image;
}
static constexpr Image NullDepth() {
Image image{};
image.data_format = u64(DataFormat::Format32);
image.num_format = u64(NumberFormat::Float);
image.dst_sel_x = u64(CompSwizzle::Red);
image.dst_sel_y = u64(CompSwizzle::Green);
image.dst_sel_z = u64(CompSwizzle::Blue);
image.dst_sel_w = u64(CompSwizzle::Alpha);
image.tiling_index = u64(TilingMode::Texture_MicroTiled);
image.type = u64(ImageType::Color2D);
return image;
}
bool Valid() const {
return (type & 0x8u) != 0;
}

View file

@ -618,8 +618,9 @@ void Rasterizer::BindTextures(const Shader::Info& stage, Shader::Backend::Bindin
if (instance.IsNullDescriptorSupported()) {
image_infos.emplace_back(VK_NULL_HANDLE, VK_NULL_HANDLE, vk::ImageLayout::eGeneral);
} else {
auto& null_image = texture_cache.GetImageView(VideoCore::NULL_IMAGE_VIEW_ID);
image_infos.emplace_back(VK_NULL_HANDLE, *null_image.image_view,
auto& null_image_view =
texture_cache.FindTexture(VideoCore::NULL_IMAGE_ID, desc.view_info);
image_infos.emplace_back(VK_NULL_HANDLE, *null_image_view.image_view,
vk::ImageLayout::eGeneral);
}
} else {

View file

@ -34,8 +34,6 @@ struct ImageViewInfo {
struct Image;
constexpr Common::SlotId NULL_IMAGE_VIEW_ID{0};
struct ImageView {
ImageView(const Vulkan::Instance& instance, const ImageViewInfo& info, Image& image,
ImageId image_id);

View file

@ -8,6 +8,7 @@
#include "common/debug.h"
#include "video_core/buffer_cache/buffer_cache.h"
#include "video_core/page_manager.h"
#include "video_core/renderer_vulkan/liverpool_to_vk.h"
#include "video_core/renderer_vulkan/vk_instance.h"
#include "video_core/renderer_vulkan/vk_scheduler.h"
#include "video_core/texture_cache/host_compatibility.h"
@ -23,31 +24,41 @@ TextureCache::TextureCache(const Vulkan::Instance& instance_, Vulkan::Scheduler&
BufferCache& buffer_cache_, PageManager& tracker_)
: instance{instance_}, scheduler{scheduler_}, buffer_cache{buffer_cache_}, tracker{tracker_},
tile_manager{instance, scheduler} {
// Create basic null image at fixed image ID.
const auto null_id = GetNullImage(vk::Format::eR8G8B8A8Unorm);
ASSERT(null_id.index == NULL_IMAGE_ID.index);
}
TextureCache::~TextureCache() = default;
ImageId TextureCache::GetNullImage(const vk::Format format) {
const auto existing_image = null_images.find(format);
if (existing_image != null_images.end()) {
return existing_image->second;
}
ImageInfo info{};
info.pixel_format = vk::Format::eR8G8B8A8Unorm;
info.pixel_format = format;
info.type = vk::ImageType::e2D;
info.tiling_idx = u32(AmdGpu::TilingMode::Texture_MicroTiled);
info.tiling_idx = static_cast<u32>(AmdGpu::TilingMode::Texture_MicroTiled);
info.num_bits = 32;
info.UpdateSize();
const ImageId null_id = slot_images.insert(instance, scheduler, info);
ASSERT(null_id.index == NULL_IMAGE_ID.index);
auto& img = slot_images[null_id];
const vk::Image& null_image = img.image;
Vulkan::SetObjectName(instance.GetDevice(), null_image, "Null Image");
Vulkan::SetObjectName(instance.GetDevice(), null_image,
fmt::format("Null Image ({})", vk::to_string(format)));
img.flags = ImageFlagBits::Empty;
img.track_addr = img.info.guest_address;
img.track_addr_end = img.info.guest_address + img.info.guest_size;
ImageViewInfo view_info;
const auto null_view_id =
slot_image_views.insert(instance, view_info, slot_images[null_id], null_id);
ASSERT(null_view_id.index == NULL_IMAGE_VIEW_ID.index);
const vk::ImageView& null_image_view = slot_image_views[null_view_id].image_view.get();
Vulkan::SetObjectName(instance.GetDevice(), null_image_view, "Null Image View");
null_images.emplace(format, null_id);
return null_id;
}
TextureCache::~TextureCache() = default;
void TextureCache::MarkAsMaybeDirty(ImageId image_id, Image& image) {
if (image.hash == 0) {
// Initialize hash
@ -296,7 +307,7 @@ ImageId TextureCache::FindImage(BaseDesc& desc, FindFlags flags) {
const auto& info = desc.info;
if (info.guest_address == 0) [[unlikely]] {
return NULL_IMAGE_ID;
return GetNullImage(info.pixel_format);
}
std::scoped_lock lock{mutex};

View file

@ -246,6 +246,9 @@ private:
}
}
/// Gets or creates a null image for a particular format.
ImageId GetNullImage(vk::Format format);
/// Create an image from the given parameters
[[nodiscard]] ImageId InsertImage(const ImageInfo& info, VAddr cpu_addr);
@ -285,6 +288,7 @@ private:
Common::SlotVector<Image> slot_images;
Common::SlotVector<ImageView> slot_image_views;
tsl::robin_map<u64, Sampler> samplers;
tsl::robin_map<vk::Format, ImageId> null_images;
PageTable page_table;
std::mutex mutex;