mirror of
https://github.com/KytyPS5/KytyPS5.git
synced 2026-10-21 11:26:56 +02:00
Rewrite descriptor bindings and unify graphics push constants
This commit is contained in:
@@ -9,7 +9,6 @@
|
||||
#include "graphics/host_gpu/graphicContext.h"
|
||||
#include "graphics/host_gpu/renderer/debug.h"
|
||||
#include "graphics/host_gpu/renderer/image/textureCommon.h"
|
||||
#include "graphics/host_gpu/renderer/pipeline/descriptorCache.h"
|
||||
#include "graphics/host_gpu/renderer/render.h"
|
||||
#include "graphics/host_gpu/renderer/renderContext.h"
|
||||
#include "graphics/host_gpu/vulkanCommon.h"
|
||||
|
||||
@@ -55,13 +55,14 @@ public:
|
||||
void DeferPriorityOperation(Common::UniqueFunction<void>&& operation);
|
||||
[[nodiscard]] static bool InDeferredOperation() noexcept;
|
||||
|
||||
[[nodiscard]] bool Active() const noexcept { return m_current >= 0; }
|
||||
void CheckActive() const;
|
||||
RenderCommandBuffer& Current() const;
|
||||
[[nodiscard]] uint64_t CurrentTick() const noexcept { return m_master.CurrentTick(); }
|
||||
[[nodiscard]] bool IsFree(uint64_t tick);
|
||||
[[nodiscard]] RenderContext& Context() const noexcept { return m_context; }
|
||||
[[nodiscard]] GraphicContext& Graphics() const noexcept { return m_graphics; }
|
||||
[[nodiscard]] bool Active() const noexcept { return m_current >= 0; }
|
||||
void CheckActive() const;
|
||||
RenderCommandBuffer& Current() const;
|
||||
[[nodiscard]] uint64_t CurrentTick() const noexcept { return m_master.CurrentTick(); }
|
||||
[[nodiscard]] bool IsFree(uint64_t tick);
|
||||
[[nodiscard]] MasterSemaphore& GetMasterSemaphore() noexcept { return m_master; }
|
||||
[[nodiscard]] RenderContext& Context() const noexcept { return m_context; }
|
||||
[[nodiscard]] GraphicContext& Graphics() const noexcept { return m_graphics; }
|
||||
|
||||
private:
|
||||
static constexpr size_t CommandBufferGrowStep = 4;
|
||||
|
||||
@@ -9,7 +9,6 @@
|
||||
#include "graphics/host_gpu/renderer/debug.h"
|
||||
#include "graphics/host_gpu/renderer/depthRenderTarget.h"
|
||||
#include "graphics/host_gpu/renderer/image/imageView.h"
|
||||
#include "graphics/host_gpu/renderer/pipeline/descriptorCache.h"
|
||||
#include "graphics/host_gpu/renderer/render.h"
|
||||
#include "graphics/host_gpu/renderer/renderContext.h"
|
||||
#include "graphics/host_gpu/vma.h"
|
||||
@@ -30,8 +29,8 @@ void ReportVulkanFatal(const char* what, vk::Result result, uint32_t slot, uint6
|
||||
" args=%u,%u,%u,%u,0x%016" PRIx64 "\n",
|
||||
what, VulkanToString(result).c_str(), static_cast<int>(result), slot, submit_seq, debug_op,
|
||||
debug_submit, arg0, arg1, arg2, arg3, arg4);
|
||||
std::printf("%s failed: %s (%d), slot=%u submit_seq=%" PRIu64 " debug_op=%u debug_submit=%" PRIu64
|
||||
" args=%u,%u,%u,%u,0x%016" PRIx64 "\n",
|
||||
std::printf("%s failed: %s (%d), slot=%u submit_seq=%" PRIu64
|
||||
" debug_op=%u debug_submit=%" PRIu64 " args=%u,%u,%u,%u,0x%016" PRIx64 "\n",
|
||||
what, VulkanToString(result).c_str(), static_cast<int>(result), slot, submit_seq,
|
||||
debug_op, debug_submit, arg0, arg1, arg2, arg3, arg4);
|
||||
std::fflush(stdout);
|
||||
@@ -68,23 +67,11 @@ void CommandBuffer::Release() {
|
||||
|
||||
m_slot->busy = false;
|
||||
m_slot->Reset();
|
||||
RecycleDescriptorsAfterFence();
|
||||
m_slot = nullptr;
|
||||
|
||||
EXIT_NOT_IMPLEMENTED(!IsInvalid());
|
||||
}
|
||||
|
||||
void CommandBuffer::RecycleDescriptorAfterFence(VulkanDescriptorSet& set) {
|
||||
m_descriptor_sets_after_fence.push_back(&set);
|
||||
}
|
||||
|
||||
void CommandBuffer::RecycleDescriptorsAfterFence() {
|
||||
for (auto* set: m_descriptor_sets_after_fence) {
|
||||
m_context.GetDescriptorCache().Recycle(*set);
|
||||
}
|
||||
m_descriptor_sets_after_fence.clear();
|
||||
}
|
||||
|
||||
void CommandBuffer::Begin() const {
|
||||
EXIT_IF(m_rendering);
|
||||
auto buffer = Handle();
|
||||
@@ -218,9 +205,6 @@ void CommandBuffer::FinalizeFence(bool reset_recording) {
|
||||
m_slot->Reset();
|
||||
}
|
||||
}
|
||||
if (was_executed) {
|
||||
RecycleDescriptorsAfterFence();
|
||||
}
|
||||
}
|
||||
|
||||
void CommandBuffer::BeginRendering(const RenderState& state) const {
|
||||
|
||||
@@ -13,7 +13,6 @@
|
||||
#include "graphics/host_gpu/renderer/debug.h"
|
||||
#include "graphics/host_gpu/renderer/image/imageView.h"
|
||||
#include "graphics/host_gpu/renderer/image/textureCommon.h"
|
||||
#include "graphics/host_gpu/renderer/pipeline/descriptorCache.h"
|
||||
#include "graphics/host_gpu/renderer/render.h"
|
||||
#include "graphics/host_gpu/renderer/renderContext.h"
|
||||
#include "graphics/host_gpu/vulkanCommon.h"
|
||||
@@ -290,8 +289,8 @@ void RenderExecutor::ResolveRenderDepthTarget(uint64_t submit_id, RenderCommandB
|
||||
|
||||
r.stencil_clear_enable =
|
||||
has_stencil && rc.stencil_clear_enable && !z.depth_view.stencil_write_disable;
|
||||
r.stencil_clear_value = hw.GetStencilClearValue();
|
||||
r.stencil_test_enable = has_stencil && dc.stencil_enable;
|
||||
r.stencil_clear_value = hw.GetStencilClearValue();
|
||||
r.stencil_test_enable = has_stencil && dc.stencil_enable;
|
||||
if (r.stencil_test_enable) {
|
||||
const bool stencil_ops_disabled =
|
||||
rc.stencil_clear_enable || z.depth_view.stencil_write_disable;
|
||||
|
||||
@@ -1,318 +0,0 @@
|
||||
#include "graphics/host_gpu/renderer/pipeline/descriptorCache.h"
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "common/profiler.h"
|
||||
#include "graphics/shader/recompiler/ir/ShaderIR.h"
|
||||
|
||||
#include <array>
|
||||
|
||||
namespace Libs::Graphics {
|
||||
namespace {
|
||||
|
||||
using BindingKind = ShaderRecompiler::IR::DescriptorBindingKind;
|
||||
|
||||
vk::ShaderStageFlags StageFlags(DescriptorCache::Stage stage) {
|
||||
switch (stage) {
|
||||
case DescriptorCache::Stage::Vertex: return vk::ShaderStageFlagBits::eVertex;
|
||||
case DescriptorCache::Stage::Pixel: return vk::ShaderStageFlagBits::eFragment;
|
||||
case DescriptorCache::Stage::Compute: return vk::ShaderStageFlagBits::eCompute;
|
||||
default: EXIT("unknown descriptor stage\n");
|
||||
}
|
||||
}
|
||||
|
||||
bool IsSampledImage(BindingKind kind) {
|
||||
switch (kind) {
|
||||
case BindingKind::Sampled1D:
|
||||
case BindingKind::Sampled1DArray:
|
||||
case BindingKind::Sampled2D:
|
||||
case BindingKind::Sampled2DArray:
|
||||
case BindingKind::Sampled2DMsaa:
|
||||
case BindingKind::Sampled2DMsaaArray:
|
||||
case BindingKind::Sampled3D:
|
||||
case BindingKind::SampledUint1D:
|
||||
case BindingKind::SampledUint1DArray:
|
||||
case BindingKind::SampledUint2D:
|
||||
case BindingKind::SampledUint2DArray:
|
||||
case BindingKind::SampledUint2DMsaa:
|
||||
case BindingKind::SampledUint2DMsaaArray:
|
||||
case BindingKind::SampledUint3D: return true;
|
||||
default: return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool IsStorageImage(BindingKind kind) {
|
||||
switch (kind) {
|
||||
case BindingKind::Storage1D:
|
||||
case BindingKind::Storage1DArray:
|
||||
case BindingKind::Storage2D:
|
||||
case BindingKind::Storage2DArray:
|
||||
case BindingKind::Storage3D:
|
||||
case BindingKind::StorageUint1D:
|
||||
case BindingKind::StorageUint1DArray:
|
||||
case BindingKind::StorageUint2D:
|
||||
case BindingKind::StorageUint2DArray:
|
||||
case BindingKind::StorageUint3D:
|
||||
case BindingKind::StorageAtomic1D:
|
||||
case BindingKind::StorageAtomic1DArray:
|
||||
case BindingKind::StorageAtomic2D:
|
||||
case BindingKind::StorageAtomic2DArray:
|
||||
case BindingKind::StorageAtomic3D: return true;
|
||||
default: return false;
|
||||
}
|
||||
}
|
||||
|
||||
vk::DescriptorType DescriptorType(BindingKind kind) {
|
||||
if (kind == BindingKind::Samplers) {
|
||||
return vk::DescriptorType::eSampler;
|
||||
}
|
||||
if (IsSampledImage(kind)) {
|
||||
return vk::DescriptorType::eSampledImage;
|
||||
}
|
||||
if (IsStorageImage(kind)) {
|
||||
return vk::DescriptorType::eStorageImage;
|
||||
}
|
||||
return vk::DescriptorType::eStorageBuffer;
|
||||
}
|
||||
|
||||
uint32_t DescriptorCount(const ShaderRecompiler::IR::DescriptorBinding& binding) {
|
||||
return binding.resources.empty() ? 1u : static_cast<uint32_t>(binding.resources.size());
|
||||
}
|
||||
|
||||
std::vector<uint32_t> LayoutKey(DescriptorCache::Stage stage,
|
||||
const ShaderRecompiler::IR::Program& program) {
|
||||
std::vector<uint32_t> key;
|
||||
key.reserve(1u + program.bindings.descriptors.size() * 4u);
|
||||
key.push_back(static_cast<uint32_t>(stage));
|
||||
for (const auto& binding: program.bindings.descriptors) {
|
||||
key.push_back(static_cast<uint32_t>(binding.kind));
|
||||
key.push_back(binding.binding);
|
||||
key.push_back(DescriptorCount(binding));
|
||||
key.push_back(static_cast<uint32_t>(DescriptorType(binding.kind)));
|
||||
}
|
||||
return key;
|
||||
}
|
||||
|
||||
vk::DescriptorBufferInfo BufferInfo(const BufferView& view) {
|
||||
EXIT_IF(view.buffer == nullptr);
|
||||
return {view.buffer, view.offset, view.range};
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
vk::DescriptorImageInfo DescriptorCache::MakeImageInfo(const TextureBinding& texture,
|
||||
uint32_t element) {
|
||||
const auto view =
|
||||
texture.mip_views.empty()
|
||||
? (element == 0u ? texture.image_view : vk::ImageView {})
|
||||
: (element < texture.mip_views.size() ? texture.mip_views[element] : vk::ImageView {});
|
||||
EXIT_IF(!texture.image_id || view == nullptr || texture.layout == vk::ImageLayout::eUndefined);
|
||||
return {nullptr, view, texture.layout};
|
||||
}
|
||||
|
||||
DescriptorCache::~DescriptorCache() {
|
||||
for (auto& [layout, sets]: m_free_sets_by_layout) {
|
||||
(void)layout;
|
||||
for (auto* set: sets) {
|
||||
delete set;
|
||||
}
|
||||
}
|
||||
for (const auto& [key, layout]: m_descriptor_set_layouts) {
|
||||
(void)key;
|
||||
m_graphics.device.destroyDescriptorSetLayout(layout, nullptr);
|
||||
}
|
||||
for (const auto& pool: m_pools) {
|
||||
m_graphics.device.destroyDescriptorPool(pool.pool, nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
vk::DescriptorSetLayout
|
||||
DescriptorCache::GetDescriptorSetLayoutInternal(Stage stage,
|
||||
const ShaderRecompiler::IR::Program& program) {
|
||||
const auto key = LayoutKey(stage, program);
|
||||
if (const auto found = m_descriptor_set_layouts.find(key);
|
||||
found != m_descriptor_set_layouts.end()) {
|
||||
return found->second;
|
||||
}
|
||||
|
||||
std::vector<vk::DescriptorSetLayoutBinding> bindings;
|
||||
bindings.reserve(program.bindings.descriptors.size());
|
||||
for (const auto& descriptor: program.bindings.descriptors) {
|
||||
bindings.push_back({descriptor.binding, DescriptorType(descriptor.kind),
|
||||
DescriptorCount(descriptor), StageFlags(stage), nullptr});
|
||||
}
|
||||
if (bindings.empty()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
vk::DescriptorSetLayoutCreateInfo info {};
|
||||
info.sType = vk::StructureType::eDescriptorSetLayoutCreateInfo;
|
||||
info.bindingCount = static_cast<uint32_t>(bindings.size());
|
||||
info.pBindings = bindings.data();
|
||||
vk::DescriptorSetLayout layout = nullptr;
|
||||
const auto result = m_graphics.device.createDescriptorSetLayout(&info, nullptr, &layout);
|
||||
EXIT_NOT_IMPLEMENTED(result != vk::Result::eSuccess || layout == nullptr);
|
||||
m_descriptor_set_layouts.emplace(key, layout);
|
||||
return layout;
|
||||
}
|
||||
|
||||
void DescriptorCache::CreatePool() {
|
||||
KYTY_PROFILER_FUNCTION();
|
||||
constexpr uint32_t MaxSets = 512;
|
||||
const vk::DescriptorPoolSize sizes[] = {
|
||||
{vk::DescriptorType::eStorageBuffer,
|
||||
MaxSets * (ShaderRecompiler::IR::ShaderInfo::MaxBuffers +
|
||||
ShaderRecompiler::IR::ShaderInfo::MaxAddresses + 3u)},
|
||||
{vk::DescriptorType::eSampledImage, MaxSets * ShaderRecompiler::IR::ShaderInfo::MaxImages},
|
||||
{vk::DescriptorType::eStorageImage, MaxSets * ShaderRecompiler::IR::ShaderInfo::MaxImages},
|
||||
{vk::DescriptorType::eSampler, MaxSets * ShaderRecompiler::IR::ShaderInfo::MaxSamplers},
|
||||
};
|
||||
vk::DescriptorPoolCreateInfo info {};
|
||||
info.sType = vk::StructureType::eDescriptorPoolCreateInfo;
|
||||
info.poolSizeCount = static_cast<uint32_t>(std::size(sizes));
|
||||
info.pPoolSizes = sizes;
|
||||
info.maxSets = MaxSets;
|
||||
const auto pool_id = static_cast<int>(m_pools.size());
|
||||
auto& pool = m_pools.emplace_back();
|
||||
const auto result = m_graphics.device.createDescriptorPool(&info, nullptr, &pool.pool);
|
||||
EXIT_NOT_IMPLEMENTED(result != vk::Result::eSuccess || pool.pool == nullptr);
|
||||
pool.next_free_pool = m_first_free_pool;
|
||||
m_first_free_pool = pool_id;
|
||||
}
|
||||
|
||||
VulkanDescriptorSet* DescriptorCache::Allocate(Stage stage,
|
||||
const ShaderRecompiler::IR::Program& program) {
|
||||
KYTY_PROFILER_FUNCTION();
|
||||
Common::LockGuard lock(m_mutex);
|
||||
const auto layout = GetDescriptorSetLayoutInternal(stage, program);
|
||||
EXIT_IF(layout == nullptr);
|
||||
auto& free_sets = m_free_sets_by_layout[layout];
|
||||
if (!free_sets.empty()) {
|
||||
auto* result = free_sets.back();
|
||||
free_sets.pop_back();
|
||||
return result;
|
||||
}
|
||||
|
||||
constexpr uint32_t Batch = 32;
|
||||
for (int attempt = 0; attempt < 2; attempt++) {
|
||||
for (int pool_id = m_first_free_pool; pool_id != -1;
|
||||
pool_id = m_pools[pool_id].next_free_pool) {
|
||||
auto& pool = m_pools[pool_id];
|
||||
std::array<vk::DescriptorSetLayout, Batch> layouts;
|
||||
layouts.fill(layout);
|
||||
std::array<vk::DescriptorSet, Batch> sets {};
|
||||
vk::DescriptorSetAllocateInfo info {};
|
||||
info.sType = vk::StructureType::eDescriptorSetAllocateInfo;
|
||||
info.descriptorPool = pool.pool;
|
||||
info.descriptorSetCount = Batch;
|
||||
info.pSetLayouts = layouts.data();
|
||||
if (m_graphics.device.allocateDescriptorSets(&info, sets.data()) ==
|
||||
vk::Result::eSuccess) {
|
||||
free_sets.reserve(free_sets.size() + Batch - 1u);
|
||||
for (uint32_t i = 0; i + 1u < Batch; i++) {
|
||||
free_sets.push_back(new VulkanDescriptorSet {sets[i], layout, pool_id});
|
||||
}
|
||||
return new VulkanDescriptorSet {sets.back(), layout, pool_id};
|
||||
}
|
||||
m_first_free_pool = pool.next_free_pool;
|
||||
break;
|
||||
}
|
||||
CreatePool();
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void DescriptorCache::Recycle(VulkanDescriptorSet& set) {
|
||||
EXIT_IF(set.set == nullptr || set.layout == nullptr);
|
||||
Common::LockGuard lock(m_mutex);
|
||||
m_free_sets_by_layout[set.layout].push_back(&set);
|
||||
}
|
||||
|
||||
VulkanDescriptorSet& DescriptorCache::GetDescriptor(Stage stage,
|
||||
const ShaderRecompiler::IR::Program& program,
|
||||
const NativeDescriptors& data) {
|
||||
KYTY_PROFILER_FUNCTION();
|
||||
EXIT_IF(data.buffers.size() != program.info.buffers.size() ||
|
||||
data.images.size() != program.info.images.size() ||
|
||||
data.samplers.size() != program.info.samplers.size() ||
|
||||
data.addresses.size() != program.info.addresses.size());
|
||||
auto* set = Allocate(stage, program);
|
||||
EXIT_NOT_IMPLEMENTED(set == nullptr);
|
||||
|
||||
size_t descriptor_count = 0;
|
||||
for (const auto& binding: program.bindings.descriptors) {
|
||||
descriptor_count += DescriptorCount(binding);
|
||||
}
|
||||
std::vector<vk::DescriptorBufferInfo> buffer_infos;
|
||||
std::vector<vk::DescriptorImageInfo> image_infos;
|
||||
std::vector<vk::WriteDescriptorSet> writes;
|
||||
buffer_infos.reserve(descriptor_count);
|
||||
image_infos.reserve(descriptor_count);
|
||||
writes.reserve(program.bindings.descriptors.size());
|
||||
std::vector<uint32_t> image_occurrences(data.images.size());
|
||||
|
||||
for (const auto& binding: program.bindings.descriptors) {
|
||||
vk::WriteDescriptorSet write {};
|
||||
write.sType = vk::StructureType::eWriteDescriptorSet;
|
||||
write.dstSet = set->set;
|
||||
write.dstBinding = binding.binding;
|
||||
write.descriptorType = DescriptorType(binding.kind);
|
||||
write.descriptorCount = DescriptorCount(binding);
|
||||
const auto buffer_start = buffer_infos.size();
|
||||
const auto image_start = image_infos.size();
|
||||
switch (binding.kind) {
|
||||
case BindingKind::Buffers:
|
||||
for (const auto resource: binding.resources) {
|
||||
buffer_infos.push_back(BufferInfo(data.buffers.at(resource)));
|
||||
}
|
||||
break;
|
||||
case BindingKind::AddressMemory:
|
||||
for (const auto resource: binding.resources) {
|
||||
buffer_infos.push_back(BufferInfo(data.addresses.at(resource)));
|
||||
}
|
||||
break;
|
||||
case BindingKind::FlattenedSrt:
|
||||
buffer_infos.push_back(BufferInfo(data.flattened_srt));
|
||||
break;
|
||||
case BindingKind::UserData: buffer_infos.push_back(BufferInfo(data.user_data)); break;
|
||||
case BindingKind::Gds: buffer_infos.push_back(BufferInfo(data.gds)); break;
|
||||
case BindingKind::Samplers:
|
||||
for (const auto resource: binding.resources) {
|
||||
const auto sampler = data.samplers.at(resource);
|
||||
EXIT_IF(sampler == nullptr);
|
||||
image_infos.push_back({sampler, nullptr, vk::ImageLayout::eUndefined});
|
||||
}
|
||||
break;
|
||||
default: {
|
||||
for (const auto resource: binding.resources) {
|
||||
const auto& texture = data.images.at(resource);
|
||||
image_infos.push_back(MakeImageInfo(texture, image_occurrences.at(resource)++));
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (buffer_infos.size() != buffer_start) {
|
||||
write.pBufferInfo = buffer_infos.data() + buffer_start;
|
||||
}
|
||||
if (image_infos.size() != image_start) {
|
||||
write.pImageInfo = image_infos.data() + image_start;
|
||||
}
|
||||
writes.push_back(write);
|
||||
}
|
||||
for (uint32_t i = 0; i < data.images.size(); i++) {
|
||||
const auto expected = data.images[i].mip_views.empty()
|
||||
? 1u
|
||||
: static_cast<uint32_t>(data.images[i].mip_views.size());
|
||||
EXIT_IF(image_occurrences[i] != expected);
|
||||
}
|
||||
m_graphics.device.updateDescriptorSets(static_cast<uint32_t>(writes.size()), writes.data(), 0,
|
||||
nullptr);
|
||||
return *set;
|
||||
}
|
||||
|
||||
vk::DescriptorSetLayout
|
||||
DescriptorCache::GetDescriptorSetLayout(Stage stage, const ShaderRecompiler::IR::Program& program) {
|
||||
Common::LockGuard lock(m_mutex);
|
||||
return GetDescriptorSetLayoutInternal(stage, program);
|
||||
}
|
||||
|
||||
} // namespace Libs::Graphics
|
||||
@@ -1,118 +0,0 @@
|
||||
#ifndef EMULATOR_SRC_GRAPHICS_HOST_GPU_RENDERER_DESCRIPTORCACHE_H_
|
||||
#define EMULATOR_SRC_GRAPHICS_HOST_GPU_RENDERER_DESCRIPTORCACHE_H_
|
||||
|
||||
#include "common/abi.h"
|
||||
#include "common/assert.h"
|
||||
#include "common/common.h"
|
||||
#include "common/threads.h"
|
||||
#include "graphics/host_gpu/graphicContext.h"
|
||||
#include "graphics/host_gpu/renderer/cache/bufferCache.h"
|
||||
#include "graphics/host_gpu/renderer/cache/textureCache.h"
|
||||
#include "graphics/host_gpu/vulkanCommon.h"
|
||||
#include "graphics/shader/shaderBindings.h"
|
||||
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <span>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
namespace Libs::Graphics {
|
||||
|
||||
namespace ShaderRecompiler::IR {
|
||||
struct DescriptorValue;
|
||||
struct ImageResource;
|
||||
struct Program;
|
||||
struct ResourceSnapshot;
|
||||
} // namespace ShaderRecompiler::IR
|
||||
|
||||
class CommandBuffer;
|
||||
struct DescriptorCacheTestAccess;
|
||||
struct ShaderStageRuntime;
|
||||
|
||||
struct VulkanDescriptorSet {
|
||||
vk::DescriptorSet set = nullptr;
|
||||
vk::DescriptorSetLayout layout = nullptr;
|
||||
int pool_id = -1;
|
||||
};
|
||||
|
||||
struct BufferView {
|
||||
vk::Buffer buffer = nullptr;
|
||||
vk::DeviceSize offset = 0;
|
||||
vk::DeviceSize range = VK_WHOLE_SIZE;
|
||||
};
|
||||
|
||||
class DescriptorCache {
|
||||
public:
|
||||
enum class Stage { Unknown, Vertex, Pixel, Compute };
|
||||
|
||||
struct TextureBinding {
|
||||
ImageId image_id;
|
||||
vk::ImageView image_view = nullptr;
|
||||
TextureCache::ImageDesc desc;
|
||||
vk::ImageLayout layout = vk::ImageLayout::eUndefined;
|
||||
std::vector<vk::ImageView> mip_views;
|
||||
};
|
||||
|
||||
struct NativeDescriptors {
|
||||
std::vector<BufferView> buffers;
|
||||
std::vector<TextureBinding> images;
|
||||
std::vector<vk::Sampler> samplers;
|
||||
std::vector<BufferView> addresses;
|
||||
BufferView gds;
|
||||
BufferView flattened_srt;
|
||||
BufferView user_data;
|
||||
};
|
||||
|
||||
struct PreparedBindings {
|
||||
std::shared_ptr<const ShaderRecompiler::IR::Program> program;
|
||||
std::shared_ptr<const ShaderRecompiler::IR::ResourceSnapshot> snapshot;
|
||||
NativeDescriptors resources;
|
||||
std::vector<BufferId> buffer_ids;
|
||||
std::vector<BufferId> address_ids;
|
||||
std::vector<uint32_t> flattened_srt;
|
||||
std::vector<uint32_t> user_data;
|
||||
vk::ShaderStageFlags shader_stage;
|
||||
Stage stage = Stage::Unknown;
|
||||
bool committed = false;
|
||||
};
|
||||
|
||||
explicit DescriptorCache(GraphicContext& graphics): m_graphics(graphics) {
|
||||
EXIT_NOT_IMPLEMENTED(!Common::Thread::IsMainThread());
|
||||
}
|
||||
~DescriptorCache();
|
||||
KYTY_CLASS_NO_COPY(DescriptorCache);
|
||||
|
||||
vk::DescriptorSetLayout GetDescriptorSetLayout(Stage stage,
|
||||
const ShaderRecompiler::IR::Program& program);
|
||||
void Recycle(VulkanDescriptorSet& set);
|
||||
VulkanDescriptorSet& GetDescriptor(Stage stage, const ShaderRecompiler::IR::Program& program,
|
||||
const NativeDescriptors& descriptors);
|
||||
|
||||
private:
|
||||
friend struct DescriptorCacheTestAccess;
|
||||
|
||||
struct Pool {
|
||||
vk::DescriptorPool pool = nullptr;
|
||||
int next_free_pool = -1;
|
||||
};
|
||||
|
||||
static vk::DescriptorImageInfo MakeImageInfo(const TextureBinding& texture,
|
||||
uint32_t element = 0);
|
||||
void CreatePool();
|
||||
VulkanDescriptorSet* Allocate(Stage stage, const ShaderRecompiler::IR::Program& program);
|
||||
vk::DescriptorSetLayout
|
||||
GetDescriptorSetLayoutInternal(Stage stage, const ShaderRecompiler::IR::Program& program);
|
||||
|
||||
GraphicContext& m_graphics;
|
||||
Common::Mutex m_mutex;
|
||||
std::vector<Pool> m_pools;
|
||||
int m_first_free_pool = -1;
|
||||
std::unordered_map<vk::DescriptorSetLayout, std::vector<VulkanDescriptorSet*>>
|
||||
m_free_sets_by_layout;
|
||||
std::map<std::vector<uint32_t>, vk::DescriptorSetLayout> m_descriptor_set_layouts;
|
||||
};
|
||||
|
||||
} // namespace Libs::Graphics
|
||||
|
||||
#endif // EMULATOR_SRC_GRAPHICS_HOST_GPU_RENDERER_DESCRIPTORCACHE_H_
|
||||
@@ -0,0 +1,97 @@
|
||||
#include "graphics/host_gpu/renderer/pipeline/descriptorHeap.h"
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "common/profiler.h"
|
||||
#include "graphics/host_gpu/graphicContext.h"
|
||||
#include "graphics/host_gpu/renderer/masterSemaphore.h"
|
||||
|
||||
namespace Libs::Graphics {
|
||||
namespace {
|
||||
|
||||
constexpr uint32_t DescriptorHeapCount = 1024;
|
||||
constexpr std::array DescriptorPoolSizes = {
|
||||
vk::DescriptorPoolSize {vk::DescriptorType::eStorageBuffer, 8192},
|
||||
vk::DescriptorPoolSize {vk::DescriptorType::eSampledImage, 8192},
|
||||
vk::DescriptorPoolSize {vk::DescriptorType::eStorageImage, 1024},
|
||||
vk::DescriptorPoolSize {vk::DescriptorType::eSampler, 1024},
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
DescriptorHeap::DescriptorHeap(GraphicContext& graphics, MasterSemaphore& master_semaphore)
|
||||
: m_graphics(graphics), m_master_semaphore(master_semaphore) {
|
||||
CreatePool();
|
||||
}
|
||||
|
||||
DescriptorHeap::~DescriptorHeap() {
|
||||
m_graphics.device.destroyDescriptorPool(m_current_pool, nullptr);
|
||||
for (const auto& [pool, tick]: m_pending_pools) {
|
||||
m_master_semaphore.Wait(tick);
|
||||
m_graphics.device.destroyDescriptorPool(pool, nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
vk::DescriptorSet DescriptorHeap::Commit(vk::DescriptorSetLayout layout) {
|
||||
KYTY_PROFILER_FUNCTION();
|
||||
EXIT_IF(layout == nullptr);
|
||||
|
||||
auto& batch = m_sets[layout];
|
||||
if (batch.size != 0) {
|
||||
return batch.sets[--batch.size];
|
||||
}
|
||||
if (Allocate(layout, batch)) {
|
||||
return batch.sets[--batch.size];
|
||||
}
|
||||
|
||||
m_pending_pools.emplace_back(m_current_pool, m_master_semaphore.CurrentTick());
|
||||
if (const auto& [pool, tick] = m_pending_pools.front(); m_master_semaphore.IsFree(tick)) {
|
||||
m_current_pool = pool;
|
||||
m_pending_pools.pop_front();
|
||||
EXIT_IF(m_graphics.device.resetDescriptorPool(m_current_pool, {}) != vk::Result::eSuccess);
|
||||
} else {
|
||||
CreatePool();
|
||||
}
|
||||
|
||||
m_sets.clear();
|
||||
auto& fresh_batch = m_sets[layout];
|
||||
EXIT_IF(!Allocate(layout, fresh_batch));
|
||||
return fresh_batch.sets[--fresh_batch.size];
|
||||
}
|
||||
|
||||
bool DescriptorHeap::Allocate(vk::DescriptorSetLayout layout, Batch& batch) {
|
||||
std::array<vk::DescriptorSetLayout, DescriptorSetBatch> layouts;
|
||||
layouts.fill(layout);
|
||||
|
||||
vk::DescriptorSetAllocateInfo allocate {};
|
||||
allocate.sType = vk::StructureType::eDescriptorSetAllocateInfo;
|
||||
allocate.descriptorPool = m_current_pool;
|
||||
allocate.pSetLayouts = layouts.data();
|
||||
|
||||
for (;;) {
|
||||
allocate.descriptorSetCount = batch.allocation;
|
||||
const auto result = m_graphics.device.allocateDescriptorSets(&allocate, batch.sets.data());
|
||||
if (result == vk::Result::eSuccess) {
|
||||
batch.size = batch.allocation;
|
||||
return true;
|
||||
}
|
||||
EXIT_IF(result != vk::Result::eErrorOutOfPoolMemory &&
|
||||
result != vk::Result::eErrorFragmentedPool);
|
||||
if (batch.allocation == 1) {
|
||||
return false;
|
||||
}
|
||||
batch.allocation /= 2;
|
||||
}
|
||||
}
|
||||
|
||||
void DescriptorHeap::CreatePool() {
|
||||
vk::DescriptorPoolCreateInfo create {};
|
||||
create.sType = vk::StructureType::eDescriptorPoolCreateInfo;
|
||||
create.flags = {};
|
||||
create.maxSets = DescriptorHeapCount;
|
||||
create.poolSizeCount = static_cast<uint32_t>(DescriptorPoolSizes.size());
|
||||
create.pPoolSizes = DescriptorPoolSizes.data();
|
||||
EXIT_IF(m_graphics.device.createDescriptorPool(&create, nullptr, &m_current_pool) !=
|
||||
vk::Result::eSuccess);
|
||||
}
|
||||
|
||||
} // namespace Libs::Graphics
|
||||
@@ -0,0 +1,45 @@
|
||||
#ifndef EMULATOR_SRC_GRAPHICS_HOST_GPU_RENDERER_DESCRIPTORHEAP_H_
|
||||
#define EMULATOR_SRC_GRAPHICS_HOST_GPU_RENDERER_DESCRIPTORHEAP_H_
|
||||
|
||||
#include "common/common.h"
|
||||
#include "graphics/host_gpu/vulkanCommon.h"
|
||||
|
||||
#include <array>
|
||||
#include <deque>
|
||||
#include <unordered_map>
|
||||
|
||||
namespace Libs::Graphics {
|
||||
|
||||
struct GraphicContext;
|
||||
class MasterSemaphore;
|
||||
|
||||
class DescriptorHeap {
|
||||
public:
|
||||
DescriptorHeap(GraphicContext& graphics, MasterSemaphore& master_semaphore);
|
||||
~DescriptorHeap();
|
||||
KYTY_CLASS_NO_COPY(DescriptorHeap);
|
||||
|
||||
[[nodiscard]] vk::DescriptorSet Commit(vk::DescriptorSetLayout layout);
|
||||
|
||||
private:
|
||||
static constexpr uint32_t DescriptorSetBatch = 32;
|
||||
|
||||
struct Batch {
|
||||
std::array<vk::DescriptorSet, DescriptorSetBatch> sets {};
|
||||
uint32_t size = 0;
|
||||
uint32_t allocation = DescriptorSetBatch;
|
||||
};
|
||||
|
||||
[[nodiscard]] bool Allocate(vk::DescriptorSetLayout layout, Batch& batch);
|
||||
void CreatePool();
|
||||
|
||||
GraphicContext& m_graphics;
|
||||
MasterSemaphore& m_master_semaphore;
|
||||
vk::DescriptorPool m_current_pool = nullptr;
|
||||
std::deque<std::pair<vk::DescriptorPool, uint64_t>> m_pending_pools;
|
||||
std::unordered_map<vk::DescriptorSetLayout, Batch> m_sets;
|
||||
};
|
||||
|
||||
} // namespace Libs::Graphics
|
||||
|
||||
#endif // EMULATOR_SRC_GRAPHICS_HOST_GPU_RENDERER_DESCRIPTORHEAP_H_
|
||||
@@ -17,7 +17,6 @@
|
||||
#include "graphics/host_gpu/renderer/debug.h"
|
||||
#include "graphics/host_gpu/renderer/image/imageView.h"
|
||||
#include "graphics/host_gpu/renderer/image/textureCommon.h"
|
||||
#include "graphics/host_gpu/renderer/pipeline/descriptorCache.h"
|
||||
#include "graphics/host_gpu/renderer/pipeline/shaderResourceBarrier.h"
|
||||
#include "graphics/host_gpu/renderer/render.h"
|
||||
#include "graphics/host_gpu/renderer/renderContext.h"
|
||||
@@ -45,14 +44,95 @@
|
||||
|
||||
namespace Libs::Graphics {
|
||||
|
||||
static const char* ShaderStageResourceName(DescriptorCache::Stage stage) {
|
||||
switch (stage) {
|
||||
case DescriptorCache::Stage::Vertex: return "Vertex";
|
||||
case DescriptorCache::Stage::Pixel: return "Pixel";
|
||||
case DescriptorCache::Stage::Compute: return "Compute";
|
||||
case DescriptorCache::Stage::Unknown: return "Unknown";
|
||||
namespace {
|
||||
|
||||
using BindingKind = ShaderRecompiler::IR::DescriptorBindingKind;
|
||||
|
||||
bool IsSampledImage(BindingKind kind) {
|
||||
switch (kind) {
|
||||
case BindingKind::Sampled1D:
|
||||
case BindingKind::Sampled1DArray:
|
||||
case BindingKind::Sampled2D:
|
||||
case BindingKind::Sampled2DArray:
|
||||
case BindingKind::Sampled2DMsaa:
|
||||
case BindingKind::Sampled2DMsaaArray:
|
||||
case BindingKind::Sampled3D:
|
||||
case BindingKind::SampledUint1D:
|
||||
case BindingKind::SampledUint1DArray:
|
||||
case BindingKind::SampledUint2D:
|
||||
case BindingKind::SampledUint2DArray:
|
||||
case BindingKind::SampledUint2DMsaa:
|
||||
case BindingKind::SampledUint2DMsaaArray:
|
||||
case BindingKind::SampledUint3D: return true;
|
||||
default: return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool IsStorageImage(BindingKind kind) {
|
||||
switch (kind) {
|
||||
case BindingKind::Storage1D:
|
||||
case BindingKind::Storage1DArray:
|
||||
case BindingKind::Storage2D:
|
||||
case BindingKind::Storage2DArray:
|
||||
case BindingKind::Storage3D:
|
||||
case BindingKind::StorageUint1D:
|
||||
case BindingKind::StorageUint1DArray:
|
||||
case BindingKind::StorageUint2D:
|
||||
case BindingKind::StorageUint2DArray:
|
||||
case BindingKind::StorageUint3D:
|
||||
case BindingKind::StorageAtomic1D:
|
||||
case BindingKind::StorageAtomic1DArray:
|
||||
case BindingKind::StorageAtomic2D:
|
||||
case BindingKind::StorageAtomic2DArray:
|
||||
case BindingKind::StorageAtomic3D: return true;
|
||||
default: return false;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
vk::DescriptorType NativeDescriptorType(BindingKind kind) {
|
||||
if (kind == BindingKind::Samplers) {
|
||||
return vk::DescriptorType::eSampler;
|
||||
}
|
||||
if (IsSampledImage(kind)) {
|
||||
return vk::DescriptorType::eSampledImage;
|
||||
}
|
||||
if (IsStorageImage(kind)) {
|
||||
return vk::DescriptorType::eStorageImage;
|
||||
}
|
||||
return vk::DescriptorType::eStorageBuffer;
|
||||
}
|
||||
|
||||
uint32_t NativeDescriptorCount(const ShaderRecompiler::IR::DescriptorBinding& binding) {
|
||||
return binding.resources.empty() ? 1u : static_cast<uint32_t>(binding.resources.size());
|
||||
}
|
||||
|
||||
vk::DescriptorImageInfo MakeImageInfo(const TextureBinding& texture, uint32_t element) {
|
||||
const auto view =
|
||||
texture.mip_views.empty()
|
||||
? (element == 0u ? texture.image_view : vk::ImageView {})
|
||||
: (element < texture.mip_views.size() ? texture.mip_views[element] : vk::ImageView {});
|
||||
EXIT_IF(!texture.image_id || view == nullptr || texture.layout == vk::ImageLayout::eUndefined);
|
||||
return {nullptr, view, texture.layout};
|
||||
}
|
||||
|
||||
static const char* ShaderStageResourceName(ShaderType stage) {
|
||||
switch (stage) {
|
||||
case ShaderType::Vertex: return "Vertex";
|
||||
case ShaderType::Pixel: return "Pixel";
|
||||
case ShaderType::Compute: return "Compute";
|
||||
default: return "Unknown";
|
||||
}
|
||||
}
|
||||
|
||||
static vk::ShaderStageFlags NativeShaderStage(ShaderType stage) {
|
||||
switch (stage) {
|
||||
case ShaderType::Vertex: return vk::ShaderStageFlagBits::eVertex;
|
||||
case ShaderType::Pixel: return vk::ShaderStageFlagBits::eFragment;
|
||||
case ShaderType::Compute: return vk::ShaderStageFlagBits::eCompute;
|
||||
default: EXIT("unknown native shader stage\n");
|
||||
}
|
||||
return "Unknown";
|
||||
}
|
||||
|
||||
static void BindNullStorageBuffer(RenderContext& context, BufferView& dst) {
|
||||
@@ -87,11 +167,11 @@ static bool IsMultisampledTexture(Prospero::ImageType type) {
|
||||
type == Prospero::ImageType::kColor2DMsaaArray;
|
||||
}
|
||||
|
||||
static BufferView
|
||||
NativeStorageBuffer(RenderContext& context, const ShaderBufferResource& descriptor,
|
||||
const ShaderRecompiler::IR::BufferResource& resource,
|
||||
DescriptorCache::Stage stage, uint32_t slot, uint32_t& buffer_offset,
|
||||
BufferId id) {
|
||||
static BufferView NativeStorageBuffer(RenderContext& context,
|
||||
const ShaderBufferResource& descriptor,
|
||||
const ShaderRecompiler::IR::BufferResource& resource,
|
||||
ShaderType stage, uint32_t slot, uint32_t& buffer_offset,
|
||||
BufferId id) {
|
||||
BufferView result;
|
||||
buffer_offset = 0;
|
||||
|
||||
@@ -113,8 +193,8 @@ NativeStorageBuffer(RenderContext& context, const ShaderBufferResource& descript
|
||||
size > graphics.GetPhysicalDeviceProperties().limits.maxStorageBufferRange) {
|
||||
EXIT("storage buffer range or device alignment is unsupported\n");
|
||||
}
|
||||
auto [buffer, offset] = context.GetBufferCache().ObtainBuffer(
|
||||
address, size, resource.written, resource.formatted, id);
|
||||
auto [buffer, offset] = context.GetBufferCache().ObtainBuffer(address, size, resource.written,
|
||||
resource.formatted, id);
|
||||
const auto aligned_offset = offset - offset % alignment;
|
||||
const auto adjustment = offset - aligned_offset;
|
||||
const auto max_range = graphics.GetPhysicalDeviceProperties().limits.maxStorageBufferRange;
|
||||
@@ -136,10 +216,10 @@ NativeStorageBuffer(RenderContext& context, const ShaderBufferResource& descript
|
||||
return result;
|
||||
}
|
||||
|
||||
static BufferView NativeAddressBuffer(
|
||||
RenderContext& context, const ShaderRecompiler::IR::AddressResource& resource,
|
||||
const ShaderRecompiler::IR::ResourceSnapshot::Address& address,
|
||||
DescriptorCache::Stage stage, uint32_t slot, uint32_t& address_offset, BufferId id) {
|
||||
static BufferView
|
||||
NativeAddressBuffer(RenderContext& context, const ShaderRecompiler::IR::AddressResource& resource,
|
||||
const ShaderRecompiler::IR::ResourceSnapshot::Address& address,
|
||||
ShaderType stage, uint32_t slot, uint32_t& address_offset, BufferId id) {
|
||||
BufferView result;
|
||||
address_offset = 0;
|
||||
if (address.binding_base == 0) {
|
||||
@@ -640,9 +720,8 @@ static ImageViewInfo TextureViewInfo(const ShaderRecompiler::IR::ImageResource&
|
||||
return view;
|
||||
}
|
||||
|
||||
DescriptorCache::TextureBinding
|
||||
RenderExecutor::ResolveTexture(const ShaderRecompiler::IR::ImageResource& resource,
|
||||
const ShaderRecompiler::IR::DescriptorValue& value) {
|
||||
TextureBinding RenderExecutor::ResolveTexture(const ShaderRecompiler::IR::ImageResource& resource,
|
||||
const ShaderRecompiler::IR::DescriptorValue& value) {
|
||||
ShaderTextureResource descriptor;
|
||||
CopyNativeDescriptor(value, descriptor.fields);
|
||||
const bool storage = resource.written;
|
||||
@@ -813,7 +892,7 @@ static BufferView NativeUpload(RenderContext& context, std::span<const uint32_t>
|
||||
EXIT_IF(data.empty());
|
||||
auto& command_buffer = context.GetCommandScheduler().Current();
|
||||
EXIT_IF(command_buffer.IsInvalid() || command_buffer.IsExecute());
|
||||
auto& buffer = context.GetBufferCache().GetUtilityBuffer(MemoryUsage::Stream);
|
||||
auto& buffer = context.GetBufferCache().GetUtilityBuffer(MemoryUsage::Stream);
|
||||
const auto offset = buffer.Copy(data.data(), data.size_bytes(), 256);
|
||||
return {.buffer = buffer.Handle(), .offset = offset, .range = data.size_bytes()};
|
||||
}
|
||||
@@ -853,9 +932,7 @@ void RenderExecutor::ResetBindings() {
|
||||
m_bound_images.clear();
|
||||
}
|
||||
|
||||
DescriptorCache::PreparedBindings
|
||||
RenderExecutor::PrepareBindings(const ShaderStageRuntime& runtime,
|
||||
vk::ShaderStageFlags shader_stage, DescriptorCache::Stage stage) {
|
||||
PreparedBindings RenderExecutor::PrepareBindings(const ShaderStageRuntime& runtime) {
|
||||
KYTY_PROFILER_FUNCTION();
|
||||
EXIT_IF(!runtime);
|
||||
const auto& program = *runtime.program;
|
||||
@@ -865,12 +942,10 @@ RenderExecutor::PrepareBindings(const ShaderStageRuntime& runtime,
|
||||
EXIT("invalid native shader runtime snapshot: %s\n", error.c_str());
|
||||
}
|
||||
|
||||
DescriptorCache::PreparedBindings prepared;
|
||||
prepared.program = runtime.program;
|
||||
prepared.snapshot = runtime.resources;
|
||||
prepared.shader_stage = shader_stage;
|
||||
prepared.stage = stage;
|
||||
auto& descriptors = prepared.resources;
|
||||
PreparedBindings prepared;
|
||||
prepared.program = runtime.program.get();
|
||||
prepared.snapshot = runtime.resources.get();
|
||||
auto& descriptors = prepared.resources;
|
||||
descriptors.buffers.reserve(program.info.buffers.size());
|
||||
descriptors.images.reserve(program.info.images.size());
|
||||
for (uint32_t i = 0; i < program.info.images.size(); i++) {
|
||||
@@ -900,7 +975,7 @@ RenderExecutor::PrepareBindings(const ShaderStageRuntime& runtime,
|
||||
return prepared;
|
||||
}
|
||||
|
||||
void RenderExecutor::FindBuffers(DescriptorCache::PreparedBindings& prepared) {
|
||||
void RenderExecutor::FindBuffers(PreparedBindings& prepared) {
|
||||
KYTY_PROFILER_FUNCTION();
|
||||
EXIT_IF(prepared.program == nullptr || prepared.snapshot == nullptr);
|
||||
const auto& program = *prepared.program;
|
||||
@@ -916,8 +991,7 @@ void RenderExecutor::FindBuffers(DescriptorCache::PreparedBindings& prepared) {
|
||||
const auto stride = descriptor.Stride();
|
||||
const auto records = descriptor.NumRecords();
|
||||
EXIT_IF(stride != 0 && records > UINT64_MAX / stride);
|
||||
const auto requested_size =
|
||||
stride != 0 ? static_cast<uint64_t>(stride) * records : records;
|
||||
const auto requested_size = stride != 0 ? static_cast<uint64_t>(stride) * records : records;
|
||||
if (address == 0 || requested_size == 0) {
|
||||
prepared.buffer_ids.emplace_back();
|
||||
continue;
|
||||
@@ -936,19 +1010,18 @@ void RenderExecutor::FindBuffers(DescriptorCache::PreparedBindings& prepared) {
|
||||
continue;
|
||||
}
|
||||
EXIT_IF(resource.written);
|
||||
const auto limit =
|
||||
resource.kind == ShaderRecompiler::IR::ResourceKind::Flat
|
||||
? ShaderRecompiler::IR::FlatAddressWindowSize
|
||||
: static_cast<uint64_t>(m_context.GetGraphics()
|
||||
.GetPhysicalDeviceProperties()
|
||||
.limits.maxStorageBufferRange);
|
||||
uint64_t size = 0;
|
||||
const auto limit = resource.kind == ShaderRecompiler::IR::ResourceKind::Flat
|
||||
? ShaderRecompiler::IR::FlatAddressWindowSize
|
||||
: static_cast<uint64_t>(m_context.GetGraphics()
|
||||
.GetPhysicalDeviceProperties()
|
||||
.limits.maxStorageBufferRange);
|
||||
uint64_t size = 0;
|
||||
EXIT_IF(!HostMemoryQueryRange(address.binding_base, limit, HostMemoryAccess::Mapped, size));
|
||||
prepared.address_ids.push_back(cache.FindBuffer(address.binding_base, size));
|
||||
}
|
||||
}
|
||||
|
||||
void RenderExecutor::RebindBuffers(DescriptorCache::PreparedBindings& prepared) {
|
||||
void RenderExecutor::RebindBuffers(PreparedBindings& prepared) {
|
||||
KYTY_PROFILER_FUNCTION();
|
||||
EXIT_IF(prepared.program == nullptr || prepared.snapshot == nullptr);
|
||||
const auto& program = *prepared.program;
|
||||
@@ -972,7 +1045,7 @@ void RenderExecutor::RebindBuffers(DescriptorCache::PreparedBindings& prepared)
|
||||
CopyNativeDescriptor(snapshot.buffers[i], descriptor.fields);
|
||||
uint32_t buffer_offset = 0;
|
||||
resources.buffers.push_back(NativeStorageBuffer(m_context, descriptor,
|
||||
program.info.buffers[i], prepared.stage, i,
|
||||
program.info.buffers[i], program.stage, i,
|
||||
buffer_offset, prepared.buffer_ids[i]));
|
||||
pack_memory_offset(i, buffer_offset);
|
||||
}
|
||||
@@ -980,9 +1053,9 @@ void RenderExecutor::RebindBuffers(DescriptorCache::PreparedBindings& prepared)
|
||||
resources.addresses.reserve(program.info.addresses.size());
|
||||
for (uint32_t i = 0; i < program.info.addresses.size(); i++) {
|
||||
uint32_t address_offset = 0;
|
||||
resources.addresses.push_back(
|
||||
NativeAddressBuffer(m_context, program.info.addresses[i], snapshot.addresses[i],
|
||||
prepared.stage, i, address_offset, prepared.address_ids[i]));
|
||||
resources.addresses.push_back(NativeAddressBuffer(m_context, program.info.addresses[i],
|
||||
snapshot.addresses[i], program.stage, i,
|
||||
address_offset, prepared.address_ids[i]));
|
||||
pack_memory_offset(static_cast<uint32_t>(program.info.buffers.size()) + i, address_offset);
|
||||
}
|
||||
if (!prepared.flattened_srt.empty()) {
|
||||
@@ -994,7 +1067,7 @@ void RenderExecutor::RebindBuffers(DescriptorCache::PreparedBindings& prepared)
|
||||
}
|
||||
}
|
||||
|
||||
void RenderExecutor::RebindImages(DescriptorCache::PreparedBindings& prepared) {
|
||||
void RenderExecutor::RebindImages(PreparedBindings& prepared) {
|
||||
KYTY_PROFILER_FUNCTION();
|
||||
EXIT_IF(prepared.program == nullptr || prepared.snapshot == nullptr);
|
||||
const auto& program = *prepared.program;
|
||||
@@ -1047,12 +1120,10 @@ RenderExecutor::GraphicsBindings
|
||||
RenderExecutor::PrepareGraphicsBindings(const ShaderStageRuntime& vertex,
|
||||
const ShaderStageRuntime& pixel, bool pixel_active) {
|
||||
GraphicsBindings bindings {
|
||||
.vertex = PrepareBindings(vertex, vk::ShaderStageFlagBits::eVertex,
|
||||
DescriptorCache::Stage::Vertex),
|
||||
.vertex = PrepareBindings(vertex),
|
||||
};
|
||||
if (pixel_active) {
|
||||
bindings.pixel.emplace(PrepareBindings(pixel, vk::ShaderStageFlagBits::eFragment,
|
||||
DescriptorCache::Stage::Pixel));
|
||||
bindings.pixel.emplace(PrepareBindings(pixel));
|
||||
}
|
||||
FindBuffers(bindings.vertex);
|
||||
if (bindings.pixel) {
|
||||
@@ -1071,74 +1142,190 @@ RenderExecutor::PrepareGraphicsBindings(const ShaderStageRuntime& vertex,
|
||||
|
||||
void RenderExecutor::CommitBindings(CommandBuffer& buffer,
|
||||
vk::PipelineBindPoint pipeline_bind_point,
|
||||
vk::PipelineLayout layout,
|
||||
DescriptorCache::PreparedBindings& prepared) {
|
||||
const PipelineCache::Pipeline& pipeline,
|
||||
std::span<PreparedBindings* const> prepared_bindings) {
|
||||
KYTY_PROFILER_FUNCTION();
|
||||
EXIT_IF(prepared.program == nullptr || prepared.stage == DescriptorCache::Stage::Unknown ||
|
||||
prepared.committed);
|
||||
const auto& program = *prepared.program;
|
||||
auto& descriptors = prepared.resources;
|
||||
auto vk_buffer = buffer.Handle();
|
||||
const auto shader_stages = ShaderPipelineStages(prepared.shader_stage);
|
||||
if (descriptors.gds.buffer != nullptr) {
|
||||
buffer.EndRendering();
|
||||
const auto barrier = MakeGdsDependency(descriptors.gds.buffer);
|
||||
vk_buffer.pipelineBarrier(
|
||||
vk::PipelineStageFlagBits::eHost | vk::PipelineStageFlagBits::eTransfer |
|
||||
vk::PipelineStageFlagBits::eAllGraphics | vk::PipelineStageFlagBits::eComputeShader,
|
||||
shader_stages, vk::DependencyFlags {}, 0, nullptr, 1, &barrier, 0, nullptr);
|
||||
}
|
||||
|
||||
for (uint32_t i = 0; i < program.info.images.size(); i++) {
|
||||
auto& image = m_context.GetTextureCache().GetImage(descriptors.images[i].image_id);
|
||||
auto& binding = descriptors.images[i];
|
||||
const auto& view = binding.desc.view_info;
|
||||
const ImageSubresourceRange range {view.base_level, view.level_count, view.base_layer,
|
||||
view.layer_count};
|
||||
const bool storage = binding.desc.type == TextureCache::BindingType::Storage;
|
||||
if (image.info.data.Empty()) {
|
||||
image.Transit(vk::ImageLayout::eGeneral,
|
||||
storage
|
||||
? vk::AccessFlagBits2::eShaderRead | vk::AccessFlagBits2::eShaderWrite
|
||||
: vk::AccessFlagBits2::eShaderRead,
|
||||
range, vk_buffer);
|
||||
} else if ((image.binding.force_general || image.binding.is_target) &&
|
||||
!image.info.IsDepth()) {
|
||||
const vk::AccessFlags2 storage_access = image.binding.shader_write
|
||||
? vk::AccessFlagBits2::eShaderWrite
|
||||
: vk::AccessFlags2 {};
|
||||
image.Transit(vk::ImageLayout::eGeneral,
|
||||
vk::AccessFlagBits2::eShaderRead | storage_access |
|
||||
vk::AccessFlagBits2::eColorAttachmentRead |
|
||||
vk::AccessFlagBits2::eColorAttachmentWrite,
|
||||
{}, vk_buffer);
|
||||
} else if (storage) {
|
||||
image.Transit(vk::ImageLayout::eGeneral,
|
||||
vk::AccessFlagBits2::eShaderRead | vk::AccessFlagBits2::eShaderWrite,
|
||||
range, vk_buffer);
|
||||
} else {
|
||||
image.Transit(image.info.IsDepth() ? vk::ImageLayout::eDepthStencilReadOnlyOptimal
|
||||
: vk::ImageLayout::eShaderReadOnlyOptimal,
|
||||
vk::AccessFlagBits2::eShaderRead, range, vk_buffer);
|
||||
auto vk_buffer = buffer.Handle();
|
||||
size_t descriptor_count = 0;
|
||||
size_t write_count = 0;
|
||||
constexpr auto GraphicsStages =
|
||||
vk::ShaderStageFlagBits::eVertex | vk::ShaderStageFlagBits::eFragment;
|
||||
const auto push_constant_stages = pipeline_bind_point == vk::PipelineBindPoint::eGraphics
|
||||
? vk::ShaderStageFlags {GraphicsStages}
|
||||
: vk::ShaderStageFlags {vk::ShaderStageFlagBits::eCompute};
|
||||
for (const auto* prepared: prepared_bindings) {
|
||||
EXIT_IF(prepared == nullptr || prepared->program == nullptr ||
|
||||
prepared->snapshot == nullptr || prepared->committed);
|
||||
write_count += prepared->program->bindings.descriptors.size();
|
||||
for (const auto& binding: prepared->program->bindings.descriptors) {
|
||||
descriptor_count += NativeDescriptorCount(binding);
|
||||
}
|
||||
binding.layout = image.backing.state.layout;
|
||||
const auto shader_stage = NativeShaderStage(prepared->program->stage);
|
||||
EXIT_IF((pipeline_bind_point == vk::PipelineBindPoint::eGraphics &&
|
||||
(shader_stage & GraphicsStages) == vk::ShaderStageFlags {}) ||
|
||||
(pipeline_bind_point == vk::PipelineBindPoint::eCompute &&
|
||||
shader_stage != vk::ShaderStageFlagBits::eCompute));
|
||||
}
|
||||
m_descriptor_buffers.clear();
|
||||
m_descriptor_images.clear();
|
||||
m_descriptor_writes.clear();
|
||||
m_push_constants.fill(0);
|
||||
m_descriptor_buffers.reserve(descriptor_count);
|
||||
m_descriptor_images.reserve(descriptor_count);
|
||||
m_descriptor_writes.reserve(write_count);
|
||||
|
||||
if (!program.bindings.descriptors.empty()) {
|
||||
auto& set =
|
||||
m_context.GetDescriptorCache().GetDescriptor(prepared.stage, program, descriptors);
|
||||
vk_buffer.bindDescriptorSets(pipeline_bind_point, layout, program.bindings.descriptor_set,
|
||||
1, &set.set, 0, nullptr);
|
||||
buffer.RecycleDescriptorAfterFence(set);
|
||||
for (auto* prepared: prepared_bindings) {
|
||||
const auto& program = *prepared->program;
|
||||
auto& descriptors = prepared->resources;
|
||||
const auto shader_stage = NativeShaderStage(program.stage);
|
||||
const auto shader_stages = ShaderPipelineStages(shader_stage);
|
||||
if (descriptors.gds.buffer != nullptr) {
|
||||
buffer.EndRendering();
|
||||
const auto barrier = MakeGdsDependency(descriptors.gds.buffer);
|
||||
vk_buffer.pipelineBarrier(
|
||||
vk::PipelineStageFlagBits::eHost | vk::PipelineStageFlagBits::eTransfer |
|
||||
vk::PipelineStageFlagBits::eAllGraphics |
|
||||
vk::PipelineStageFlagBits::eComputeShader,
|
||||
shader_stages, vk::DependencyFlags {}, 0, nullptr, 1, &barrier, 0, nullptr);
|
||||
}
|
||||
|
||||
for (uint32_t i = 0; i < program.info.images.size(); i++) {
|
||||
auto& image = m_context.GetTextureCache().GetImage(descriptors.images[i].image_id);
|
||||
auto& binding = descriptors.images[i];
|
||||
const auto& view = binding.desc.view_info;
|
||||
const ImageSubresourceRange range {view.base_level, view.level_count, view.base_layer,
|
||||
view.layer_count};
|
||||
const bool storage = binding.desc.type == TextureCache::BindingType::Storage;
|
||||
if (image.info.data.Empty()) {
|
||||
image.Transit(vk::ImageLayout::eGeneral,
|
||||
storage ? vk::AccessFlagBits2::eShaderRead |
|
||||
vk::AccessFlagBits2::eShaderWrite
|
||||
: vk::AccessFlagBits2::eShaderRead,
|
||||
range, vk_buffer);
|
||||
} else if ((image.binding.force_general || image.binding.is_target) &&
|
||||
!image.info.IsDepth()) {
|
||||
const vk::AccessFlags2 storage_access = image.binding.shader_write
|
||||
? vk::AccessFlagBits2::eShaderWrite
|
||||
: vk::AccessFlags2 {};
|
||||
image.Transit(vk::ImageLayout::eGeneral,
|
||||
vk::AccessFlagBits2::eShaderRead | storage_access |
|
||||
vk::AccessFlagBits2::eColorAttachmentRead |
|
||||
vk::AccessFlagBits2::eColorAttachmentWrite,
|
||||
{}, vk_buffer);
|
||||
} else if (storage) {
|
||||
image.Transit(vk::ImageLayout::eGeneral,
|
||||
vk::AccessFlagBits2::eShaderRead | vk::AccessFlagBits2::eShaderWrite,
|
||||
range, vk_buffer);
|
||||
} else {
|
||||
image.Transit(image.info.IsDepth() ? vk::ImageLayout::eDepthStencilReadOnlyOptimal
|
||||
: vk::ImageLayout::eShaderReadOnlyOptimal,
|
||||
vk::AccessFlagBits2::eShaderRead, range, vk_buffer);
|
||||
}
|
||||
binding.layout = image.backing.state.layout;
|
||||
}
|
||||
|
||||
m_image_occurrences.assign(descriptors.images.size(), 0);
|
||||
for (const auto& binding: program.bindings.descriptors) {
|
||||
vk::WriteDescriptorSet write {};
|
||||
write.sType = vk::StructureType::eWriteDescriptorSet;
|
||||
write.dstBinding = ShaderRecompiler::IR::NativeBinding(program.stage, binding.kind);
|
||||
write.descriptorType = NativeDescriptorType(binding.kind);
|
||||
write.descriptorCount = NativeDescriptorCount(binding);
|
||||
const auto buffer_start = m_descriptor_buffers.size();
|
||||
const auto image_start = m_descriptor_images.size();
|
||||
switch (binding.kind) {
|
||||
case BindingKind::Buffers:
|
||||
for (const auto resource: binding.resources) {
|
||||
const auto& view = descriptors.buffers.at(resource);
|
||||
EXIT_IF(view.buffer == nullptr);
|
||||
m_descriptor_buffers.emplace_back(view.buffer, view.offset, view.range);
|
||||
}
|
||||
break;
|
||||
case BindingKind::AddressMemory:
|
||||
for (const auto resource: binding.resources) {
|
||||
const auto& view = descriptors.addresses.at(resource);
|
||||
EXIT_IF(view.buffer == nullptr);
|
||||
m_descriptor_buffers.emplace_back(view.buffer, view.offset, view.range);
|
||||
}
|
||||
break;
|
||||
case BindingKind::FlattenedSrt:
|
||||
case BindingKind::UserData:
|
||||
case BindingKind::Gds: {
|
||||
const auto& view =
|
||||
binding.kind == BindingKind::FlattenedSrt ? descriptors.flattened_srt
|
||||
: binding.kind == BindingKind::UserData ? descriptors.user_data
|
||||
: descriptors.gds;
|
||||
EXIT_IF(view.buffer == nullptr);
|
||||
m_descriptor_buffers.emplace_back(view.buffer, view.offset, view.range);
|
||||
break;
|
||||
}
|
||||
case BindingKind::Samplers:
|
||||
for (const auto resource: binding.resources) {
|
||||
const auto sampler = descriptors.samplers.at(resource);
|
||||
EXIT_IF(sampler == nullptr);
|
||||
m_descriptor_images.emplace_back(sampler, nullptr,
|
||||
vk::ImageLayout::eUndefined);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
for (const auto resource: binding.resources) {
|
||||
m_descriptor_images.push_back(MakeImageInfo(
|
||||
descriptors.images.at(resource), m_image_occurrences.at(resource)++));
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (m_descriptor_buffers.size() != buffer_start) {
|
||||
write.pBufferInfo = m_descriptor_buffers.data() + buffer_start;
|
||||
}
|
||||
if (m_descriptor_images.size() != image_start) {
|
||||
write.pImageInfo = m_descriptor_images.data() + image_start;
|
||||
}
|
||||
m_descriptor_writes.push_back(write);
|
||||
}
|
||||
for (uint32_t i = 0; i < descriptors.images.size(); i++) {
|
||||
const auto expected =
|
||||
descriptors.images[i].mip_views.empty()
|
||||
? 1u
|
||||
: static_cast<uint32_t>(descriptors.images[i].mip_views.size());
|
||||
EXIT_IF(m_image_occurrences[i] != expected);
|
||||
}
|
||||
|
||||
if (program.bindings.push_constant_size != 0) {
|
||||
const auto offset = program.bindings.push_constant_offset;
|
||||
EXIT_IF(offset % sizeof(uint32_t) != 0 ||
|
||||
program.bindings.push_constant_size !=
|
||||
prepared->user_data.size() * sizeof(uint32_t) ||
|
||||
offset + program.bindings.push_constant_size >
|
||||
ShaderRecompiler::IR::NativePushConstantSize);
|
||||
std::copy(prepared->user_data.begin(), prepared->user_data.end(),
|
||||
m_push_constants.begin() + offset / sizeof(uint32_t));
|
||||
}
|
||||
}
|
||||
if (program.bindings.push_constant_size != 0) {
|
||||
EXIT_IF(program.bindings.push_constant_size !=
|
||||
prepared.user_data.size() * sizeof(uint32_t));
|
||||
vk_buffer.pushConstants(layout, prepared.shader_stage,
|
||||
program.bindings.push_constant_offset,
|
||||
program.bindings.push_constant_size, prepared.user_data.data());
|
||||
vk_buffer.pushConstants(pipeline.pipeline_layout, push_constant_stages, 0,
|
||||
ShaderRecompiler::IR::NativePushConstantSize,
|
||||
m_push_constants.data());
|
||||
|
||||
if (!m_descriptor_writes.empty()) {
|
||||
EXIT_IF(pipeline.descriptor_set_layout == nullptr);
|
||||
if (pipeline.uses_push_descriptors) {
|
||||
vk_buffer.pushDescriptorSetKHR(pipeline_bind_point, pipeline.pipeline_layout, 0,
|
||||
static_cast<uint32_t>(m_descriptor_writes.size()),
|
||||
m_descriptor_writes.data());
|
||||
} else {
|
||||
const auto set = m_context.GetDescriptorHeap().Commit(pipeline.descriptor_set_layout);
|
||||
for (auto& write: m_descriptor_writes) {
|
||||
write.dstSet = set;
|
||||
}
|
||||
m_context.GetGraphics().device.updateDescriptorSets(
|
||||
static_cast<uint32_t>(m_descriptor_writes.size()), m_descriptor_writes.data(), 0,
|
||||
nullptr);
|
||||
vk_buffer.bindDescriptorSets(pipeline_bind_point, pipeline.pipeline_layout, 0, 1, &set,
|
||||
0, nullptr);
|
||||
}
|
||||
}
|
||||
for (auto* prepared: prepared_bindings) {
|
||||
prepared->committed = true;
|
||||
}
|
||||
prepared.committed = true;
|
||||
}
|
||||
|
||||
} // namespace Libs::Graphics
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
#define EMULATOR_SRC_GRAPHICS_HOST_GPU_RENDERER_DESCRIPTORS_H_
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "graphics/host_gpu/renderer/cache/bufferCache.h"
|
||||
#include "graphics/host_gpu/renderer/cache/textureCache.h"
|
||||
#include "graphics/host_gpu/renderer/image/image.h"
|
||||
#include "graphics/host_gpu/vulkanCommon.h"
|
||||
#include "graphics/shader/recompiler/ir/ShaderIR.h"
|
||||
@@ -10,9 +12,56 @@
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
#include <type_traits>
|
||||
#include <vector>
|
||||
|
||||
namespace Libs::Graphics {
|
||||
|
||||
namespace ShaderRecompiler::IR {
|
||||
struct ResourceSnapshot;
|
||||
}
|
||||
|
||||
struct BufferView {
|
||||
vk::Buffer buffer = nullptr;
|
||||
vk::DeviceSize offset = 0;
|
||||
vk::DeviceSize range = VK_WHOLE_SIZE;
|
||||
};
|
||||
|
||||
struct TextureBinding {
|
||||
ImageId image_id;
|
||||
vk::ImageView image_view = nullptr;
|
||||
TextureCache::ImageDesc desc;
|
||||
vk::ImageLayout layout = vk::ImageLayout::eUndefined;
|
||||
std::vector<vk::ImageView> mip_views;
|
||||
};
|
||||
|
||||
struct NativeDescriptors {
|
||||
std::vector<BufferView> buffers;
|
||||
std::vector<TextureBinding> images;
|
||||
std::vector<vk::Sampler> samplers;
|
||||
std::vector<BufferView> addresses;
|
||||
BufferView gds;
|
||||
BufferView flattened_srt;
|
||||
BufferView user_data;
|
||||
};
|
||||
|
||||
struct PreparedBindings {
|
||||
const ShaderRecompiler::IR::Program* program = nullptr;
|
||||
const ShaderRecompiler::IR::ResourceSnapshot* snapshot = nullptr;
|
||||
NativeDescriptors resources;
|
||||
std::vector<BufferId> buffer_ids;
|
||||
std::vector<BufferId> address_ids;
|
||||
std::vector<uint32_t> flattened_srt;
|
||||
std::vector<uint32_t> user_data;
|
||||
bool committed = false;
|
||||
};
|
||||
|
||||
[[nodiscard]] vk::DescriptorType
|
||||
NativeDescriptorType(ShaderRecompiler::IR::DescriptorBindingKind kind);
|
||||
[[nodiscard]] uint32_t
|
||||
NativeDescriptorCount(const ShaderRecompiler::IR::DescriptorBinding& binding);
|
||||
[[nodiscard]] vk::DescriptorImageInfo MakeImageInfo(const TextureBinding& texture,
|
||||
uint32_t element = 0);
|
||||
|
||||
template <typename T>
|
||||
[[nodiscard]] T DecodeNativeDescriptor(const ShaderRecompiler::IR::DescriptorValue& value) {
|
||||
static_assert(std::is_trivially_copyable_v<T>);
|
||||
@@ -34,11 +83,9 @@ ResolveTargetTextureView(const ShaderRecompiler::IR::ImageResource& resource,
|
||||
Prospero::ImageType type, uint32_t base_layer, uint32_t image_layers);
|
||||
|
||||
[[nodiscard]] bool IsSupportedDepthTargetDescriptor(const ShaderTextureResource& descriptor,
|
||||
const Image& image,
|
||||
bool r128 = false);
|
||||
const Image& image, bool r128 = false);
|
||||
[[nodiscard]] bool IsSupportedDepthTextureEncoding(const ShaderTextureResource& descriptor,
|
||||
const Image& image,
|
||||
bool r128 = false);
|
||||
const Image& image, bool r128 = false);
|
||||
[[nodiscard]] bool
|
||||
IsSupportedSampledVideoOutView(const ShaderRecompiler::IR::ImageResource& resource,
|
||||
const ShaderTextureResource& descriptor, const Image& image);
|
||||
|
||||
@@ -42,6 +42,7 @@ PipelineCache::~PipelineCache() {
|
||||
(void)key;
|
||||
m_graphics.device.destroyPipeline(pipeline->pipeline, nullptr);
|
||||
m_graphics.device.destroyPipelineLayout(pipeline->pipeline_layout, nullptr);
|
||||
m_graphics.device.destroyDescriptorSetLayout(pipeline->descriptor_set_layout, nullptr);
|
||||
}
|
||||
};
|
||||
destroy(m_graphics_pipelines);
|
||||
@@ -210,9 +211,9 @@ PipelineCache::GraphicsPipeline& PipelineCache::CreateGraphicsPipeline(
|
||||
auto cached = std::make_unique<GraphicsPipeline>(p);
|
||||
LogPipelineTrace("CreatePipelineInternal begin", vs_id.hash0, vs_id.crc32, ps_id.hash0,
|
||||
ps_id.crc32);
|
||||
CreatePipelineInternal(m_graphics, m_descriptor_cache, *cached, rendering, vs_input_info,
|
||||
vs_spirv, ps_input_info, ps_spirv, static_params, vs_id.hash0,
|
||||
vs_id.crc32, ps_id.hash0, ps_id.crc32, ps_active);
|
||||
CreatePipelineInternal(m_graphics, *cached, rendering, vs_input_info, vs_spirv, ps_input_info,
|
||||
ps_spirv, static_params, vs_id.hash0, vs_id.crc32, ps_id.hash0,
|
||||
ps_id.crc32, ps_active);
|
||||
LogPipelineTrace("CreatePipelineInternal done", vs_id.hash0, vs_id.crc32, ps_id.hash0,
|
||||
ps_id.crc32);
|
||||
|
||||
@@ -252,7 +253,7 @@ PipelineCache::CreateComputePipeline(ShaderComputeInputInfo& input_info,
|
||||
}
|
||||
|
||||
auto cached = std::make_unique<ComputePipeline>(p);
|
||||
CreatePipelineInternal(m_graphics, m_descriptor_cache, *cached, input_info, cs_spirv);
|
||||
CreatePipelineInternal(m_graphics, *cached, input_info, cs_spirv);
|
||||
|
||||
EXIT_NOT_IMPLEMENTED(cached->pipeline == nullptr);
|
||||
EXIT_NOT_IMPLEMENTED(cached->pipeline_layout == nullptr);
|
||||
|
||||
@@ -21,7 +21,6 @@ struct GraphicContext;
|
||||
struct RenderColorInfo;
|
||||
struct RenderDepthInfo;
|
||||
class RenderCommandBuffer;
|
||||
class DescriptorCache;
|
||||
|
||||
namespace HW {
|
||||
class Context;
|
||||
@@ -98,16 +97,17 @@ struct PipelineRenderingState {
|
||||
|
||||
class PipelineCache {
|
||||
public:
|
||||
PipelineCache(GraphicContext& graphics, DescriptorCache& descriptor_cache)
|
||||
: m_graphics(graphics), m_descriptor_cache(descriptor_cache) {
|
||||
explicit PipelineCache(GraphicContext& graphics): m_graphics(graphics) {
|
||||
EXIT_NOT_IMPLEMENTED(!Common::Thread::IsMainThread());
|
||||
}
|
||||
~PipelineCache();
|
||||
KYTY_CLASS_NO_COPY(PipelineCache);
|
||||
|
||||
struct Pipeline {
|
||||
vk::PipelineLayout pipeline_layout = nullptr;
|
||||
vk::Pipeline pipeline = nullptr;
|
||||
vk::PipelineLayout pipeline_layout = nullptr;
|
||||
vk::Pipeline pipeline = nullptr;
|
||||
vk::DescriptorSetLayout descriptor_set_layout = nullptr;
|
||||
bool uses_push_descriptors = false;
|
||||
};
|
||||
|
||||
struct GraphicsPipeline: Pipeline {
|
||||
@@ -201,8 +201,7 @@ private:
|
||||
}
|
||||
};
|
||||
|
||||
GraphicContext& m_graphics;
|
||||
DescriptorCache& m_descriptor_cache;
|
||||
GraphicContext& m_graphics;
|
||||
std::unordered_map<GraphicsPipelineKey, std::unique_ptr<GraphicsPipeline>,
|
||||
GraphicsPipelineKeyHash>
|
||||
m_graphics_pipelines;
|
||||
@@ -214,16 +213,14 @@ private:
|
||||
void LogPipelineTrace(const char* phase, uint32_t vs_hash0, uint32_t vs_crc32, uint32_t ps_hash0,
|
||||
uint32_t ps_crc32);
|
||||
void CreatePipelineInternal(
|
||||
GraphicContext& graphics, DescriptorCache& descriptor_cache,
|
||||
PipelineCache::GraphicsPipeline& pipeline, const PipelineRenderingState& rendering,
|
||||
const ShaderVertexInputInfo& vs_input_info, std::span<const uint32_t> vs_shader,
|
||||
const ShaderPixelInputInfo* ps_input_info, std::span<const uint32_t> ps_shader,
|
||||
const PipelineStaticParameters& static_params, uint32_t vs_hash0, uint32_t vs_crc32,
|
||||
uint32_t ps_hash0, uint32_t ps_crc32, bool ps_active);
|
||||
void CreatePipelineInternal(GraphicContext& graphics, DescriptorCache& descriptor_cache,
|
||||
PipelineCache::ComputePipeline& pipeline,
|
||||
const ShaderComputeInputInfo& input_info,
|
||||
std::span<const uint32_t> cs_shader);
|
||||
GraphicContext& graphics, PipelineCache::GraphicsPipeline& pipeline,
|
||||
const PipelineRenderingState& rendering, const ShaderVertexInputInfo& vs_input_info,
|
||||
std::span<const uint32_t> vs_shader, const ShaderPixelInputInfo* ps_input_info,
|
||||
std::span<const uint32_t> ps_shader, const PipelineStaticParameters& static_params,
|
||||
uint32_t vs_hash0, uint32_t vs_crc32, uint32_t ps_hash0, uint32_t ps_crc32, bool ps_active);
|
||||
void CreatePipelineInternal(GraphicContext& graphics, PipelineCache::ComputePipeline& pipeline,
|
||||
const ShaderComputeInputInfo& input_info,
|
||||
std::span<const uint32_t> cs_shader);
|
||||
|
||||
} // namespace Libs::Graphics
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
#include "graphics/guest_gpu/gpu_defs.h"
|
||||
#include "graphics/host_gpu/graphicContext.h"
|
||||
#include "graphics/host_gpu/renderer/debug.h"
|
||||
#include "graphics/host_gpu/renderer/pipeline/descriptorCache.h"
|
||||
#include "graphics/host_gpu/renderer/pipeline/descriptors.h"
|
||||
#include "graphics/host_gpu/renderer/pipeline/pipelineCache.h"
|
||||
#include "graphics/host_gpu/renderer/render.h"
|
||||
#include "graphics/host_gpu/renderer/renderContext.h"
|
||||
@@ -19,6 +19,7 @@
|
||||
#include <algorithm>
|
||||
#include <limits>
|
||||
#include <span>
|
||||
#include <vector>
|
||||
|
||||
namespace Libs::Graphics {
|
||||
|
||||
@@ -388,40 +389,42 @@ static vk::BlendOp GetBlendOp(uint32_t op) {
|
||||
return vk::BlendOp::eAdd;
|
||||
}
|
||||
|
||||
static void CreateLayout(DescriptorCache& descriptor_cache,
|
||||
std::span<vk::DescriptorSetLayout> set_layouts, uint32_t& set_layouts_num,
|
||||
std::span<vk::PushConstantRange> push_constant_info,
|
||||
uint32_t& push_constant_info_num,
|
||||
const ShaderRecompiler::IR::Program& program,
|
||||
vk::ShaderStageFlags vk_stage, DescriptorCache::Stage stage) {
|
||||
const auto& bindings = program.bindings;
|
||||
const bool need_descriptor = !bindings.descriptors.empty();
|
||||
|
||||
if (bindings.push_constant_size != 0) {
|
||||
auto index = push_constant_info_num;
|
||||
|
||||
push_constant_info[index].stageFlags = vk_stage;
|
||||
push_constant_info[index].offset = bindings.push_constant_offset;
|
||||
push_constant_info[index].size = bindings.push_constant_size;
|
||||
push_constant_info_num++;
|
||||
static void AddLayoutBindings(std::vector<vk::DescriptorSetLayoutBinding>& descriptor_bindings,
|
||||
const ShaderRecompiler::IR::Program& program,
|
||||
vk::ShaderStageFlagBits stage) {
|
||||
for (const auto& binding: program.bindings.descriptors) {
|
||||
descriptor_bindings.push_back(
|
||||
{ShaderRecompiler::IR::NativeBinding(program.stage, binding.kind),
|
||||
NativeDescriptorType(binding.kind), NativeDescriptorCount(binding), stage, nullptr});
|
||||
}
|
||||
}
|
||||
|
||||
if (need_descriptor) {
|
||||
EXIT_IF(bindings.descriptor_set != set_layouts_num);
|
||||
|
||||
set_layouts[set_layouts_num] = descriptor_cache.GetDescriptorSetLayout(stage, program);
|
||||
set_layouts_num++;
|
||||
static void CreateDescriptorLayout(GraphicContext& graphics, PipelineCache::Pipeline& pipeline,
|
||||
std::span<const vk::DescriptorSetLayoutBinding> bindings) {
|
||||
uint32_t descriptor_count = 0;
|
||||
for (const auto& binding: bindings) {
|
||||
descriptor_count += binding.descriptorCount;
|
||||
}
|
||||
pipeline.uses_push_descriptors = descriptor_count <= graphics.max_push_descriptors;
|
||||
|
||||
vk::DescriptorSetLayoutCreateInfo create {};
|
||||
create.sType = vk::StructureType::eDescriptorSetLayoutCreateInfo;
|
||||
create.flags = pipeline.uses_push_descriptors
|
||||
? vk::DescriptorSetLayoutCreateFlagBits::ePushDescriptorKHR
|
||||
: vk::DescriptorSetLayoutCreateFlags {};
|
||||
create.bindingCount = static_cast<uint32_t>(bindings.size());
|
||||
create.pBindings = bindings.data();
|
||||
EXIT_IF(graphics.device.createDescriptorSetLayout(
|
||||
&create, nullptr, &pipeline.descriptor_set_layout) != vk::Result::eSuccess);
|
||||
}
|
||||
|
||||
// NOLINTNEXTLINE(readability-function-cognitive-complexity)
|
||||
void CreatePipelineInternal(
|
||||
GraphicContext& graphics, DescriptorCache& descriptor_cache,
|
||||
PipelineCache::GraphicsPipeline& pipeline, const PipelineRenderingState& rendering,
|
||||
const ShaderVertexInputInfo& vs_input_info, std::span<const uint32_t> vs_shader,
|
||||
const ShaderPixelInputInfo* ps_input_info, std::span<const uint32_t> ps_shader,
|
||||
const PipelineStaticParameters& static_params, uint32_t vs_hash0, uint32_t vs_crc32,
|
||||
uint32_t ps_hash0, uint32_t ps_crc32, bool ps_active) {
|
||||
GraphicContext& graphics, PipelineCache::GraphicsPipeline& pipeline,
|
||||
const PipelineRenderingState& rendering, const ShaderVertexInputInfo& vs_input_info,
|
||||
std::span<const uint32_t> vs_shader, const ShaderPixelInputInfo* ps_input_info,
|
||||
std::span<const uint32_t> ps_shader, const PipelineStaticParameters& static_params,
|
||||
uint32_t vs_hash0, uint32_t vs_crc32, uint32_t ps_hash0, uint32_t ps_crc32, bool ps_active) {
|
||||
EXIT_IF(ps_active && ps_input_info == nullptr);
|
||||
|
||||
const bool rect_list = static_params.topology == vk::PrimitiveTopology::ePatchList;
|
||||
@@ -823,40 +826,36 @@ void CreatePipelineInternal(
|
||||
color_blending.blendConstants[2] = static_params.blend_color_blue;
|
||||
color_blending.blendConstants[3] = static_params.blend_color_alpha;
|
||||
|
||||
vk::DescriptorSetLayout set_layouts[2] = {};
|
||||
uint32_t set_layouts_num = 0;
|
||||
|
||||
vk::PushConstantRange push_constant_info[2];
|
||||
uint32_t push_constant_info_num = 0;
|
||||
|
||||
EXIT_IF(!vs_input_info.stage);
|
||||
CreateLayout(descriptor_cache, set_layouts, set_layouts_num, push_constant_info,
|
||||
push_constant_info_num, *vs_input_info.stage.program,
|
||||
vk::ShaderStageFlagBits::eVertex, DescriptorCache::Stage::Vertex);
|
||||
std::vector<vk::DescriptorSetLayoutBinding> descriptor_bindings;
|
||||
AddLayoutBindings(descriptor_bindings, *vs_input_info.stage.program,
|
||||
vk::ShaderStageFlagBits::eVertex);
|
||||
if (ps_active) {
|
||||
EXIT_IF(!ps_input_info->stage);
|
||||
CreateLayout(descriptor_cache, set_layouts, set_layouts_num, push_constant_info,
|
||||
push_constant_info_num, *ps_input_info->stage.program,
|
||||
vk::ShaderStageFlagBits::eFragment, DescriptorCache::Stage::Pixel);
|
||||
AddLayoutBindings(descriptor_bindings, *ps_input_info->stage.program,
|
||||
vk::ShaderStageFlagBits::eFragment);
|
||||
}
|
||||
CreateDescriptorLayout(graphics, pipeline, descriptor_bindings);
|
||||
constexpr auto GraphicsStages =
|
||||
vk::ShaderStageFlagBits::eVertex | vk::ShaderStageFlagBits::eFragment;
|
||||
const vk::PushConstantRange push_constants {
|
||||
GraphicsStages, 0, ShaderRecompiler::IR::NativePushConstantSize};
|
||||
|
||||
vk::PipelineLayoutCreateInfo pipeline_layout_info {};
|
||||
pipeline_layout_info.sType = vk::StructureType::ePipelineLayoutCreateInfo;
|
||||
pipeline_layout_info.pNext = nullptr;
|
||||
pipeline_layout_info.flags = {};
|
||||
pipeline_layout_info.setLayoutCount = set_layouts_num;
|
||||
pipeline_layout_info.pSetLayouts = (set_layouts_num > 0 ? set_layouts : nullptr);
|
||||
pipeline_layout_info.pushConstantRangeCount = push_constant_info_num;
|
||||
pipeline_layout_info.pPushConstantRanges =
|
||||
push_constant_info_num > 0 ? push_constant_info : nullptr;
|
||||
pipeline_layout_info.setLayoutCount = 1;
|
||||
pipeline_layout_info.pSetLayouts = &pipeline.descriptor_set_layout;
|
||||
pipeline_layout_info.pushConstantRangeCount = 1;
|
||||
pipeline_layout_info.pPushConstantRanges = &push_constants;
|
||||
|
||||
EXIT_IF(pipeline.pipeline_layout != nullptr);
|
||||
|
||||
if (graphics_debug_dump_enabled()) {
|
||||
LOGF("PipelineTrace: vkCreatePipelineLayout begin VS=0x%08" PRIx32 "/0x%08" PRIx32
|
||||
" PS=0x%08" PRIx32 "/0x%08" PRIx32 " set_layouts=%" PRIu32 " push_constants=%" PRIu32
|
||||
"\n",
|
||||
vs_hash0, vs_crc32, ps_hash0, ps_crc32, set_layouts_num, push_constant_info_num);
|
||||
" PS=0x%08" PRIx32 "/0x%08" PRIx32 " set_layouts=1 push_constants=%" PRIu32 "\n",
|
||||
vs_hash0, vs_crc32, ps_hash0, ps_crc32, 1u);
|
||||
}
|
||||
result = graphics.device.createPipelineLayout(&pipeline_layout_info, nullptr,
|
||||
&pipeline.pipeline_layout);
|
||||
@@ -988,10 +987,9 @@ void CreatePipelineInternal(
|
||||
}
|
||||
|
||||
// NOLINTNEXTLINE(readability-function-cognitive-complexity)
|
||||
void CreatePipelineInternal(GraphicContext& graphics, DescriptorCache& descriptor_cache,
|
||||
PipelineCache::ComputePipeline& pipeline,
|
||||
const ShaderComputeInputInfo& input_info,
|
||||
std::span<const uint32_t> cs_shader) {
|
||||
void CreatePipelineInternal(GraphicContext& graphics, PipelineCache::ComputePipeline& pipeline,
|
||||
const ShaderComputeInputInfo& input_info,
|
||||
std::span<const uint32_t> cs_shader) {
|
||||
vk::ShaderModule comp_shader_module = nullptr;
|
||||
|
||||
vk::ShaderModuleCreateInfo create_info {};
|
||||
@@ -1030,30 +1028,26 @@ void CreatePipelineInternal(GraphicContext& graphics, DescriptorCache& descripto
|
||||
comp_shader_stage_info.pNext = &comp_subgroup_size;
|
||||
}
|
||||
|
||||
vk::DescriptorSetLayout set_layouts[1] = {};
|
||||
uint32_t set_layouts_num = 0;
|
||||
|
||||
vk::PushConstantRange push_constant_info[1];
|
||||
uint32_t push_constant_info_num = 0;
|
||||
|
||||
CreateLayout(descriptor_cache, set_layouts, set_layouts_num, push_constant_info,
|
||||
push_constant_info_num, *input_info.stage.program,
|
||||
vk::ShaderStageFlagBits::eCompute, DescriptorCache::Stage::Compute);
|
||||
std::vector<vk::DescriptorSetLayoutBinding> descriptor_bindings;
|
||||
AddLayoutBindings(descriptor_bindings, *input_info.stage.program,
|
||||
vk::ShaderStageFlagBits::eCompute);
|
||||
CreateDescriptorLayout(graphics, pipeline, descriptor_bindings);
|
||||
const vk::PushConstantRange push_constants {vk::ShaderStageFlagBits::eCompute, 0,
|
||||
ShaderRecompiler::IR::NativePushConstantSize};
|
||||
|
||||
vk::PipelineLayoutCreateInfo pipeline_layout_info {};
|
||||
pipeline_layout_info.sType = vk::StructureType::ePipelineLayoutCreateInfo;
|
||||
pipeline_layout_info.pNext = nullptr;
|
||||
pipeline_layout_info.flags = {};
|
||||
pipeline_layout_info.setLayoutCount = set_layouts_num;
|
||||
pipeline_layout_info.pSetLayouts = (set_layouts_num > 0 ? set_layouts : nullptr);
|
||||
pipeline_layout_info.pushConstantRangeCount = push_constant_info_num;
|
||||
pipeline_layout_info.pPushConstantRanges =
|
||||
push_constant_info_num > 0 ? push_constant_info : nullptr;
|
||||
pipeline_layout_info.setLayoutCount = 1;
|
||||
pipeline_layout_info.pSetLayouts = &pipeline.descriptor_set_layout;
|
||||
pipeline_layout_info.pushConstantRangeCount = 1;
|
||||
pipeline_layout_info.pPushConstantRanges = &push_constants;
|
||||
|
||||
EXIT_IF(pipeline.pipeline_layout != nullptr);
|
||||
|
||||
LOGF("PipelineTrace: vkCreatePipelineLayout CS begin set_layouts=%u push_constants=%u\n",
|
||||
set_layouts_num, push_constant_info_num);
|
||||
LOGF("PipelineTrace: vkCreatePipelineLayout CS begin set_layouts=1 push_constants=%u\n",
|
||||
1u);
|
||||
result = graphics.device.createPipelineLayout(&pipeline_layout_info, nullptr,
|
||||
&pipeline.pipeline_layout);
|
||||
LOGF("PipelineTrace: vkCreatePipelineLayout CS done result=%s layout=%p\n",
|
||||
|
||||
@@ -4,11 +4,14 @@
|
||||
#include "common/abi.h"
|
||||
#include "common/assert.h"
|
||||
#include "common/common.h"
|
||||
#include "graphics/host_gpu/renderer/pipeline/descriptorCache.h"
|
||||
#include "graphics/host_gpu/renderer/pipeline/descriptors.h"
|
||||
#include "graphics/host_gpu/renderer/pipeline/pipelineCache.h"
|
||||
#include "graphics/host_gpu/renderer/renderTarget.h"
|
||||
#include "graphics/host_gpu/vulkanCommon.h"
|
||||
|
||||
#include <array>
|
||||
#include <optional>
|
||||
#include <span>
|
||||
#include <vector>
|
||||
|
||||
namespace Libs::Graphics {
|
||||
@@ -23,7 +26,6 @@ struct GraphicContext;
|
||||
struct ShaderBufferResource;
|
||||
struct ShaderComputeInputInfo;
|
||||
struct CommandSlot;
|
||||
struct VulkanDescriptorSet;
|
||||
struct RenderDepthInfo;
|
||||
struct RenderColorInfo;
|
||||
struct DrawCallInfo;
|
||||
@@ -92,7 +94,6 @@ public:
|
||||
void WaitForFenceOnly();
|
||||
void WaitForFence();
|
||||
void WaitForFenceAndReset();
|
||||
void RecycleDescriptorAfterFence(VulkanDescriptorSet& set);
|
||||
|
||||
[[nodiscard]] vk::CommandBuffer Handle() const;
|
||||
[[nodiscard]] GraphicContext& GetGraphics() const noexcept { return m_graphics; }
|
||||
@@ -102,25 +103,23 @@ public:
|
||||
private:
|
||||
void Release();
|
||||
void FinalizeFence(bool reset_recording);
|
||||
void RecycleDescriptorsAfterFence();
|
||||
|
||||
RenderContext& m_context;
|
||||
CommandScheduler& m_scheduler;
|
||||
GraphicContext& m_graphics;
|
||||
CommandSlot* m_slot = nullptr;
|
||||
bool m_execute = false;
|
||||
bool m_fence_waited = false;
|
||||
uint64_t m_submit_seq = 0;
|
||||
uint32_t m_debug_op = 0;
|
||||
uint64_t m_debug_submit_id = 0;
|
||||
uint32_t m_debug_arg0 = 0;
|
||||
uint32_t m_debug_arg1 = 0;
|
||||
uint32_t m_debug_arg2 = 0;
|
||||
uint32_t m_debug_arg3 = 0;
|
||||
uint64_t m_debug_arg4 = 0;
|
||||
std::vector<VulkanDescriptorSet*> m_descriptor_sets_after_fence;
|
||||
mutable RenderState m_render_state;
|
||||
mutable bool m_rendering = false;
|
||||
RenderContext& m_context;
|
||||
CommandScheduler& m_scheduler;
|
||||
GraphicContext& m_graphics;
|
||||
CommandSlot* m_slot = nullptr;
|
||||
bool m_execute = false;
|
||||
bool m_fence_waited = false;
|
||||
uint64_t m_submit_seq = 0;
|
||||
uint32_t m_debug_op = 0;
|
||||
uint64_t m_debug_submit_id = 0;
|
||||
uint32_t m_debug_arg0 = 0;
|
||||
uint32_t m_debug_arg1 = 0;
|
||||
uint32_t m_debug_arg2 = 0;
|
||||
uint32_t m_debug_arg3 = 0;
|
||||
uint64_t m_debug_arg4 = 0;
|
||||
mutable RenderState m_render_state;
|
||||
mutable bool m_rendering = false;
|
||||
};
|
||||
|
||||
class RenderCommandBuffer final: public CommandBuffer {
|
||||
@@ -159,24 +158,22 @@ public:
|
||||
void DispatchDirect(uint64_t submit_id, RenderCommandBuffer& buffer, uint32_t thread_group_x,
|
||||
uint32_t thread_group_y, uint32_t thread_group_z, uint32_t mode);
|
||||
|
||||
[[nodiscard]] DescriptorCache::PreparedBindings
|
||||
PrepareBindings(const ShaderStageRuntime& runtime, vk::ShaderStageFlags shader_stage,
|
||||
DescriptorCache::Stage stage);
|
||||
void FindBuffers(DescriptorCache::PreparedBindings& bindings);
|
||||
void RebindBuffers(DescriptorCache::PreparedBindings& bindings);
|
||||
void RebindImages(DescriptorCache::PreparedBindings& bindings);
|
||||
[[nodiscard]] PreparedBindings PrepareBindings(const ShaderStageRuntime& runtime);
|
||||
void FindBuffers(PreparedBindings& bindings);
|
||||
void RebindBuffers(PreparedBindings& bindings);
|
||||
void RebindImages(PreparedBindings& bindings);
|
||||
void CommitBindings(CommandBuffer& buffer, vk::PipelineBindPoint pipeline_bind_point,
|
||||
vk::PipelineLayout layout, DescriptorCache::PreparedBindings& bindings);
|
||||
const PipelineCache::Pipeline& pipeline,
|
||||
std::span<PreparedBindings* const> bindings);
|
||||
|
||||
private:
|
||||
struct GraphicsBindings {
|
||||
DescriptorCache::PreparedBindings vertex;
|
||||
std::optional<DescriptorCache::PreparedBindings> pixel;
|
||||
PreparedBindings vertex;
|
||||
std::optional<PreparedBindings> pixel;
|
||||
};
|
||||
|
||||
[[nodiscard]] DescriptorCache::TextureBinding
|
||||
ResolveTexture(const ShaderRecompiler::IR::ImageResource& resource,
|
||||
const ShaderRecompiler::IR::DescriptorValue& value);
|
||||
[[nodiscard]] TextureBinding ResolveTexture(const ShaderRecompiler::IR::ImageResource& resource,
|
||||
const ShaderRecompiler::IR::DescriptorValue& value);
|
||||
[[nodiscard]] GraphicsBindings PrepareGraphicsBindings(const ShaderStageRuntime& vertex,
|
||||
const ShaderStageRuntime& pixel,
|
||||
bool pixel_active);
|
||||
@@ -207,8 +204,14 @@ private:
|
||||
[[nodiscard]] bool TryConsumeComputeMetaClear(const ShaderComputeInputInfo& input,
|
||||
const RenderCommandBuffer& buffer);
|
||||
|
||||
RenderContext& m_context;
|
||||
std::vector<ImageId> m_bound_images;
|
||||
RenderContext& m_context;
|
||||
std::vector<ImageId> m_bound_images;
|
||||
std::vector<vk::DescriptorBufferInfo> m_descriptor_buffers;
|
||||
std::vector<vk::DescriptorImageInfo> m_descriptor_images;
|
||||
std::vector<vk::WriteDescriptorSet> m_descriptor_writes;
|
||||
std::vector<uint32_t> m_image_occurrences;
|
||||
std::array<uint32_t, ShaderRecompiler::IR::NativePushConstantSize / sizeof(uint32_t)>
|
||||
m_push_constants {};
|
||||
|
||||
friend struct RenderExecutorTestAccess;
|
||||
};
|
||||
|
||||
@@ -11,7 +11,6 @@
|
||||
#include "graphics/guest_gpu/hardwareContext.h"
|
||||
#include "graphics/host_gpu/graphicContext.h"
|
||||
#include "graphics/host_gpu/renderer/image/imageInfo.h"
|
||||
#include "graphics/host_gpu/renderer/pipeline/descriptorCache.h"
|
||||
#include "graphics/host_gpu/renderer/pipeline/descriptors.h"
|
||||
#include "graphics/host_gpu/renderer/pipeline/pipelineCache.h"
|
||||
#include "graphics/host_gpu/renderer/pipeline/shaderResourceBarrier.h"
|
||||
@@ -334,14 +333,15 @@ void RenderExecutor::DispatchDirect(uint64_t submit_id, RenderCommandBuffer& buf
|
||||
buffer.EndRendering();
|
||||
auto& pipeline =
|
||||
m_context.GetPipelineCache().CreateComputePipeline(input_info, sh_ctx.GetCs(), cs_shader);
|
||||
auto bindings = PrepareBindings(input_info.stage, vk::ShaderStageFlagBits::eCompute,
|
||||
DescriptorCache::Stage::Compute);
|
||||
auto bindings = PrepareBindings(input_info.stage);
|
||||
FindBuffers(bindings);
|
||||
RebindBuffers(bindings);
|
||||
RebindImages(bindings);
|
||||
|
||||
auto vk_buffer = buffer.Handle();
|
||||
CommitBindings(buffer, vk::PipelineBindPoint::eCompute, pipeline.pipeline_layout, bindings);
|
||||
auto vk_buffer = buffer.Handle();
|
||||
PreparedBindings* descriptor_stage = &bindings;
|
||||
CommitBindings(buffer, vk::PipelineBindPoint::eCompute, pipeline,
|
||||
std::span {&descriptor_stage, 1u});
|
||||
bool has_storage_writes = HasShaderBufferWrites(input_info.stage);
|
||||
has_storage_writes =
|
||||
std::any_of(program.info.images.begin(), program.info.images.end(),
|
||||
|
||||
@@ -13,8 +13,9 @@ namespace Libs::Graphics {
|
||||
|
||||
RenderContext::RenderContext(GraphicContext& graphics)
|
||||
: m_graphics(graphics), m_render_executor(*this), m_command_scheduler(*this, graphics),
|
||||
m_descriptor_cache(graphics), m_pipeline_cache(graphics, m_descriptor_cache),
|
||||
m_sampler_cache(graphics), m_gpu_resources(graphics, m_command_scheduler) {
|
||||
m_descriptor_heap(graphics, m_command_scheduler.GetMasterSemaphore()),
|
||||
m_pipeline_cache(graphics), m_sampler_cache(graphics),
|
||||
m_gpu_resources(graphics, m_command_scheduler) {
|
||||
EXIT_NOT_IMPLEMENTED(!Common::Thread::IsMainThread());
|
||||
}
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
#include "graphics/host_gpu/renderer/cache/samplerCache.h"
|
||||
#include "graphics/host_gpu/renderer/cache/textureCache.h"
|
||||
#include "graphics/host_gpu/renderer/commandScheduler.h"
|
||||
#include "graphics/host_gpu/renderer/pipeline/descriptorCache.h"
|
||||
#include "graphics/host_gpu/renderer/pipeline/descriptorHeap.h"
|
||||
#include "graphics/host_gpu/renderer/pipeline/pipelineCache.h"
|
||||
#include "kernel/eventQueue.h"
|
||||
|
||||
@@ -41,7 +41,7 @@ public:
|
||||
Common::Mutex& GetMutex() { return m_mutex; }
|
||||
CommandScheduler& GetCommandScheduler() { return m_command_scheduler; }
|
||||
PipelineCache& GetPipelineCache() { return m_pipeline_cache; }
|
||||
DescriptorCache& GetDescriptorCache() { return m_descriptor_cache; }
|
||||
DescriptorHeap& GetDescriptorHeap() { return m_descriptor_heap; }
|
||||
SamplerCache& GetSamplerCache() { return m_sampler_cache; }
|
||||
GpuResourceManager& GetGpuResources() { return m_gpu_resources; }
|
||||
BufferCache& GetBufferCache() { return m_gpu_resources.GetBufferCache(); }
|
||||
@@ -63,7 +63,7 @@ private:
|
||||
Common::Mutex m_mutex;
|
||||
RenderExecutor m_render_executor;
|
||||
CommandScheduler m_command_scheduler;
|
||||
DescriptorCache m_descriptor_cache;
|
||||
DescriptorHeap m_descriptor_heap;
|
||||
PipelineCache m_pipeline_cache;
|
||||
SamplerCache m_sampler_cache;
|
||||
GpuResourceManager m_gpu_resources;
|
||||
|
||||
@@ -15,7 +15,6 @@
|
||||
#include "graphics/host_gpu/renderer/colorRenderTarget.h"
|
||||
#include "graphics/host_gpu/renderer/debug.h"
|
||||
#include "graphics/host_gpu/renderer/depthRenderTarget.h"
|
||||
#include "graphics/host_gpu/renderer/pipeline/descriptorCache.h"
|
||||
#include "graphics/host_gpu/renderer/pipeline/pipelineCache.h"
|
||||
#include "graphics/host_gpu/renderer/pipeline/shaderResourceBarrier.h"
|
||||
#include "graphics/host_gpu/renderer/render.h"
|
||||
@@ -798,9 +797,9 @@ static uint64_t VertexBufferDescriptorSize(const ShaderVertexInputBuffer& buffer
|
||||
}
|
||||
|
||||
struct VertexBufferRange {
|
||||
uint64_t base_address = 0;
|
||||
uint64_t requested_end = 0;
|
||||
uint64_t acquired_end = 0;
|
||||
uint64_t base_address = 0;
|
||||
uint64_t requested_end = 0;
|
||||
uint64_t acquired_end = 0;
|
||||
std::pair<Buffer*, uint64_t> binding;
|
||||
|
||||
[[nodiscard]] uint64_t RequestedSize() const { return requested_end - base_address; }
|
||||
@@ -862,15 +861,14 @@ static PreparedVertexBuffers AcquireVertexBuffers(RenderCommandBuffer& b
|
||||
Libs::LibKernel::Memory::ClampRangeSize(range.base_address, range.RequestedSize());
|
||||
range.acquired_end = range.base_address + size;
|
||||
range.binding = cache.ObtainBuffer(range.base_address, size, false);
|
||||
SetVulkanObjectNameF(buffer.GetContext().GetGraphics().device,
|
||||
range.binding.first->Handle(),
|
||||
"Kyty.VertexBufferRange[guest=0x{:016x} size=0x{:x}]",
|
||||
range.base_address, size);
|
||||
SetVulkanObjectNameF(
|
||||
buffer.GetContext().GetGraphics().device, range.binding.first->Handle(),
|
||||
"Kyty.VertexBufferRange[guest=0x{:016x} size=0x{:x}]", range.base_address, size);
|
||||
}
|
||||
|
||||
// Rebuild slot bindings, offsetting non-empty slots into their acquired merged range.
|
||||
PreparedVertexBuffers prepared;
|
||||
prepared.count = static_cast<uint32_t>(vs_input_info.buffers_num);
|
||||
prepared.count = static_cast<uint32_t>(vs_input_info.buffers_num);
|
||||
vk::Buffer null_buffer = nullptr;
|
||||
for (int i = 0; i < vs_input_info.buffers_num; i++) {
|
||||
const auto& vertex = vs_input_info.buffers[i];
|
||||
@@ -1062,7 +1060,8 @@ static void RefreshShaders(RenderCommandBuffer& buffer, const DrawCallInfo& draw
|
||||
LogDrawPhase(draw.name, "ShaderCompileInfoPS");
|
||||
}
|
||||
if (!ShaderCompileInfoPS(pixel_shader_info, shader_regs, state.vs_input_info,
|
||||
target_export_mapping, state.ps_input_info, state.ps_shader)) {
|
||||
target_export_mapping,
|
||||
state.ps_input_info, state.ps_shader)) {
|
||||
EXIT("ShaderCompileInfoPS failed for draw %s\n", draw.name);
|
||||
}
|
||||
}
|
||||
@@ -1091,8 +1090,8 @@ static PreparedIndexBuffer PrepareIndexBuffer(RenderCommandBuffer& buffe
|
||||
prepared.offset = stream.Copy(source.host_data, source.size, 16);
|
||||
prepared.buffer = stream.Handle();
|
||||
} else {
|
||||
auto [buffer_ptr, offset] = buffer.GetContext().GetBufferCache().ObtainBuffer(
|
||||
source.address, source.size, false);
|
||||
auto [buffer_ptr, offset] =
|
||||
buffer.GetContext().GetBufferCache().ObtainBuffer(source.address, source.size, false);
|
||||
prepared.buffer = buffer_ptr->Handle();
|
||||
prepared.offset = offset;
|
||||
}
|
||||
@@ -1108,7 +1107,8 @@ static PreparedIndexBuffer PrepareIndexBuffer(RenderCommandBuffer& buffe
|
||||
return prepared;
|
||||
}
|
||||
|
||||
static void CommitVertexBuffers(vk::CommandBuffer vk_buffer, const PreparedVertexBuffers& prepared) {
|
||||
static void CommitVertexBuffers(vk::CommandBuffer vk_buffer,
|
||||
const PreparedVertexBuffers& prepared) {
|
||||
for (uint32_t i = 0; i < prepared.count; i++) {
|
||||
EXIT_IF(prepared.buffers[i] == nullptr);
|
||||
}
|
||||
@@ -1230,15 +1230,18 @@ void RenderExecutor::ExecutePreparedDraw(uint64_t submit_id, RenderCommandBuffer
|
||||
SetDrawDebugPhase(buffer, submit_id, draw, 0x200u);
|
||||
}
|
||||
CommitVertexBuffers(vk_buffer, vertex_bindings);
|
||||
CommitBindings(buffer, vk::PipelineBindPoint::eGraphics, pipeline.pipeline_layout,
|
||||
bindings.vertex);
|
||||
if (bindings.pixel.has_value()) {
|
||||
if (set_auto_debug) {
|
||||
SetDrawDebugPhase(buffer, submit_id, draw, 0x300u);
|
||||
}
|
||||
CommitBindings(buffer, vk::PipelineBindPoint::eGraphics, pipeline.pipeline_layout,
|
||||
*bindings.pixel);
|
||||
}
|
||||
std::array<PreparedBindings*, 2> descriptor_stages {&bindings.vertex, nullptr};
|
||||
const size_t descriptor_stage_count = bindings.pixel.has_value() ? 2u : 1u;
|
||||
if (bindings.pixel) {
|
||||
descriptor_stages[1] = &*bindings.pixel;
|
||||
}
|
||||
CommitBindings(buffer, vk::PipelineBindPoint::eGraphics, pipeline,
|
||||
std::span {descriptor_stages.data(), descriptor_stage_count});
|
||||
CommitIndexBuffer(vk_buffer, index_binding);
|
||||
|
||||
const auto dynamic_params =
|
||||
|
||||
Reference in New Issue
Block a user