video_core: Preliminary storage image support and more (#188)

* vk_rasterizer: Clear depth buffer when DB_RENDER_CONTROL says so

* video_core: Preliminary storage image support, more opcodes

* renderer_vulkan: a fix for vertex buffers merging

* renderer_vulkan: a heuristic for blend override when alpha out is masked

---------

Co-authored-by: psucien <bad_cast@protonmail.com>
This commit is contained in:
TheTurtle 2024-06-10 22:35:14 +03:00 committed by GitHub
parent 23f11a3fda
commit 7b1a317b09
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
30 changed files with 429 additions and 101 deletions

View file

@ -334,6 +334,19 @@ vk::Format SurfaceFormat(AmdGpu::DataFormat data_format, AmdGpu::NumberFormat nu
if (data_format == AmdGpu::DataFormat::Format32 && num_format == AmdGpu::NumberFormat::Float) {
return vk::Format::eR32Sfloat;
}
if (data_format == AmdGpu::DataFormat::Format16_16_16_16 &&
num_format == AmdGpu::NumberFormat::Float) {
return vk::Format::eR16G16B16A16Sfloat;
}
if (data_format == AmdGpu::DataFormat::Format32 && num_format == AmdGpu::NumberFormat::Uint) {
return vk::Format::eR32Uint;
}
if (data_format == AmdGpu::DataFormat::Format32 && num_format == AmdGpu::NumberFormat::Sint) {
return vk::Format::eR32Sint;
}
if (data_format == AmdGpu::DataFormat::Format8_8 && num_format == AmdGpu::NumberFormat::Unorm) {
return vk::Format::eR8G8Unorm;
}
UNREACHABLE_MSG("Unknown data_format={} and num_format={}", u32(data_format), u32(num_format));
}

View file

@ -111,14 +111,15 @@ void ComputePipeline::BindResources(Core::MemoryManager* memory, StreamBuffer& s
for (const auto& image : info.images) {
const auto tsharp = info.ReadUd<AmdGpu::Image>(image.sgpr_base, image.dword_offset);
const auto& image_view = texture_cache.FindImageView(tsharp);
const auto& image_view = texture_cache.FindImageView(tsharp, image.is_storage);
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,
.descriptorType = image.is_storage ? vk::DescriptorType::eStorageImage
: vk::DescriptorType::eSampledImage,
.pImageInfo = &image_infos.back(),
});
}

View file

@ -196,7 +196,7 @@ GraphicsPipeline::GraphicsPipeline(const Instance& instance_, Scheduler& schedul
const auto dst_color = LiverpoolToVK::BlendFactor(control.color_dst_factor);
const auto color_blend = LiverpoolToVK::BlendOp(control.color_func);
attachments[i] = vk::PipelineColorBlendAttachmentState{
.blendEnable = key.blend_controls[i].enable,
.blendEnable = control.enable,
.srcColorBlendFactor = src_color,
.dstColorBlendFactor = dst_color,
.colorBlendOp = color_blend,
@ -215,6 +215,29 @@ GraphicsPipeline::GraphicsPipeline(const Instance& instance_, Scheduler& schedul
vk::ColorComponentFlagBits::eB | vk::ColorComponentFlagBits::eA
: key.write_masks[i],
};
// On GCN GPU there is an additional mask which allows to control color components exported
// from a pixel shader. A situation possible, when the game may mask out the alpha channel,
// while it is still need to be used in blending ops. For such cases, HW will default alpha
// to 1 and perform the blending, while shader normally outputs 0 in the last component.
// Unfortunatelly, Vulkan doesn't provide any control on blend inputs, so below we detecting
// such cases and override alpha value in order to emulate HW behaviour.
const auto has_alpha_masked_out =
(key.cb_shader_mask.GetMask(i) & Liverpool::ColorBufferMask::ComponentA) == 0;
const auto has_src_alpha_in_src_blend = src_color == vk::BlendFactor::eSrcAlpha ||
src_color == vk::BlendFactor::eOneMinusSrcAlpha;
const auto has_src_alpha_in_dst_blend = dst_color == vk::BlendFactor::eSrcAlpha ||
dst_color == vk::BlendFactor::eOneMinusSrcAlpha;
if (has_alpha_masked_out && has_src_alpha_in_src_blend) {
attachments[i].srcColorBlendFactor = src_color == vk::BlendFactor::eSrcAlpha
? vk::BlendFactor::eOne
: vk::BlendFactor::eZero; // 1-A
}
if (has_alpha_masked_out && has_src_alpha_in_dst_blend) {
attachments[i].dstColorBlendFactor = dst_color == vk::BlendFactor::eSrcAlpha
? vk::BlendFactor::eOne
: vk::BlendFactor::eZero; // 1-A
}
}
const vk::PipelineColorBlendStateCreateInfo color_blending = {
@ -318,7 +341,7 @@ void GraphicsPipeline::BindResources(Core::MemoryManager* memory, StreamBuffer&
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);
const auto& image_view = texture_cache.FindImageView(tsharp, image.is_storage);
image_infos.emplace_back(VK_NULL_HANDLE, *image_view.image_view,
vk::ImageLayout::eShaderReadOnlyOptimal);
set_writes.push_back({
@ -326,7 +349,8 @@ void GraphicsPipeline::BindResources(Core::MemoryManager* memory, StreamBuffer&
.dstBinding = binding++,
.dstArrayElement = 0,
.descriptorCount = 1,
.descriptorType = vk::DescriptorType::eSampledImage,
.descriptorType = image.is_storage ? vk::DescriptorType::eStorageImage
: vk::DescriptorType::eSampledImage,
.pImageInfo = &image_infos.back(),
});
}
@ -387,11 +411,11 @@ void GraphicsPipeline::BindVertexBuffers(StreamBuffer& staging) const {
boost::container::static_vector<BufferRange, MaxVertexBufferCount> ranges_merged{ranges[0]};
for (auto range : ranges) {
auto& prev_range = ranges.back();
auto& prev_range = ranges_merged.back();
if (prev_range.end_address < range.base_address) {
ranges_merged.emplace_back(range);
} else {
ranges_merged.back().end_address = std::max(prev_range.end_address, range.end_address);
prev_range.end_address = std::max(prev_range.end_address, range.end_address);
}
}

View file

@ -46,6 +46,7 @@ struct GraphicsPipelineKey {
Liverpool::CullMode cull_mode;
Liverpool::FrontFace front_face;
Liverpool::ClipSpace clip_space;
Liverpool::ColorBufferMask cb_shader_mask{};
std::array<Liverpool::BlendControl, Liverpool::NumColorBuffers> blend_controls;
std::array<vk::ColorComponentFlags, Liverpool::NumColorBuffers> write_masks;

View file

@ -132,6 +132,7 @@ void PipelineCache::RefreshGraphicsKey() {
key.blend_controls[remapped_cb].enable.Assign(key.blend_controls[remapped_cb].enable &&
!col_buf.info.blend_bypass);
key.write_masks[remapped_cb] = vk::ColorComponentFlags{regs.color_target_mask.GetMask(cb)};
key.cb_shader_mask = regs.color_shader_mask;
++remapped_cb;
}

View file

@ -60,13 +60,16 @@ void Rasterizer::Draw(bool is_indexed, u32 index_offset) {
});
}
if (regs.depth_control.depth_enable && regs.depth_buffer.Address() != 0) {
const bool is_clear = regs.depth_render_control.depth_clear_enable;
const auto& image_view =
texture_cache.DepthTarget(regs.depth_buffer, liverpool->last_db_extent);
depth_attachment = {
.imageView = *image_view.image_view,
.imageLayout = vk::ImageLayout::eGeneral,
.loadOp = vk::AttachmentLoadOp::eLoad,
.storeOp = vk::AttachmentStoreOp::eStore,
.loadOp = is_clear ? vk::AttachmentLoadOp::eClear : vk::AttachmentLoadOp::eLoad,
.storeOp = is_clear ? vk::AttachmentStoreOp::eNone : vk::AttachmentStoreOp::eStore,
.clearValue = vk::ClearValue{.depthStencil = {.depth = regs.depth_clear,
.stencil = regs.stencil_clear}},
};
num_depth_attachments++;
}