diff --git a/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp b/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp index 7f70aee7c..164c30c56 100644 --- a/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp +++ b/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp @@ -510,7 +510,8 @@ Id ImageType(EmitContext& ctx, const ImageResource& desc, Id sampled_type) { case AmdGpu::ImageType::Color3D: 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); + return ctx.TypeImage(sampled_type, spv::Dim::Cube, false, desc.is_array, false, sampled, + format); default: break; } diff --git a/src/shader_recompiler/frontend/decode.cpp b/src/shader_recompiler/frontend/decode.cpp index 26a2c1a6c..6020f93bb 100644 --- a/src/shader_recompiler/frontend/decode.cpp +++ b/src/shader_recompiler/frontend/decode.cpp @@ -1032,6 +1032,7 @@ void GcnDecodeContext::decodeInstructionMIMG(uint64_t hexInstruction) { m_instruction.control.mimg = *reinterpret_cast(&hexInstruction); m_instruction.control.mimg.mod = getMimgModifier(m_instruction.opcode); + ASSERT(m_instruction.control.mimg.r128 == 0); } void GcnDecodeContext::decodeInstructionDS(uint64_t hexInstruction) { diff --git a/src/shader_recompiler/frontend/translate/vector_memory.cpp b/src/shader_recompiler/frontend/translate/vector_memory.cpp index 212d7fdc5..7ecc2e762 100644 --- a/src/shader_recompiler/frontend/translate/vector_memory.cpp +++ b/src/shader_recompiler/frontend/translate/vector_memory.cpp @@ -546,6 +546,7 @@ void Translator::IMAGE_SAMPLE(const GcnInst& inst) { info.has_offset.Assign(flags.test(MimgModifier::Offset)); info.explicit_lod.Assign(explicit_lod); info.has_derivatives.Assign(has_derivatives); + info.is_array.Assign(mimg.da); // Issue IR instruction, leaving unknown fields blank to patch later. const IR::Value texel = [&]() -> IR::Value { @@ -630,6 +631,7 @@ void Translator::IMAGE_GATHER(const GcnInst& inst) { info.has_offset.Assign(flags.test(MimgModifier::Offset)); // info.explicit_lod.Assign(explicit_lod); info.gather_comp.Assign(std::bit_width(mimg.dmask) - 1); + info.is_array.Assign(mimg.da); // Issue IR instruction, leaving unknown fields blank to patch later. const IR::Value texel = [&]() -> IR::Value { diff --git a/src/shader_recompiler/info.h b/src/shader_recompiler/info.h index d8282bf49..2ae4420b2 100644 --- a/src/shader_recompiler/info.h +++ b/src/shader_recompiler/info.h @@ -64,9 +64,10 @@ struct ImageResource { u32 dword_offset; AmdGpu::ImageType type; AmdGpu::NumberFormat nfmt; - bool is_storage; - bool is_depth; + bool is_storage{}; + bool is_depth{}; bool is_atomic{}; + bool is_array{}; constexpr AmdGpu::Image GetSharp(const Info& info) const noexcept; }; diff --git a/src/shader_recompiler/ir/passes/resource_tracking_pass.cpp b/src/shader_recompiler/ir/passes/resource_tracking_pass.cpp index d8519a8ff..f438f2693 100644 --- a/src/shader_recompiler/ir/passes/resource_tracking_pass.cpp +++ b/src/shader_recompiler/ir/passes/resource_tracking_pass.cpp @@ -491,6 +491,7 @@ void PatchImageInstruction(IR::Block& block, IR::Inst& inst, Info& info, Descrip .is_storage = is_storage, .is_depth = bool(inst_info.is_depth), .is_atomic = IsImageAtomicInstruction(inst), + .is_array = bool(inst_info.is_array), }); // Read sampler sharp. This doesn't exist for IMAGE_LOAD/IMAGE_STORE instructions diff --git a/src/shader_recompiler/ir/reg.h b/src/shader_recompiler/ir/reg.h index fba04f33e..4783d08e5 100644 --- a/src/shader_recompiler/ir/reg.h +++ b/src/shader_recompiler/ir/reg.h @@ -59,6 +59,7 @@ union TextureInstInfo { BitField<5, 1, u32> has_offset; BitField<6, 2, u32> gather_comp; BitField<8, 1, u32> has_derivatives; + BitField<9, 1, u32> is_array; }; union BufferInstInfo { diff --git a/src/video_core/renderer_vulkan/vk_instance.cpp b/src/video_core/renderer_vulkan/vk_instance.cpp index 0bc73e14f..8bec96cfb 100644 --- a/src/video_core/renderer_vulkan/vk_instance.cpp +++ b/src/video_core/renderer_vulkan/vk_instance.cpp @@ -282,6 +282,7 @@ bool Instance::CreateDevice() { vk::PhysicalDeviceFeatures2{ .features{ .robustBufferAccess = features.robustBufferAccess, + .imageCubeArray = features.imageCubeArray, .independentBlend = features.independentBlend, .geometryShader = features.geometryShader, .logicOp = features.logicOp, diff --git a/src/video_core/renderer_vulkan/vk_pipeline_common.cpp b/src/video_core/renderer_vulkan/vk_pipeline_common.cpp index 50df7aecd..fa7fbc0b8 100644 --- a/src/video_core/renderer_vulkan/vk_pipeline_common.cpp +++ b/src/video_core/renderer_vulkan/vk_pipeline_common.cpp @@ -3,6 +3,7 @@ #include +#include "shader_recompiler/info.h" #include "video_core/renderer_vulkan/vk_instance.h" #include "video_core/renderer_vulkan/vk_pipeline_common.h" #include "video_core/renderer_vulkan/vk_scheduler.h" @@ -21,7 +22,7 @@ Pipeline::~Pipeline() = default; void Pipeline::BindTextures(VideoCore::TextureCache& texture_cache, const Shader::Info& stage, u32& binding, DescriptorWrites& set_writes) const { - using ImageBindingInfo = std::tuple; + using ImageBindingInfo = std::tuple; boost::container::static_vector image_bindings; for (const auto& image_desc : stage.images) { @@ -31,9 +32,9 @@ void Pipeline::BindTextures(VideoCore::TextureCache& texture_cache, const Shader const auto image_id = texture_cache.FindImage(image_info); auto& image = texture_cache.GetImage(image_id); image.flags |= VideoCore::ImageFlagBits::Bound; - image_bindings.emplace_back(image_id, tsharp, image_desc.is_storage); + image_bindings.emplace_back(image_id, tsharp, image_desc); } else { - image_bindings.emplace_back(VideoCore::ImageId{}, tsharp, image_desc.is_storage); + image_bindings.emplace_back(VideoCore::ImageId{}, tsharp, image_desc); } if (texture_cache.IsMeta(tsharp.Address())) { @@ -42,7 +43,7 @@ void Pipeline::BindTextures(VideoCore::TextureCache& texture_cache, const Shader } // Second pass to re-bind images that were updated after binding - for (auto [image_id, tsharp, is_storage] : image_bindings) { + for (auto [image_id, tsharp, desc] : image_bindings) { if (!image_id) { if (instance.IsNullDescriptorSupported()) { image_infos.emplace_back(VK_NULL_HANDLE, VK_NULL_HANDLE, vk::ImageLayout::eGeneral); @@ -56,7 +57,7 @@ void Pipeline::BindTextures(VideoCore::TextureCache& texture_cache, const Shader if (True(image.flags & VideoCore::ImageFlagBits::NeedsRebind)) { image_id = texture_cache.FindImage(image.info); } - VideoCore::ImageViewInfo view_info{tsharp, is_storage}; + VideoCore::ImageViewInfo view_info{tsharp, desc}; auto& image_view = texture_cache.FindTexture(image_id, view_info); image_infos.emplace_back(VK_NULL_HANDLE, *image_view.image_view, texture_cache.GetImage(image_id).last_state.layout); @@ -69,8 +70,8 @@ void Pipeline::BindTextures(VideoCore::TextureCache& texture_cache, const Shader .dstBinding = binding++, .dstArrayElement = 0, .descriptorCount = 1, - .descriptorType = - is_storage ? vk::DescriptorType::eStorageImage : vk::DescriptorType::eSampledImage, + .descriptorType = desc.is_storage ? vk::DescriptorType::eStorageImage + : vk::DescriptorType::eSampledImage, .pImageInfo = &image_infos.back(), }); } diff --git a/src/video_core/renderer_vulkan/vk_platform.cpp b/src/video_core/renderer_vulkan/vk_platform.cpp index feadda96c..6abd00aaa 100644 --- a/src/video_core/renderer_vulkan/vk_platform.cpp +++ b/src/video_core/renderer_vulkan/vk_platform.cpp @@ -44,7 +44,6 @@ static VKAPI_ATTR VkBool32 VKAPI_CALL DebugUtilsCallback( case 0xc81ad50e: case 0xb7c39078: case 0x32868fde: // vkCreateBufferView(): pCreateInfo->range does not equal VK_WHOLE_SIZE - case 0x92d66fc1: // `pMultisampleState is NULL` for depth only passes (confirmed VL error) return VK_FALSE; default: break; diff --git a/src/video_core/texture_cache/image.cpp b/src/video_core/texture_cache/image.cpp index bec11ea1d..cd6119a7e 100644 --- a/src/video_core/texture_cache/image.cpp +++ b/src/video_core/texture_cache/image.cpp @@ -200,11 +200,13 @@ boost::container::small_vector Image::GetBarriers( // resource transition for the next time. const auto mips = needs_partial_transition - ? std::ranges::views::iota(subres_range->base.level, subres_range->extent.levels) + ? std::ranges::views::iota(subres_range->base.level, + subres_range->base.level + subres_range->extent.levels) : std::views::iota(0u, info.resources.levels); const auto layers = needs_partial_transition - ? std::ranges::views::iota(subres_range->base.layer, subres_range->extent.layers) + ? std::ranges::views::iota(subres_range->base.layer, + subres_range->base.layer + subres_range->extent.layers) : std::views::iota(0u, info.resources.layers); for (u32 mip : mips) { diff --git a/src/video_core/texture_cache/image_view.cpp b/src/video_core/texture_cache/image_view.cpp index 87ecd3b1e..a9a1a6c63 100644 --- a/src/video_core/texture_cache/image_view.cpp +++ b/src/video_core/texture_cache/image_view.cpp @@ -2,6 +2,7 @@ // SPDX-License-Identifier: GPL-2.0-or-later #include "common/logging/log.h" +#include "shader_recompiler/info.h" #include "video_core/amdgpu/resource.h" #include "video_core/renderer_vulkan/liverpool_to_vk.h" #include "video_core/renderer_vulkan/vk_instance.h" @@ -66,8 +67,8 @@ vk::Format TrySwizzleFormat(vk::Format format, u32 dst_sel) { return format; } -ImageViewInfo::ImageViewInfo(const AmdGpu::Image& image, bool is_storage_) noexcept - : is_storage{is_storage_} { +ImageViewInfo::ImageViewInfo(const AmdGpu::Image& image, const Shader::ImageResource& desc) noexcept + : is_storage{desc.is_storage} { const auto dfmt = image.GetDataFmt(); auto nfmt = image.GetNumberFmt(); if (is_storage && nfmt == AmdGpu::NumberFormat::Srgb) { @@ -76,19 +77,24 @@ ImageViewInfo::ImageViewInfo(const AmdGpu::Image& image, bool is_storage_) noexc format = Vulkan::LiverpoolToVK::SurfaceFormat(dfmt, nfmt); range.base.level = image.base_level; range.base.layer = image.base_array; - range.extent.levels = image.last_level + 1; - range.extent.layers = image.last_array + 1; + range.extent.levels = image.last_level - image.base_level + 1; + range.extent.layers = image.last_array - image.base_array + 1; type = ConvertImageViewType(image.GetType()); // Adjust view type for partial cubemaps and arrays if (image.IsPartialCubemap()) { type = vk::ImageViewType::e2DArray; } - if (type == vk::ImageViewType::eCube && range.extent.layers > 6) { - type = vk::ImageViewType::eCubeArray; + if (type == vk::ImageViewType::eCube) { + if (desc.is_array) { + type = vk::ImageViewType::eCubeArray; + } else { + // Some games try to bind an array of cubemaps while shader reads only single one. + range.extent.layers = std::min(range.extent.layers, 6u); + } } if (type == vk::ImageViewType::e3D && range.extent.layers > 1) { - // Some games pass incorrect layer count for 3D textures so we need to fixup it + // Some games pass incorrect layer count for 3D textures so we need to fixup it. range.extent.layers = 1; } @@ -116,7 +122,7 @@ ImageViewInfo::ImageViewInfo(const AmdGpu::Liverpool::ColorBuffer& col_buffer, const auto base_format = Vulkan::LiverpoolToVK::SurfaceFormat(col_buffer.info.format, col_buffer.NumFormat()); range.base.layer = col_buffer.view.slice_start; - range.extent.layers = col_buffer.NumSlices(); + range.extent.layers = col_buffer.NumSlices() - range.base.layer; format = Vulkan::LiverpoolToVK::AdjustColorBufferFormat( base_format, col_buffer.info.comp_swap.Value(), is_vo_surface); } @@ -128,7 +134,7 @@ ImageViewInfo::ImageViewInfo(const AmdGpu::Liverpool::DepthBuffer& depth_buffer, depth_buffer.stencil_info.format); is_storage = ctl.depth_write_enable; range.base.layer = view.slice_start; - range.extent.layers = view.NumSlices(); + range.extent.layers = view.NumSlices() - range.base.layer; } ImageView::ImageView(const Vulkan::Instance& instance, const ImageViewInfo& info_, Image& image, @@ -160,9 +166,9 @@ ImageView::ImageView(const Vulkan::Instance& instance, const ImageViewInfo& info .subresourceRange{ .aspectMask = aspect, .baseMipLevel = info.range.base.level, - .levelCount = info.range.extent.levels - info.range.base.level, + .levelCount = info.range.extent.levels, .baseArrayLayer = info.range.base.layer, - .layerCount = info.range.extent.layers - info.range.base.layer, + .layerCount = info.range.extent.layers, }, }; image_view = instance.GetDevice().createImageViewUnique(image_view_ci); diff --git a/src/video_core/texture_cache/image_view.h b/src/video_core/texture_cache/image_view.h index 7d53590dd..ba8d2c72b 100644 --- a/src/video_core/texture_cache/image_view.h +++ b/src/video_core/texture_cache/image_view.h @@ -3,6 +3,7 @@ #pragma once +#include "shader_recompiler/info.h" #include "video_core/amdgpu/liverpool.h" #include "video_core/amdgpu/resource.h" #include "video_core/renderer_vulkan/vk_common.h" @@ -17,7 +18,7 @@ namespace VideoCore { struct ImageViewInfo { ImageViewInfo() = default; - ImageViewInfo(const AmdGpu::Image& image, bool is_storage) noexcept; + ImageViewInfo(const AmdGpu::Image& image, const Shader::ImageResource& desc) noexcept; ImageViewInfo(const AmdGpu::Liverpool::ColorBuffer& col_buffer, bool is_vo_surface) noexcept; ImageViewInfo(const AmdGpu::Liverpool::DepthBuffer& depth_buffer, AmdGpu::Liverpool::DepthView view, AmdGpu::Liverpool::DepthControl ctl);