video_core: Add image support

This commit is contained in:
raphaelthegreat 2024-05-27 01:07:46 +03:00
parent 729e166cd3
commit d59b102b6f
48 changed files with 1264 additions and 259 deletions

View file

@ -63,6 +63,16 @@ struct Liverpool {
const uintptr_t addr = uintptr_t(address_hi) << 40 | uintptr_t(address_lo) << 8;
return reinterpret_cast<const T*>(addr);
}
std::span<const u32> Code() const {
u32 code_size = 1;
const u32* code = Address<u32>();
static constexpr std::string_view PostHeader = "OrbShdr";
while (std::memcmp(code + code_size, PostHeader.data(), PostHeader.size()) != 0) {
code_size++;
}
return std::span{code, code_size};
}
};
union PsInputControl {
@ -228,7 +238,7 @@ struct Liverpool {
enum class ZFormat : u32 {
Invald = 0,
Z16 = 1,
Z32Float = 2,
Z32Float = 3,
};
enum class StencilFormat : u32 {
@ -353,8 +363,9 @@ struct Liverpool {
BitField<0, 8, u32> base_addr_hi;
u32 base_addr_lo;
VAddr Address() const {
return base_addr_lo | u64(base_addr_hi) << 32;
template <typename T = VAddr>
T Address() const {
return reinterpret_cast<T>(base_addr_lo | u64(base_addr_hi) << 32);
}
};
@ -448,6 +459,53 @@ struct Liverpool {
u32 data_w;
};
struct BlendConstants {
float red;
float green;
float blue;
float alpha;
};
union BlendControl {
enum class BlendFactor : u32 {
Zero = 0,
One = 1,
SrcColor = 2,
OneMinusSrcColor = 3,
SrcAlpha = 4,
OneMinusSrcAlpha = 5,
DstAlpha = 6,
OneMinusDstAlpha = 7,
DstColor = 8,
OneMinusDstColor = 9,
SrcAlphaSaturate = 10,
ConstantColor = 13,
OneMinusConstantColor = 14,
Src1Color = 15,
InvSrc1Color = 16,
Src1Alpha = 17,
InvSrc1Alpha = 18,
ConstantAlpha = 19,
OneMinusConstantAlpha = 20,
};
enum class BlendFunc : u32 {
Add = 0,
Subtract = 1,
Min = 2,
Max = 3,
};
BitField<0, 5, BlendFactor> color_src_factor;
BitField<5, 3, BlendFunc> color_func;
BitField<8, 5, BlendFactor> color_dst_factor;
BitField<16, 5, BlendFactor> alpha_src_factor;
BitField<21, 3, BlendFunc> alpha_func;
BitField<24, 5, BlendFactor> alpha_dst_factor;
BitField<29, 1, u32> separate_alpha_blend;
BitField<30, 1, u32> enable;
};
struct ColorBuffer {
enum class EndianSwap : u32 {
None = 0,
@ -577,7 +635,9 @@ struct Liverpool {
INSERT_PADDING_WORDS(0xA094 - 0xA08E - 2);
std::array<ViewportScissor, NumViewports> viewport_scissors;
std::array<ViewportDepth, NumViewports> viewport_depths;
INSERT_PADDING_WORDS(0xA10B - 0xA0D4);
INSERT_PADDING_WORDS(0xA105 - 0xA0D4);
BlendConstants blend_constants;
INSERT_PADDING_WORDS(0xA10B - 0xA105 - 4);
StencilControl stencil_control;
StencilRefMask stencil_ref_front;
StencilRefMask stencil_ref_back;
@ -593,7 +653,9 @@ struct Liverpool {
ShaderPosFormat shader_pos_format;
ShaderExportFormat z_export_format;
ColorExportFormat color_export_format;
INSERT_PADDING_WORDS(0xA1F9 - 0xA1C3 - 3);
INSERT_PADDING_WORDS(0xA1E0 - 0xA1C3 - 3);
std::array<BlendControl, NumColorBuffers> blend_control;
INSERT_PADDING_WORDS(0xA1F9 - 0xA1E0 - 8);
IndexBufferBase index_base_address;
INSERT_PADDING_WORDS(1);
u32 draw_initiator;
@ -732,6 +794,7 @@ static_assert(GFX6_3D_REG_INDEX(num_interp) == 0xA1B6);
static_assert(GFX6_3D_REG_INDEX(shader_pos_format) == 0xA1C3);
static_assert(GFX6_3D_REG_INDEX(z_export_format) == 0xA1C4);
static_assert(GFX6_3D_REG_INDEX(color_export_format) == 0xA1C5);
static_assert(GFX6_3D_REG_INDEX(blend_control) == 0xA1E0);
static_assert(GFX6_3D_REG_INDEX(index_base_address) == 0xA1F9);
static_assert(GFX6_3D_REG_INDEX(draw_initiator) == 0xA1FC);
static_assert(GFX6_3D_REG_INDEX(clipper_control) == 0xA204);

View file

@ -31,4 +31,196 @@ struct Buffer {
};
};
enum class ImageType : u64 {
Buffer = 0,
Color1D = 8,
Color2D = 9,
Color3D = 10,
Cube = 11,
Color1DArray = 12,
Color2DArray = 13,
Color2DMsaa = 14,
Color2DMsaaArray = 15,
};
constexpr std::string_view NameOf(ImageType type) {
switch (type) {
case ImageType::Buffer:
return "Buffer";
case ImageType::Color1D:
return "Color1D";
case ImageType::Color2D:
return "Color2D";
case ImageType::Color3D:
return "Color3D";
case ImageType::Cube:
return "Cube";
case ImageType::Color1DArray:
return "Color1DArray";
case ImageType::Color2DArray:
return "Color2DArray";
case ImageType::Color2DMsaa:
return "Color2DMsaa";
case ImageType::Color2DMsaaArray:
return "Color2DMsaaArray";
default:
return "Unknown";
}
}
struct Image {
union {
BitField<0, 40, u64> base_address;
BitField<40, 12, u64> min_lod;
BitField<52, 6, u64> data_format;
BitField<58, 4, u64> num_format;
BitField<62, 2, u64> mtype;
};
union {
BitField<0, 14, u64> width;
BitField<14, 14, u64> height;
BitField<28, 3, u64> perf_modulation;
BitField<31, 1, u64> interlaced;
BitField<32, 3, u64> dst_sel_x;
BitField<35, 3, u64> dst_sel_y;
BitField<38, 3, u64> dst_sel_z;
BitField<41, 3, u64> dst_sel_w;
BitField<44, 4, u64> base_level;
BitField<48, 4, u64> last_level;
BitField<52, 5, u64> tiling_index;
BitField<57, 1, u64> pow2pad;
BitField<58, 1, u64> mtype2;
BitField<59, 1, u64> atc;
BitField<60, 4, ImageType> type;
};
VAddr Address() const {
return base_address << 8;
}
DataFormat GetDataFmt() const noexcept {
return static_cast<DataFormat>(data_format.Value());
}
NumberFormat GetNumberFmt() const noexcept {
return static_cast<NumberFormat>(num_format.Value());
}
};
// 8.2.7. Image Sampler [RDNA 2 Instruction Set Architecture]
enum class ClampMode : u64 {
Wrap = 0,
Mirror = 1,
ClampLastTexel = 2,
MirrorOnceLastTexel = 3,
ClampHalfBorder = 4,
MirrorOnceHalfBorder = 5,
ClampBorder = 6,
MirrorOnceBorder = 7,
};
enum class AnisoRatio : u64 {
One,
Two,
Four,
Eight,
Sixteen,
};
enum class DepthCompare : u64 {
Never = 0,
Less = 1,
Equal = 2,
LessEqual = 3,
Greater = 4,
NotEqual = 5,
GreaterEqual = 6,
Always = 7,
};
enum class FilterMode : u64 {
Blend = 0,
Min = 1,
Max = 2,
};
enum class Filter : u64 {
Point = 0,
Bilinear = 1,
AnisoPoint = 2,
AnisoLinear = 3,
};
enum class MipFilter : u64 {
None = 0,
Point = 1,
Linear = 2,
};
enum class BorderColor : u64 {
OpaqueBlack = 0,
TransparentBlack = 1,
White = 2,
Custom = 3,
};
// Table 8.12 Sampler Resource Definition
struct Sampler {
union {
BitField<0, 3, ClampMode> clamp_x;
BitField<3, 3, ClampMode> clamp_y;
BitField<6, 3, ClampMode> clamp_z;
BitField<9, 3, AnisoRatio> max_aniso;
BitField<12, 3, DepthCompare> depth_compare_func;
BitField<15, 1, u64> force_unnormalized;
BitField<16, 3, u64> aniso_threshold;
BitField<19, 1, u64> mc_coord_trunc;
BitField<20, 1, u64> force_degamma;
BitField<21, 6, u64> aniso_bias;
BitField<27, 1, u64> trunc_coord;
BitField<28, 1, u64> disable_cube_wrap;
BitField<29, 2, FilterMode> filter_mode;
BitField<32, 12, u64> min_lod;
BitField<44, 12, u64> max_lod;
BitField<56, 4, u64> perf_mip;
BitField<60, 4, u64> perf_z;
};
union {
BitField<0, 14, u64> lod_bias;
BitField<14, 6, u64> lod_bias_sec;
BitField<20, 2, Filter> xy_mag_filter;
BitField<22, 2, Filter> xy_min_filter;
BitField<24, 2, u64> z_filter;
BitField<26, 2, MipFilter> mip_filter;
BitField<28, 1, u64> mip_point_preclamp;
BitField<29, 1, u64> disable_lsb_ceil;
BitField<30, 2, u64> unused0;
BitField<32, 12, u64> border_color_ptr;
BitField<42, 18, u64> unused1;
BitField<62, 2, BorderColor> border_color_type;
};
float LodBias() const noexcept {
return static_cast<float>(lod_bias);
}
float MinLod() const noexcept {
return static_cast<float>(min_lod);
}
float MaxLod() const noexcept {
return static_cast<float>(max_lod);
}
};
} // namespace AmdGpu
template <>
struct fmt::formatter<AmdGpu::ImageType> {
constexpr auto parse(format_parse_context& ctx) {
return ctx.begin();
}
auto format(AmdGpu::ImageType type, format_context& ctx) const {
return fmt::format_to(ctx.out(), "{}", AmdGpu::NameOf(type));
}
};

View file

@ -6,6 +6,8 @@
namespace Vulkan::LiverpoolToVK {
using DepthBuffer = Liverpool::DepthBuffer;
vk::StencilOp StencilOp(Liverpool::StencilFunc op) {
switch (op) {
case Liverpool::StencilFunc::Keep:
@ -77,6 +79,8 @@ vk::PrimitiveTopology PrimitiveType(Liverpool::PrimitiveType type) {
case Liverpool::PrimitiveType::QuadList:
// Needs to generate index buffer on the fly.
return vk::PrimitiveTopology::eTriangleList;
case Liverpool::PrimitiveType::RectList:
return vk::PrimitiveTopology::eTriangleList;
default:
UNREACHABLE();
return vk::PrimitiveTopology::eTriangleList;
@ -113,6 +117,161 @@ vk::CullModeFlags CullMode(Liverpool::CullMode mode) {
}
}
vk::BlendFactor BlendFactor(Liverpool::BlendControl::BlendFactor factor) {
using BlendFactor = Liverpool::BlendControl::BlendFactor;
switch (factor) {
case BlendFactor::Zero:
return vk::BlendFactor::eZero;
case BlendFactor::One:
return vk::BlendFactor::eOne;
case BlendFactor::SrcColor:
return vk::BlendFactor::eSrcColor;
case BlendFactor::OneMinusSrcColor:
return vk::BlendFactor::eOneMinusSrcColor;
case BlendFactor::SrcAlpha:
return vk::BlendFactor::eSrcAlpha;
case BlendFactor::OneMinusSrcAlpha:
return vk::BlendFactor::eOneMinusSrcAlpha;
case BlendFactor::DstAlpha:
return vk::BlendFactor::eDstAlpha;
case BlendFactor::OneMinusDstAlpha:
return vk::BlendFactor::eOneMinusDstAlpha;
case BlendFactor::DstColor:
return vk::BlendFactor::eDstColor;
case BlendFactor::OneMinusDstColor:
return vk::BlendFactor::eOneMinusDstColor;
case BlendFactor::SrcAlphaSaturate:
return vk::BlendFactor::eSrcAlphaSaturate;
case BlendFactor::ConstantColor:
return vk::BlendFactor::eConstantColor;
case BlendFactor::OneMinusConstantColor:
return vk::BlendFactor::eOneMinusConstantColor;
case BlendFactor::Src1Color:
return vk::BlendFactor::eSrc1Color;
case BlendFactor::InvSrc1Color:
return vk::BlendFactor::eOneMinusSrc1Color;
case BlendFactor::Src1Alpha:
return vk::BlendFactor::eSrc1Alpha;
case BlendFactor::InvSrc1Alpha:
return vk::BlendFactor::eOneMinusSrc1Alpha;
case BlendFactor::ConstantAlpha:
return vk::BlendFactor::eConstantAlpha;
case BlendFactor::OneMinusConstantAlpha:
return vk::BlendFactor::eOneMinusConstantAlpha;
default:
UNREACHABLE();
}
}
vk::BlendOp BlendOp(Liverpool::BlendControl::BlendFunc func) {
using BlendFunc = Liverpool::BlendControl::BlendFunc;
switch (func) {
case BlendFunc::Add:
return vk::BlendOp::eAdd;
case BlendFunc::Subtract:
return vk::BlendOp::eSubtract;
case BlendFunc::Min:
return vk::BlendOp::eMin;
case BlendFunc::Max:
return vk::BlendOp::eMax;
default:
UNREACHABLE();
}
}
// https://github.com/chaotic-cx/mesa-mirror/blob/0954afff5/src/amd/vulkan/radv_sampler.c#L21
vk::SamplerAddressMode ClampMode(AmdGpu::ClampMode mode) {
switch (mode) {
case AmdGpu::ClampMode::Wrap:
return vk::SamplerAddressMode::eRepeat;
case AmdGpu::ClampMode::Mirror:
return vk::SamplerAddressMode::eMirroredRepeat;
case AmdGpu::ClampMode::ClampLastTexel:
return vk::SamplerAddressMode::eClampToEdge;
case AmdGpu::ClampMode::MirrorOnceLastTexel:
return vk::SamplerAddressMode::eMirrorClampToEdge;
case AmdGpu::ClampMode::ClampBorder:
return vk::SamplerAddressMode::eClampToBorder;
default:
UNREACHABLE();
}
}
vk::CompareOp DepthCompare(AmdGpu::DepthCompare comp) {
switch (comp) {
case AmdGpu::DepthCompare::Never:
return vk::CompareOp::eNever;
case AmdGpu::DepthCompare::Less:
return vk::CompareOp::eLess;
case AmdGpu::DepthCompare::Equal:
return vk::CompareOp::eEqual;
case AmdGpu::DepthCompare::LessEqual:
return vk::CompareOp::eLessOrEqual;
case AmdGpu::DepthCompare::Greater:
return vk::CompareOp::eGreater;
case AmdGpu::DepthCompare::NotEqual:
return vk::CompareOp::eNotEqual;
case AmdGpu::DepthCompare::GreaterEqual:
return vk::CompareOp::eGreaterOrEqual;
case AmdGpu::DepthCompare::Always:
return vk::CompareOp::eAlways;
}
}
vk::Filter Filter(AmdGpu::Filter filter) {
switch (filter) {
case AmdGpu::Filter::Point:
case AmdGpu::Filter::AnisoPoint:
return vk::Filter::eNearest;
case AmdGpu::Filter::Bilinear:
case AmdGpu::Filter::AnisoLinear:
return vk::Filter::eLinear;
default:
UNREACHABLE();
}
}
vk::SamplerReductionMode FilterMode(AmdGpu::FilterMode mode) {
switch (mode) {
case AmdGpu::FilterMode::Blend:
return vk::SamplerReductionMode::eWeightedAverage;
case AmdGpu::FilterMode::Min:
return vk::SamplerReductionMode::eMin;
case AmdGpu::FilterMode::Max:
return vk::SamplerReductionMode::eMax;
default:
UNREACHABLE();
}
}
vk::SamplerMipmapMode MipFilter(AmdGpu::MipFilter filter) {
switch (filter) {
case AmdGpu::MipFilter::Point:
return vk::SamplerMipmapMode::eNearest;
case AmdGpu::MipFilter::Linear:
return vk::SamplerMipmapMode::eLinear;
case AmdGpu::MipFilter::None:
return vk::SamplerMipmapMode::eNearest;
default:
UNREACHABLE();
}
}
vk::BorderColor BorderColor(AmdGpu::BorderColor color) {
switch (color) {
case AmdGpu::BorderColor::OpaqueBlack:
return vk::BorderColor::eFloatOpaqueBlack;
case AmdGpu::BorderColor::TransparentBlack:
return vk::BorderColor::eFloatTransparentBlack;
case AmdGpu::BorderColor::White:
return vk::BorderColor::eFloatOpaqueWhite;
case AmdGpu::BorderColor::Custom:
return vk::BorderColor::eFloatCustomEXT;
default:
UNREACHABLE();
}
}
vk::Format SurfaceFormat(AmdGpu::DataFormat data_format, AmdGpu::NumberFormat num_format) {
if (data_format == AmdGpu::DataFormat::Format32_32_32_32 &&
num_format == AmdGpu::NumberFormat::Float) {
@ -130,11 +289,22 @@ vk::Format SurfaceFormat(AmdGpu::DataFormat data_format, AmdGpu::NumberFormat nu
num_format == AmdGpu::NumberFormat::Srgb) {
return vk::Format::eR8G8B8A8Srgb;
}
if (data_format == AmdGpu::DataFormat::Format32_32_32 &&
num_format == AmdGpu::NumberFormat::Float) {
return vk::Format::eR32G32B32Sfloat;
}
if (data_format == AmdGpu::DataFormat::Format32_32 &&
num_format == AmdGpu::NumberFormat::Float) {
return vk::Format::eR32G32Sfloat;
}
UNREACHABLE();
}
vk::Format DepthFormat(Liverpool::DepthBuffer::ZFormat z_format,
Liverpool::DepthBuffer::StencilFormat stencil_format) {
vk::Format DepthFormat(DepthBuffer::ZFormat z_format, DepthBuffer::StencilFormat stencil_format) {
if (z_format == DepthBuffer::ZFormat::Z32Float &&
stencil_format == DepthBuffer::StencilFormat::Stencil8) {
return vk::Format::eD32SfloatS8Uint;
}
UNREACHABLE();
}

View file

@ -5,6 +5,7 @@
#include "video_core/amdgpu/liverpool.h"
#include "video_core/amdgpu/pixel_format.h"
#include "video_core/amdgpu/resource.h"
#include "video_core/renderer_vulkan/vk_common.h"
namespace Vulkan::LiverpoolToVK {
@ -21,6 +22,22 @@ vk::PolygonMode PolygonMode(Liverpool::PolygonMode mode);
vk::CullModeFlags CullMode(Liverpool::CullMode mode);
vk::BlendFactor BlendFactor(Liverpool::BlendControl::BlendFactor factor);
vk::BlendOp BlendOp(Liverpool::BlendControl::BlendFunc func);
vk::SamplerAddressMode ClampMode(AmdGpu::ClampMode mode);
vk::CompareOp DepthCompare(AmdGpu::DepthCompare comp);
vk::Filter Filter(AmdGpu::Filter filter);
vk::SamplerReductionMode FilterMode(AmdGpu::FilterMode mode);
vk::SamplerMipmapMode MipFilter(AmdGpu::MipFilter filter);
vk::BorderColor BorderColor(AmdGpu::BorderColor color);
vk::Format SurfaceFormat(AmdGpu::DataFormat data_format, AmdGpu::NumberFormat num_format);
vk::Format DepthFormat(Liverpool::DepthBuffer::ZFormat z_format,

View file

@ -10,6 +10,8 @@
#include "video_core/renderer_vulkan/vk_graphics_pipeline.h"
#include "video_core/renderer_vulkan/vk_instance.h"
#include "video_core/renderer_vulkan/vk_scheduler.h"
#include "video_core/renderer_vulkan/vk_stream_buffer.h"
#include "video_core/texture_cache/texture_cache.h"
namespace Vulkan {
@ -25,8 +27,7 @@ GraphicsPipeline::GraphicsPipeline(const Instance& instance_, Scheduler& schedul
}
stages[i] = *infos[i];
}
desc_layout = BuildSetLayout();
BuildDescSetLayout();
const vk::DescriptorSetLayout set_layout = *desc_layout;
const vk::PipelineLayoutCreateInfo layout_info = {
.setLayoutCount = 1U,
@ -81,20 +82,6 @@ GraphicsPipeline::GraphicsPipeline(const Instance& instance_, Scheduler& schedul
.sampleShadingEnable = false,
};
const vk::PipelineColorBlendAttachmentState colorblend_attachment = {
.blendEnable = false,
.colorWriteMask = vk::ColorComponentFlagBits::eR | vk::ColorComponentFlagBits::eG |
vk::ColorComponentFlagBits::eB | vk::ColorComponentFlagBits::eA,
};
const vk::PipelineColorBlendStateCreateInfo color_blending = {
.logicOpEnable = false,
.logicOp = vk::LogicOp::eCopy,
.attachmentCount = 1,
.pAttachments = &colorblend_attachment,
.blendConstants = std::array{1.0f, 1.0f, 1.0f, 1.0f},
};
const vk::Viewport viewport = {
.x = 0.0f,
.y = 0.0f,
@ -119,6 +106,7 @@ GraphicsPipeline::GraphicsPipeline(const Instance& instance_, Scheduler& schedul
boost::container::static_vector<vk::DynamicState, 14> dynamic_states = {
vk::DynamicState::eViewport,
vk::DynamicState::eScissor,
vk::DynamicState::eBlendConstants,
};
const vk::PipelineDynamicStateCreateInfo dynamic_info = {
@ -174,6 +162,30 @@ GraphicsPipeline::GraphicsPipeline(const Instance& instance_, Scheduler& schedul
.stencilAttachmentFormat = vk::Format::eUndefined,
};
std::array<vk::PipelineColorBlendAttachmentState, Liverpool::NumColorBuffers> attachments;
for (u32 i = 0; i < num_color_formats; i++) {
const auto& control = key.blend_controls[i];
attachments[i] = vk::PipelineColorBlendAttachmentState{
.blendEnable = key.blend_controls[i].enable,
.srcColorBlendFactor = LiverpoolToVK::BlendFactor(control.color_src_factor),
.dstColorBlendFactor = LiverpoolToVK::BlendFactor(control.color_dst_factor),
.colorBlendOp = LiverpoolToVK::BlendOp(control.color_func),
.srcAlphaBlendFactor = LiverpoolToVK::BlendFactor(control.alpha_src_factor),
.dstAlphaBlendFactor = LiverpoolToVK::BlendFactor(control.color_dst_factor),
.alphaBlendOp = LiverpoolToVK::BlendOp(control.alpha_func),
.colorWriteMask = vk::ColorComponentFlagBits::eR | vk::ColorComponentFlagBits::eG |
vk::ColorComponentFlagBits::eB | vk::ColorComponentFlagBits::eA,
};
}
const vk::PipelineColorBlendStateCreateInfo color_blending = {
.logicOpEnable = false,
.logicOp = vk::LogicOp::eCopy,
.attachmentCount = num_color_formats,
.pAttachments = attachments.data(),
.blendConstants = std::array{1.0f, 1.0f, 1.0f, 1.0f},
};
const vk::GraphicsPipelineCreateInfo pipeline_info = {
.pNext = &pipeline_rendering_ci,
.stageCount = shader_count,
@ -199,14 +211,31 @@ GraphicsPipeline::GraphicsPipeline(const Instance& instance_, Scheduler& schedul
GraphicsPipeline::~GraphicsPipeline() = default;
vk::UniqueDescriptorSetLayout GraphicsPipeline::BuildSetLayout() const {
void GraphicsPipeline::BuildDescSetLayout() {
u32 binding{};
boost::container::small_vector<vk::DescriptorSetLayoutBinding, 32> bindings;
for (const auto& stage : stages) {
for (const auto& buffer : stage.buffers) {
bindings.push_back({
.binding = binding++,
.descriptorType = vk::DescriptorType::eStorageBuffer,
.descriptorType = buffer.is_storage ? vk::DescriptorType::eStorageBuffer
: vk::DescriptorType::eUniformBuffer,
.descriptorCount = 1,
.stageFlags = vk::ShaderStageFlagBits::eVertex | vk::ShaderStageFlagBits::eFragment,
});
}
for (const auto& image : stage.images) {
bindings.push_back({
.binding = binding++,
.descriptorType = vk::DescriptorType::eSampledImage,
.descriptorCount = 1,
.stageFlags = vk::ShaderStageFlagBits::eVertex | vk::ShaderStageFlagBits::eFragment,
});
}
for (const auto& sampler : stage.samplers) {
bindings.push_back({
.binding = binding++,
.descriptorType = vk::DescriptorType::eSampler,
.descriptorCount = 1,
.stageFlags = vk::ShaderStageFlagBits::eVertex | vk::ShaderStageFlagBits::eFragment,
});
@ -217,12 +246,24 @@ vk::UniqueDescriptorSetLayout GraphicsPipeline::BuildSetLayout() const {
.bindingCount = static_cast<u32>(bindings.size()),
.pBindings = bindings.data(),
};
return instance.GetDevice().createDescriptorSetLayoutUnique(desc_layout_ci);
desc_layout = instance.GetDevice().createDescriptorSetLayoutUnique(desc_layout_ci);
}
void GraphicsPipeline::BindResources(Core::MemoryManager* memory) const {
void GraphicsPipeline::BindResources(Core::MemoryManager* memory, StreamBuffer& staging,
VideoCore::TextureCache& texture_cache) const {
static constexpr u64 MinUniformAlignment = 64;
const auto map_staging = [&](auto src, size_t size) {
const auto [data, offset, _] = staging.Map(size, MinUniformAlignment);
std::memcpy(data, reinterpret_cast<const void*>(src), size);
staging.Commit(size);
return offset;
};
std::array<vk::Buffer, MaxVertexBufferCount> buffers;
std::array<vk::DeviceSize, MaxVertexBufferCount> offsets;
VAddr base_address = 0;
u32 start_offset = 0;
// Bind vertex buffer.
const auto& vs_info = stages[0];
@ -230,38 +271,77 @@ void GraphicsPipeline::BindResources(Core::MemoryManager* memory) const {
for (u32 i = 0; i < num_buffers; ++i) {
const auto& input = vs_info.vs_inputs[i];
const auto buffer = vs_info.ReadUd<AmdGpu::Buffer>(input.sgpr_base, input.dword_offset);
std::tie(buffers[i], offsets[i]) = memory->GetVulkanBuffer(buffer.base_address);
if (i == 0) {
start_offset =
map_staging(buffer.base_address.Value(), buffer.stride * buffer.num_records);
base_address = buffer.base_address;
}
buffers[i] = staging.Handle();
offsets[i] = start_offset + buffer.base_address - base_address;
}
const auto cmdbuf = scheduler.CommandBuffer();
cmdbuf.bindVertexBuffers(0, num_buffers, buffers.data(), offsets.data());
if (num_buffers > 0) {
cmdbuf.bindVertexBuffers(0, num_buffers, buffers.data(), offsets.data());
}
// Bind resource buffers and textures.
boost::container::static_vector<vk::DescriptorBufferInfo, 4> buffer_infos;
boost::container::static_vector<vk::DescriptorImageInfo, 8> image_infos;
boost::container::small_vector<vk::WriteDescriptorSet, 16> set_writes;
u32 binding{};
for (const auto& stage : stages) {
for (const auto& buffer : stage.buffers) {
const auto vsharp = stage.ReadUd<AmdGpu::Buffer>(buffer.sgpr_base, buffer.dword_offset);
const auto [vk_buffer, offset] = memory->GetVulkanBuffer(vsharp.base_address);
buffer_infos.push_back({
.buffer = vk_buffer,
.offset = offset,
.range = vsharp.stride * vsharp.num_records,
});
const u32 size = vsharp.stride * vsharp.num_records;
const u32 offset = map_staging(vsharp.base_address.Value(), size);
buffer_infos.emplace_back(staging.Handle(), offset, size);
set_writes.push_back({
.dstSet = VK_NULL_HANDLE,
.dstBinding = binding,
.dstBinding = binding++,
.dstArrayElement = 0,
.descriptorCount = 1,
.descriptorType = vk::DescriptorType::eStorageBuffer,
.descriptorType = buffer.is_storage ? vk::DescriptorType::eStorageBuffer
: vk::DescriptorType::eUniformBuffer,
.pBufferInfo = &buffer_infos.back(),
});
}
for (const auto& image : stage.images) {
const auto tsharp = stage.ReadUd<AmdGpu::Image>(image.sgpr_base, image.dword_offset);
const auto& image_view = texture_cache.FindImageView(tsharp);
image_infos.emplace_back(VK_NULL_HANDLE, *image_view.image_view,
vk::ImageLayout::eGeneral);
set_writes.push_back({
.dstSet = VK_NULL_HANDLE,
.dstBinding = binding++,
.dstArrayElement = 0,
.descriptorCount = 1,
.descriptorType = vk::DescriptorType::eSampledImage,
.pImageInfo = &image_infos.back(),
});
}
for (const auto& sampler : stage.samplers) {
const auto ssharp =
stage.ReadUd<AmdGpu::Sampler>(sampler.sgpr_base, sampler.dword_offset);
const auto vk_sampler = texture_cache.GetSampler(ssharp);
image_infos.emplace_back(vk_sampler, VK_NULL_HANDLE, vk::ImageLayout::eGeneral);
set_writes.push_back({
.dstSet = VK_NULL_HANDLE,
.dstBinding = binding++,
.dstArrayElement = 0,
.descriptorCount = 1,
.descriptorType = vk::DescriptorType::eSampler,
.pImageInfo = &image_infos.back(),
});
}
}
cmdbuf.pushDescriptorSetKHR(vk::PipelineBindPoint::eGraphics, *pipeline_layout, 0, set_writes);
if (!set_writes.empty()) {
cmdbuf.pushDescriptorSetKHR(vk::PipelineBindPoint::eGraphics, *pipeline_layout, 0,
set_writes);
}
}
} // namespace Vulkan

View file

@ -11,6 +11,10 @@ namespace Core {
class MemoryManager;
}
namespace VideoCore {
class TextureCache;
}
namespace Vulkan {
static constexpr u32 MaxVertexBufferCount = 32;
@ -18,6 +22,7 @@ static constexpr u32 MaxShaderStages = 5;
class Instance;
class Scheduler;
class StreamBuffer;
using Liverpool = AmdGpu::Liverpool;
@ -33,6 +38,7 @@ struct PipelineKey {
Liverpool::PrimitiveType prim_type;
Liverpool::PolygonMode polygon_mode;
Liverpool::CullMode cull_mode;
std::array<Liverpool::BlendControl, Liverpool::NumColorBuffers> blend_controls;
bool operator==(const PipelineKey& key) const noexcept {
return std::memcmp(this, &key, sizeof(PipelineKey)) == 0;
@ -48,14 +54,15 @@ public:
std::array<vk::ShaderModule, MaxShaderStages> modules);
~GraphicsPipeline();
void BindResources(Core::MemoryManager* memory) const;
void BindResources(Core::MemoryManager* memory, StreamBuffer& staging,
VideoCore::TextureCache& texture_cache) const;
[[nodiscard]] vk::Pipeline Handle() const noexcept {
return *pipeline;
}
private:
vk::UniqueDescriptorSetLayout BuildSetLayout() const;
void BuildDescSetLayout();
private:
const Instance& instance;

View file

@ -151,6 +151,7 @@ bool Instance::CreateDevice() {
custom_border_color = add_extension(VK_EXT_CUSTOM_BORDER_COLOR_EXTENSION_NAME);
index_type_uint8 = add_extension(VK_KHR_INDEX_TYPE_UINT8_EXTENSION_NAME);
add_extension(VK_KHR_PUSH_DESCRIPTOR_EXTENSION_NAME);
add_extension(VK_KHR_MAINTENANCE_4_EXTENSION_NAME);
const auto family_properties = physical_device.getQueueFamilyProperties();
if (family_properties.empty()) {
@ -213,6 +214,9 @@ bool Instance::CreateDevice() {
vk::PhysicalDeviceIndexTypeUint8FeaturesEXT{
.indexTypeUint8 = true,
},
vk::PhysicalDeviceMaintenance4Features{
.maintenance4 = true,
},
};
if (!index_type_uint8) {

View file

@ -1,11 +1,13 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <fstream>
#include <xxhash.h>
#include "common/config.h"
#include "common/io_file.h"
#include "common/path_util.h"
#include "shader_recompiler/backend/spirv/emit_spirv.h"
#include "shader_recompiler/recompiler.h"
#include "shader_recompiler/runtime_info.h"
#include "video_core/amdgpu/resource.h"
#include "video_core/renderer_vulkan/vk_instance.h"
#include "video_core/renderer_vulkan/vk_pipeline_cache.h"
#include "video_core/renderer_vulkan/vk_scheduler.h"
@ -41,6 +43,9 @@ PipelineCache::PipelineCache(const Instance& instance_, Scheduler& scheduler_,
: instance{instance_}, scheduler{scheduler_}, liverpool{liverpool_}, inst_pool{8192},
block_pool{512} {
pipeline_cache = instance.GetDevice().createPipelineCacheUnique({});
profile = Shader::Profile{
.supported_spirv = 0x00010600U,
};
}
const GraphicsPipeline* PipelineCache::GetPipeline() {
@ -63,6 +68,7 @@ void PipelineCache::RefreshKey() {
key.stencil_ref_back = regs.stencil_ref_back;
key.prim_type = regs.primitive_type;
key.polygon_mode = regs.polygon_control.PolyMode();
key.blend_controls = regs.blend_control;
const auto& db = regs.depth_buffer;
key.depth_format = key.depth.depth_enable
@ -81,17 +87,15 @@ void PipelineCache::RefreshKey() {
key.stage_hashes[i] = 0;
continue;
}
const u32* code = pgm->Address<u32>();
Shader::BinaryInfo bininfo;
std::memcpy(&bininfo, code + (code[1] + 1) * 2, sizeof(bininfo));
key.stage_hashes[i] = bininfo.shader_hash;
const auto code = pgm->Code();
key.stage_hashes[i] = XXH3_64bits(code.data(), code.size_bytes());
}
}
std::unique_ptr<GraphicsPipeline> PipelineCache::CreatePipeline() {
const auto& regs = liverpool->regs;
u32 binding{};
std::array<Shader::IR::Program, MaxShaderStages> programs;
std::array<const Shader::Info*, MaxShaderStages> infos{};
@ -101,40 +105,49 @@ std::unique_ptr<GraphicsPipeline> PipelineCache::CreatePipeline() {
continue;
}
auto* pgm = regs.ProgramForStage(i);
const u32* code = pgm->Address<u32>();
const auto code = pgm->Code();
Shader::BinaryInfo bininfo;
std::memcpy(&bininfo, code + (code[1] + 1) * 2, sizeof(bininfo));
const u32 num_dwords = bininfo.length / sizeof(u32);
const auto it = module_map.find(bininfo.shader_hash);
const auto it = module_map.find(graphics_key.stage_hashes[i]);
if (it != module_map.end()) {
stages[i] = *it->second;
continue;
}
// Dump shader code if requested.
const auto stage = Shader::Stage{i};
const u64 hash = graphics_key.stage_hashes[i];
if (Config::dumpShaders()) {
DumpShader(code, hash, stage, "bin");
}
block_pool.ReleaseContents();
inst_pool.ReleaseContents();
// Recompile shader to IR.
const auto stage = Shader::Stage{i};
const Shader::Info info = MakeShaderInfo(stage, pgm->user_data, regs);
programs[i] = Shader::TranslateProgram(inst_pool, block_pool, std::span{code, num_dwords},
std::move(info));
programs[i] = Shader::TranslateProgram(inst_pool, block_pool, code, std::move(info));
// Compile IR to SPIR-V
const auto profile = Shader::Profile{.supported_spirv = 0x00010600U};
const auto spv_code = Shader::Backend::SPIRV::EmitSPIRV(profile, programs[i]);
std::ofstream file("shader0.spv", std::ios::out | std::ios::binary);
file.write((const char*)spv_code.data(), spv_code.size() * 4);
file.close();
const auto spv_code = Shader::Backend::SPIRV::EmitSPIRV(profile, programs[i], binding);
stages[i] = CompileSPV(spv_code, instance.GetDevice());
infos[i] = &programs[i].info;
if (Config::dumpShaders()) {
DumpShader(spv_code, hash, stage, "spv");
}
}
return std::make_unique<GraphicsPipeline>(instance, scheduler, graphics_key, *pipeline_cache,
infos, stages);
}
void PipelineCache::DumpShader(std::span<const u32> code, u64 hash, Shader::Stage stage,
std::string_view ext) {
using namespace Common::FS;
const auto dump_dir = GetUserPath(PathType::ShaderDir) / "dumps";
const auto filename = fmt::format("{}_{:#X}.{}", stage, hash, ext);
const auto file = IOFile{dump_dir / filename, FileAccessMode::Write};
file.WriteSpan(code);
}
} // namespace Vulkan

View file

@ -6,6 +6,7 @@
#include <tsl/robin_map.h>
#include "shader_recompiler/ir/basic_block.h"
#include "shader_recompiler/object_pool.h"
#include "shader_recompiler/profile.h"
#include "video_core/renderer_vulkan/vk_graphics_pipeline.h"
namespace Shader {
@ -32,6 +33,8 @@ private:
std::unique_ptr<GraphicsPipeline> CreatePipeline();
void DumpShader(std::span<const u32> code, u64 hash, Shader::Stage stage, std::string_view ext);
private:
const Instance& instance;
Scheduler& scheduler;
@ -41,6 +44,7 @@ private:
tsl::robin_map<size_t, vk::UniqueShaderModule> module_map;
std::array<vk::ShaderModule, MaxShaderStages> stages{};
tsl::robin_map<PipelineKey, std::unique_ptr<GraphicsPipeline>> graphics_pipelines;
Shader::Profile profile{};
PipelineKey graphics_key{};
Shader::ObjectPool<Shader::IR::Inst> inst_pool;
Shader::ObjectPool<Shader::IR::Block> block_pool;

View file

@ -12,16 +12,17 @@
namespace Vulkan {
static constexpr vk::BufferUsageFlags VertexIndexFlags = vk::BufferUsageFlagBits::eVertexBuffer |
vk::BufferUsageFlagBits::eIndexBuffer |
vk::BufferUsageFlagBits::eTransferDst;
static constexpr vk::BufferUsageFlags VertexIndexFlags =
vk::BufferUsageFlagBits::eVertexBuffer | vk::BufferUsageFlagBits::eIndexBuffer |
vk::BufferUsageFlagBits::eTransferDst | vk::BufferUsageFlagBits::eUniformBuffer |
vk::BufferUsageFlagBits::eStorageBuffer;
Rasterizer::Rasterizer(const Instance& instance_, Scheduler& scheduler_,
VideoCore::TextureCache& texture_cache_, AmdGpu::Liverpool* liverpool_)
: 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, 64_MB} {
vertex_index_buffer{instance, scheduler, VertexIndexFlags, 128_MB} {
if (!Config::nullGpu()) {
liverpool->BindRasterizer(this);
}
@ -35,9 +36,10 @@ void Rasterizer::Draw(bool is_indexed) {
const auto cmdbuf = scheduler.CommandBuffer();
const auto& regs = liverpool->regs;
const u32 num_indices = SetupIndexBuffer(is_indexed);
const auto& image_view = texture_cache.RenderTarget(regs.color_buffers[0]);
const GraphicsPipeline* pipeline = pipeline_cache.GetPipeline();
pipeline->BindResources(memory);
pipeline->BindResources(memory, vertex_index_buffer, texture_cache);
const auto& image_view = texture_cache.RenderTarget(regs.color_buffers[0]);
const vk::RenderingAttachmentInfo color_info = {
.imageView = *image_view.image_view,
@ -88,18 +90,30 @@ u32 Rasterizer::SetupIndexBuffer(bool& is_indexed) {
return regs.num_indices;
}
const VAddr index_address = regs.index_base_address.Address();
const auto [buffer, offset] = memory->GetVulkanBuffer(index_address);
const vk::IndexType index_type =
regs.index_buffer_type.index_type == Liverpool::IndexType::Index16 ? vk::IndexType::eUint16
: vk::IndexType::eUint32;
// Figure out index type and size.
const bool is_index16 = regs.index_buffer_type.index_type == Liverpool::IndexType::Index16;
const vk::IndexType index_type = is_index16 ? vk::IndexType::eUint16 : vk::IndexType::eUint32;
const u32 index_size = is_index16 ? sizeof(u16) : sizeof(u32);
// Upload index data to stream buffer.
const auto index_address = regs.index_base_address.Address<const void*>();
const u32 index_buffer_size = regs.num_indices * 4;
const auto [data, offset, _] = vertex_index_buffer.Map(index_buffer_size);
std::memcpy(data, index_address, index_buffer_size);
vertex_index_buffer.Commit(index_buffer_size);
// Bind index buffer.
const auto cmdbuf = scheduler.CommandBuffer();
cmdbuf.bindIndexBuffer(buffer, offset, index_type);
cmdbuf.bindIndexBuffer(vertex_index_buffer.Handle(), offset, index_type);
return regs.num_indices;
}
void Rasterizer::UpdateDynamicState() {
UpdateViewportScissorState();
auto& regs = liverpool->regs;
const auto cmdbuf = scheduler.CommandBuffer();
cmdbuf.setBlendConstants(&regs.blend_constants.red);
}
void Rasterizer::UpdateViewportScissorState() {

View file

@ -44,6 +44,22 @@ using Libraries::VideoOut::TilingMode;
return usage;
}
[[nodiscard]] vk::ImageType ConvertImageType(AmdGpu::ImageType type) noexcept {
switch (type) {
case AmdGpu::ImageType::Color1D:
return vk::ImageType::e1D;
case AmdGpu::ImageType::Color2D:
case AmdGpu::ImageType::Color1DArray:
case AmdGpu::ImageType::Cube:
return vk::ImageType::e2D;
case AmdGpu::ImageType::Color3D:
case AmdGpu::ImageType::Color2DArray:
return vk::ImageType::e3D;
default:
UNREACHABLE();
}
}
ImageInfo::ImageInfo(const Libraries::VideoOut::BufferAttributeGroup& group) noexcept {
const auto& attrib = group.attrib;
is_tiled = attrib.tiling_mode == TilingMode::Tile;
@ -72,10 +88,23 @@ ImageInfo::ImageInfo(const AmdGpu::Liverpool::ColorBuffer& buffer) noexcept {
type = vk::ImageType::e2D;
size.width = buffer.Pitch();
size.height = buffer.Height();
size.depth = 1;
pitch = size.width;
guest_size_bytes = buffer.slice.tile_max * (buffer.view.slice_max + 1);
}
ImageInfo::ImageInfo(const AmdGpu::Image& image) noexcept {
is_tiled = false;
pixel_format = LiverpoolToVK::SurfaceFormat(image.GetDataFmt(), image.GetNumberFmt());
type = ConvertImageType(image.type);
size.width = image.width + 1;
size.height = image.height + 1;
size.depth = 1;
// TODO: Derive this properly from tiling params
pitch = size.width;
guest_size_bytes = size.width * size.height * 4;
}
UniqueImage::UniqueImage(vk::Device device_, VmaAllocator allocator_)
: device{device_}, allocator{allocator_} {}
@ -109,7 +138,7 @@ Image::Image(const Vulkan::Instance& instance_, Vulkan::Scheduler& scheduler_,
: instance{&instance_}, scheduler{&scheduler_}, info{info_},
image{instance->GetDevice(), instance->GetAllocator()}, cpu_addr{cpu_addr},
cpu_addr_end{cpu_addr + info.guest_size_bytes} {
vk::ImageCreateFlags flags{};
vk::ImageCreateFlags flags{vk::ImageCreateFlagBits::eMutableFormat};
if (info.type == vk::ImageType::e2D && info.resources.layers >= 6 &&
info.size.width == info.size.height) {
flags |= vk::ImageCreateFlagBits::eCubeCompatible;

View file

@ -7,6 +7,7 @@
#include "common/types.h"
#include "core/libraries/videoout/buffer.h"
#include "video_core/amdgpu/liverpool.h"
#include "video_core/amdgpu/resource.h"
#include "video_core/renderer_vulkan/vk_common.h"
#include "video_core/texture_cache/image_view.h"
#include "video_core/texture_cache/types.h"
@ -34,6 +35,7 @@ struct ImageInfo {
ImageInfo() = default;
explicit ImageInfo(const Libraries::VideoOut::BufferAttributeGroup& group) noexcept;
explicit ImageInfo(const AmdGpu::Liverpool::ColorBuffer& buffer) noexcept;
explicit ImageInfo(const AmdGpu::Image& image) noexcept;
bool is_tiled = false;
vk::Format pixel_format = vk::Format::eUndefined;

View file

@ -1,11 +1,62 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "video_core/renderer_vulkan/liverpool_to_vk.h"
#include "video_core/renderer_vulkan/vk_instance.h"
#include "video_core/texture_cache/image_view.h"
namespace VideoCore {
vk::ImageViewType ConvertImageViewType(AmdGpu::ImageType type) {
switch (type) {
case AmdGpu::ImageType::Color1D:
return vk::ImageViewType::e1D;
case AmdGpu::ImageType::Color1DArray:
return vk::ImageViewType::e1DArray;
case AmdGpu::ImageType::Color2D:
case AmdGpu::ImageType::Cube:
return vk::ImageViewType::e2D;
case AmdGpu::ImageType::Color2DArray:
return vk::ImageViewType::e2DArray;
case AmdGpu::ImageType::Color3D:
return vk::ImageViewType::e3D;
default:
UNREACHABLE();
}
}
vk::ComponentSwizzle ConvertComponentSwizzle(u32 dst_sel) {
switch (dst_sel) {
case 0:
return vk::ComponentSwizzle::eZero;
case 1:
return vk::ComponentSwizzle::eOne;
case 4:
return vk::ComponentSwizzle::eR;
case 5:
return vk::ComponentSwizzle::eG;
case 6:
return vk::ComponentSwizzle::eB;
case 7:
return vk::ComponentSwizzle::eA;
default:
UNREACHABLE();
}
}
ImageViewInfo::ImageViewInfo(const AmdGpu::Image& image) noexcept {
type = ConvertImageViewType(image.type);
format = Vulkan::LiverpoolToVK::SurfaceFormat(image.GetDataFmt(), image.GetNumberFmt());
range.base.level = image.base_level;
range.base.layer = 0;
range.extent.levels = 1;
range.extent.layers = 1;
mapping.r = ConvertComponentSwizzle(image.dst_sel_x);
mapping.g = ConvertComponentSwizzle(image.dst_sel_y);
mapping.b = ConvertComponentSwizzle(image.dst_sel_z);
mapping.a = ConvertComponentSwizzle(image.dst_sel_w);
}
ImageView::ImageView(const Vulkan::Instance& instance, Vulkan::Scheduler& scheduler,
const ImageViewInfo& info_, vk::Image image)
: info{info_} {

View file

@ -3,6 +3,7 @@
#pragma once
#include "video_core/amdgpu/resource.h"
#include "video_core/renderer_vulkan/vk_common.h"
#include "video_core/texture_cache/types.h"
@ -14,6 +15,9 @@ class Scheduler;
namespace VideoCore {
struct ImageViewInfo {
explicit ImageViewInfo() = default;
explicit ImageViewInfo(const AmdGpu::Image& image) noexcept;
vk::ImageViewType type = vk::ImageViewType::e2D;
vk::Format format = vk::Format::eR8G8B8A8Unorm;
SubresourceRange range;

View file

@ -0,0 +1,32 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "video_core/renderer_vulkan/liverpool_to_vk.h"
#include "video_core/renderer_vulkan/vk_instance.h"
#include "video_core/texture_cache/sampler.h"
namespace VideoCore {
Sampler::Sampler(const Vulkan::Instance& instance, const AmdGpu::Sampler& sampler) {
using namespace Vulkan;
const vk::SamplerCreateInfo sampler_ci = {
.magFilter = LiverpoolToVK::Filter(sampler.xy_mag_filter),
.minFilter = LiverpoolToVK::Filter(sampler.xy_min_filter),
.mipmapMode = LiverpoolToVK::MipFilter(sampler.mip_filter),
.addressModeU = LiverpoolToVK::ClampMode(sampler.clamp_x),
.addressModeV = LiverpoolToVK::ClampMode(sampler.clamp_y),
.addressModeW = LiverpoolToVK::ClampMode(sampler.clamp_z),
.mipLodBias = sampler.LodBias(),
.compareEnable = sampler.depth_compare_func != AmdGpu::DepthCompare::Never,
.compareOp = LiverpoolToVK::DepthCompare(sampler.depth_compare_func),
.minLod = sampler.MinLod(),
.maxLod = sampler.MaxLod(),
.borderColor = LiverpoolToVK::BorderColor(sampler.border_color_type),
.unnormalizedCoordinates = bool(sampler.force_unnormalized),
};
handle = instance.GetDevice().createSamplerUnique(sampler_ci);
}
Sampler::~Sampler() = default;
} // namespace VideoCore

View file

@ -0,0 +1,34 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "video_core/amdgpu/resource.h"
#include "video_core/renderer_vulkan/vk_common.h"
namespace Vulkan {
class Instance;
}
namespace VideoCore {
class Sampler {
public:
explicit Sampler(const Vulkan::Instance& instance, const AmdGpu::Sampler& sampler);
~Sampler();
Sampler(const Sampler&) = delete;
Sampler& operator=(const Sampler&) = delete;
Sampler(Sampler&&) = default;
Sampler& operator=(Sampler&&) = default;
vk::Sampler Handle() const noexcept {
return *handle;
}
private:
vk::UniqueSampler handle;
};
} // namespace VideoCore

View file

@ -1,10 +1,9 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <limits>
#include <xxhash.h>
#include "common/assert.h"
#include "common/config.h"
#include "core/libraries/videoout/buffer.h"
#include "core/virtual_memory.h"
#include "video_core/renderer_vulkan/vk_scheduler.h"
#include "video_core/texture_cache/texture_cache.h"
@ -137,6 +136,21 @@ Image& TextureCache::FindImage(const ImageInfo& info, VAddr cpu_address) {
return image;
}
ImageView& TextureCache::FindImageView(const AmdGpu::Image& desc) {
Image& image = FindImage(ImageInfo{desc}, desc.Address());
const ImageViewInfo view_info{desc};
if (const ImageViewId view_id = image.FindView(view_info); view_id) {
return slot_image_views[view_id];
}
const ImageViewId view_id =
slot_image_views.insert(instance, scheduler, view_info, image.image);
image.image_view_infos.emplace_back(view_info);
image.image_view_ids.emplace_back(view_id);
return slot_image_views[view_id];
}
ImageView& TextureCache::RenderTarget(const AmdGpu::Liverpool::ColorBuffer& buffer) {
const ImageInfo info{buffer};
auto& image = FindImage(info, buffer.Address());
@ -159,7 +173,7 @@ void TextureCache::RefreshImage(Image& image) {
image.flags &= ~ImageFlagBits::CpuModified;
// Upload data to the staging buffer.
const auto [data, offset, _] = staging.Map(image.info.guest_size_bytes, 0);
const auto [data, offset, _] = staging.Map(image.info.guest_size_bytes, 4);
const u8* image_data = reinterpret_cast<const u8*>(image.cpu_addr);
if (image.info.is_tiled) {
ConvertTileToLinear(data, image_data, image.info.size.width, image.info.size.height,
@ -202,6 +216,12 @@ void TextureCache::RefreshImage(Image& image) {
vk::AccessFlagBits::eShaderRead | vk::AccessFlagBits::eTransferRead);
}
vk::Sampler TextureCache::GetSampler(const AmdGpu::Sampler& sampler) {
const u64 hash = XXH3_64bits(&sampler, sizeof(sampler));
const auto [it, new_sampler] = samplers.try_emplace(hash, instance, sampler);
return it->second.Handle();
}
void TextureCache::RegisterImage(ImageId image_id) {
Image& image = slot_images[image_id];
ASSERT_MSG(False(image.flags & ImageFlagBits::Registered),

View file

@ -7,9 +7,11 @@
#include <boost/icl/interval_map.hpp>
#include <tsl/robin_map.h>
#include "video_core/amdgpu/resource.h"
#include "video_core/renderer_vulkan/vk_stream_buffer.h"
#include "video_core/texture_cache/image.h"
#include "video_core/texture_cache/image_view.h"
#include "video_core/texture_cache/sampler.h"
#include "video_core/texture_cache/slot_vector.h"
namespace Core::Libraries::VideoOut {
@ -36,12 +38,18 @@ public:
/// Retrieves the image handle of the image with the provided attributes and address.
Image& FindImage(const ImageInfo& info, VAddr cpu_address);
/// Retrieves an image view with the properties of the specified image descriptor.
ImageView& FindImageView(const AmdGpu::Image& image);
/// Retrieves the render target with specified properties
ImageView& RenderTarget(const AmdGpu::Liverpool::ColorBuffer& buffer);
/// Reuploads image contents.
void RefreshImage(Image& image);
/// Retrieves the sampler that matches the provided S# descriptor.
vk::Sampler GetSampler(const AmdGpu::Sampler& sampler);
private:
/// Iterate over all page indices in a range
template <typename Func>
@ -121,6 +129,7 @@ private:
Vulkan::StreamBuffer staging;
SlotVector<Image> slot_images;
SlotVector<ImageView> slot_image_views;
tsl::robin_map<u64, Sampler> samplers;
tsl::robin_pg_map<u64, std::vector<ImageId>> page_table;
boost::icl::interval_map<VAddr, s32> cached_pages;
#ifdef _WIN64

View file

@ -2,6 +2,7 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#include <cstring>
#include "common/assert.h"
#include "video_core/texture_cache/tile_manager.h"
namespace VideoCore {