diff --git a/src/core/libraries/network/net.cpp b/src/core/libraries/network/net.cpp index 1569a51ce..958f9264e 100644 --- a/src/core/libraries/network/net.cpp +++ b/src/core/libraries/network/net.cpp @@ -559,7 +559,7 @@ int PS4_SYSV_ABI sceNetEpollDestroy() { } int PS4_SYSV_ABI sceNetEpollWait() { - LOG_ERROR(Lib_Net, "(STUBBED) called"); + LOG_TRACE(Lib_Net, "(STUBBED) called"); return ORBIS_OK; } diff --git a/src/core/libraries/network/netctl.cpp b/src/core/libraries/network/netctl.cpp index ab1cb8ae6..a1c8e81c0 100644 --- a/src/core/libraries/network/netctl.cpp +++ b/src/core/libraries/network/netctl.cpp @@ -79,7 +79,7 @@ int PS4_SYSV_ABI sceNetCtlUnregisterCallbackV6() { } int PS4_SYSV_ABI sceNetCtlCheckCallback() { - LOG_ERROR(Lib_NetCtl, "(STUBBED) called"); + LOG_TRACE(Lib_NetCtl, "(STUBBED) called"); return ORBIS_OK; } diff --git a/src/core/libraries/np_manager/np_manager.cpp b/src/core/libraries/np_manager/np_manager.cpp index ee4b3d6b6..33308abc1 100644 --- a/src/core/libraries/np_manager/np_manager.cpp +++ b/src/core/libraries/np_manager/np_manager.cpp @@ -870,7 +870,7 @@ int PS4_SYSV_ABI sceNpAsmTerminate() { } int PS4_SYSV_ABI sceNpCheckCallback() { - LOG_ERROR(Lib_NpManager, "(STUBBED) called"); + LOG_TRACE(Lib_NpManager, "(STUBBED) called"); return ORBIS_OK; } @@ -3510,4 +3510,4 @@ void RegisterlibSceNpManager(Core::Loader::SymbolsResolver* sym) { sceNpUnregisterStateCallbackForToolkit); }; -} // namespace Libraries::NpManager \ No newline at end of file +} // namespace Libraries::NpManager diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_image.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_image.cpp index 17def57ab..28da6ed56 100644 --- a/src/shader_recompiler/backend/spirv/emit_spirv_image.cpp +++ b/src/shader_recompiler/backend/spirv/emit_spirv_image.cpp @@ -106,8 +106,7 @@ Id EmitImageQueryDimensions(EmitContext& ctx, IR::Inst* inst, u32 handle, Id lod const auto type = ctx.info.images[handle & 0xFFFF].type; const Id zero = ctx.u32_zero_value; const auto mips{[&] { return skip_mips ? zero : ctx.OpImageQueryLevels(ctx.U32[1], image); }}; - const bool uses_lod{type != AmdGpu::ImageType::Color2DMsaa && - type != AmdGpu::ImageType::Buffer}; + const bool uses_lod{type != AmdGpu::ImageType::Color2DMsaa}; const auto query{[&](Id type) { return uses_lod ? ctx.OpImageQuerySizeLod(type, image, lod) : ctx.OpImageQuerySize(type, image); diff --git a/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp b/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp index fdc886d70..6d8de362c 100644 --- a/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp +++ b/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp @@ -49,7 +49,7 @@ EmitContext::EmitContext(const Profile& profile_, IR::Program& program, u32& bin DefineInterfaces(program); DefineBuffers(info); DefineImagesAndSamplers(info); - DefineSharedMemory(info); + DefineSharedMemory(); } EmitContext::~EmitContext() = default; @@ -399,7 +399,12 @@ spv::ImageFormat GetFormat(const AmdGpu::Image& image) { image.GetNumberFmt() == AmdGpu::NumberFormat::Float) { return spv::ImageFormat::R11fG11fB10f; } - UNREACHABLE(); + if (image.GetDataFmt() == AmdGpu::DataFormat::Format32_32_32_32 && + image.GetNumberFmt() == AmdGpu::NumberFormat::Float) { + return spv::ImageFormat::Rgba32f; + } + UNREACHABLE_MSG("Unknown storage format data_format={}, num_format={}", + image.GetDataFmt(), image.GetNumberFmt()); } Id ImageType(EmitContext& ctx, const ImageResource& desc, Id sampled_type) { @@ -419,8 +424,6 @@ Id ImageType(EmitContext& ctx, const ImageResource& desc, Id sampled_type) { return ctx.TypeImage(sampled_type, spv::Dim::Dim3D, false, false, false, sampled, format); case AmdGpu::ImageType::Cube: return ctx.TypeImage(sampled_type, spv::Dim::Cube, false, false, false, sampled, format); - case AmdGpu::ImageType::Buffer: - throw NotImplementedException("Image buffer"); default: break; } @@ -478,10 +481,14 @@ void EmitContext::DefineImagesAndSamplers(const Info& info) { } } -void EmitContext::DefineSharedMemory(const Info& info) { - if (info.shared_memory_size == 0) { +void EmitContext::DefineSharedMemory() { + static constexpr size_t DefaultSharedMemSize = 16_KB; + if (!info.uses_shared) { return; } + if (info.shared_memory_size == 0) { + info.shared_memory_size = DefaultSharedMemSize; + } const auto make{[&](Id element_type, u32 element_size) { const u32 num_elements{Common::DivCeil(info.shared_memory_size, element_size)}; const Id array_type{TypeArray(element_type, ConstU32(num_elements))}; diff --git a/src/shader_recompiler/backend/spirv/spirv_emit_context.h b/src/shader_recompiler/backend/spirv/spirv_emit_context.h index 4c4495f20..2aa1bf780 100644 --- a/src/shader_recompiler/backend/spirv/spirv_emit_context.h +++ b/src/shader_recompiler/backend/spirv/spirv_emit_context.h @@ -232,7 +232,7 @@ private: void DefineOutputs(const Info& info); void DefineBuffers(const Info& info); void DefineImagesAndSamplers(const Info& info); - void DefineSharedMemory(const Info& info); + void DefineSharedMemory(); SpirvAttribute GetAttributeInfo(AmdGpu::NumberFormat fmt, Id id); }; diff --git a/src/shader_recompiler/frontend/format.cpp b/src/shader_recompiler/frontend/format.cpp index f7a178adc..c14f33908 100644 --- a/src/shader_recompiler/frontend/format.cpp +++ b/src/shader_recompiler/frontend/format.cpp @@ -2779,11 +2779,11 @@ constexpr std::array InstructionFormatDS = {{ // 60 = DS_READ_U16 {InstClass::DsIdxRd, InstCategory::DataShare, 3, 1, ScalarType::Uint32, ScalarType::Uint32}, // 61 = DS_CONSUME - {InstClass::DsAppendCon, InstCategory::DataShare, 3, 1, ScalarType::Undefined, - ScalarType::Undefined}, + {InstClass::DsAppendCon, InstCategory::DataShare, 3, 1, ScalarType::Uint32, + ScalarType::Uint32}, // 62 = DS_APPEND - {InstClass::DsAppendCon, InstCategory::DataShare, 3, 1, ScalarType::Undefined, - ScalarType::Undefined}, + {InstClass::DsAppendCon, InstCategory::DataShare, 3, 1, ScalarType::Uint32, + ScalarType::Uint32}, // 63 = DS_ORDERED_COUNT {InstClass::GdsOrdCnt, InstCategory::DataShare, 3, 1, ScalarType::Undefined, ScalarType::Undefined}, diff --git a/src/shader_recompiler/frontend/translate/scalar_alu.cpp b/src/shader_recompiler/frontend/translate/scalar_alu.cpp index 42709dc62..b6dc967fb 100644 --- a/src/shader_recompiler/frontend/translate/scalar_alu.cpp +++ b/src/shader_recompiler/frontend/translate/scalar_alu.cpp @@ -37,6 +37,8 @@ void Translator::EmitScalarAlu(const GcnInst& inst) { return S_CMP(ConditionOp::EQ, false, inst); case Opcode::S_CMP_GE_U32: return S_CMP(ConditionOp::GE, false, inst); + case Opcode::S_CMP_GT_U32: + return S_CMP(ConditionOp::GT, false, inst); case Opcode::S_OR_B64: return S_OR_B64(NegateMode::None, false, inst); case Opcode::S_NOR_B64: diff --git a/src/shader_recompiler/frontend/translate/translate.h b/src/shader_recompiler/frontend/translate/translate.h index 23920b647..1a0726af0 100644 --- a/src/shader_recompiler/frontend/translate/translate.h +++ b/src/shader_recompiler/frontend/translate/translate.h @@ -135,6 +135,7 @@ public: void V_SQRT_F32(const GcnInst& inst); void V_MIN_F32(const GcnInst& inst, bool is_legacy = false); void V_MIN3_F32(const GcnInst& inst); + void V_MIN3_I32(const GcnInst& inst); void V_MADMK_F32(const GcnInst& inst); void V_CUBEMA_F32(const GcnInst& inst); void V_CUBESC_F32(const GcnInst& inst); diff --git a/src/shader_recompiler/frontend/translate/vector_alu.cpp b/src/shader_recompiler/frontend/translate/vector_alu.cpp index 6f417af9f..839e74720 100644 --- a/src/shader_recompiler/frontend/translate/vector_alu.cpp +++ b/src/shader_recompiler/frontend/translate/vector_alu.cpp @@ -205,6 +205,8 @@ void Translator::EmitVectorAlu(const GcnInst& inst) { return V_MIN_F32(inst, false); case Opcode::V_MIN3_F32: return V_MIN3_F32(inst); + case Opcode::V_MIN3_I32: + return V_MIN3_I32(inst); case Opcode::V_MIN_LEGACY_F32: return V_MIN_F32(inst, true); case Opcode::V_MADMK_F32: @@ -580,6 +582,13 @@ void Translator::V_MIN3_F32(const GcnInst& inst) { SetDst(inst.dst[0], ir.FPMin(src0, ir.FPMin(src1, src2))); } +void Translator::V_MIN3_I32(const GcnInst& inst) { + const IR::U32 src0{GetSrc(inst.src[0])}; + const IR::U32 src1{GetSrc(inst.src[1])}; + const IR::U32 src2{GetSrc(inst.src[2])}; + SetDst(inst.dst[0], ir.SMin(src0, ir.SMin(src1, src2))); +} + void Translator::V_MADMK_F32(const GcnInst& inst) { const IR::F32 src0{GetSrc(inst.src[0], true)}; const IR::F32 src1{GetSrc(inst.src[1], true)}; diff --git a/src/shader_recompiler/ir/passes/resource_tracking_pass.cpp b/src/shader_recompiler/ir/passes/resource_tracking_pass.cpp index aab73652b..96946284f 100644 --- a/src/shader_recompiler/ir/passes/resource_tracking_pass.cpp +++ b/src/shader_recompiler/ir/passes/resource_tracking_pass.cpp @@ -1,8 +1,7 @@ // SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later -#pragma clang optimize off + #include -#include #include #include "shader_recompiler/ir/basic_block.h" #include "shader_recompiler/ir/breadth_first_search.h" @@ -435,8 +434,8 @@ void PatchBufferInstruction(IR::Block& block, IR::Inst& inst, Info& info, } } else { const u32 stride = buffer.GetStride(); - //ASSERT_MSG(stride >= 4, "non-formatting load_buffer_* is not implemented for stride {}", - // stride); + ASSERT_MSG(stride >= 4, "non-formatting load_buffer_* is not implemented for stride {}", + stride); } IR::U32 address = ir.Imm32(inst_info.inst_offset.Value()); @@ -484,7 +483,11 @@ void PatchImageInstruction(IR::Block& block, IR::Inst& inst, Info& info, Descrip const auto tsharp = TrackSharp(tsharp_handle); const auto image = info.ReadUd(tsharp.sgpr_base, tsharp.dword_offset); const auto inst_info = inst.Flags(); - ASSERT(image.GetType() != AmdGpu::ImageType::Buffer); + if (image.GetType() == AmdGpu::ImageType::Invalid) { + IR::IREmitter ir{block, IR::Block::InstructionList::s_iterator_to(inst)}; + inst.ReplaceUsesWith(ir.CompositeConstruct(ir.Imm32(0.f), ir.Imm32(0.f), ir.Imm32(0.f), ir.Imm32(0.f))); + return; + } u32 image_binding = descriptors.Add(ImageResource{ .sgpr_base = tsharp.sgpr_base, .dword_offset = tsharp.dword_offset, @@ -495,30 +498,31 @@ void PatchImageInstruction(IR::Block& block, IR::Inst& inst, Info& info, Descrip }); // Read sampler sharp. This doesn't exist for IMAGE_LOAD/IMAGE_STORE instructions - if (has_sampler) { - u32 sampler_binding{}; + const u32 sampler_binding = [&] { + if (!has_sampler) { + return 0U; + } const IR::Value& handle = producer->Arg(1); // Inline sampler resource. if (handle.IsImmediate()) { - sampler_binding = descriptors.Add(SamplerResource{ + return descriptors.Add(SamplerResource{ .sgpr_base = std::numeric_limits::max(), .dword_offset = 0, .inline_sampler = AmdGpu::Sampler{.raw0 = handle.U32()}, }); - } else { - // Normal sampler resource. - const auto ssharp_handle = handle.InstRecursive(); - const auto& [ssharp_ud, disable_aniso] = TryDisableAnisoLod0(ssharp_handle); - const auto ssharp = TrackSharp(ssharp_ud); - sampler_binding = descriptors.Add(SamplerResource{ - .sgpr_base = ssharp.sgpr_base, - .dword_offset = ssharp.dword_offset, - .associated_image = image_binding, - .disable_aniso = disable_aniso, - }); } - image_binding |= (sampler_binding << 16); - } + // Normal sampler resource. + const auto ssharp_handle = handle.InstRecursive(); + const auto& [ssharp_ud, disable_aniso] = TryDisableAnisoLod0(ssharp_handle); + const auto ssharp = TrackSharp(ssharp_ud); + return descriptors.Add(SamplerResource{ + .sgpr_base = ssharp.sgpr_base, + .dword_offset = ssharp.dword_offset, + .associated_image = image_binding, + .disable_aniso = disable_aniso, + }); + }(); + image_binding |= (sampler_binding << 16); // Patch image handle IR::IREmitter ir{block, IR::Block::InstructionList::s_iterator_to(inst)}; diff --git a/src/shader_recompiler/ir/passes/shader_info_collection_pass.cpp b/src/shader_recompiler/ir/passes/shader_info_collection_pass.cpp index b51ce94ee..7100b3844 100644 --- a/src/shader_recompiler/ir/passes/shader_info_collection_pass.cpp +++ b/src/shader_recompiler/ir/passes/shader_info_collection_pass.cpp @@ -20,11 +20,19 @@ void Visit(Info& info, IR::Inst& inst) { case IR::Opcode::LoadSharedU8: case IR::Opcode::WriteSharedU8: info.uses_shared_u8 = true; + info.uses_shared = true; break; case IR::Opcode::LoadSharedS16: case IR::Opcode::LoadSharedU16: case IR::Opcode::WriteSharedU16: info.uses_shared_u16 = true; + info.uses_shared = true; + break; + case IR::Opcode::LoadSharedU32: + case IR::Opcode::LoadSharedU64: + case IR::Opcode::WriteSharedU32: + case IR::Opcode::WriteSharedU64: + info.uses_shared = true; break; case IR::Opcode::ConvertF32F16: case IR::Opcode::BitCastF16U16: diff --git a/src/shader_recompiler/runtime_info.h b/src/shader_recompiler/runtime_info.h index 5c72d7e6b..277c38b72 100644 --- a/src/shader_recompiler/runtime_info.h +++ b/src/shader_recompiler/runtime_info.h @@ -178,6 +178,7 @@ struct Info { bool has_image_gather{}; bool has_image_query{}; bool uses_group_quad{}; + bool uses_shared{}; bool uses_shared_u8{}; bool uses_shared_u16{}; bool uses_fp16{}; diff --git a/src/video_core/amdgpu/pixel_format.cpp b/src/video_core/amdgpu/pixel_format.cpp index 6618e72ad..6744891a6 100644 --- a/src/video_core/amdgpu/pixel_format.cpp +++ b/src/video_core/amdgpu/pixel_format.cpp @@ -7,6 +7,77 @@ namespace AmdGpu { +std::string_view NameOf(DataFormat fmt) { + switch (fmt) { + case DataFormat::FormatInvalid: + return "FormatInvalid"; + case DataFormat::Format8: + return "Format8"; + case DataFormat::Format16: + return "Format16"; + case DataFormat::Format8_8: + return "Format8_8"; + case DataFormat::Format32: + return "Format32"; + case DataFormat::Format16_16: + return "Format16_16"; + case DataFormat::Format10_11_11: + return "Format10_11_11"; + case DataFormat::Format11_11_10: + return "Format11_11_10"; + case DataFormat::Format10_10_10_2: + return "Format10_10_10_2"; + case DataFormat::Format2_10_10_10: + return "Format2_10_10_10"; + case DataFormat::Format8_8_8_8: + return "Format8_8_8_8"; + case DataFormat::Format32_32: + return "Format32_32"; + case DataFormat::Format16_16_16_16: + return "Format16_16_16_16"; + case DataFormat::Format32_32_32: + return "Format32_32_32"; + case DataFormat::Format32_32_32_32: + return "Format32_32_32_32"; + case DataFormat::Format5_6_5: + return "Format5_6_5"; + case DataFormat::Format1_5_5_5: + return "Format1_5_5_5"; + case DataFormat::Format5_5_5_1: + return "Format5_5_5_1"; + case DataFormat::Format4_4_4_4: + return "Format4_4_4_4"; + case DataFormat::Format8_24: + return "Format8_24"; + case DataFormat::Format24_8: + return "Format24_8"; + case DataFormat::FormatX24_8_32: + return "FormatX24_8_32"; + case DataFormat::FormatGB_GR: + return "FormatGB_GR"; + case DataFormat::FormatBG_RG: + return "FormatBG_RG"; + case DataFormat::Format5_9_9_9: + return "Format5_9_9_9"; + case DataFormat::FormatBc1: + return "FormatBc1"; + case DataFormat::FormatBc2: + return "FormatBc2"; + case DataFormat::FormatBc3: + return "FormatBc3"; + case DataFormat::FormatBc4: + return "FormatBc4"; + case DataFormat::FormatBc5: + return "FormatBc5"; + case DataFormat::FormatBc6: + return "FormatBc6"; + case DataFormat::FormatBc7: + return "FormatBc7"; + default: + UNREACHABLE(); + } +} + std::string_view NameOf(NumberFormat fmt) { switch (fmt) { case NumberFormat::Unorm: diff --git a/src/video_core/amdgpu/pixel_format.h b/src/video_core/amdgpu/pixel_format.h index 2a38c5a02..1004ed7d2 100644 --- a/src/video_core/amdgpu/pixel_format.h +++ b/src/video_core/amdgpu/pixel_format.h @@ -61,6 +61,7 @@ enum class NumberFormat : u32 { Ubscaled = 13, }; +[[nodiscard]] std::string_view NameOf(DataFormat fmt); [[nodiscard]] std::string_view NameOf(NumberFormat fmt); int NumComponents(DataFormat format); @@ -70,6 +71,16 @@ s32 ComponentOffset(DataFormat format, u32 comp); } // namespace AmdGpu +template <> +struct fmt::formatter { + constexpr auto parse(format_parse_context& ctx) { + return ctx.begin(); + } + auto format(AmdGpu::DataFormat fmt, format_context& ctx) const { + return fmt::format_to(ctx.out(), "{}", AmdGpu::NameOf(fmt)); + } +}; + template <> struct fmt::formatter { constexpr auto parse(format_parse_context& ctx) { diff --git a/src/video_core/amdgpu/resource.h b/src/video_core/amdgpu/resource.h index 7d3465af1..01271792b 100644 --- a/src/video_core/amdgpu/resource.h +++ b/src/video_core/amdgpu/resource.h @@ -75,7 +75,7 @@ struct Buffer { static_assert(sizeof(Buffer) == 16); // 128bits enum class ImageType : u64 { - Buffer = 0, + Invalid = 0, Color1D = 8, Color2D = 9, Color3D = 10, @@ -88,8 +88,8 @@ enum class ImageType : u64 { constexpr std::string_view NameOf(ImageType type) { switch (type) { - case ImageType::Buffer: - return "Buffer"; + case ImageType::Invalid: + return "Invalid"; case ImageType::Color1D: return "Color1D"; case ImageType::Color2D: diff --git a/src/video_core/renderer_vulkan/liverpool_to_vk.cpp b/src/video_core/renderer_vulkan/liverpool_to_vk.cpp index 21ade4010..06b9359a0 100644 --- a/src/video_core/renderer_vulkan/liverpool_to_vk.cpp +++ b/src/video_core/renderer_vulkan/liverpool_to_vk.cpp @@ -337,6 +337,8 @@ std::span GetAllFormats() { vk::Format::eR32Sfloat, vk::Format::eR32Sint, vk::Format::eR32Uint, + vk::Format::eBc6HUfloatBlock, + vk::Format::eR16G16Unorm, }; return formats; } @@ -527,6 +529,10 @@ vk::Format SurfaceFormat(AmdGpu::DataFormat data_format, AmdGpu::NumberFormat nu num_format == AmdGpu::NumberFormat::Snorm) { return vk::Format::eR8G8B8A8Snorm; } + if (data_format == AmdGpu::DataFormat::FormatBc6 && + num_format == AmdGpu::NumberFormat::Unorm) { + return vk::Format::eBc6HUfloatBlock; + } UNREACHABLE_MSG("Unknown data_format={} and num_format={}", u32(data_format), u32(num_format)); } diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp index afd589d51..67994485a 100644 --- a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp @@ -302,14 +302,6 @@ std::unique_ptr PipelineCache::CreateComputePipeline() { block_pool.ReleaseContents(); inst_pool.ReleaseContents(); - if (compute_key == 0xa71733ca || compute_key == 0xa55ad01d) { - return nullptr; - } - - if (compute_key == 4248155022) { - printf("test\n"); - } - // Recompile shader to IR. try { LOG_INFO(Render_Vulkan, "Compiling cs shader {:#x}", compute_key); diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp index ff5e97d5b..4a50ef0a3 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp +++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp @@ -23,7 +23,7 @@ Rasterizer::Rasterizer(const Instance& instance_, Scheduler& scheduler_, : instance{instance_}, scheduler{scheduler_}, texture_cache{texture_cache_}, liverpool{liverpool_}, memory{Core::Memory::Instance()}, pipeline_cache{instance, scheduler, liverpool}, - vertex_index_buffer{instance, scheduler, VertexIndexFlags, 2_GB, BufferType::Upload} { + vertex_index_buffer{instance, scheduler, VertexIndexFlags, 4_GB, BufferType::Upload} { if (!Config::nullGpu()) { liverpool->BindRasterizer(this); } diff --git a/src/video_core/texture_cache/image_view.h b/src/video_core/texture_cache/image_view.h index 590ac9be6..fbc62db36 100644 --- a/src/video_core/texture_cache/image_view.h +++ b/src/video_core/texture_cache/image_view.h @@ -35,6 +35,8 @@ struct ImageViewInfo { struct Image; +constexpr Common::SlotId NULL_IMAGE_VIEW_ID{0}; + struct ImageView { explicit ImageView(const Vulkan::Instance& instance, const ImageViewInfo& info, Image& image, ImageId image_id, std::optional usage_override = {}); diff --git a/src/video_core/texture_cache/texture_cache.cpp b/src/video_core/texture_cache/texture_cache.cpp index 6a0cae891..7b8a55547 100644 --- a/src/video_core/texture_cache/texture_cache.cpp +++ b/src/video_core/texture_cache/texture_cache.cpp @@ -183,6 +183,10 @@ ImageView& TextureCache::RegisterImageView(ImageId image_id, const ImageViewInfo } ImageView& TextureCache::FindTexture(const ImageInfo& info, const ImageViewInfo& view_info) { + if (info.guest_address == 0) [[unlikely]] { + return slot_image_views[NULL_IMAGE_VIEW_ID]; + } + const ImageId image_id = FindImage(info); Image& image = slot_images[image_id]; auto& usage = image.info.usage;