// SPDX-FileCopyrightText: Copyright 2025 shadPS4 Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later #pragma once #include "common/types.h" #include "shader_recompiler/ir/type.h" #include "video_core/amdgpu/resource.h" #include namespace Shader { static constexpr u32 NUM_USER_DATA_REGS = 16; static constexpr u32 NUM_IMAGES = 64; static constexpr u32 NUM_BUFFERS = 40; static constexpr u32 NUM_SAMPLERS = 16; static constexpr u32 NUM_FMASKS = 8; enum class BufferType : u32 { Guest, Flatbuf, BdaPagetable, FaultBuffer, GdsBuffer, SharedMemory, }; struct Info; struct BufferResource { u32 sharp_idx; IR::Type used_types; AmdGpu::Buffer inline_cbuf; BufferType buffer_type; u8 instance_attrib{}; bool is_written{}; bool is_formatted{}; bool IsSpecial() const noexcept { return buffer_type != BufferType::Guest; } bool IsStorage([[maybe_unused]] const AmdGpu::Buffer buffer) const noexcept { // When using uniform buffers, a size is required at compilation time, so we need to // either compile a lot of shader specializations to handle each size or just force it to // the maximum possible size always. However, for some vendors the shader-supplied size is // used for bounds checking uniform buffer accesses, so the latter would effectively turn // off buffer robustness behavior. Instead, force storage buffers which are bounds checked // using the actual buffer size. We are assuming the performance hit from this is // acceptable. return true; // buffer.GetSize() > profile.max_ubo_size || is_written; } constexpr AmdGpu::Buffer GetSharp(const auto& info) const noexcept { AmdGpu::Buffer buffer{}; if (inline_cbuf) { buffer = inline_cbuf; if (inline_cbuf.base_address > 1) { buffer.base_address += info.pgm_base; // address fixup } } else { buffer = info.template ReadUdSharp(sharp_idx); } if (!buffer.Valid()) { LOG_DEBUG(Render, "Encountered invalid buffer sharp"); return AmdGpu::Buffer::Null(); } return buffer; } }; using BufferResourceList = boost::container::static_vector; struct ImageResource { u32 sharp_idx; bool is_depth{}; bool is_atomic{}; bool is_array{}; bool is_written{}; bool is_r128{}; constexpr AmdGpu::Image GetSharp(const auto& info) const noexcept { AmdGpu::Image image{}; if (!is_r128) { image = info.template ReadUdSharp(sharp_idx); } else { const auto raw = info.template ReadUdSharp(sharp_idx); std::memcpy(&image, &raw, sizeof(raw)); } if (!image.Valid()) { LOG_DEBUG(Render_Vulkan, "Encountered invalid image sharp"); image = AmdGpu::Image::Null(is_depth); } else if (is_depth) { const auto data_fmt = image.GetDataFmt(); if (data_fmt != AmdGpu::DataFormat::Format16 && data_fmt != AmdGpu::DataFormat::Format32) { LOG_DEBUG(Render_Vulkan, "Encountered non-depth image used with depth instruction!"); image = AmdGpu::Image::Null(true); } } return image; } }; using ImageResourceList = boost::container::static_vector; struct SamplerResource { u32 sharp_idx; AmdGpu::Sampler inline_sampler; u32 is_inline_sampler : 1; u32 associated_image : 4; u32 disable_aniso : 1; constexpr AmdGpu::Sampler GetSharp(const auto& info) const noexcept { return is_inline_sampler ? inline_sampler : info.template ReadUdSharp(sharp_idx); } }; using SamplerResourceList = boost::container::static_vector; struct FMaskResource { u32 sharp_idx; constexpr AmdGpu::Image GetSharp(const auto& info) const noexcept { return info.template ReadUdSharp(sharp_idx); } }; using FMaskResourceList = boost::container::static_vector; struct PushData { static constexpr u32 XOffsetIndex = 0; static constexpr u32 YOffsetIndex = 1; static constexpr u32 XScaleIndex = 2; static constexpr u32 YScaleIndex = 3; static constexpr u32 UdRegsIndex = 4; static constexpr u32 BufOffsetIndex = UdRegsIndex + NUM_USER_DATA_REGS / 4; float xoffset; float yoffset; float xscale; float yscale; std::array ud_regs; std::array buf_offsets; void AddOffset(u32 binding, u32 offset) { ASSERT(offset < 256 && binding < buf_offsets.size()); buf_offsets[binding] = offset; } }; static_assert(sizeof(PushData) <= 128, "PushData size is greater than minimum size guaranteed by Vulkan spec"); } // namespace Shader