Image subresources barriers (#904)

* video_core: texture: image subresources state tracking

* shader_recompiler: use one binding if the same image is read and written

* video_core: added rebinding of changed textures after overlap resolve

* don't use pointers; slight `FindTexture` refactoring

* video_core: buffer_cache: don't copy over the image size

* redundant barriers removed; fixes

* regression fixes

* texture_cache: 3d texture layers count fixup

* shader_recompiler: support for partially bound cubemaps

* added support for cubemap arrays

* don't bind unused color buffers

* fixed depth promotion to do not use stencil

* doors

* bonfire lit

* cubemap array index calculation

* final touches
This commit is contained in:
psucien 2024-09-21 21:45:56 +02:00 committed by GitHub
parent 913a46173a
commit 5f4ddc14fc
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
35 changed files with 495 additions and 283 deletions

View file

@ -4,6 +4,7 @@
#pragma once
#include <span>
#include "common/assert.h"
#include "video_core/amdgpu/liverpool.h"
#include "video_core/amdgpu/pixel_format.h"
#include "video_core/amdgpu/resource.h"
@ -55,4 +56,13 @@ vk::SampleCountFlagBits NumSamples(u32 num_samples, vk::SampleCountFlags support
void EmitQuadToTriangleListIndices(u8* out_indices, u32 num_vertices);
static inline vk::Format PromoteFormatToDepth(vk::Format fmt) {
if (fmt == vk::Format::eR32Sfloat) {
return vk::Format::eD32Sfloat;
} else if (fmt == vk::Format::eR16Unorm) {
return vk::Format::eD16Unorm;
}
UNREACHABLE();
}
} // namespace Vulkan::LiverpoolToVK

View file

@ -202,7 +202,8 @@ Frame* RendererVulkan::PrepareFrameInternal(VideoCore::Image& image, bool is_eop
scheduler.EndRendering();
const auto cmdbuf = scheduler.CommandBuffer();
image.Transit(vk::ImageLayout::eTransferSrcOptimal, vk::AccessFlagBits::eTransferRead, cmdbuf);
image.Transit(vk::ImageLayout::eTransferSrcOptimal, vk::AccessFlagBits2::eTransferRead, {},
cmdbuf);
const std::array pre_barrier{
vk::ImageMemoryBarrier{
@ -228,7 +229,7 @@ Frame* RendererVulkan::PrepareFrameInternal(VideoCore::Image& image, bool is_eop
// Post-processing (Anti-aliasing, FSR etc) goes here. For now just blit to the frame image.
cmdbuf.blitImage(
image.image, image.layout, frame->image, vk::ImageLayout::eTransferDstOptimal,
image.image, image.last_state.layout, frame->image, vk::ImageLayout::eTransferDstOptimal,
MakeImageBlit(image.info.size.width, image.info.size.height, frame->width, frame->height),
vk::Filter::eLinear);
@ -269,6 +270,9 @@ void RendererVulkan::Present(Frame* frame) {
auto& scheduler = present_scheduler;
const auto cmdbuf = scheduler.CommandBuffer();
ImGui::Core::Render(cmdbuf, frame);
{
auto* profiler_ctx = instance.GetProfilerContext();
TracyVkNamedZoneC(profiler_ctx, renderer_gpu_zone, cmdbuf, "Host frame",
@ -326,8 +330,6 @@ void RendererVulkan::Present(Frame* frame) {
},
};
ImGui::Core::Render(cmdbuf, frame);
cmdbuf.pipelineBarrier(vk::PipelineStageFlagBits::eColorAttachmentOutput,
vk::PipelineStageFlagBits::eTransfer,
vk::DependencyFlagBits::eByRegion, {}, {}, pre_barriers);

View file

@ -2,6 +2,7 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#include <boost/container/small_vector.hpp>
#include "common/alignment.h"
#include "video_core/buffer_cache/buffer_cache.h"
#include "video_core/renderer_vulkan/vk_compute_pipeline.h"
@ -15,7 +16,7 @@ ComputePipeline::ComputePipeline(const Instance& instance_, Scheduler& scheduler
DescriptorHeap& desc_heap_, vk::PipelineCache pipeline_cache,
u64 compute_key_, const Shader::Info& info_,
vk::ShaderModule module)
: instance{instance_}, scheduler{scheduler_}, desc_heap{desc_heap_}, compute_key{compute_key_},
: Pipeline{instance_, scheduler_, desc_heap_, pipeline_cache}, compute_key{compute_key_},
info{&info_} {
const vk::PipelineShaderStageCreateInfo shader_ci = {
.stage = vk::ShaderStageFlagBits::eCompute,
@ -108,12 +109,13 @@ bool ComputePipeline::BindResources(VideoCore::BufferCache& buffer_cache,
// Bind resource buffers and textures.
boost::container::static_vector<vk::BufferView, 8> buffer_views;
boost::container::static_vector<vk::DescriptorBufferInfo, 32> buffer_infos;
boost::container::static_vector<vk::DescriptorImageInfo, 32> image_infos;
boost::container::small_vector<vk::WriteDescriptorSet, 16> set_writes;
boost::container::small_vector<vk::BufferMemoryBarrier2, 16> buffer_barriers;
Shader::PushData push_data{};
u32 binding{};
image_infos.clear();
for (const auto& desc : info->buffers) {
bool is_storage = true;
if (desc.is_gds_buffer) {
@ -213,35 +215,8 @@ bool ComputePipeline::BindResources(VideoCore::BufferCache& buffer_cache,
});
}
for (const auto& image_desc : info->images) {
const auto tsharp = image_desc.GetSharp(*info);
if (tsharp.GetDataFmt() != AmdGpu::DataFormat::FormatInvalid) {
VideoCore::ImageInfo image_info{tsharp, image_desc.is_depth};
VideoCore::ImageViewInfo view_info{tsharp, image_desc.is_storage};
const auto& image_view = texture_cache.FindTexture(image_info, view_info);
const auto& image = texture_cache.GetImage(image_view.image_id);
image_infos.emplace_back(VK_NULL_HANDLE, *image_view.image_view, image.layout);
} else if (instance.IsNullDescriptorSupported()) {
image_infos.emplace_back(VK_NULL_HANDLE, VK_NULL_HANDLE, vk::ImageLayout::eGeneral);
} else {
auto& null_image = texture_cache.GetImageView(VideoCore::NULL_IMAGE_VIEW_ID);
image_infos.emplace_back(VK_NULL_HANDLE, *null_image.image_view,
vk::ImageLayout::eGeneral);
}
set_writes.push_back({
.dstSet = VK_NULL_HANDLE,
.dstBinding = binding++,
.dstArrayElement = 0,
.descriptorCount = 1,
.descriptorType = image_desc.is_storage ? vk::DescriptorType::eStorageImage
: vk::DescriptorType::eSampledImage,
.pImageInfo = &image_infos.back(),
});
BindTextures(texture_cache, *info, binding, set_writes);
if (texture_cache.IsMeta(tsharp.Address())) {
LOG_WARNING(Render_Vulkan, "Unexpected metadata read by a CS shader (texture)");
}
}
for (const auto& sampler : info->samplers) {
const auto ssharp = sampler.GetSharp(*info);
if (ssharp.force_degamma) {

View file

@ -3,9 +3,8 @@
#pragma once
#include <boost/container/small_vector.hpp>
#include "shader_recompiler/info.h"
#include "video_core/renderer_vulkan/vk_common.h"
#include "video_core/renderer_vulkan/vk_pipeline_common.h"
namespace VideoCore {
class BufferCache;
@ -18,27 +17,17 @@ class Instance;
class Scheduler;
class DescriptorHeap;
class ComputePipeline {
class ComputePipeline : public Pipeline {
public:
explicit ComputePipeline(const Instance& instance, Scheduler& scheduler,
DescriptorHeap& desc_heap, vk::PipelineCache pipeline_cache,
u64 compute_key, const Shader::Info& info, vk::ShaderModule module);
ComputePipeline(const Instance& instance, Scheduler& scheduler, DescriptorHeap& desc_heap,
vk::PipelineCache pipeline_cache, u64 compute_key, const Shader::Info& info,
vk::ShaderModule module);
~ComputePipeline();
[[nodiscard]] vk::Pipeline Handle() const noexcept {
return *pipeline;
}
bool BindResources(VideoCore::BufferCache& buffer_cache,
VideoCore::TextureCache& texture_cache) const;
private:
const Instance& instance;
Scheduler& scheduler;
DescriptorHeap& desc_heap;
vk::UniquePipeline pipeline;
vk::UniquePipelineLayout pipeline_layout;
vk::UniqueDescriptorSetLayout desc_layout;
u64 compute_key;
const Shader::Info* info;
bool uses_push_descriptors{};

View file

@ -21,7 +21,7 @@ GraphicsPipeline::GraphicsPipeline(const Instance& instance_, Scheduler& schedul
vk::PipelineCache pipeline_cache,
std::span<const Shader::Info*, MaxShaderStages> infos,
std::span<const vk::ShaderModule> modules)
: instance{instance_}, scheduler{scheduler_}, desc_heap{desc_heap_}, key{key_} {
: Pipeline{instance_, scheduler_, desc_heap_, pipeline_cache}, key{key_} {
const vk::Device device = instance.GetDevice();
std::ranges::copy(infos, stages.begin());
BuildDescSetLayout();
@ -41,8 +41,8 @@ GraphicsPipeline::GraphicsPipeline(const Instance& instance_, Scheduler& schedul
};
pipeline_layout = instance.GetDevice().createPipelineLayoutUnique(layout_info);
boost::container::static_vector<vk::VertexInputBindingDescription, 32> bindings;
boost::container::static_vector<vk::VertexInputAttributeDescription, 32> attributes;
boost::container::static_vector<vk::VertexInputBindingDescription, 32> vertex_bindings;
boost::container::static_vector<vk::VertexInputAttributeDescription, 32> vertex_attributes;
const auto& vs_info = stages[u32(Shader::Stage::Vertex)];
for (const auto& input : vs_info->vs_inputs) {
if (input.instance_step_rate == Shader::Info::VsInput::InstanceIdType::OverStepRate0 ||
@ -52,13 +52,13 @@ GraphicsPipeline::GraphicsPipeline(const Instance& instance_, Scheduler& schedul
}
const auto buffer = vs_info->ReadUd<AmdGpu::Buffer>(input.sgpr_base, input.dword_offset);
attributes.push_back({
vertex_attributes.push_back({
.location = input.binding,
.binding = input.binding,
.format = LiverpoolToVK::SurfaceFormat(buffer.GetDataFmt(), buffer.GetNumberFmt()),
.offset = 0,
});
bindings.push_back({
vertex_bindings.push_back({
.binding = input.binding,
.stride = buffer.GetStride(),
.inputRate = input.instance_step_rate == Shader::Info::VsInput::None
@ -68,10 +68,10 @@ GraphicsPipeline::GraphicsPipeline(const Instance& instance_, Scheduler& schedul
}
const vk::PipelineVertexInputStateCreateInfo vertex_input_info = {
.vertexBindingDescriptionCount = static_cast<u32>(bindings.size()),
.pVertexBindingDescriptions = bindings.data(),
.vertexAttributeDescriptionCount = static_cast<u32>(attributes.size()),
.pVertexAttributeDescriptions = attributes.data(),
.vertexBindingDescriptionCount = static_cast<u32>(vertex_bindings.size()),
.pVertexBindingDescriptions = vertex_bindings.data(),
.vertexAttributeDescriptionCount = static_cast<u32>(vertex_attributes.size()),
.pVertexAttributeDescriptions = vertex_attributes.data(),
};
if (key.prim_type == Liverpool::PrimitiveType::RectList && !IsEmbeddedVs()) {
@ -291,8 +291,9 @@ GraphicsPipeline::GraphicsPipeline(const Instance& instance_, Scheduler& schedul
GraphicsPipeline::~GraphicsPipeline() = default;
void GraphicsPipeline::BuildDescSetLayout() {
u32 binding{};
boost::container::small_vector<vk::DescriptorSetLayoutBinding, 32> bindings;
u32 binding{};
for (const auto* stage : stages) {
if (!stage) {
continue;
@ -352,12 +353,13 @@ void GraphicsPipeline::BindResources(const Liverpool::Regs& regs,
// Bind resource buffers and textures.
boost::container::static_vector<vk::BufferView, 8> buffer_views;
boost::container::static_vector<vk::DescriptorBufferInfo, 32> buffer_infos;
boost::container::static_vector<vk::DescriptorImageInfo, 32> image_infos;
boost::container::small_vector<vk::WriteDescriptorSet, 16> set_writes;
boost::container::small_vector<vk::BufferMemoryBarrier2, 16> buffer_barriers;
Shader::PushData push_data{};
u32 binding{};
image_infos.clear();
for (const auto* stage : stages) {
if (!stage) {
continue;
@ -444,44 +446,15 @@ void GraphicsPipeline::BindResources(const Liverpool::Regs& regs,
});
}
boost::container::static_vector<AmdGpu::Image, 32> tsharps;
for (const auto& image_desc : stage->images) {
const auto tsharp = image_desc.GetSharp(*stage);
if (tsharp.GetDataFmt() != AmdGpu::DataFormat::FormatInvalid) {
tsharps.emplace_back(tsharp);
VideoCore::ImageInfo image_info{tsharp, image_desc.is_depth};
VideoCore::ImageViewInfo view_info{tsharp, image_desc.is_storage};
const auto& image_view = texture_cache.FindTexture(image_info, view_info);
const auto& image = texture_cache.GetImage(image_view.image_id);
image_infos.emplace_back(VK_NULL_HANDLE, *image_view.image_view, image.layout);
} else if (instance.IsNullDescriptorSupported()) {
image_infos.emplace_back(VK_NULL_HANDLE, VK_NULL_HANDLE, vk::ImageLayout::eGeneral);
} else {
auto& null_image = texture_cache.GetImageView(VideoCore::NULL_IMAGE_VIEW_ID);
image_infos.emplace_back(VK_NULL_HANDLE, *null_image.image_view,
vk::ImageLayout::eGeneral);
}
set_writes.push_back({
.dstSet = VK_NULL_HANDLE,
.dstBinding = binding++,
.dstArrayElement = 0,
.descriptorCount = 1,
.descriptorType = image_desc.is_storage ? vk::DescriptorType::eStorageImage
: vk::DescriptorType::eSampledImage,
.pImageInfo = &image_infos.back(),
});
BindTextures(texture_cache, *stage, binding, set_writes);
if (texture_cache.IsMeta(tsharp.Address())) {
LOG_WARNING(Render_Vulkan, "Unexpected metadata read by a PS shader (texture)");
}
}
for (const auto& sampler : stage->samplers) {
auto ssharp = sampler.GetSharp(*stage);
if (ssharp.force_degamma) {
LOG_WARNING(Render_Vulkan, "Texture requires gamma correction");
}
if (sampler.disable_aniso) {
const auto& tsharp = tsharps[sampler.associated_image];
const auto& tsharp = stage->images[sampler.associated_image].GetSharp(*stage);
if (tsharp.base_level == 0 && tsharp.last_level == 0) {
ssharp.max_aniso.Assign(AmdGpu::AnisoRatio::One);
}

View file

@ -5,7 +5,7 @@
#include "common/types.h"
#include "video_core/renderer_vulkan/liverpool_to_vk.h"
#include "video_core/renderer_vulkan/vk_common.h"
#include "video_core/renderer_vulkan/vk_compute_pipeline.h"
#include "video_core/renderer_vulkan/vk_pipeline_common.h"
namespace VideoCore {
class BufferCache;
@ -33,6 +33,7 @@ struct GraphicsPipelineKey {
Liverpool::DepthControl depth_stencil;
u32 depth_bias_enable;
u32 num_samples;
u32 mrt_mask;
Liverpool::StencilControl stencil;
Liverpool::PrimitiveType prim_type;
u32 enable_primitive_restart;
@ -50,26 +51,17 @@ struct GraphicsPipelineKey {
}
};
class GraphicsPipeline {
class GraphicsPipeline : public Pipeline {
public:
explicit GraphicsPipeline(const Instance& instance, Scheduler& scheduler,
DescriptorHeap& desc_heap, const GraphicsPipelineKey& key,
vk::PipelineCache pipeline_cache,
std::span<const Shader::Info*, MaxShaderStages> stages,
std::span<const vk::ShaderModule> modules);
GraphicsPipeline(const Instance& instance, Scheduler& scheduler, DescriptorHeap& desc_heap,
const GraphicsPipelineKey& key, vk::PipelineCache pipeline_cache,
std::span<const Shader::Info*, MaxShaderStages> stages,
std::span<const vk::ShaderModule> modules);
~GraphicsPipeline();
void BindResources(const Liverpool::Regs& regs, VideoCore::BufferCache& buffer_cache,
VideoCore::TextureCache& texture_cache) const;
vk::Pipeline Handle() const noexcept {
return *pipeline;
}
vk::PipelineLayout GetLayout() const {
return *pipeline_layout;
}
const Shader::Info& GetStage(Shader::Stage stage) const noexcept {
return *stages[u32(stage)];
}
@ -83,6 +75,10 @@ public:
return key.write_masks;
}
auto GetMrtMask() const {
return key.mrt_mask;
}
bool IsDepthEnabled() const {
return key.depth_stencil.depth_enable.Value();
}
@ -91,12 +87,6 @@ private:
void BuildDescSetLayout();
private:
const Instance& instance;
Scheduler& scheduler;
DescriptorHeap& desc_heap;
vk::UniquePipeline pipeline;
vk::UniquePipelineLayout pipeline_layout;
vk::UniqueDescriptorSetLayout desc_layout;
std::array<const Shader::Info*, MaxShaderStages> stages{};
GraphicsPipelineKey key;
bool uses_push_descriptors{};

View file

@ -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,

View file

@ -234,18 +234,20 @@ bool PipelineCache::RefreshGraphicsKey() {
key.front_face = regs.polygon_control.front_face;
key.num_samples = regs.aa_config.NumSamples();
const auto skip_cb_binding =
const bool skip_cb_binding =
regs.color_control.mode == AmdGpu::Liverpool::ColorControl::OperationMode::Disable;
// `RenderingInfo` is assumed to be initialized with a contiguous array of valid color
// attachments. This might be not a case as HW color buffers can be bound in an arbitrary order.
// We need to do some arrays compaction at this stage
// attachments. This might be not a case as HW color buffers can be bound in an arbitrary
// order. We need to do some arrays compaction at this stage
key.color_formats.fill(vk::Format::eUndefined);
key.blend_controls.fill({});
key.write_masks.fill({});
key.mrt_swizzles.fill(Liverpool::ColorBuffer::SwapMode::Standard);
int remapped_cb{};
for (auto cb = 0u; cb < Liverpool::NumColorBuffers; ++cb) {
// First pass of bindings check to idenitfy formats and swizzles and pass them to rhe shader
// recompiler.
for (auto cb = 0u, remapped_cb = 0u; cb < Liverpool::NumColorBuffers; ++cb) {
auto const& col_buf = regs.color_buffers[cb];
if (skip_cb_binding || !col_buf || !regs.color_target_mask.GetMask(cb)) {
continue;
@ -258,11 +260,6 @@ bool PipelineCache::RefreshGraphicsKey() {
if (base_format == key.color_formats[remapped_cb]) {
key.mrt_swizzles[remapped_cb] = col_buf.info.comp_swap.Value();
}
key.blend_controls[remapped_cb] = regs.blend_control[cb];
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.SetMask(remapped_cb, regs.color_shader_mask.GetMask(cb));
++remapped_cb;
}
@ -309,6 +306,28 @@ bool PipelineCache::RefreshGraphicsKey() {
std::tie(infos[i], modules[i], key.stage_hashes[i]) = GetProgram(stage, params, binding);
}
const auto* fs_info = infos[u32(Shader::Stage::Fragment)];
key.mrt_mask = fs_info ? fs_info->mrt_mask : 0u;
// Second pass to fill remain CB pipeline key data
for (auto cb = 0u, remapped_cb = 0u; cb < Liverpool::NumColorBuffers; ++cb) {
auto const& col_buf = regs.color_buffers[cb];
if (skip_cb_binding || !col_buf || !regs.color_target_mask.GetMask(cb) ||
(key.mrt_mask & (1u << cb)) == 0) {
key.color_formats[cb] = vk::Format::eUndefined;
key.mrt_swizzles[cb] = Liverpool::ColorBuffer::SwapMode::Standard;
continue;
}
key.blend_controls[remapped_cb] = regs.blend_control[cb];
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.SetMask(remapped_cb, regs.color_shader_mask.GetMask(cb));
++remapped_cb;
}
return true;
}

View file

@ -0,0 +1,80 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <boost/container/static_vector.hpp>
#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"
#include "video_core/texture_cache/texture_cache.h"
namespace Vulkan {
boost::container::static_vector<vk::DescriptorImageInfo, 32> Pipeline::image_infos;
Pipeline::Pipeline(const Instance& instance_, Scheduler& scheduler_, DescriptorHeap& desc_heap_,
vk::PipelineCache pipeline_cache)
: instance{instance_}, scheduler{scheduler_}, desc_heap{desc_heap_} {}
Pipeline::~Pipeline() = default;
void Pipeline::BindTextures(VideoCore::TextureCache& texture_cache, const Shader::Info& stage,
u32& binding, DescriptorWrites& set_writes) const {
using ImageBindingInfo = std::tuple<VideoCore::ImageId, AmdGpu::Image, Shader::ImageResource>;
boost::container::static_vector<ImageBindingInfo, 32> image_bindings;
for (const auto& image_desc : stage.images) {
const auto tsharp = image_desc.GetSharp(stage);
if (tsharp.GetDataFmt() != AmdGpu::DataFormat::FormatInvalid) {
VideoCore::ImageInfo image_info{tsharp, image_desc};
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);
} else {
image_bindings.emplace_back(VideoCore::ImageId{}, tsharp, image_desc);
}
if (texture_cache.IsMeta(tsharp.Address())) {
LOG_WARNING(Render_Vulkan, "Unexpected metadata read by a PS shader (texture)");
}
}
// Second pass to re-bind images that were updated after binding
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);
} else {
auto& null_image = texture_cache.GetImageView(VideoCore::NULL_IMAGE_VIEW_ID);
image_infos.emplace_back(VK_NULL_HANDLE, *null_image.image_view,
vk::ImageLayout::eGeneral);
}
} else {
auto& image = texture_cache.GetImage(image_id);
if (True(image.flags & VideoCore::ImageFlagBits::NeedsRebind)) {
image_id = texture_cache.FindImage(image.info);
}
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);
image.flags &=
~(VideoCore::ImageFlagBits::NeedsRebind | VideoCore::ImageFlagBits::Bound);
}
set_writes.push_back({
.dstSet = VK_NULL_HANDLE,
.dstBinding = binding++,
.dstArrayElement = 0,
.descriptorCount = 1,
.descriptorType = desc.is_storage ? vk::DescriptorType::eStorageImage
: vk::DescriptorType::eSampledImage,
.pImageInfo = &image_infos.back(),
});
}
}
} // namespace Vulkan

View file

@ -0,0 +1,48 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "shader_recompiler/info.h"
#include "video_core/renderer_vulkan/vk_common.h"
namespace VideoCore {
class BufferCache;
class TextureCache;
} // namespace VideoCore
namespace Vulkan {
class Instance;
class Scheduler;
class DescriptorHeap;
class Pipeline {
public:
Pipeline(const Instance& instance, Scheduler& scheduler, DescriptorHeap& desc_heap,
vk::PipelineCache pipeline_cache);
virtual ~Pipeline();
vk::Pipeline Handle() const noexcept {
return *pipeline;
}
vk::PipelineLayout GetLayout() const noexcept {
return *pipeline_layout;
}
using DescriptorWrites = boost::container::small_vector<vk::WriteDescriptorSet, 16>;
void BindTextures(VideoCore::TextureCache& texture_cache, const Shader::Info& stage,
u32& binding, DescriptorWrites& set_writes) const;
protected:
const Instance& instance;
Scheduler& scheduler;
DescriptorHeap& desc_heap;
vk::UniquePipeline pipeline;
vk::UniquePipelineLayout pipeline_layout;
vk::UniqueDescriptorSetLayout desc_layout;
static boost::container::static_vector<vk::DescriptorImageInfo, 32> image_infos;
};
} // namespace Vulkan

View file

@ -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;

View file

@ -62,7 +62,7 @@ void Rasterizer::Draw(bool is_indexed, u32 index_offset) {
buffer_cache.BindVertexBuffers(vs_info);
const u32 num_indices = buffer_cache.BindIndexBuffer(is_indexed, index_offset);
BeginRendering();
BeginRendering(*pipeline);
UpdateDynamicState(*pipeline);
const auto [vertex_offset, instance_offset] = vs_info.GetDrawOffsets();
@ -102,7 +102,7 @@ void Rasterizer::DrawIndirect(bool is_indexed, VAddr address, u32 offset, u32 si
buffer_cache.BindVertexBuffers(vs_info);
const u32 num_indices = buffer_cache.BindIndexBuffer(is_indexed, 0);
BeginRendering();
BeginRendering(*pipeline);
UpdateDynamicState(*pipeline);
const auto [buffer, base] = buffer_cache.ObtainBuffer(address, size, true);
@ -179,7 +179,7 @@ void Rasterizer::Finish() {
scheduler.Finish();
}
void Rasterizer::BeginRendering() {
void Rasterizer::BeginRendering(const GraphicsPipeline& pipeline) {
const auto& regs = liverpool->regs;
RenderState state;
@ -199,6 +199,13 @@ void Rasterizer::BeginRendering() {
continue;
}
// Skip stale color buffers if shader doesn't output to them. Otherwise it will perform
// an unnecessary transition and may result in state conflict if the resource is already
// bound for reading.
if ((pipeline.GetMrtMask() & (1 << col_buf_id)) == 0) {
continue;
}
const auto& hint = liverpool->last_cb_extent[col_buf_id];
VideoCore::ImageInfo image_info{col_buf, hint};
VideoCore::ImageViewInfo view_info{col_buf, false /*!!image.info.usage.vo_buffer*/};
@ -240,7 +247,7 @@ void Rasterizer::BeginRendering() {
state.depth_image = image.image;
state.depth_attachment = {
.imageView = *image_view.image_view,
.imageLayout = image.layout,
.imageLayout = image.last_state.layout,
.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,

View file

@ -52,7 +52,7 @@ public:
void Finish();
private:
void BeginRendering();
void BeginRendering(const GraphicsPipeline& pipeline);
void UpdateDynamicState(const GraphicsPipeline& pipeline);
void UpdateViewportScissorState();

View file

@ -59,58 +59,6 @@ void Scheduler::EndRendering() {
}
is_rendering = false;
current_cmdbuf.endRendering();
boost::container::static_vector<vk::ImageMemoryBarrier, 9> barriers;
for (size_t i = 0; i < render_state.num_color_attachments; ++i) {
barriers.push_back(vk::ImageMemoryBarrier{
.srcAccessMask = vk::AccessFlagBits::eColorAttachmentWrite,
.dstAccessMask = vk::AccessFlagBits::eShaderRead | vk::AccessFlagBits::eShaderWrite,
.oldLayout = vk::ImageLayout::eColorAttachmentOptimal,
.newLayout = vk::ImageLayout::eColorAttachmentOptimal,
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.image = render_state.color_images[i],
.subresourceRange =
{
.aspectMask = vk::ImageAspectFlagBits::eColor,
.baseMipLevel = 0,
.levelCount = VK_REMAINING_MIP_LEVELS,
.baseArrayLayer = 0,
.layerCount = VK_REMAINING_ARRAY_LAYERS,
},
});
}
if (render_state.has_depth || render_state.has_stencil) {
barriers.push_back(vk::ImageMemoryBarrier{
.srcAccessMask = vk::AccessFlagBits::eDepthStencilAttachmentWrite,
.dstAccessMask = vk::AccessFlagBits::eShaderRead | vk::AccessFlagBits::eShaderWrite,
.oldLayout = render_state.depth_attachment.imageLayout,
.newLayout = render_state.depth_attachment.imageLayout,
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.image = render_state.depth_image,
.subresourceRange =
{
.aspectMask = vk::ImageAspectFlagBits::eDepth |
(render_state.has_stencil ? vk::ImageAspectFlagBits::eStencil
: vk::ImageAspectFlagBits::eNone),
.baseMipLevel = 0,
.levelCount = VK_REMAINING_MIP_LEVELS,
.baseArrayLayer = 0,
.layerCount = VK_REMAINING_ARRAY_LAYERS,
},
});
}
if (!barriers.empty()) {
const auto src_stages =
vk::PipelineStageFlagBits::eColorAttachmentOutput |
(render_state.has_depth ? vk::PipelineStageFlagBits::eLateFragmentTests |
vk::PipelineStageFlagBits::eEarlyFragmentTests
: vk::PipelineStageFlagBits::eNone);
current_cmdbuf.pipelineBarrier(src_stages, vk::PipelineStageFlagBits::eFragmentShader,
vk::DependencyFlagBits::eByRegion, {}, {}, barriers);
}
}
void Scheduler::Flush(SubmitInfo& info) {