diff --git a/src/shader_recompiler/ir_opt/texture_pass.cpp b/src/shader_recompiler/ir_opt/texture_pass.cpp index 20b8591072..d4b9228965 100644 --- a/src/shader_recompiler/ir_opt/texture_pass.cpp +++ b/src/shader_recompiler/ir_opt/texture_pass.cpp @@ -244,9 +244,179 @@ std::optional TryGetConstant(IR::Value& value, Environment& env) { } std::optional TryGetConstBuffer(const IR::Inst* inst, Environment& env) { - switch (inst->GetOpcode()) { + const auto opcode = inst->GetOpcode(); + + switch (opcode) { default: + LOG_DEBUG(Render_Vulkan, "TryGetConstBuffer: Unhandled opcode {}", static_cast(opcode)); + return std::nullopt; + case IR::Opcode::BitCastU16F16: + case IR::Opcode::BitCastF16U16: + case IR::Opcode::BitCastU32F32: + case IR::Opcode::BitCastF32U32: + case IR::Opcode::BitCastU64F64: + case IR::Opcode::BitCastF64U64: + return Track(inst->Arg(0), env); + case IR::Opcode::ConvertU32U64: + case IR::Opcode::ConvertU64U32: + return Track(inst->Arg(0), env); + case IR::Opcode::SelectU1: + case IR::Opcode::SelectU8: + case IR::Opcode::SelectU16: + case IR::Opcode::SelectU32: + case IR::Opcode::SelectU64: + case IR::Opcode::SelectF16: + case IR::Opcode::SelectF32: + case IR::Opcode::SelectF64: { + if (auto result = Track(inst->Arg(1), env)) { + return result; + } + if (auto result = Track(inst->Arg(2), env)) { + return result; + } + LOG_DEBUG(Render_Vulkan, "Select operation failed both branches"); + return std::nullopt; + } + case IR::Opcode::IAdd32: { + const IR::Value op1{inst->Arg(0)}; + const IR::Value op2{inst->Arg(1)}; + + if (op2.IsImmediate()) { + if (auto res = Track(op1, env)) { + res->offset += op2.U32(); + return res; + } + } + if (op1.IsImmediate()) { + if (auto res = Track(op2, env)) { + res->offset += op1.U32(); + return res; + } + } + + if (auto result = Track(op1, env)) { + return result; + } + if (auto result = Track(op2, env)) { + return result; + } + return std::nullopt; + } + case IR::Opcode::IAdd64: { + const IR::Value op1{inst->Arg(0)}; + const IR::Value op2{inst->Arg(1)}; + + if (op2.IsImmediate()) { + if (auto res = Track(op1, env)) { + res->offset += static_cast(op2.U64()); + return res; + } + } + if (op1.IsImmediate()) { + if (auto res = Track(op2, env)) { + res->offset += static_cast(op1.U64()); + return res; + } + } + + if (auto result = Track(op1, env)) { + return result; + } + if (auto result = Track(op2, env)) { + return result; + } + return std::nullopt; + } + case IR::Opcode::ShiftRightLogical32: { + const IR::Value base{inst->Arg(0)}; + const IR::Value shift{inst->Arg(1)}; + if (auto res = Track(base, env)) { + if (shift.IsImmediate()) { + res->offset += (shift.U32() / 8); + } + return res; + } + return std::nullopt; + } + case IR::Opcode::ShiftRightLogical64: { + const IR::Value base{inst->Arg(0)}; + const IR::Value shift{inst->Arg(1)}; + if (auto res = Track(base, env)) { + if (shift.IsImmediate()) { + res->offset += (shift.U32() / 8); + } + return res; + } + return std::nullopt; + } + case IR::Opcode::BitFieldUExtract: { + const IR::Value base{inst->Arg(0)}; + const IR::Value offset{inst->Arg(1)}; + const IR::Value count{inst->Arg(2)}; + + if (auto res = Track(base, env)) { + if (offset.IsImmediate()) { + res->offset += (offset.U32() / 8); + res->shift_left = offset.U32() % 32; + } + return res; + } + return std::nullopt; + } + case IR::Opcode::PackUint2x32: { + const IR::Value composite{inst->Arg(0)}; + if (composite.IsImmediate()) { + return std::nullopt; + } + IR::Inst* const composite_inst{composite.InstRecursive()}; + if (composite_inst->GetOpcode() == IR::Opcode::CompositeConstructU32x2) { + std::optional lhs{Track(composite_inst->Arg(0), env)}; + std::optional rhs{Track(composite_inst->Arg(1), env)}; + if (!lhs || !rhs) { + return std::nullopt; + } + if (lhs->has_secondary || rhs->has_secondary) { + return std::nullopt; + } + if (lhs->count > 1 || rhs->count > 1) { + return std::nullopt; + } + return ConstBufferAddr{ + .index = lhs->index, + .offset = lhs->offset, + .shift_left = lhs->shift_left, + .secondary_index = rhs->index, + .secondary_offset = rhs->offset, + .secondary_shift_left = rhs->shift_left, + .dynamic_offset = {}, + .count = 1, + .has_secondary = true, + }; + } + return std::nullopt; + } + case IR::Opcode::UnpackUint2x32: + return Track(inst->Arg(0), env); + case IR::Opcode::CompositeConstructU32x2: + if (auto result = Track(inst->Arg(0), env)) { + return result; + } + if (auto result = Track(inst->Arg(1), env)) { + return result; + } + return std::nullopt; + case IR::Opcode::CompositeExtractU32x2: { + const IR::Value composite{inst->Arg(0)}; + const IR::Value index_val{inst->Arg(1)}; + + if (auto res = Track(composite, env)) { + if (index_val.IsImmediate()) { + res->offset += index_val.U32() * 4; + } + return res; + } return std::nullopt; + } case IR::Opcode::BitwiseOr32: { std::optional lhs{TrackCached(inst->Arg(0), env)}; std::optional rhs{TrackCached(inst->Arg(1), env)}; @@ -259,9 +429,23 @@ std::optional TryGetConstBuffer(const IR::Inst* inst, Environme if (lhs->count > 1 || rhs->count > 1) { return std::nullopt; } - if (lhs->shift_left > 0 || lhs->index > rhs->index || lhs->offset > rhs->offset) { + + bool lhs_is_low = true; + + if (lhs->index == rhs->index) { + if (lhs->offset > rhs->offset) { + lhs_is_low = false; + } else if (lhs->offset == rhs->offset) { + if (lhs->shift_left > rhs->shift_left) { + lhs_is_low = false; + } + } + } + + if (!lhs_is_low) { std::swap(lhs, rhs); } + return ConstBufferAddr{ .index = lhs->index, .offset = lhs->offset, @@ -284,7 +468,6 @@ std::optional TryGetConstBuffer(const IR::Inst* inst, Environme lhs->shift_left = shift.U32(); } return lhs; - break; } case IR::Opcode::BitwiseAnd32: { IR::Value op1{inst->Arg(0)}; @@ -310,10 +493,11 @@ std::optional TryGetConstBuffer(const IR::Inst* inst, Environme } std::optional lhs{TrackCached(op1, env)}; if (lhs) { - lhs->shift_left = static_cast(std::countr_zero(op2.U32())); + if (op2.IsImmediate()) { + lhs->shift_left = static_cast(std::countr_zero(op2.U32())); + } } return lhs; - break; } case IR::Opcode::GetCbufU32x2: case IR::Opcode::GetCbufU32: @@ -367,15 +551,42 @@ std::optional TryGetConstBuffer(const IR::Inst* inst, Environme }; } +// TODO:xbzk: shall be dropped when Track method cover all bindless stuff +static ConstBufferAddr last_valid_addr = ConstBufferAddr{ + .index = 0, + .offset = 0, + .shift_left = 0, + .secondary_index = 0, + .secondary_offset = 0, + .secondary_shift_left = 0, + .dynamic_offset = {}, + .count = 1, + .has_secondary = false, +}; + TextureInst MakeInst(Environment& env, IR::Block* block, IR::Inst& inst) { ConstBufferAddr addr; if (IsBindless(inst)) { const std::optional track_addr{TrackCached(inst.Arg(0), env)}; if (!track_addr) { - throw NotImplementedException("Failed to track bindless texture constant buffer"); + //throw NotImplementedException("Failed to track bindless texture constant buffer"); + LOG_DEBUG(Render_Vulkan, "!!! TRACKING FAILED !!! Using fallback - last_valid: cbuf[{}][{}]", + last_valid_addr.index, last_valid_addr.offset); + addr = last_valid_addr; // TODO:xbzk: shall be dropped when Track method cover all bindless stuff } else { addr = *track_addr; + + if (addr.count == 1 && addr.dynamic_offset.IsEmpty()) { + u32 handle = GetTextureHandle(env, addr); + LOG_DEBUG(Render_Vulkan, "SUCCESS: cbuf[{}][{}] = HANDLE 0x{:08X}", + addr.index, addr.offset, handle); + } else { + LOG_DEBUG(Render_Vulkan, "SUCCESS: cbuf[{}][{}] (dynamic, count={})", + addr.index, addr.offset, addr.count); + } + + last_valid_addr = addr; // TODO:xbzk: shall be dropped when Track method cover all bindless stuff } } else { addr = ConstBufferAddr{ @@ -590,12 +801,21 @@ void TexturePass(Environment& env, IR::Program& program, const HostTranslateInfo program.info.image_descriptors, }; for (TextureInst& texture_inst : to_replace) { - // TODO: Handle arrays IR::Inst* const inst{texture_inst.inst}; + + LOG_DEBUG(Render_Vulkan, "Pre Processing texture inst: opcode={}", + inst->GetOpcode()); + + // TODO: Handle arrays inst->ReplaceOpcode(IndexedInstruction(*inst)); const auto& cbuf{texture_inst.cbuf}; auto flags{inst->Flags()}; + + LOG_DEBUG(Render_Vulkan, "Post Processing texture inst: opcode={}, type={}, cbuf[{}][{}]", + inst->GetOpcode(), static_cast(flags.type.Value()), + cbuf.index, cbuf.offset); + bool is_multisample{false}; switch (inst->GetOpcode()) { case IR::Opcode::ImageQueryDimensions: @@ -646,6 +866,7 @@ void TexturePass(Environment& env, IR::Program& program, const HostTranslateInfo case IR::Opcode::ImageAtomicXor32: case IR::Opcode::ImageAtomicExchange32: case IR::Opcode::ImageWrite: { + LOG_DEBUG(Render_Vulkan, " -> Image read/write path"); if (cbuf.has_secondary) { throw NotImplementedException("Unexpected separate sampler"); } @@ -679,7 +900,9 @@ void TexturePass(Environment& env, IR::Program& program, const HostTranslateInfo break; } default: + LOG_DEBUG(Render_Vulkan, " -> Default path, adding descriptor"); if (flags.type == TextureType::Buffer) { + LOG_DEBUG(Render_Vulkan, " -> Adding TextureBufferDescriptor"); index = descriptors.Add(TextureBufferDescriptor{ .has_secondary = cbuf.has_secondary, .cbuf_index = cbuf.index, @@ -692,6 +915,7 @@ void TexturePass(Environment& env, IR::Program& program, const HostTranslateInfo .size_shift = DESCRIPTOR_SIZE_SHIFT, }); } else { + LOG_DEBUG(Render_Vulkan, " -> Adding TextureDescriptor at cbuf[{}][{}]", cbuf.index, cbuf.offset); index = descriptors.Add(TextureDescriptor{ .type = flags.type, .is_depth = flags.is_depth != 0, @@ -706,6 +930,7 @@ void TexturePass(Environment& env, IR::Program& program, const HostTranslateInfo .count = cbuf.count, .size_shift = DESCRIPTOR_SIZE_SHIFT, }); + LOG_DEBUG(Render_Vulkan, " -> Assigned descriptor index {}", index); } break; } diff --git a/src/video_core/renderer_vulkan/pipeline_helper.h b/src/video_core/renderer_vulkan/pipeline_helper.h index 910e07a606..d5bdd8a5b5 100644 --- a/src/video_core/renderer_vulkan/pipeline_helper.h +++ b/src/video_core/renderer_vulkan/pipeline_helper.h @@ -184,13 +184,42 @@ inline void PushImageDescriptors(TextureCache& texture_cache, const u32 num_image_buffers = Shader::NumDescriptors(info.image_buffer_descriptors); views += num_texture_buffers; views += num_image_buffers; + bool fix_shadows = Settings::values.hack_fix_shadowarray.GetValue(); for (const auto& desc : info.texture_descriptors) { + VideoCommon::ImageViewId fallback_view_id = VideoCommon::NULL_IMAGE_VIEW_ID; + VideoCommon::SamplerId fallback_sampler_id = VideoCommon::NULL_SAMPLER_ID; + + const VideoCommon::ImageViewInOut* temp_views = views; + const VideoCommon::SamplerId* temp_samplers = samplers; + + if (fix_shadows) { + for (u32 index = 0; index < desc.count; ++index) { + if (temp_views[index].id != VideoCommon::NULL_IMAGE_VIEW_ID) { + fallback_view_id = temp_views[index].id; + fallback_sampler_id = temp_samplers[index]; + break; + } + } + } + for (u32 index = 0; index < desc.count; ++index) { const VideoCommon::ImageViewId image_view_id{(views++)->id}; const VideoCommon::SamplerId sampler_id{*(samplers++)}; - ImageView& image_view{texture_cache.GetImageView(image_view_id)}; + + VideoCommon::ImageViewId actual_view_id = image_view_id; + VideoCommon::SamplerId actual_sampler_id = sampler_id; + + if (fix_shadows && + image_view_id == VideoCommon::NULL_IMAGE_VIEW_ID && + fallback_view_id != VideoCommon::NULL_IMAGE_VIEW_ID) { + actual_view_id = fallback_view_id; + actual_sampler_id = fallback_sampler_id; + } + + ImageView& image_view{texture_cache.GetImageView(actual_view_id)}; + const VkImageView vk_image_view{image_view.Handle(desc.type)}; - const Sampler& sampler{texture_cache.GetSampler(sampler_id)}; + const Sampler& sampler{texture_cache.GetSampler(actual_sampler_id)}; const bool use_fallback_sampler{sampler.HasAddedAnisotropy() && !image_view.SupportsAnisotropy()}; const VkSampler vk_sampler{use_fallback_sampler ? sampler.HandleWithDefaultAnisotropy() diff --git a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp index c5fd916689..a399defd4c 100644 --- a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp +++ b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp @@ -393,6 +393,9 @@ bool GraphicsPipeline::ConfigureImpl(bool is_indexed) { } } + LOG_DEBUG(Render_Vulkan, "Stage {} Texture: cbuf[{}][{}] -> handle 0x{:08X}", + stage, desc.cbuf_index, desc.cbuf_offset + (index << desc.size_shift), handle.first); + if (handle.first == 0) { views[view_index++] = { .index = 0, @@ -432,8 +435,6 @@ bool GraphicsPipeline::ConfigureImpl(bool is_indexed) { config_stage(4); } - // Data exists in the slot the shadows want but it seems this outputs a invalid image for all of them. - // The problem is either inside this function or the texture_descriptors above is pulling junk data texture_cache.FillGraphicsImageViews(std::span(views.data(), view_index)); VideoCommon::ImageViewInOut* texture_buffer_it{views.data()}; @@ -501,7 +502,9 @@ bool GraphicsPipeline::ConfigureImpl(bool is_indexed) { buffer_cache.BindHostStageBuffers(stage); PushImageDescriptors(texture_cache, guest_descriptor_queue, stage_infos[stage], rescaling, samplers_it, views_it); - const auto& info{stage_infos[0]}; + LOG_DEBUG(Render_Vulkan, "=== Preparing Stage {} ===", stage); + const Shader::Info& info{stage_infos[stage]}; + LOG_DEBUG(Render_Vulkan, "Stage {} has {} texture descriptors", stage, info.texture_descriptors.size()); if (info.uses_render_area) { render_area.uses_render_area = true; render_area.words = {static_cast(regs.surface_clip.width),