mirror of
https://github.com/KytyPS5/KytyPS5.git
synced 2026-10-28 22:06:59 +01: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/graphicContext.h"
|
||||||
#include "graphics/host_gpu/renderer/debug.h"
|
#include "graphics/host_gpu/renderer/debug.h"
|
||||||
#include "graphics/host_gpu/renderer/image/textureCommon.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/render.h"
|
||||||
#include "graphics/host_gpu/renderer/renderContext.h"
|
#include "graphics/host_gpu/renderer/renderContext.h"
|
||||||
#include "graphics/host_gpu/vulkanCommon.h"
|
#include "graphics/host_gpu/vulkanCommon.h"
|
||||||
|
|||||||
@@ -60,6 +60,7 @@ public:
|
|||||||
RenderCommandBuffer& Current() const;
|
RenderCommandBuffer& Current() const;
|
||||||
[[nodiscard]] uint64_t CurrentTick() const noexcept { return m_master.CurrentTick(); }
|
[[nodiscard]] uint64_t CurrentTick() const noexcept { return m_master.CurrentTick(); }
|
||||||
[[nodiscard]] bool IsFree(uint64_t tick);
|
[[nodiscard]] bool IsFree(uint64_t tick);
|
||||||
|
[[nodiscard]] MasterSemaphore& GetMasterSemaphore() noexcept { return m_master; }
|
||||||
[[nodiscard]] RenderContext& Context() const noexcept { return m_context; }
|
[[nodiscard]] RenderContext& Context() const noexcept { return m_context; }
|
||||||
[[nodiscard]] GraphicContext& Graphics() const noexcept { return m_graphics; }
|
[[nodiscard]] GraphicContext& Graphics() const noexcept { return m_graphics; }
|
||||||
|
|
||||||
|
|||||||
@@ -9,7 +9,6 @@
|
|||||||
#include "graphics/host_gpu/renderer/debug.h"
|
#include "graphics/host_gpu/renderer/debug.h"
|
||||||
#include "graphics/host_gpu/renderer/depthRenderTarget.h"
|
#include "graphics/host_gpu/renderer/depthRenderTarget.h"
|
||||||
#include "graphics/host_gpu/renderer/image/imageView.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/render.h"
|
||||||
#include "graphics/host_gpu/renderer/renderContext.h"
|
#include "graphics/host_gpu/renderer/renderContext.h"
|
||||||
#include "graphics/host_gpu/vma.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",
|
" args=%u,%u,%u,%u,0x%016" PRIx64 "\n",
|
||||||
what, VulkanToString(result).c_str(), static_cast<int>(result), slot, submit_seq, debug_op,
|
what, VulkanToString(result).c_str(), static_cast<int>(result), slot, submit_seq, debug_op,
|
||||||
debug_submit, arg0, arg1, arg2, arg3, arg4);
|
debug_submit, arg0, arg1, arg2, arg3, arg4);
|
||||||
std::printf("%s failed: %s (%d), slot=%u submit_seq=%" PRIu64 " debug_op=%u debug_submit=%" PRIu64
|
std::printf("%s failed: %s (%d), slot=%u submit_seq=%" PRIu64
|
||||||
" args=%u,%u,%u,%u,0x%016" PRIx64 "\n",
|
" 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,
|
what, VulkanToString(result).c_str(), static_cast<int>(result), slot, submit_seq,
|
||||||
debug_op, debug_submit, arg0, arg1, arg2, arg3, arg4);
|
debug_op, debug_submit, arg0, arg1, arg2, arg3, arg4);
|
||||||
std::fflush(stdout);
|
std::fflush(stdout);
|
||||||
@@ -68,23 +67,11 @@ void CommandBuffer::Release() {
|
|||||||
|
|
||||||
m_slot->busy = false;
|
m_slot->busy = false;
|
||||||
m_slot->Reset();
|
m_slot->Reset();
|
||||||
RecycleDescriptorsAfterFence();
|
|
||||||
m_slot = nullptr;
|
m_slot = nullptr;
|
||||||
|
|
||||||
EXIT_NOT_IMPLEMENTED(!IsInvalid());
|
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 {
|
void CommandBuffer::Begin() const {
|
||||||
EXIT_IF(m_rendering);
|
EXIT_IF(m_rendering);
|
||||||
auto buffer = Handle();
|
auto buffer = Handle();
|
||||||
@@ -218,9 +205,6 @@ void CommandBuffer::FinalizeFence(bool reset_recording) {
|
|||||||
m_slot->Reset();
|
m_slot->Reset();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (was_executed) {
|
|
||||||
RecycleDescriptorsAfterFence();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void CommandBuffer::BeginRendering(const RenderState& state) const {
|
void CommandBuffer::BeginRendering(const RenderState& state) const {
|
||||||
|
|||||||
@@ -13,7 +13,6 @@
|
|||||||
#include "graphics/host_gpu/renderer/debug.h"
|
#include "graphics/host_gpu/renderer/debug.h"
|
||||||
#include "graphics/host_gpu/renderer/image/imageView.h"
|
#include "graphics/host_gpu/renderer/image/imageView.h"
|
||||||
#include "graphics/host_gpu/renderer/image/textureCommon.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/render.h"
|
||||||
#include "graphics/host_gpu/renderer/renderContext.h"
|
#include "graphics/host_gpu/renderer/renderContext.h"
|
||||||
#include "graphics/host_gpu/vulkanCommon.h"
|
#include "graphics/host_gpu/vulkanCommon.h"
|
||||||
|
|||||||
@@ -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/debug.h"
|
||||||
#include "graphics/host_gpu/renderer/image/imageView.h"
|
#include "graphics/host_gpu/renderer/image/imageView.h"
|
||||||
#include "graphics/host_gpu/renderer/image/textureCommon.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/pipeline/shaderResourceBarrier.h"
|
||||||
#include "graphics/host_gpu/renderer/render.h"
|
#include "graphics/host_gpu/renderer/render.h"
|
||||||
#include "graphics/host_gpu/renderer/renderContext.h"
|
#include "graphics/host_gpu/renderer/renderContext.h"
|
||||||
@@ -45,14 +44,95 @@
|
|||||||
|
|
||||||
namespace Libs::Graphics {
|
namespace Libs::Graphics {
|
||||||
|
|
||||||
static const char* ShaderStageResourceName(DescriptorCache::Stage stage) {
|
namespace {
|
||||||
switch (stage) {
|
|
||||||
case DescriptorCache::Stage::Vertex: return "Vertex";
|
using BindingKind = ShaderRecompiler::IR::DescriptorBindingKind;
|
||||||
case DescriptorCache::Stage::Pixel: return "Pixel";
|
|
||||||
case DescriptorCache::Stage::Compute: return "Compute";
|
bool IsSampledImage(BindingKind kind) {
|
||||||
case DescriptorCache::Stage::Unknown: return "Unknown";
|
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) {
|
static void BindNullStorageBuffer(RenderContext& context, BufferView& dst) {
|
||||||
@@ -87,10 +167,10 @@ static bool IsMultisampledTexture(Prospero::ImageType type) {
|
|||||||
type == Prospero::ImageType::kColor2DMsaaArray;
|
type == Prospero::ImageType::kColor2DMsaaArray;
|
||||||
}
|
}
|
||||||
|
|
||||||
static BufferView
|
static BufferView NativeStorageBuffer(RenderContext& context,
|
||||||
NativeStorageBuffer(RenderContext& context, const ShaderBufferResource& descriptor,
|
const ShaderBufferResource& descriptor,
|
||||||
const ShaderRecompiler::IR::BufferResource& resource,
|
const ShaderRecompiler::IR::BufferResource& resource,
|
||||||
DescriptorCache::Stage stage, uint32_t slot, uint32_t& buffer_offset,
|
ShaderType stage, uint32_t slot, uint32_t& buffer_offset,
|
||||||
BufferId id) {
|
BufferId id) {
|
||||||
BufferView result;
|
BufferView result;
|
||||||
buffer_offset = 0;
|
buffer_offset = 0;
|
||||||
@@ -113,8 +193,8 @@ NativeStorageBuffer(RenderContext& context, const ShaderBufferResource& descript
|
|||||||
size > graphics.GetPhysicalDeviceProperties().limits.maxStorageBufferRange) {
|
size > graphics.GetPhysicalDeviceProperties().limits.maxStorageBufferRange) {
|
||||||
EXIT("storage buffer range or device alignment is unsupported\n");
|
EXIT("storage buffer range or device alignment is unsupported\n");
|
||||||
}
|
}
|
||||||
auto [buffer, offset] = context.GetBufferCache().ObtainBuffer(
|
auto [buffer, offset] = context.GetBufferCache().ObtainBuffer(address, size, resource.written,
|
||||||
address, size, resource.written, resource.formatted, id);
|
resource.formatted, id);
|
||||||
const auto aligned_offset = offset - offset % alignment;
|
const auto aligned_offset = offset - offset % alignment;
|
||||||
const auto adjustment = offset - aligned_offset;
|
const auto adjustment = offset - aligned_offset;
|
||||||
const auto max_range = graphics.GetPhysicalDeviceProperties().limits.maxStorageBufferRange;
|
const auto max_range = graphics.GetPhysicalDeviceProperties().limits.maxStorageBufferRange;
|
||||||
@@ -136,10 +216,10 @@ NativeStorageBuffer(RenderContext& context, const ShaderBufferResource& descript
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
static BufferView NativeAddressBuffer(
|
static BufferView
|
||||||
RenderContext& context, const ShaderRecompiler::IR::AddressResource& resource,
|
NativeAddressBuffer(RenderContext& context, const ShaderRecompiler::IR::AddressResource& resource,
|
||||||
const ShaderRecompiler::IR::ResourceSnapshot::Address& address,
|
const ShaderRecompiler::IR::ResourceSnapshot::Address& address,
|
||||||
DescriptorCache::Stage stage, uint32_t slot, uint32_t& address_offset, BufferId id) {
|
ShaderType stage, uint32_t slot, uint32_t& address_offset, BufferId id) {
|
||||||
BufferView result;
|
BufferView result;
|
||||||
address_offset = 0;
|
address_offset = 0;
|
||||||
if (address.binding_base == 0) {
|
if (address.binding_base == 0) {
|
||||||
@@ -640,8 +720,7 @@ static ImageViewInfo TextureViewInfo(const ShaderRecompiler::IR::ImageResource&
|
|||||||
return view;
|
return view;
|
||||||
}
|
}
|
||||||
|
|
||||||
DescriptorCache::TextureBinding
|
TextureBinding RenderExecutor::ResolveTexture(const ShaderRecompiler::IR::ImageResource& resource,
|
||||||
RenderExecutor::ResolveTexture(const ShaderRecompiler::IR::ImageResource& resource,
|
|
||||||
const ShaderRecompiler::IR::DescriptorValue& value) {
|
const ShaderRecompiler::IR::DescriptorValue& value) {
|
||||||
ShaderTextureResource descriptor;
|
ShaderTextureResource descriptor;
|
||||||
CopyNativeDescriptor(value, descriptor.fields);
|
CopyNativeDescriptor(value, descriptor.fields);
|
||||||
@@ -853,9 +932,7 @@ void RenderExecutor::ResetBindings() {
|
|||||||
m_bound_images.clear();
|
m_bound_images.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
DescriptorCache::PreparedBindings
|
PreparedBindings RenderExecutor::PrepareBindings(const ShaderStageRuntime& runtime) {
|
||||||
RenderExecutor::PrepareBindings(const ShaderStageRuntime& runtime,
|
|
||||||
vk::ShaderStageFlags shader_stage, DescriptorCache::Stage stage) {
|
|
||||||
KYTY_PROFILER_FUNCTION();
|
KYTY_PROFILER_FUNCTION();
|
||||||
EXIT_IF(!runtime);
|
EXIT_IF(!runtime);
|
||||||
const auto& program = *runtime.program;
|
const auto& program = *runtime.program;
|
||||||
@@ -865,11 +942,9 @@ RenderExecutor::PrepareBindings(const ShaderStageRuntime& runtime,
|
|||||||
EXIT("invalid native shader runtime snapshot: %s\n", error.c_str());
|
EXIT("invalid native shader runtime snapshot: %s\n", error.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
DescriptorCache::PreparedBindings prepared;
|
PreparedBindings prepared;
|
||||||
prepared.program = runtime.program;
|
prepared.program = runtime.program.get();
|
||||||
prepared.snapshot = runtime.resources;
|
prepared.snapshot = runtime.resources.get();
|
||||||
prepared.shader_stage = shader_stage;
|
|
||||||
prepared.stage = stage;
|
|
||||||
auto& descriptors = prepared.resources;
|
auto& descriptors = prepared.resources;
|
||||||
descriptors.buffers.reserve(program.info.buffers.size());
|
descriptors.buffers.reserve(program.info.buffers.size());
|
||||||
descriptors.images.reserve(program.info.images.size());
|
descriptors.images.reserve(program.info.images.size());
|
||||||
@@ -900,7 +975,7 @@ RenderExecutor::PrepareBindings(const ShaderStageRuntime& runtime,
|
|||||||
return prepared;
|
return prepared;
|
||||||
}
|
}
|
||||||
|
|
||||||
void RenderExecutor::FindBuffers(DescriptorCache::PreparedBindings& prepared) {
|
void RenderExecutor::FindBuffers(PreparedBindings& prepared) {
|
||||||
KYTY_PROFILER_FUNCTION();
|
KYTY_PROFILER_FUNCTION();
|
||||||
EXIT_IF(prepared.program == nullptr || prepared.snapshot == nullptr);
|
EXIT_IF(prepared.program == nullptr || prepared.snapshot == nullptr);
|
||||||
const auto& program = *prepared.program;
|
const auto& program = *prepared.program;
|
||||||
@@ -916,8 +991,7 @@ void RenderExecutor::FindBuffers(DescriptorCache::PreparedBindings& prepared) {
|
|||||||
const auto stride = descriptor.Stride();
|
const auto stride = descriptor.Stride();
|
||||||
const auto records = descriptor.NumRecords();
|
const auto records = descriptor.NumRecords();
|
||||||
EXIT_IF(stride != 0 && records > UINT64_MAX / stride);
|
EXIT_IF(stride != 0 && records > UINT64_MAX / stride);
|
||||||
const auto requested_size =
|
const auto requested_size = stride != 0 ? static_cast<uint64_t>(stride) * records : records;
|
||||||
stride != 0 ? static_cast<uint64_t>(stride) * records : records;
|
|
||||||
if (address == 0 || requested_size == 0) {
|
if (address == 0 || requested_size == 0) {
|
||||||
prepared.buffer_ids.emplace_back();
|
prepared.buffer_ids.emplace_back();
|
||||||
continue;
|
continue;
|
||||||
@@ -936,8 +1010,7 @@ void RenderExecutor::FindBuffers(DescriptorCache::PreparedBindings& prepared) {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
EXIT_IF(resource.written);
|
EXIT_IF(resource.written);
|
||||||
const auto limit =
|
const auto limit = resource.kind == ShaderRecompiler::IR::ResourceKind::Flat
|
||||||
resource.kind == ShaderRecompiler::IR::ResourceKind::Flat
|
|
||||||
? ShaderRecompiler::IR::FlatAddressWindowSize
|
? ShaderRecompiler::IR::FlatAddressWindowSize
|
||||||
: static_cast<uint64_t>(m_context.GetGraphics()
|
: static_cast<uint64_t>(m_context.GetGraphics()
|
||||||
.GetPhysicalDeviceProperties()
|
.GetPhysicalDeviceProperties()
|
||||||
@@ -948,7 +1021,7 @@ void RenderExecutor::FindBuffers(DescriptorCache::PreparedBindings& prepared) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void RenderExecutor::RebindBuffers(DescriptorCache::PreparedBindings& prepared) {
|
void RenderExecutor::RebindBuffers(PreparedBindings& prepared) {
|
||||||
KYTY_PROFILER_FUNCTION();
|
KYTY_PROFILER_FUNCTION();
|
||||||
EXIT_IF(prepared.program == nullptr || prepared.snapshot == nullptr);
|
EXIT_IF(prepared.program == nullptr || prepared.snapshot == nullptr);
|
||||||
const auto& program = *prepared.program;
|
const auto& program = *prepared.program;
|
||||||
@@ -972,7 +1045,7 @@ void RenderExecutor::RebindBuffers(DescriptorCache::PreparedBindings& prepared)
|
|||||||
CopyNativeDescriptor(snapshot.buffers[i], descriptor.fields);
|
CopyNativeDescriptor(snapshot.buffers[i], descriptor.fields);
|
||||||
uint32_t buffer_offset = 0;
|
uint32_t buffer_offset = 0;
|
||||||
resources.buffers.push_back(NativeStorageBuffer(m_context, descriptor,
|
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]));
|
buffer_offset, prepared.buffer_ids[i]));
|
||||||
pack_memory_offset(i, buffer_offset);
|
pack_memory_offset(i, buffer_offset);
|
||||||
}
|
}
|
||||||
@@ -980,9 +1053,9 @@ void RenderExecutor::RebindBuffers(DescriptorCache::PreparedBindings& prepared)
|
|||||||
resources.addresses.reserve(program.info.addresses.size());
|
resources.addresses.reserve(program.info.addresses.size());
|
||||||
for (uint32_t i = 0; i < program.info.addresses.size(); i++) {
|
for (uint32_t i = 0; i < program.info.addresses.size(); i++) {
|
||||||
uint32_t address_offset = 0;
|
uint32_t address_offset = 0;
|
||||||
resources.addresses.push_back(
|
resources.addresses.push_back(NativeAddressBuffer(m_context, program.info.addresses[i],
|
||||||
NativeAddressBuffer(m_context, program.info.addresses[i], snapshot.addresses[i],
|
snapshot.addresses[i], program.stage, i,
|
||||||
prepared.stage, i, address_offset, prepared.address_ids[i]));
|
address_offset, prepared.address_ids[i]));
|
||||||
pack_memory_offset(static_cast<uint32_t>(program.info.buffers.size()) + i, address_offset);
|
pack_memory_offset(static_cast<uint32_t>(program.info.buffers.size()) + i, address_offset);
|
||||||
}
|
}
|
||||||
if (!prepared.flattened_srt.empty()) {
|
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();
|
KYTY_PROFILER_FUNCTION();
|
||||||
EXIT_IF(prepared.program == nullptr || prepared.snapshot == nullptr);
|
EXIT_IF(prepared.program == nullptr || prepared.snapshot == nullptr);
|
||||||
const auto& program = *prepared.program;
|
const auto& program = *prepared.program;
|
||||||
@@ -1047,12 +1120,10 @@ RenderExecutor::GraphicsBindings
|
|||||||
RenderExecutor::PrepareGraphicsBindings(const ShaderStageRuntime& vertex,
|
RenderExecutor::PrepareGraphicsBindings(const ShaderStageRuntime& vertex,
|
||||||
const ShaderStageRuntime& pixel, bool pixel_active) {
|
const ShaderStageRuntime& pixel, bool pixel_active) {
|
||||||
GraphicsBindings bindings {
|
GraphicsBindings bindings {
|
||||||
.vertex = PrepareBindings(vertex, vk::ShaderStageFlagBits::eVertex,
|
.vertex = PrepareBindings(vertex),
|
||||||
DescriptorCache::Stage::Vertex),
|
|
||||||
};
|
};
|
||||||
if (pixel_active) {
|
if (pixel_active) {
|
||||||
bindings.pixel.emplace(PrepareBindings(pixel, vk::ShaderStageFlagBits::eFragment,
|
bindings.pixel.emplace(PrepareBindings(pixel));
|
||||||
DescriptorCache::Stage::Pixel));
|
|
||||||
}
|
}
|
||||||
FindBuffers(bindings.vertex);
|
FindBuffers(bindings.vertex);
|
||||||
if (bindings.pixel) {
|
if (bindings.pixel) {
|
||||||
@@ -1071,21 +1142,50 @@ RenderExecutor::PrepareGraphicsBindings(const ShaderStageRuntime& vertex,
|
|||||||
|
|
||||||
void RenderExecutor::CommitBindings(CommandBuffer& buffer,
|
void RenderExecutor::CommitBindings(CommandBuffer& buffer,
|
||||||
vk::PipelineBindPoint pipeline_bind_point,
|
vk::PipelineBindPoint pipeline_bind_point,
|
||||||
vk::PipelineLayout layout,
|
const PipelineCache::Pipeline& pipeline,
|
||||||
DescriptorCache::PreparedBindings& prepared) {
|
std::span<PreparedBindings* const> prepared_bindings) {
|
||||||
KYTY_PROFILER_FUNCTION();
|
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();
|
auto vk_buffer = buffer.Handle();
|
||||||
const auto shader_stages = ShaderPipelineStages(prepared.shader_stage);
|
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);
|
||||||
|
}
|
||||||
|
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);
|
||||||
|
|
||||||
|
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) {
|
if (descriptors.gds.buffer != nullptr) {
|
||||||
buffer.EndRendering();
|
buffer.EndRendering();
|
||||||
const auto barrier = MakeGdsDependency(descriptors.gds.buffer);
|
const auto barrier = MakeGdsDependency(descriptors.gds.buffer);
|
||||||
vk_buffer.pipelineBarrier(
|
vk_buffer.pipelineBarrier(
|
||||||
vk::PipelineStageFlagBits::eHost | vk::PipelineStageFlagBits::eTransfer |
|
vk::PipelineStageFlagBits::eHost | vk::PipelineStageFlagBits::eTransfer |
|
||||||
vk::PipelineStageFlagBits::eAllGraphics | vk::PipelineStageFlagBits::eComputeShader,
|
vk::PipelineStageFlagBits::eAllGraphics |
|
||||||
|
vk::PipelineStageFlagBits::eComputeShader,
|
||||||
shader_stages, vk::DependencyFlags {}, 0, nullptr, 1, &barrier, 0, nullptr);
|
shader_stages, vk::DependencyFlags {}, 0, nullptr, 1, &barrier, 0, nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1098,8 +1198,8 @@ void RenderExecutor::CommitBindings(CommandBuffer& buffer,
|
|||||||
const bool storage = binding.desc.type == TextureCache::BindingType::Storage;
|
const bool storage = binding.desc.type == TextureCache::BindingType::Storage;
|
||||||
if (image.info.data.Empty()) {
|
if (image.info.data.Empty()) {
|
||||||
image.Transit(vk::ImageLayout::eGeneral,
|
image.Transit(vk::ImageLayout::eGeneral,
|
||||||
storage
|
storage ? vk::AccessFlagBits2::eShaderRead |
|
||||||
? vk::AccessFlagBits2::eShaderRead | vk::AccessFlagBits2::eShaderWrite
|
vk::AccessFlagBits2::eShaderWrite
|
||||||
: vk::AccessFlagBits2::eShaderRead,
|
: vk::AccessFlagBits2::eShaderRead,
|
||||||
range, vk_buffer);
|
range, vk_buffer);
|
||||||
} else if ((image.binding.force_general || image.binding.is_target) &&
|
} else if ((image.binding.force_general || image.binding.is_target) &&
|
||||||
@@ -1124,21 +1224,108 @@ void RenderExecutor::CommitBindings(CommandBuffer& buffer,
|
|||||||
binding.layout = image.backing.state.layout;
|
binding.layout = image.backing.state.layout;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!program.bindings.descriptors.empty()) {
|
m_image_occurrences.assign(descriptors.images.size(), 0);
|
||||||
auto& set =
|
for (const auto& binding: program.bindings.descriptors) {
|
||||||
m_context.GetDescriptorCache().GetDescriptor(prepared.stage, program, descriptors);
|
vk::WriteDescriptorSet write {};
|
||||||
vk_buffer.bindDescriptorSets(pipeline_bind_point, layout, program.bindings.descriptor_set,
|
write.sType = vk::StructureType::eWriteDescriptorSet;
|
||||||
1, &set.set, 0, nullptr);
|
write.dstBinding = ShaderRecompiler::IR::NativeBinding(program.stage, binding.kind);
|
||||||
buffer.RecycleDescriptorAfterFence(set);
|
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) {
|
if (program.bindings.push_constant_size != 0) {
|
||||||
EXIT_IF(program.bindings.push_constant_size !=
|
const auto offset = program.bindings.push_constant_offset;
|
||||||
prepared.user_data.size() * sizeof(uint32_t));
|
EXIT_IF(offset % sizeof(uint32_t) != 0 ||
|
||||||
vk_buffer.pushConstants(layout, prepared.shader_stage,
|
program.bindings.push_constant_size !=
|
||||||
program.bindings.push_constant_offset,
|
prepared->user_data.size() * sizeof(uint32_t) ||
|
||||||
program.bindings.push_constant_size, prepared.user_data.data());
|
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));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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
|
} // namespace Libs::Graphics
|
||||||
|
|||||||
@@ -2,6 +2,8 @@
|
|||||||
#define EMULATOR_SRC_GRAPHICS_HOST_GPU_RENDERER_DESCRIPTORS_H_
|
#define EMULATOR_SRC_GRAPHICS_HOST_GPU_RENDERER_DESCRIPTORS_H_
|
||||||
|
|
||||||
#include "common/assert.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/renderer/image/image.h"
|
||||||
#include "graphics/host_gpu/vulkanCommon.h"
|
#include "graphics/host_gpu/vulkanCommon.h"
|
||||||
#include "graphics/shader/recompiler/ir/ShaderIR.h"
|
#include "graphics/shader/recompiler/ir/ShaderIR.h"
|
||||||
@@ -10,9 +12,56 @@
|
|||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <type_traits>
|
#include <type_traits>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
namespace Libs::Graphics {
|
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>
|
template <typename T>
|
||||||
[[nodiscard]] T DecodeNativeDescriptor(const ShaderRecompiler::IR::DescriptorValue& value) {
|
[[nodiscard]] T DecodeNativeDescriptor(const ShaderRecompiler::IR::DescriptorValue& value) {
|
||||||
static_assert(std::is_trivially_copyable_v<T>);
|
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);
|
Prospero::ImageType type, uint32_t base_layer, uint32_t image_layers);
|
||||||
|
|
||||||
[[nodiscard]] bool IsSupportedDepthTargetDescriptor(const ShaderTextureResource& descriptor,
|
[[nodiscard]] bool IsSupportedDepthTargetDescriptor(const ShaderTextureResource& descriptor,
|
||||||
const Image& image,
|
const Image& image, bool r128 = false);
|
||||||
bool r128 = false);
|
|
||||||
[[nodiscard]] bool IsSupportedDepthTextureEncoding(const ShaderTextureResource& descriptor,
|
[[nodiscard]] bool IsSupportedDepthTextureEncoding(const ShaderTextureResource& descriptor,
|
||||||
const Image& image,
|
const Image& image, bool r128 = false);
|
||||||
bool r128 = false);
|
|
||||||
[[nodiscard]] bool
|
[[nodiscard]] bool
|
||||||
IsSupportedSampledVideoOutView(const ShaderRecompiler::IR::ImageResource& resource,
|
IsSupportedSampledVideoOutView(const ShaderRecompiler::IR::ImageResource& resource,
|
||||||
const ShaderTextureResource& descriptor, const Image& image);
|
const ShaderTextureResource& descriptor, const Image& image);
|
||||||
|
|||||||
@@ -42,6 +42,7 @@ PipelineCache::~PipelineCache() {
|
|||||||
(void)key;
|
(void)key;
|
||||||
m_graphics.device.destroyPipeline(pipeline->pipeline, nullptr);
|
m_graphics.device.destroyPipeline(pipeline->pipeline, nullptr);
|
||||||
m_graphics.device.destroyPipelineLayout(pipeline->pipeline_layout, nullptr);
|
m_graphics.device.destroyPipelineLayout(pipeline->pipeline_layout, nullptr);
|
||||||
|
m_graphics.device.destroyDescriptorSetLayout(pipeline->descriptor_set_layout, nullptr);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
destroy(m_graphics_pipelines);
|
destroy(m_graphics_pipelines);
|
||||||
@@ -210,9 +211,9 @@ PipelineCache::GraphicsPipeline& PipelineCache::CreateGraphicsPipeline(
|
|||||||
auto cached = std::make_unique<GraphicsPipeline>(p);
|
auto cached = std::make_unique<GraphicsPipeline>(p);
|
||||||
LogPipelineTrace("CreatePipelineInternal begin", vs_id.hash0, vs_id.crc32, ps_id.hash0,
|
LogPipelineTrace("CreatePipelineInternal begin", vs_id.hash0, vs_id.crc32, ps_id.hash0,
|
||||||
ps_id.crc32);
|
ps_id.crc32);
|
||||||
CreatePipelineInternal(m_graphics, m_descriptor_cache, *cached, rendering, vs_input_info,
|
CreatePipelineInternal(m_graphics, *cached, rendering, vs_input_info, vs_spirv, ps_input_info,
|
||||||
vs_spirv, ps_input_info, ps_spirv, static_params, vs_id.hash0,
|
ps_spirv, static_params, vs_id.hash0, vs_id.crc32, ps_id.hash0,
|
||||||
vs_id.crc32, ps_id.hash0, ps_id.crc32, ps_active);
|
ps_id.crc32, ps_active);
|
||||||
LogPipelineTrace("CreatePipelineInternal done", vs_id.hash0, vs_id.crc32, ps_id.hash0,
|
LogPipelineTrace("CreatePipelineInternal done", vs_id.hash0, vs_id.crc32, ps_id.hash0,
|
||||||
ps_id.crc32);
|
ps_id.crc32);
|
||||||
|
|
||||||
@@ -252,7 +253,7 @@ PipelineCache::CreateComputePipeline(ShaderComputeInputInfo& input_info,
|
|||||||
}
|
}
|
||||||
|
|
||||||
auto cached = std::make_unique<ComputePipeline>(p);
|
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 == nullptr);
|
||||||
EXIT_NOT_IMPLEMENTED(cached->pipeline_layout == nullptr);
|
EXIT_NOT_IMPLEMENTED(cached->pipeline_layout == nullptr);
|
||||||
|
|||||||
@@ -21,7 +21,6 @@ struct GraphicContext;
|
|||||||
struct RenderColorInfo;
|
struct RenderColorInfo;
|
||||||
struct RenderDepthInfo;
|
struct RenderDepthInfo;
|
||||||
class RenderCommandBuffer;
|
class RenderCommandBuffer;
|
||||||
class DescriptorCache;
|
|
||||||
|
|
||||||
namespace HW {
|
namespace HW {
|
||||||
class Context;
|
class Context;
|
||||||
@@ -98,8 +97,7 @@ struct PipelineRenderingState {
|
|||||||
|
|
||||||
class PipelineCache {
|
class PipelineCache {
|
||||||
public:
|
public:
|
||||||
PipelineCache(GraphicContext& graphics, DescriptorCache& descriptor_cache)
|
explicit PipelineCache(GraphicContext& graphics): m_graphics(graphics) {
|
||||||
: m_graphics(graphics), m_descriptor_cache(descriptor_cache) {
|
|
||||||
EXIT_NOT_IMPLEMENTED(!Common::Thread::IsMainThread());
|
EXIT_NOT_IMPLEMENTED(!Common::Thread::IsMainThread());
|
||||||
}
|
}
|
||||||
~PipelineCache();
|
~PipelineCache();
|
||||||
@@ -108,6 +106,8 @@ public:
|
|||||||
struct Pipeline {
|
struct Pipeline {
|
||||||
vk::PipelineLayout pipeline_layout = nullptr;
|
vk::PipelineLayout pipeline_layout = nullptr;
|
||||||
vk::Pipeline pipeline = nullptr;
|
vk::Pipeline pipeline = nullptr;
|
||||||
|
vk::DescriptorSetLayout descriptor_set_layout = nullptr;
|
||||||
|
bool uses_push_descriptors = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct GraphicsPipeline: Pipeline {
|
struct GraphicsPipeline: Pipeline {
|
||||||
@@ -202,7 +202,6 @@ private:
|
|||||||
};
|
};
|
||||||
|
|
||||||
GraphicContext& m_graphics;
|
GraphicContext& m_graphics;
|
||||||
DescriptorCache& m_descriptor_cache;
|
|
||||||
std::unordered_map<GraphicsPipelineKey, std::unique_ptr<GraphicsPipeline>,
|
std::unordered_map<GraphicsPipelineKey, std::unique_ptr<GraphicsPipeline>,
|
||||||
GraphicsPipelineKeyHash>
|
GraphicsPipelineKeyHash>
|
||||||
m_graphics_pipelines;
|
m_graphics_pipelines;
|
||||||
@@ -214,14 +213,12 @@ private:
|
|||||||
void LogPipelineTrace(const char* phase, uint32_t vs_hash0, uint32_t vs_crc32, uint32_t ps_hash0,
|
void LogPipelineTrace(const char* phase, uint32_t vs_hash0, uint32_t vs_crc32, uint32_t ps_hash0,
|
||||||
uint32_t ps_crc32);
|
uint32_t ps_crc32);
|
||||||
void CreatePipelineInternal(
|
void CreatePipelineInternal(
|
||||||
GraphicContext& graphics, DescriptorCache& descriptor_cache,
|
GraphicContext& graphics, PipelineCache::GraphicsPipeline& pipeline,
|
||||||
PipelineCache::GraphicsPipeline& pipeline, const PipelineRenderingState& rendering,
|
const PipelineRenderingState& rendering, const ShaderVertexInputInfo& vs_input_info,
|
||||||
const ShaderVertexInputInfo& vs_input_info, std::span<const uint32_t> vs_shader,
|
std::span<const uint32_t> vs_shader, const ShaderPixelInputInfo* ps_input_info,
|
||||||
const ShaderPixelInputInfo* ps_input_info, std::span<const uint32_t> ps_shader,
|
std::span<const uint32_t> ps_shader, const PipelineStaticParameters& static_params,
|
||||||
const PipelineStaticParameters& static_params, uint32_t vs_hash0, uint32_t vs_crc32,
|
uint32_t vs_hash0, uint32_t vs_crc32, uint32_t ps_hash0, uint32_t ps_crc32, bool ps_active);
|
||||||
uint32_t ps_hash0, uint32_t ps_crc32, bool ps_active);
|
void CreatePipelineInternal(GraphicContext& graphics, PipelineCache::ComputePipeline& pipeline,
|
||||||
void CreatePipelineInternal(GraphicContext& graphics, DescriptorCache& descriptor_cache,
|
|
||||||
PipelineCache::ComputePipeline& pipeline,
|
|
||||||
const ShaderComputeInputInfo& input_info,
|
const ShaderComputeInputInfo& input_info,
|
||||||
std::span<const uint32_t> cs_shader);
|
std::span<const uint32_t> cs_shader);
|
||||||
|
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
#include "graphics/guest_gpu/gpu_defs.h"
|
#include "graphics/guest_gpu/gpu_defs.h"
|
||||||
#include "graphics/host_gpu/graphicContext.h"
|
#include "graphics/host_gpu/graphicContext.h"
|
||||||
#include "graphics/host_gpu/renderer/debug.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/pipeline/pipelineCache.h"
|
||||||
#include "graphics/host_gpu/renderer/render.h"
|
#include "graphics/host_gpu/renderer/render.h"
|
||||||
#include "graphics/host_gpu/renderer/renderContext.h"
|
#include "graphics/host_gpu/renderer/renderContext.h"
|
||||||
@@ -19,6 +19,7 @@
|
|||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <limits>
|
#include <limits>
|
||||||
#include <span>
|
#include <span>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
namespace Libs::Graphics {
|
namespace Libs::Graphics {
|
||||||
|
|
||||||
@@ -388,40 +389,42 @@ static vk::BlendOp GetBlendOp(uint32_t op) {
|
|||||||
return vk::BlendOp::eAdd;
|
return vk::BlendOp::eAdd;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void CreateLayout(DescriptorCache& descriptor_cache,
|
static void AddLayoutBindings(std::vector<vk::DescriptorSetLayoutBinding>& descriptor_bindings,
|
||||||
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,
|
const ShaderRecompiler::IR::Program& program,
|
||||||
vk::ShaderStageFlags vk_stage, DescriptorCache::Stage stage) {
|
vk::ShaderStageFlagBits stage) {
|
||||||
const auto& bindings = program.bindings;
|
for (const auto& binding: program.bindings.descriptors) {
|
||||||
const bool need_descriptor = !bindings.descriptors.empty();
|
descriptor_bindings.push_back(
|
||||||
|
{ShaderRecompiler::IR::NativeBinding(program.stage, binding.kind),
|
||||||
if (bindings.push_constant_size != 0) {
|
NativeDescriptorType(binding.kind), NativeDescriptorCount(binding), stage, nullptr});
|
||||||
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++;
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (need_descriptor) {
|
static void CreateDescriptorLayout(GraphicContext& graphics, PipelineCache::Pipeline& pipeline,
|
||||||
EXIT_IF(bindings.descriptor_set != set_layouts_num);
|
std::span<const vk::DescriptorSetLayoutBinding> bindings) {
|
||||||
|
uint32_t descriptor_count = 0;
|
||||||
set_layouts[set_layouts_num] = descriptor_cache.GetDescriptorSetLayout(stage, program);
|
for (const auto& binding: bindings) {
|
||||||
set_layouts_num++;
|
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)
|
// NOLINTNEXTLINE(readability-function-cognitive-complexity)
|
||||||
void CreatePipelineInternal(
|
void CreatePipelineInternal(
|
||||||
GraphicContext& graphics, DescriptorCache& descriptor_cache,
|
GraphicContext& graphics, PipelineCache::GraphicsPipeline& pipeline,
|
||||||
PipelineCache::GraphicsPipeline& pipeline, const PipelineRenderingState& rendering,
|
const PipelineRenderingState& rendering, const ShaderVertexInputInfo& vs_input_info,
|
||||||
const ShaderVertexInputInfo& vs_input_info, std::span<const uint32_t> vs_shader,
|
std::span<const uint32_t> vs_shader, const ShaderPixelInputInfo* ps_input_info,
|
||||||
const ShaderPixelInputInfo* ps_input_info, std::span<const uint32_t> ps_shader,
|
std::span<const uint32_t> ps_shader, const PipelineStaticParameters& static_params,
|
||||||
const PipelineStaticParameters& static_params, uint32_t vs_hash0, uint32_t vs_crc32,
|
uint32_t vs_hash0, uint32_t vs_crc32, uint32_t ps_hash0, uint32_t ps_crc32, bool ps_active) {
|
||||||
uint32_t ps_hash0, uint32_t ps_crc32, bool ps_active) {
|
|
||||||
EXIT_IF(ps_active && ps_input_info == nullptr);
|
EXIT_IF(ps_active && ps_input_info == nullptr);
|
||||||
|
|
||||||
const bool rect_list = static_params.topology == vk::PrimitiveTopology::ePatchList;
|
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[2] = static_params.blend_color_blue;
|
||||||
color_blending.blendConstants[3] = static_params.blend_color_alpha;
|
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);
|
EXIT_IF(!vs_input_info.stage);
|
||||||
CreateLayout(descriptor_cache, set_layouts, set_layouts_num, push_constant_info,
|
std::vector<vk::DescriptorSetLayoutBinding> descriptor_bindings;
|
||||||
push_constant_info_num, *vs_input_info.stage.program,
|
AddLayoutBindings(descriptor_bindings, *vs_input_info.stage.program,
|
||||||
vk::ShaderStageFlagBits::eVertex, DescriptorCache::Stage::Vertex);
|
vk::ShaderStageFlagBits::eVertex);
|
||||||
if (ps_active) {
|
if (ps_active) {
|
||||||
EXIT_IF(!ps_input_info->stage);
|
EXIT_IF(!ps_input_info->stage);
|
||||||
CreateLayout(descriptor_cache, set_layouts, set_layouts_num, push_constant_info,
|
AddLayoutBindings(descriptor_bindings, *ps_input_info->stage.program,
|
||||||
push_constant_info_num, *ps_input_info->stage.program,
|
vk::ShaderStageFlagBits::eFragment);
|
||||||
vk::ShaderStageFlagBits::eFragment, DescriptorCache::Stage::Pixel);
|
|
||||||
}
|
}
|
||||||
|
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 {};
|
vk::PipelineLayoutCreateInfo pipeline_layout_info {};
|
||||||
pipeline_layout_info.sType = vk::StructureType::ePipelineLayoutCreateInfo;
|
pipeline_layout_info.sType = vk::StructureType::ePipelineLayoutCreateInfo;
|
||||||
pipeline_layout_info.pNext = nullptr;
|
pipeline_layout_info.pNext = nullptr;
|
||||||
pipeline_layout_info.flags = {};
|
pipeline_layout_info.flags = {};
|
||||||
pipeline_layout_info.setLayoutCount = set_layouts_num;
|
pipeline_layout_info.setLayoutCount = 1;
|
||||||
pipeline_layout_info.pSetLayouts = (set_layouts_num > 0 ? set_layouts : nullptr);
|
pipeline_layout_info.pSetLayouts = &pipeline.descriptor_set_layout;
|
||||||
pipeline_layout_info.pushConstantRangeCount = push_constant_info_num;
|
pipeline_layout_info.pushConstantRangeCount = 1;
|
||||||
pipeline_layout_info.pPushConstantRanges =
|
pipeline_layout_info.pPushConstantRanges = &push_constants;
|
||||||
push_constant_info_num > 0 ? push_constant_info : nullptr;
|
|
||||||
|
|
||||||
EXIT_IF(pipeline.pipeline_layout != nullptr);
|
EXIT_IF(pipeline.pipeline_layout != nullptr);
|
||||||
|
|
||||||
if (graphics_debug_dump_enabled()) {
|
if (graphics_debug_dump_enabled()) {
|
||||||
LOGF("PipelineTrace: vkCreatePipelineLayout begin VS=0x%08" PRIx32 "/0x%08" PRIx32
|
LOGF("PipelineTrace: vkCreatePipelineLayout begin VS=0x%08" PRIx32 "/0x%08" PRIx32
|
||||||
" PS=0x%08" PRIx32 "/0x%08" PRIx32 " set_layouts=%" PRIu32 " push_constants=%" PRIu32
|
" PS=0x%08" PRIx32 "/0x%08" PRIx32 " set_layouts=1 push_constants=%" PRIu32 "\n",
|
||||||
"\n",
|
vs_hash0, vs_crc32, ps_hash0, ps_crc32, 1u);
|
||||||
vs_hash0, vs_crc32, ps_hash0, ps_crc32, set_layouts_num, push_constant_info_num);
|
|
||||||
}
|
}
|
||||||
result = graphics.device.createPipelineLayout(&pipeline_layout_info, nullptr,
|
result = graphics.device.createPipelineLayout(&pipeline_layout_info, nullptr,
|
||||||
&pipeline.pipeline_layout);
|
&pipeline.pipeline_layout);
|
||||||
@@ -988,8 +987,7 @@ void CreatePipelineInternal(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// NOLINTNEXTLINE(readability-function-cognitive-complexity)
|
// NOLINTNEXTLINE(readability-function-cognitive-complexity)
|
||||||
void CreatePipelineInternal(GraphicContext& graphics, DescriptorCache& descriptor_cache,
|
void CreatePipelineInternal(GraphicContext& graphics, PipelineCache::ComputePipeline& pipeline,
|
||||||
PipelineCache::ComputePipeline& pipeline,
|
|
||||||
const ShaderComputeInputInfo& input_info,
|
const ShaderComputeInputInfo& input_info,
|
||||||
std::span<const uint32_t> cs_shader) {
|
std::span<const uint32_t> cs_shader) {
|
||||||
vk::ShaderModule comp_shader_module = nullptr;
|
vk::ShaderModule comp_shader_module = nullptr;
|
||||||
@@ -1030,30 +1028,26 @@ void CreatePipelineInternal(GraphicContext& graphics, DescriptorCache& descripto
|
|||||||
comp_shader_stage_info.pNext = &comp_subgroup_size;
|
comp_shader_stage_info.pNext = &comp_subgroup_size;
|
||||||
}
|
}
|
||||||
|
|
||||||
vk::DescriptorSetLayout set_layouts[1] = {};
|
std::vector<vk::DescriptorSetLayoutBinding> descriptor_bindings;
|
||||||
uint32_t set_layouts_num = 0;
|
AddLayoutBindings(descriptor_bindings, *input_info.stage.program,
|
||||||
|
vk::ShaderStageFlagBits::eCompute);
|
||||||
vk::PushConstantRange push_constant_info[1];
|
CreateDescriptorLayout(graphics, pipeline, descriptor_bindings);
|
||||||
uint32_t push_constant_info_num = 0;
|
const vk::PushConstantRange push_constants {vk::ShaderStageFlagBits::eCompute, 0,
|
||||||
|
ShaderRecompiler::IR::NativePushConstantSize};
|
||||||
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);
|
|
||||||
|
|
||||||
vk::PipelineLayoutCreateInfo pipeline_layout_info {};
|
vk::PipelineLayoutCreateInfo pipeline_layout_info {};
|
||||||
pipeline_layout_info.sType = vk::StructureType::ePipelineLayoutCreateInfo;
|
pipeline_layout_info.sType = vk::StructureType::ePipelineLayoutCreateInfo;
|
||||||
pipeline_layout_info.pNext = nullptr;
|
pipeline_layout_info.pNext = nullptr;
|
||||||
pipeline_layout_info.flags = {};
|
pipeline_layout_info.flags = {};
|
||||||
pipeline_layout_info.setLayoutCount = set_layouts_num;
|
pipeline_layout_info.setLayoutCount = 1;
|
||||||
pipeline_layout_info.pSetLayouts = (set_layouts_num > 0 ? set_layouts : nullptr);
|
pipeline_layout_info.pSetLayouts = &pipeline.descriptor_set_layout;
|
||||||
pipeline_layout_info.pushConstantRangeCount = push_constant_info_num;
|
pipeline_layout_info.pushConstantRangeCount = 1;
|
||||||
pipeline_layout_info.pPushConstantRanges =
|
pipeline_layout_info.pPushConstantRanges = &push_constants;
|
||||||
push_constant_info_num > 0 ? push_constant_info : nullptr;
|
|
||||||
|
|
||||||
EXIT_IF(pipeline.pipeline_layout != nullptr);
|
EXIT_IF(pipeline.pipeline_layout != nullptr);
|
||||||
|
|
||||||
LOGF("PipelineTrace: vkCreatePipelineLayout CS begin set_layouts=%u push_constants=%u\n",
|
LOGF("PipelineTrace: vkCreatePipelineLayout CS begin set_layouts=1 push_constants=%u\n",
|
||||||
set_layouts_num, push_constant_info_num);
|
1u);
|
||||||
result = graphics.device.createPipelineLayout(&pipeline_layout_info, nullptr,
|
result = graphics.device.createPipelineLayout(&pipeline_layout_info, nullptr,
|
||||||
&pipeline.pipeline_layout);
|
&pipeline.pipeline_layout);
|
||||||
LOGF("PipelineTrace: vkCreatePipelineLayout CS done result=%s layout=%p\n",
|
LOGF("PipelineTrace: vkCreatePipelineLayout CS done result=%s layout=%p\n",
|
||||||
|
|||||||
@@ -4,11 +4,14 @@
|
|||||||
#include "common/abi.h"
|
#include "common/abi.h"
|
||||||
#include "common/assert.h"
|
#include "common/assert.h"
|
||||||
#include "common/common.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/renderer/renderTarget.h"
|
||||||
#include "graphics/host_gpu/vulkanCommon.h"
|
#include "graphics/host_gpu/vulkanCommon.h"
|
||||||
|
|
||||||
#include <array>
|
#include <array>
|
||||||
|
#include <optional>
|
||||||
|
#include <span>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
namespace Libs::Graphics {
|
namespace Libs::Graphics {
|
||||||
@@ -23,7 +26,6 @@ struct GraphicContext;
|
|||||||
struct ShaderBufferResource;
|
struct ShaderBufferResource;
|
||||||
struct ShaderComputeInputInfo;
|
struct ShaderComputeInputInfo;
|
||||||
struct CommandSlot;
|
struct CommandSlot;
|
||||||
struct VulkanDescriptorSet;
|
|
||||||
struct RenderDepthInfo;
|
struct RenderDepthInfo;
|
||||||
struct RenderColorInfo;
|
struct RenderColorInfo;
|
||||||
struct DrawCallInfo;
|
struct DrawCallInfo;
|
||||||
@@ -92,7 +94,6 @@ public:
|
|||||||
void WaitForFenceOnly();
|
void WaitForFenceOnly();
|
||||||
void WaitForFence();
|
void WaitForFence();
|
||||||
void WaitForFenceAndReset();
|
void WaitForFenceAndReset();
|
||||||
void RecycleDescriptorAfterFence(VulkanDescriptorSet& set);
|
|
||||||
|
|
||||||
[[nodiscard]] vk::CommandBuffer Handle() const;
|
[[nodiscard]] vk::CommandBuffer Handle() const;
|
||||||
[[nodiscard]] GraphicContext& GetGraphics() const noexcept { return m_graphics; }
|
[[nodiscard]] GraphicContext& GetGraphics() const noexcept { return m_graphics; }
|
||||||
@@ -102,7 +103,6 @@ public:
|
|||||||
private:
|
private:
|
||||||
void Release();
|
void Release();
|
||||||
void FinalizeFence(bool reset_recording);
|
void FinalizeFence(bool reset_recording);
|
||||||
void RecycleDescriptorsAfterFence();
|
|
||||||
|
|
||||||
RenderContext& m_context;
|
RenderContext& m_context;
|
||||||
CommandScheduler& m_scheduler;
|
CommandScheduler& m_scheduler;
|
||||||
@@ -118,7 +118,6 @@ private:
|
|||||||
uint32_t m_debug_arg2 = 0;
|
uint32_t m_debug_arg2 = 0;
|
||||||
uint32_t m_debug_arg3 = 0;
|
uint32_t m_debug_arg3 = 0;
|
||||||
uint64_t m_debug_arg4 = 0;
|
uint64_t m_debug_arg4 = 0;
|
||||||
std::vector<VulkanDescriptorSet*> m_descriptor_sets_after_fence;
|
|
||||||
mutable RenderState m_render_state;
|
mutable RenderState m_render_state;
|
||||||
mutable bool m_rendering = false;
|
mutable bool m_rendering = false;
|
||||||
};
|
};
|
||||||
@@ -159,23 +158,21 @@ public:
|
|||||||
void DispatchDirect(uint64_t submit_id, RenderCommandBuffer& buffer, uint32_t thread_group_x,
|
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);
|
uint32_t thread_group_y, uint32_t thread_group_z, uint32_t mode);
|
||||||
|
|
||||||
[[nodiscard]] DescriptorCache::PreparedBindings
|
[[nodiscard]] PreparedBindings PrepareBindings(const ShaderStageRuntime& runtime);
|
||||||
PrepareBindings(const ShaderStageRuntime& runtime, vk::ShaderStageFlags shader_stage,
|
void FindBuffers(PreparedBindings& bindings);
|
||||||
DescriptorCache::Stage stage);
|
void RebindBuffers(PreparedBindings& bindings);
|
||||||
void FindBuffers(DescriptorCache::PreparedBindings& bindings);
|
void RebindImages(PreparedBindings& bindings);
|
||||||
void RebindBuffers(DescriptorCache::PreparedBindings& bindings);
|
|
||||||
void RebindImages(DescriptorCache::PreparedBindings& bindings);
|
|
||||||
void CommitBindings(CommandBuffer& buffer, vk::PipelineBindPoint pipeline_bind_point,
|
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:
|
private:
|
||||||
struct GraphicsBindings {
|
struct GraphicsBindings {
|
||||||
DescriptorCache::PreparedBindings vertex;
|
PreparedBindings vertex;
|
||||||
std::optional<DescriptorCache::PreparedBindings> pixel;
|
std::optional<PreparedBindings> pixel;
|
||||||
};
|
};
|
||||||
|
|
||||||
[[nodiscard]] DescriptorCache::TextureBinding
|
[[nodiscard]] TextureBinding ResolveTexture(const ShaderRecompiler::IR::ImageResource& resource,
|
||||||
ResolveTexture(const ShaderRecompiler::IR::ImageResource& resource,
|
|
||||||
const ShaderRecompiler::IR::DescriptorValue& value);
|
const ShaderRecompiler::IR::DescriptorValue& value);
|
||||||
[[nodiscard]] GraphicsBindings PrepareGraphicsBindings(const ShaderStageRuntime& vertex,
|
[[nodiscard]] GraphicsBindings PrepareGraphicsBindings(const ShaderStageRuntime& vertex,
|
||||||
const ShaderStageRuntime& pixel,
|
const ShaderStageRuntime& pixel,
|
||||||
@@ -209,6 +206,12 @@ private:
|
|||||||
|
|
||||||
RenderContext& m_context;
|
RenderContext& m_context;
|
||||||
std::vector<ImageId> m_bound_images;
|
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;
|
friend struct RenderExecutorTestAccess;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -11,7 +11,6 @@
|
|||||||
#include "graphics/guest_gpu/hardwareContext.h"
|
#include "graphics/guest_gpu/hardwareContext.h"
|
||||||
#include "graphics/host_gpu/graphicContext.h"
|
#include "graphics/host_gpu/graphicContext.h"
|
||||||
#include "graphics/host_gpu/renderer/image/imageInfo.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/descriptors.h"
|
||||||
#include "graphics/host_gpu/renderer/pipeline/pipelineCache.h"
|
#include "graphics/host_gpu/renderer/pipeline/pipelineCache.h"
|
||||||
#include "graphics/host_gpu/renderer/pipeline/shaderResourceBarrier.h"
|
#include "graphics/host_gpu/renderer/pipeline/shaderResourceBarrier.h"
|
||||||
@@ -334,14 +333,15 @@ void RenderExecutor::DispatchDirect(uint64_t submit_id, RenderCommandBuffer& buf
|
|||||||
buffer.EndRendering();
|
buffer.EndRendering();
|
||||||
auto& pipeline =
|
auto& pipeline =
|
||||||
m_context.GetPipelineCache().CreateComputePipeline(input_info, sh_ctx.GetCs(), cs_shader);
|
m_context.GetPipelineCache().CreateComputePipeline(input_info, sh_ctx.GetCs(), cs_shader);
|
||||||
auto bindings = PrepareBindings(input_info.stage, vk::ShaderStageFlagBits::eCompute,
|
auto bindings = PrepareBindings(input_info.stage);
|
||||||
DescriptorCache::Stage::Compute);
|
|
||||||
FindBuffers(bindings);
|
FindBuffers(bindings);
|
||||||
RebindBuffers(bindings);
|
RebindBuffers(bindings);
|
||||||
RebindImages(bindings);
|
RebindImages(bindings);
|
||||||
|
|
||||||
auto vk_buffer = buffer.Handle();
|
auto vk_buffer = buffer.Handle();
|
||||||
CommitBindings(buffer, vk::PipelineBindPoint::eCompute, pipeline.pipeline_layout, bindings);
|
PreparedBindings* descriptor_stage = &bindings;
|
||||||
|
CommitBindings(buffer, vk::PipelineBindPoint::eCompute, pipeline,
|
||||||
|
std::span {&descriptor_stage, 1u});
|
||||||
bool has_storage_writes = HasShaderBufferWrites(input_info.stage);
|
bool has_storage_writes = HasShaderBufferWrites(input_info.stage);
|
||||||
has_storage_writes =
|
has_storage_writes =
|
||||||
std::any_of(program.info.images.begin(), program.info.images.end(),
|
std::any_of(program.info.images.begin(), program.info.images.end(),
|
||||||
|
|||||||
@@ -13,8 +13,9 @@ namespace Libs::Graphics {
|
|||||||
|
|
||||||
RenderContext::RenderContext(GraphicContext& graphics)
|
RenderContext::RenderContext(GraphicContext& graphics)
|
||||||
: m_graphics(graphics), m_render_executor(*this), m_command_scheduler(*this, 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_descriptor_heap(graphics, m_command_scheduler.GetMasterSemaphore()),
|
||||||
m_sampler_cache(graphics), m_gpu_resources(graphics, m_command_scheduler) {
|
m_pipeline_cache(graphics), m_sampler_cache(graphics),
|
||||||
|
m_gpu_resources(graphics, m_command_scheduler) {
|
||||||
EXIT_NOT_IMPLEMENTED(!Common::Thread::IsMainThread());
|
EXIT_NOT_IMPLEMENTED(!Common::Thread::IsMainThread());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -10,7 +10,7 @@
|
|||||||
#include "graphics/host_gpu/renderer/cache/samplerCache.h"
|
#include "graphics/host_gpu/renderer/cache/samplerCache.h"
|
||||||
#include "graphics/host_gpu/renderer/cache/textureCache.h"
|
#include "graphics/host_gpu/renderer/cache/textureCache.h"
|
||||||
#include "graphics/host_gpu/renderer/commandScheduler.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 "graphics/host_gpu/renderer/pipeline/pipelineCache.h"
|
||||||
#include "kernel/eventQueue.h"
|
#include "kernel/eventQueue.h"
|
||||||
|
|
||||||
@@ -41,7 +41,7 @@ public:
|
|||||||
Common::Mutex& GetMutex() { return m_mutex; }
|
Common::Mutex& GetMutex() { return m_mutex; }
|
||||||
CommandScheduler& GetCommandScheduler() { return m_command_scheduler; }
|
CommandScheduler& GetCommandScheduler() { return m_command_scheduler; }
|
||||||
PipelineCache& GetPipelineCache() { return m_pipeline_cache; }
|
PipelineCache& GetPipelineCache() { return m_pipeline_cache; }
|
||||||
DescriptorCache& GetDescriptorCache() { return m_descriptor_cache; }
|
DescriptorHeap& GetDescriptorHeap() { return m_descriptor_heap; }
|
||||||
SamplerCache& GetSamplerCache() { return m_sampler_cache; }
|
SamplerCache& GetSamplerCache() { return m_sampler_cache; }
|
||||||
GpuResourceManager& GetGpuResources() { return m_gpu_resources; }
|
GpuResourceManager& GetGpuResources() { return m_gpu_resources; }
|
||||||
BufferCache& GetBufferCache() { return m_gpu_resources.GetBufferCache(); }
|
BufferCache& GetBufferCache() { return m_gpu_resources.GetBufferCache(); }
|
||||||
@@ -63,7 +63,7 @@ private:
|
|||||||
Common::Mutex m_mutex;
|
Common::Mutex m_mutex;
|
||||||
RenderExecutor m_render_executor;
|
RenderExecutor m_render_executor;
|
||||||
CommandScheduler m_command_scheduler;
|
CommandScheduler m_command_scheduler;
|
||||||
DescriptorCache m_descriptor_cache;
|
DescriptorHeap m_descriptor_heap;
|
||||||
PipelineCache m_pipeline_cache;
|
PipelineCache m_pipeline_cache;
|
||||||
SamplerCache m_sampler_cache;
|
SamplerCache m_sampler_cache;
|
||||||
GpuResourceManager m_gpu_resources;
|
GpuResourceManager m_gpu_resources;
|
||||||
|
|||||||
@@ -15,7 +15,6 @@
|
|||||||
#include "graphics/host_gpu/renderer/colorRenderTarget.h"
|
#include "graphics/host_gpu/renderer/colorRenderTarget.h"
|
||||||
#include "graphics/host_gpu/renderer/debug.h"
|
#include "graphics/host_gpu/renderer/debug.h"
|
||||||
#include "graphics/host_gpu/renderer/depthRenderTarget.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/pipelineCache.h"
|
||||||
#include "graphics/host_gpu/renderer/pipeline/shaderResourceBarrier.h"
|
#include "graphics/host_gpu/renderer/pipeline/shaderResourceBarrier.h"
|
||||||
#include "graphics/host_gpu/renderer/render.h"
|
#include "graphics/host_gpu/renderer/render.h"
|
||||||
@@ -862,10 +861,9 @@ static PreparedVertexBuffers AcquireVertexBuffers(RenderCommandBuffer& b
|
|||||||
Libs::LibKernel::Memory::ClampRangeSize(range.base_address, range.RequestedSize());
|
Libs::LibKernel::Memory::ClampRangeSize(range.base_address, range.RequestedSize());
|
||||||
range.acquired_end = range.base_address + size;
|
range.acquired_end = range.base_address + size;
|
||||||
range.binding = cache.ObtainBuffer(range.base_address, size, false);
|
range.binding = cache.ObtainBuffer(range.base_address, size, false);
|
||||||
SetVulkanObjectNameF(buffer.GetContext().GetGraphics().device,
|
SetVulkanObjectNameF(
|
||||||
range.binding.first->Handle(),
|
buffer.GetContext().GetGraphics().device, range.binding.first->Handle(),
|
||||||
"Kyty.VertexBufferRange[guest=0x{:016x} size=0x{:x}]",
|
"Kyty.VertexBufferRange[guest=0x{:016x} size=0x{:x}]", range.base_address, size);
|
||||||
range.base_address, size);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Rebuild slot bindings, offsetting non-empty slots into their acquired merged range.
|
// Rebuild slot bindings, offsetting non-empty slots into their acquired merged range.
|
||||||
@@ -1062,7 +1060,8 @@ static void RefreshShaders(RenderCommandBuffer& buffer, const DrawCallInfo& draw
|
|||||||
LogDrawPhase(draw.name, "ShaderCompileInfoPS");
|
LogDrawPhase(draw.name, "ShaderCompileInfoPS");
|
||||||
}
|
}
|
||||||
if (!ShaderCompileInfoPS(pixel_shader_info, shader_regs, state.vs_input_info,
|
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);
|
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.offset = stream.Copy(source.host_data, source.size, 16);
|
||||||
prepared.buffer = stream.Handle();
|
prepared.buffer = stream.Handle();
|
||||||
} else {
|
} else {
|
||||||
auto [buffer_ptr, offset] = buffer.GetContext().GetBufferCache().ObtainBuffer(
|
auto [buffer_ptr, offset] =
|
||||||
source.address, source.size, false);
|
buffer.GetContext().GetBufferCache().ObtainBuffer(source.address, source.size, false);
|
||||||
prepared.buffer = buffer_ptr->Handle();
|
prepared.buffer = buffer_ptr->Handle();
|
||||||
prepared.offset = offset;
|
prepared.offset = offset;
|
||||||
}
|
}
|
||||||
@@ -1108,7 +1107,8 @@ static PreparedIndexBuffer PrepareIndexBuffer(RenderCommandBuffer& buffe
|
|||||||
return prepared;
|
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++) {
|
for (uint32_t i = 0; i < prepared.count; i++) {
|
||||||
EXIT_IF(prepared.buffers[i] == nullptr);
|
EXIT_IF(prepared.buffers[i] == nullptr);
|
||||||
}
|
}
|
||||||
@@ -1230,15 +1230,18 @@ void RenderExecutor::ExecutePreparedDraw(uint64_t submit_id, RenderCommandBuffer
|
|||||||
SetDrawDebugPhase(buffer, submit_id, draw, 0x200u);
|
SetDrawDebugPhase(buffer, submit_id, draw, 0x200u);
|
||||||
}
|
}
|
||||||
CommitVertexBuffers(vk_buffer, vertex_bindings);
|
CommitVertexBuffers(vk_buffer, vertex_bindings);
|
||||||
CommitBindings(buffer, vk::PipelineBindPoint::eGraphics, pipeline.pipeline_layout,
|
|
||||||
bindings.vertex);
|
|
||||||
if (bindings.pixel.has_value()) {
|
if (bindings.pixel.has_value()) {
|
||||||
if (set_auto_debug) {
|
if (set_auto_debug) {
|
||||||
SetDrawDebugPhase(buffer, submit_id, draw, 0x300u);
|
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);
|
CommitIndexBuffer(vk_buffer, index_binding);
|
||||||
|
|
||||||
const auto dynamic_params =
|
const auto dynamic_params =
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ struct VulkanInstance {
|
|||||||
uint32_t subgroup_size = 0;
|
uint32_t subgroup_size = 0;
|
||||||
uint32_t min_subgroup_size = 0;
|
uint32_t min_subgroup_size = 0;
|
||||||
uint32_t max_subgroup_size = 0;
|
uint32_t max_subgroup_size = 0;
|
||||||
|
uint32_t max_push_descriptors = 0;
|
||||||
vk::ShaderStageFlags required_subgroup_size_stages = {};
|
vk::ShaderStageFlags required_subgroup_size_stages = {};
|
||||||
Common::Mutex queue_mutex;
|
Common::Mutex queue_mutex;
|
||||||
uint32_t queue_family = static_cast<uint32_t>(-1);
|
uint32_t queue_family = static_cast<uint32_t>(-1);
|
||||||
|
|||||||
@@ -583,8 +583,8 @@ static vk::Device VulkanCreateDevice(vk::PhysicalDevice physical_device, const V
|
|||||||
robustness2.nullDescriptor = supported_robustness2.nullDescriptor;
|
robustness2.nullDescriptor = supported_robustness2.nullDescriptor;
|
||||||
}
|
}
|
||||||
|
|
||||||
const bool subgroup_size_control_enabled =
|
const bool subgroup_size_control_enabled = graphics.subgroup_size_control_enabled &&
|
||||||
graphics.subgroup_size_control_enabled && supported_features13.subgroupSizeControl == VK_TRUE;
|
supported_features13.subgroupSizeControl == VK_TRUE;
|
||||||
|
|
||||||
auto features13 = required_features13;
|
auto features13 = required_features13;
|
||||||
features13.pNext = robustness2_ext_enabled ? static_cast<void*>(&robustness2)
|
features13.pNext = robustness2_ext_enabled ? static_cast<void*>(&robustness2)
|
||||||
@@ -939,7 +939,15 @@ void WindowContext::CreateVulkan() {
|
|||||||
EXIT("Could not find suitable device");
|
EXIT("Could not find suitable device");
|
||||||
}
|
}
|
||||||
|
|
||||||
graphic_ctx.physical_device.getProperties(&graphic_ctx.physical_device_properties);
|
vk::PhysicalDevicePushDescriptorProperties push_descriptor_properties {};
|
||||||
|
vk::PhysicalDeviceProperties2 physical_device_properties {};
|
||||||
|
push_descriptor_properties.sType = vk::StructureType::ePhysicalDevicePushDescriptorProperties;
|
||||||
|
push_descriptor_properties.pNext = nullptr;
|
||||||
|
physical_device_properties.sType = vk::StructureType::ePhysicalDeviceProperties2;
|
||||||
|
physical_device_properties.pNext = &push_descriptor_properties;
|
||||||
|
graphic_ctx.physical_device.getProperties2(&physical_device_properties);
|
||||||
|
graphic_ctx.physical_device_properties = physical_device_properties.properties;
|
||||||
|
graphic_ctx.max_push_descriptors = push_descriptor_properties.maxPushDescriptors;
|
||||||
graphic_ctx.physical_device.getMemoryProperties(&graphic_ctx.physical_device_memory_properties);
|
graphic_ctx.physical_device.getMemoryProperties(&graphic_ctx.physical_device_memory_properties);
|
||||||
const auto& device_properties = graphic_ctx.GetPhysicalDeviceProperties();
|
const auto& device_properties = graphic_ctx.GetPhysicalDeviceProperties();
|
||||||
|
|
||||||
|
|||||||
@@ -798,10 +798,7 @@ bool TryRecompile(std::span<const uint32_t> code, const CompileOptions& options,
|
|||||||
if (!IR::CollectShaderInfo(ir, info_options, error)) {
|
if (!IR::CollectShaderInfo(ir, info_options, error)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
IR::BindingLayoutOptions layout_options;
|
if (!IR::AllocateBindings(ir, options.push_constant_offset, error)) {
|
||||||
layout_options.descriptor_set = options.descriptor_set;
|
|
||||||
layout_options.push_constant_offset = options.push_constant_offset;
|
|
||||||
if (!IR::AllocateBindings(ir, layout_options, error)) {
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (!Spirv::AnalyzeProgramRequirements(ir, error)) {
|
if (!Spirv::AnalyzeProgramRequirements(ir, error)) {
|
||||||
|
|||||||
@@ -21,7 +21,6 @@ struct CompileOptions {
|
|||||||
uint64_t shader_hash = 0;
|
uint64_t shader_hash = 0;
|
||||||
uint64_t shader_base = 0;
|
uint64_t shader_base = 0;
|
||||||
std::optional<uint64_t> flat_memory_base;
|
std::optional<uint64_t> flat_memory_base;
|
||||||
uint32_t descriptor_set = 0;
|
|
||||||
uint32_t push_constant_offset = 0;
|
uint32_t push_constant_offset = 0;
|
||||||
bool dump_ir = true;
|
bool dump_ir = true;
|
||||||
bool early_dump = false;
|
bool early_dump = false;
|
||||||
|
|||||||
@@ -89,18 +89,12 @@ bool ValidateNativeProgram(const IR::Program& program, std::string* error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
std::array<bool, KindCount> seen {};
|
std::array<bool, KindCount> seen {};
|
||||||
for (uint32_t i = 0; i < program.bindings.descriptors.size(); i++) {
|
for (const auto& binding: program.bindings.descriptors) {
|
||||||
const auto& binding = program.bindings.descriptors[i];
|
|
||||||
const auto kind = static_cast<size_t>(binding.kind);
|
const auto kind = static_cast<size_t>(binding.kind);
|
||||||
if (kind >= KindCount || seen[kind] || !present[kind] ||
|
if (kind >= KindCount || seen[kind] || !present[kind] ||
|
||||||
binding.resources != expected[kind]) {
|
binding.resources != expected[kind]) {
|
||||||
return Fail(error, "native descriptor groups do not match shader topology");
|
return Fail(error, "native descriptor groups do not match shader topology");
|
||||||
}
|
}
|
||||||
for (uint32_t j = 0; j < i; j++) {
|
|
||||||
if (program.bindings.descriptors[j].binding == binding.binding) {
|
|
||||||
return Fail(error, "native descriptor binding numbers are not unique");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
seen[kind] = true;
|
seen[kind] = true;
|
||||||
}
|
}
|
||||||
for (size_t i = 0; i < KindCount; i++) {
|
for (size_t i = 0; i < KindCount; i++) {
|
||||||
@@ -109,7 +103,10 @@ bool ValidateNativeProgram(const IR::Program& program, std::string* error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
const auto has_user_storage = present[static_cast<size_t>(Kind::UserData)];
|
const auto has_user_storage = present[static_cast<size_t>(Kind::UserData)];
|
||||||
if (program.bindings.memory_offset_dword != program.bindings.user_data_registers.size() ||
|
if (program.bindings.push_constant_offset % sizeof(uint32_t) != 0 ||
|
||||||
|
program.bindings.push_constant_offset + program.bindings.push_constant_size >
|
||||||
|
IR::NativePushConstantSize ||
|
||||||
|
program.bindings.memory_offset_dword != program.bindings.user_data_registers.size() ||
|
||||||
program.bindings.memory_offset_count !=
|
program.bindings.memory_offset_count !=
|
||||||
program.info.buffers.size() + program.info.addresses.size() ||
|
program.info.buffers.size() + program.info.addresses.size() ||
|
||||||
(!has_user_storage &&
|
(!has_user_storage &&
|
||||||
|
|||||||
@@ -667,8 +667,8 @@ void AddInputAnnotationsAndNames(EmitterState& state);
|
|||||||
|
|
||||||
void AddOutputAnnotationsAndNames(EmitterState& state);
|
void AddOutputAnnotationsAndNames(EmitterState& state);
|
||||||
|
|
||||||
void DecorateDescriptor(EmitterState& state, uint32_t variable, const char* name, uint32_t set,
|
void DecorateDescriptor(EmitterState& state, uint32_t variable, const char* name,
|
||||||
uint32_t binding);
|
IR::DescriptorBindingKind kind);
|
||||||
|
|
||||||
void AddDescriptorAnnotationsAndNames(EmitterState& state);
|
void AddDescriptorAnnotationsAndNames(EmitterState& state);
|
||||||
|
|
||||||
|
|||||||
@@ -510,23 +510,20 @@ void AddOutputAnnotationsAndNames(EmitterState& state) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void DecorateDescriptor(EmitterState& state, uint32_t variable, const char* name, uint32_t set,
|
void DecorateDescriptor(EmitterState& state, uint32_t variable, const char* name,
|
||||||
uint32_t binding) {
|
IR::DescriptorBindingKind kind) {
|
||||||
if (variable == 0) {
|
if (variable == 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
state.builder.AddName(variable, name);
|
state.builder.AddName(variable, name);
|
||||||
state.builder.AddAnnotation({OpDecorate, variable, DecorationDescriptorSet, set});
|
state.builder.AddAnnotation({OpDecorate, variable, DecorationDescriptorSet, 0});
|
||||||
state.builder.AddAnnotation({OpDecorate, variable, DecorationBinding, binding});
|
state.builder.AddAnnotation(
|
||||||
|
{OpDecorate, variable, DecorationBinding, IR::NativeBinding(state.program.stage, kind)});
|
||||||
}
|
}
|
||||||
|
|
||||||
void AddDescriptorAnnotationsAndNames(EmitterState& state) {
|
void AddDescriptorAnnotationsAndNames(EmitterState& state) {
|
||||||
const auto set = state.program.bindings.descriptor_set;
|
|
||||||
auto Decorate = [&](uint32_t variable, const char* name, IR::DescriptorBindingKind kind) {
|
auto Decorate = [&](uint32_t variable, const char* name, IR::DescriptorBindingKind kind) {
|
||||||
const auto* binding = DescriptorBinding(state, kind);
|
DecorateDescriptor(state, variable, name, kind);
|
||||||
if (binding != nullptr) {
|
|
||||||
DecorateDescriptor(state, variable, name, set, binding->binding);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
if (state.storage_buffer_variable != 0) {
|
if (state.storage_buffer_variable != 0) {
|
||||||
Decorate(state.storage_buffer_variable, "buffers", IR::DescriptorBindingKind::Buffers);
|
Decorate(state.storage_buffer_variable, "buffers", IR::DescriptorBindingKind::Buffers);
|
||||||
@@ -588,17 +585,13 @@ void AddDescriptorAnnotationsAndNames(EmitterState& state) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void AddVsharpAnnotationsAndNames(EmitterState& state) {
|
void AddVsharpAnnotationsAndNames(EmitterState& state) {
|
||||||
const auto& bind = state.program.bindings;
|
|
||||||
if (state.push_constant_variable != 0) {
|
if (state.push_constant_variable != 0) {
|
||||||
state.builder.AddName(PushConstantBlockType(state), "BufferResource");
|
state.builder.AddName(PushConstantBlockType(state), "BufferResource");
|
||||||
state.builder.AddName(state.push_constant_variable, "vsharp");
|
state.builder.AddName(state.push_constant_variable, "vsharp");
|
||||||
}
|
}
|
||||||
if (state.vsharp_storage_variable != 0) {
|
if (state.vsharp_storage_variable != 0) {
|
||||||
const auto* storage = DescriptorBinding(state, IR::DescriptorBindingKind::UserData);
|
|
||||||
if (storage != nullptr) {
|
|
||||||
DecorateDescriptor(state, state.vsharp_storage_variable, "user_data",
|
DecorateDescriptor(state, state.vsharp_storage_variable, "user_data",
|
||||||
bind.descriptor_set, storage->binding);
|
IR::DescriptorBindingKind::UserData);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -338,6 +338,13 @@ enum class DescriptorBindingKind {
|
|||||||
Count,
|
Count,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
constexpr uint32_t NativePushConstantSize = 128;
|
||||||
|
|
||||||
|
[[nodiscard]] constexpr uint32_t NativeBinding(ShaderType stage, DescriptorBindingKind kind) {
|
||||||
|
return static_cast<uint32_t>(kind) +
|
||||||
|
(stage == ShaderType::Pixel ? static_cast<uint32_t>(DescriptorBindingKind::Count) : 0u);
|
||||||
|
}
|
||||||
|
|
||||||
[[nodiscard]] inline std::optional<DescriptorBindingKind>
|
[[nodiscard]] inline std::optional<DescriptorBindingKind>
|
||||||
DescriptorBindingForImage(const ImageResource& image) {
|
DescriptorBindingForImage(const ImageResource& image) {
|
||||||
using Dimension = Decoder::ImageDimension;
|
using Dimension = Decoder::ImageDimension;
|
||||||
@@ -413,14 +420,12 @@ DescriptorBindingForImage(const ImageResource& image) {
|
|||||||
|
|
||||||
struct DescriptorBinding {
|
struct DescriptorBinding {
|
||||||
DescriptorBindingKind kind = DescriptorBindingKind::Buffers;
|
DescriptorBindingKind kind = DescriptorBindingKind::Buffers;
|
||||||
uint32_t binding = 0;
|
|
||||||
std::vector<uint32_t> resources;
|
std::vector<uint32_t> resources;
|
||||||
|
|
||||||
bool operator==(const DescriptorBinding& other) const = default;
|
bool operator==(const DescriptorBinding& other) const = default;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct BindingLayout {
|
struct BindingLayout {
|
||||||
uint32_t descriptor_set = 0;
|
|
||||||
uint32_t push_constant_offset = 0;
|
uint32_t push_constant_offset = 0;
|
||||||
uint32_t push_constant_size = 0;
|
uint32_t push_constant_size = 0;
|
||||||
uint32_t memory_offset_dword = 0;
|
uint32_t memory_offset_dword = 0;
|
||||||
|
|||||||
@@ -4,48 +4,18 @@
|
|||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <array>
|
#include <array>
|
||||||
#include <set>
|
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
namespace Libs::Graphics::ShaderRecompiler::IR {
|
namespace Libs::Graphics::ShaderRecompiler::IR {
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
constexpr uint32_t MaxPushConstantBytes = 128;
|
constexpr auto FirstImageBinding = DescriptorBindingKind::Sampled1D;
|
||||||
|
constexpr uint32_t ImageBindingCount =
|
||||||
constexpr std::array ImageBindingKinds = {
|
static_cast<uint32_t>(DescriptorBindingKind::StorageAtomic3D) -
|
||||||
DescriptorBindingKind::Sampled1D,
|
static_cast<uint32_t>(FirstImageBinding) + 1u;
|
||||||
DescriptorBindingKind::Sampled1DArray,
|
|
||||||
DescriptorBindingKind::Sampled2D,
|
|
||||||
DescriptorBindingKind::Sampled2DArray,
|
|
||||||
DescriptorBindingKind::Sampled2DMsaa,
|
|
||||||
DescriptorBindingKind::Sampled2DMsaaArray,
|
|
||||||
DescriptorBindingKind::Sampled3D,
|
|
||||||
DescriptorBindingKind::SampledUint1D,
|
|
||||||
DescriptorBindingKind::SampledUint1DArray,
|
|
||||||
DescriptorBindingKind::SampledUint2D,
|
|
||||||
DescriptorBindingKind::SampledUint2DArray,
|
|
||||||
DescriptorBindingKind::SampledUint2DMsaa,
|
|
||||||
DescriptorBindingKind::SampledUint2DMsaaArray,
|
|
||||||
DescriptorBindingKind::SampledUint3D,
|
|
||||||
DescriptorBindingKind::Storage1D,
|
|
||||||
DescriptorBindingKind::Storage1DArray,
|
|
||||||
DescriptorBindingKind::Storage2D,
|
|
||||||
DescriptorBindingKind::Storage2DArray,
|
|
||||||
DescriptorBindingKind::Storage3D,
|
|
||||||
DescriptorBindingKind::StorageUint1D,
|
|
||||||
DescriptorBindingKind::StorageUint1DArray,
|
|
||||||
DescriptorBindingKind::StorageUint2D,
|
|
||||||
DescriptorBindingKind::StorageUint2DArray,
|
|
||||||
DescriptorBindingKind::StorageUint3D,
|
|
||||||
DescriptorBindingKind::StorageAtomic1D,
|
|
||||||
DescriptorBindingKind::StorageAtomic1DArray,
|
|
||||||
DescriptorBindingKind::StorageAtomic2D,
|
|
||||||
DescriptorBindingKind::StorageAtomic2DArray,
|
|
||||||
DescriptorBindingKind::StorageAtomic3D,
|
|
||||||
};
|
|
||||||
|
|
||||||
bool CollectUserData(const Program& program, std::vector<uint32_t>& result) {
|
bool CollectUserData(const Program& program, std::vector<uint32_t>& result) {
|
||||||
std::set<uint32_t> registers;
|
std::array<bool, NumScalarRegs> registers {};
|
||||||
for (const auto* block: program.values->blocks) {
|
for (const auto* block: program.values->blocks) {
|
||||||
for (const auto& inst: *block) {
|
for (const auto& inst: *block) {
|
||||||
if (inst.GetOpcode() != ValueOpcode::GetUserData || !inst.HasUses()) {
|
if (inst.GetOpcode() != ValueOpcode::GetUserData || !inst.HasUses()) {
|
||||||
@@ -58,17 +28,21 @@ bool CollectUserData(const Program& program, std::vector<uint32_t>& result) {
|
|||||||
if (index >= NumScalarRegs) {
|
if (index >= NumScalarRegs) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
registers.insert(index);
|
registers[index] = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
result.clear();
|
||||||
|
for (uint32_t index = 0; index < registers.size(); index++) {
|
||||||
|
if (registers[index]) {
|
||||||
|
result.push_back(index);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
result.assign(registers.begin(), registers.end());
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void AddBinding(BindingLayout& layout, DescriptorBindingKind kind,
|
void AddBinding(BindingLayout& layout, DescriptorBindingKind kind,
|
||||||
std::vector<uint32_t> resources = {}) {
|
std::vector<uint32_t> resources = {}) {
|
||||||
layout.descriptors.push_back(
|
layout.descriptors.push_back({kind, std::move(resources)});
|
||||||
{kind, static_cast<uint32_t>(layout.descriptors.size()), std::move(resources)});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool UsesGds(const Program& program, bool& uses_gds) {
|
bool UsesGds(const Program& program, bool& uses_gds) {
|
||||||
@@ -94,7 +68,7 @@ bool UsesGds(const Program& program, bool& uses_gds) {
|
|||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
bool AllocateBindings(Program& program, const BindingLayoutOptions& options, std::string* error) {
|
bool AllocateBindings(Program& program, uint32_t push_constant_offset, std::string* error) {
|
||||||
if (!program.shader_info_complete || program.binding_layout_complete) {
|
if (!program.shader_info_complete || program.binding_layout_complete) {
|
||||||
if (error != nullptr) {
|
if (error != nullptr) {
|
||||||
*error = !program.shader_info_complete ? "shader info is not ready"
|
*error = !program.shader_info_complete ? "shader info is not ready"
|
||||||
@@ -102,28 +76,22 @@ bool AllocateBindings(Program& program, const BindingLayoutOptions& options, std
|
|||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (options.push_constant_offset > MaxPushConstantBytes) {
|
|
||||||
if (error != nullptr) {
|
|
||||||
*error = "push-constant offset exceeds the Vulkan minimum guarantee";
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (options.push_constant_offset % 4u != 0) {
|
|
||||||
if (error != nullptr) {
|
|
||||||
*error = "push-constant offset is not dword aligned";
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (program.values == nullptr) {
|
if (program.values == nullptr) {
|
||||||
if (error != nullptr) {
|
if (error != nullptr) {
|
||||||
*error = "typed value program is not available";
|
*error = "typed value program is not available";
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
if (push_constant_offset > NativePushConstantSize ||
|
||||||
|
push_constant_offset % sizeof(uint32_t) != 0) {
|
||||||
|
if (error != nullptr) {
|
||||||
|
*error = "push-constant offset exceeds the Vulkan minimum guarantee or is unaligned";
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
BindingLayout next;
|
BindingLayout next;
|
||||||
next.descriptor_set = options.descriptor_set;
|
next.push_constant_offset = push_constant_offset;
|
||||||
next.push_constant_offset = options.push_constant_offset;
|
|
||||||
if (!CollectUserData(program, next.user_data_registers)) {
|
if (!CollectUserData(program, next.user_data_registers)) {
|
||||||
if (error != nullptr) {
|
if (error != nullptr) {
|
||||||
*error = "typed shader contains an invalid user-data register";
|
*error = "typed shader contains an invalid user-data register";
|
||||||
@@ -142,7 +110,7 @@ bool AllocateBindings(Program& program, const BindingLayoutOptions& options, std
|
|||||||
AddBinding(next, DescriptorBindingKind::Buffers, std::move(resources));
|
AddBinding(next, DescriptorBindingKind::Buffers, std::move(resources));
|
||||||
}
|
}
|
||||||
|
|
||||||
std::array<std::vector<uint32_t>, ImageBindingKinds.size()> image_groups;
|
std::array<std::vector<uint32_t>, ImageBindingCount> image_groups;
|
||||||
for (uint32_t i = 0; i < program.info.images.size(); i++) {
|
for (uint32_t i = 0; i < program.info.images.size(); i++) {
|
||||||
const auto kind = DescriptorBindingForImage(program.info.images[i]);
|
const auto kind = DescriptorBindingForImage(program.info.images[i]);
|
||||||
if (!kind.has_value()) {
|
if (!kind.has_value()) {
|
||||||
@@ -151,14 +119,14 @@ bool AllocateBindings(Program& program, const BindingLayoutOptions& options, std
|
|||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
const auto group = std::find(ImageBindingKinds.begin(), ImageBindingKinds.end(), *kind);
|
const auto group = static_cast<uint32_t>(*kind) - static_cast<uint32_t>(FirstImageBinding);
|
||||||
if (group == ImageBindingKinds.end()) {
|
if (group >= image_groups.size()) {
|
||||||
if (error != nullptr) {
|
if (error != nullptr) {
|
||||||
*error = "shader info contains an unmapped image binding class";
|
*error = "shader info contains an unmapped image binding class";
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
auto& resources = image_groups[static_cast<size_t>(group - ImageBindingKinds.begin())];
|
auto& resources = image_groups[group];
|
||||||
const auto dynamic = program.info.images[i].mip_mode == ImageMipMode::DynamicStorage;
|
const auto dynamic = program.info.images[i].mip_mode == ImageMipMode::DynamicStorage;
|
||||||
const auto count = dynamic ? program.info.images[i].mip_count : 1u;
|
const auto count = dynamic ? program.info.images[i].mip_count : 1u;
|
||||||
if (count == 0u || (!dynamic && program.info.images[i].mip_count != 1u)) {
|
if (count == 0u || (!dynamic && program.info.images[i].mip_count != 1u)) {
|
||||||
@@ -171,7 +139,10 @@ bool AllocateBindings(Program& program, const BindingLayoutOptions& options, std
|
|||||||
}
|
}
|
||||||
for (uint32_t i = 0; i < image_groups.size(); i++) {
|
for (uint32_t i = 0; i < image_groups.size(); i++) {
|
||||||
if (!image_groups[i].empty()) {
|
if (!image_groups[i].empty()) {
|
||||||
AddBinding(next, ImageBindingKinds[i], std::move(image_groups[i]));
|
AddBinding(
|
||||||
|
next,
|
||||||
|
static_cast<DescriptorBindingKind>(static_cast<uint32_t>(FirstImageBinding) + i),
|
||||||
|
std::move(image_groups[i]));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -208,9 +179,8 @@ bool AllocateBindings(Program& program, const BindingLayoutOptions& options, std
|
|||||||
AddBinding(next, DescriptorBindingKind::FlattenedSrt);
|
AddBinding(next, DescriptorBindingKind::FlattenedSrt);
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto available_push_dwords = (MaxPushConstantBytes - options.push_constant_offset) / 4u;
|
const auto push_dwords = (NativePushConstantSize - push_constant_offset) / sizeof(uint32_t);
|
||||||
const auto push_limit = std::min(options.max_push_dwords, available_push_dwords);
|
if (next.ShaderDataDwords() <= push_dwords) {
|
||||||
if (next.ShaderDataDwords() <= push_limit) {
|
|
||||||
next.push_constant_size = next.ShaderDataDwords() * sizeof(uint32_t);
|
next.push_constant_size = next.ShaderDataDwords() * sizeof(uint32_t);
|
||||||
} else {
|
} else {
|
||||||
AddBinding(next, DescriptorBindingKind::UserData);
|
AddBinding(next, DescriptorBindingKind::UserData);
|
||||||
|
|||||||
@@ -5,13 +5,7 @@
|
|||||||
|
|
||||||
namespace Libs::Graphics::ShaderRecompiler::IR {
|
namespace Libs::Graphics::ShaderRecompiler::IR {
|
||||||
|
|
||||||
struct BindingLayoutOptions {
|
bool AllocateBindings(Program& program, uint32_t push_constant_offset, std::string* error);
|
||||||
uint32_t descriptor_set = 0;
|
|
||||||
uint32_t push_constant_offset = 0;
|
|
||||||
uint32_t max_push_dwords = 32;
|
|
||||||
};
|
|
||||||
|
|
||||||
bool AllocateBindings(Program& program, const BindingLayoutOptions& options, std::string* error);
|
|
||||||
|
|
||||||
const DescriptorBinding* FindBinding(const BindingLayout& layout, DescriptorBindingKind kind);
|
const DescriptorBinding* FindBinding(const BindingLayout& layout, DescriptorBindingKind kind);
|
||||||
|
|
||||||
|
|||||||
@@ -846,6 +846,11 @@ static void ShaderGetStaticInputInfoPS(
|
|||||||
regs.ps_regs.data_addr);
|
regs.ps_regs.data_addr);
|
||||||
}
|
}
|
||||||
ps_info.scratch_size_dwords = data.scratch_size_dwords;
|
ps_info.scratch_size_dwords = data.scratch_size_dwords;
|
||||||
|
ps_info.push_constant_offset =
|
||||||
|
vs_info.stage.program != nullptr
|
||||||
|
? vs_info.stage.program->bindings.push_constant_offset +
|
||||||
|
vs_info.stage.program->bindings.push_constant_size
|
||||||
|
: 0;
|
||||||
|
|
||||||
// SPI_PS_IN_CONTROL.NUM_INTERP occupies bits 5:0. Keep the remaining control
|
// SPI_PS_IN_CONTROL.NUM_INTERP occupies bits 5:0. Keep the remaining control
|
||||||
// flags in the hardware state and extract only the input count here.
|
// flags in the hardware state and extract only the input count here.
|
||||||
@@ -873,15 +878,6 @@ static void ShaderGetStaticInputInfoPS(
|
|||||||
ps_info.interpolator_settings[i] = sh.ps_interpolator_settings[i];
|
ps_info.interpolator_settings[i] = sh.ps_interpolator_settings[i];
|
||||||
}
|
}
|
||||||
|
|
||||||
ps_info.descriptor_set =
|
|
||||||
vs_info.stage.program != nullptr && !vs_info.stage.program->bindings.descriptors.empty()
|
|
||||||
? 1
|
|
||||||
: 0;
|
|
||||||
ps_info.push_constant_offset = vs_info.stage.program != nullptr
|
|
||||||
? vs_info.stage.program->bindings.push_constant_offset +
|
|
||||||
vs_info.stage.program->bindings.push_constant_size
|
|
||||||
: 0;
|
|
||||||
|
|
||||||
for (int i = 0; i < 8; i++) {
|
for (int i = 0; i < 8; i++) {
|
||||||
ps_info.target_output_mode[i] = sh.target_output_mode[i];
|
ps_info.target_output_mode[i] = sh.target_output_mode[i];
|
||||||
ps_info.target_export_mapping[i] = sh.target_output_mode[i] != 0
|
ps_info.target_export_mapping[i] = sh.target_output_mode[i] != 0
|
||||||
@@ -1020,11 +1016,10 @@ static void LogShaderProgramCacheHit(const char* stage, uint64_t shader_hash, ui
|
|||||||
|
|
||||||
static std::string ShaderDescribeSpecialization(const ShaderRecompiler::IR::Program& program) {
|
static std::string ShaderDescribeSpecialization(const ShaderRecompiler::IR::Program& program) {
|
||||||
std::string ret = fmt::format(
|
std::string ret = fmt::format(
|
||||||
"set={} push={} groups={} user={} buffers={} images={} samplers={} addresses={} srt={}",
|
"push={} groups={} user={} buffers={} images={} samplers={} addresses={} srt={}",
|
||||||
program.bindings.descriptor_set, program.bindings.push_constant_size,
|
program.bindings.push_constant_size, program.bindings.descriptors.size(),
|
||||||
program.bindings.descriptors.size(), program.bindings.user_data_registers.size(),
|
program.bindings.user_data_registers.size(), program.info.buffers.size(),
|
||||||
program.info.buffers.size(), program.info.images.size(), program.info.samplers.size(),
|
program.info.images.size(), program.info.samplers.size(), program.info.addresses.size(),
|
||||||
program.info.addresses.size(),
|
|
||||||
program.values != nullptr ? program.values->srt_reads.size() : 0u);
|
program.values != nullptr ? program.values->srt_reads.size() : 0u);
|
||||||
for (uint32_t i = 0; i < program.info.buffers.size(); i++) {
|
for (uint32_t i = 0; i < program.info.buffers.size(); i++) {
|
||||||
const auto& buffer = program.info.buffers[i];
|
const auto& buffer = program.info.buffers[i];
|
||||||
@@ -1050,8 +1045,6 @@ static std::string ShaderDescribeSpecialization(const ShaderRecompiler::IR::Prog
|
|||||||
static void ShaderAppendNativeSpecialization(std::vector<uint32_t>& ids,
|
static void ShaderAppendNativeSpecialization(std::vector<uint32_t>& ids,
|
||||||
const ShaderRecompiler::IR::Program& program) {
|
const ShaderRecompiler::IR::Program& program) {
|
||||||
EXIT_IF(!program.binding_layout_complete);
|
EXIT_IF(!program.binding_layout_complete);
|
||||||
ids.push_back(program.bindings.descriptor_set);
|
|
||||||
ids.push_back(program.bindings.push_constant_offset);
|
|
||||||
ids.push_back(program.bindings.push_constant_size);
|
ids.push_back(program.bindings.push_constant_size);
|
||||||
ids.push_back(program.bindings.memory_offset_dword);
|
ids.push_back(program.bindings.memory_offset_dword);
|
||||||
ids.push_back(program.bindings.memory_offset_count);
|
ids.push_back(program.bindings.memory_offset_count);
|
||||||
@@ -1061,7 +1054,6 @@ static void ShaderAppendNativeSpecialization(std::vector<uint32_t>&
|
|||||||
ids.push_back(static_cast<uint32_t>(program.bindings.descriptors.size()));
|
ids.push_back(static_cast<uint32_t>(program.bindings.descriptors.size()));
|
||||||
for (const auto& binding: program.bindings.descriptors) {
|
for (const auto& binding: program.bindings.descriptors) {
|
||||||
ids.push_back(static_cast<uint32_t>(binding.kind));
|
ids.push_back(static_cast<uint32_t>(binding.kind));
|
||||||
ids.push_back(binding.binding);
|
|
||||||
ids.push_back(static_cast<uint32_t>(binding.resources.size()));
|
ids.push_back(static_cast<uint32_t>(binding.resources.size()));
|
||||||
ids.insert(ids.end(), binding.resources.begin(), binding.resources.end());
|
ids.insert(ids.end(), binding.resources.begin(), binding.resources.end());
|
||||||
}
|
}
|
||||||
@@ -1456,8 +1448,6 @@ bool ShaderCompileSpirvVS(const HW::VertexShaderInfo& regs, const HW::ShaderRegi
|
|||||||
options.scratch_dwords = input_info.scratch_size_dwords;
|
options.scratch_dwords = input_info.scratch_size_dwords;
|
||||||
options.user_data = regs.gs_user_sgpr.value;
|
options.user_data = regs.gs_user_sgpr.value;
|
||||||
options.read_specialization_memory = ReadShaderGuestMemory;
|
options.read_specialization_memory = ReadShaderGuestMemory;
|
||||||
options.descriptor_set = 0;
|
|
||||||
options.push_constant_offset = 0;
|
|
||||||
options.input_info.vertex = &input_info;
|
options.input_info.vertex = &input_info;
|
||||||
options.dump_ir = ShaderRecompilerTextDumpEnabled();
|
options.dump_ir = ShaderRecompilerTextDumpEnabled();
|
||||||
options.early_dump = options.dump_ir;
|
options.early_dump = options.dump_ir;
|
||||||
@@ -1507,10 +1497,9 @@ bool ShaderCompileSpirvPS(const HW::PixelShaderInfo& regs, const HW::ShaderRegis
|
|||||||
options.shader_base = shader_addr;
|
options.shader_base = shader_addr;
|
||||||
options.user_data_count = regs.ps_regs.rsrc2.user_sgpr;
|
options.user_data_count = regs.ps_regs.rsrc2.user_sgpr;
|
||||||
options.scratch_dwords = input_info.scratch_size_dwords;
|
options.scratch_dwords = input_info.scratch_size_dwords;
|
||||||
|
options.push_constant_offset = input_info.push_constant_offset;
|
||||||
options.user_data = regs.ps_user_sgpr.value;
|
options.user_data = regs.ps_user_sgpr.value;
|
||||||
options.read_specialization_memory = ReadShaderGuestMemory;
|
options.read_specialization_memory = ReadShaderGuestMemory;
|
||||||
options.descriptor_set = input_info.descriptor_set;
|
|
||||||
options.push_constant_offset = input_info.push_constant_offset;
|
|
||||||
options.input_info.pixel = &input_info;
|
options.input_info.pixel = &input_info;
|
||||||
options.dump_ir = ShaderRecompilerTextDumpEnabled();
|
options.dump_ir = ShaderRecompilerTextDumpEnabled();
|
||||||
options.early_dump = options.dump_ir;
|
options.early_dump = options.dump_ir;
|
||||||
@@ -1560,8 +1549,6 @@ bool ShaderCompileSpirvCS(const HW::ComputeShaderInfo& regs, const HW::ShaderReg
|
|||||||
options.scratch_dwords = input_info.scratch_size_dwords;
|
options.scratch_dwords = input_info.scratch_size_dwords;
|
||||||
options.user_data = regs.cs_user_sgpr.value;
|
options.user_data = regs.cs_user_sgpr.value;
|
||||||
options.read_specialization_memory = ReadShaderGuestMemory;
|
options.read_specialization_memory = ReadShaderGuestMemory;
|
||||||
options.descriptor_set = 0;
|
|
||||||
options.push_constant_offset = 0;
|
|
||||||
options.input_info.compute = &input_info;
|
options.input_info.compute = &input_info;
|
||||||
options.wave_size = input_info.wave_size;
|
options.wave_size = input_info.wave_size;
|
||||||
options.dump_ir = ShaderRecompilerTextDumpEnabled();
|
options.dump_ir = ShaderRecompilerTextDumpEnabled();
|
||||||
@@ -1666,7 +1653,6 @@ ShaderId ShaderGetIdPS(const HW::PixelShaderInfo& regs, const ShaderPixelInputIn
|
|||||||
ret.hash0 = (regs.ps_regs.chksum >> 32u) & 0xffffffffu;
|
ret.hash0 = (regs.ps_regs.chksum >> 32u) & 0xffffffffu;
|
||||||
ret.crc32 = regs.ps_regs.chksum & 0xffffffffu;
|
ret.crc32 = regs.ps_regs.chksum & 0xffffffffu;
|
||||||
|
|
||||||
ret.ids.push_back(input_info.descriptor_set);
|
|
||||||
ret.ids.push_back(input_info.push_constant_offset);
|
ret.ids.push_back(input_info.push_constant_offset);
|
||||||
ret.ids.push_back(input_info.scratch_size_dwords);
|
ret.ids.push_back(input_info.scratch_size_dwords);
|
||||||
ret.ids.push_back(input_info.input_num);
|
ret.ids.push_back(input_info.input_num);
|
||||||
@@ -1678,6 +1664,7 @@ ShaderId ShaderGetIdPS(const HW::PixelShaderInfo& regs, const ShaderPixelInputIn
|
|||||||
ret.ids.push_back(static_cast<uint32_t>(input_info.ps_front_face));
|
ret.ids.push_back(static_cast<uint32_t>(input_info.ps_front_face));
|
||||||
ret.ids.push_back(static_cast<uint32_t>(input_info.ps_no_perspective));
|
ret.ids.push_back(static_cast<uint32_t>(input_info.ps_no_perspective));
|
||||||
ret.ids.push_back(static_cast<uint32_t>(input_info.ps_pixel_kill_enable));
|
ret.ids.push_back(static_cast<uint32_t>(input_info.ps_pixel_kill_enable));
|
||||||
|
ret.ids.push_back(static_cast<uint32_t>(input_info.ps_depth_export_enable));
|
||||||
ret.ids.push_back(static_cast<uint32_t>(input_info.ps_sample_mask_export_enable));
|
ret.ids.push_back(static_cast<uint32_t>(input_info.ps_sample_mask_export_enable));
|
||||||
ret.ids.push_back(static_cast<uint32_t>(input_info.ps_early_z));
|
ret.ids.push_back(static_cast<uint32_t>(input_info.ps_early_z));
|
||||||
ret.ids.push_back(static_cast<uint32_t>(input_info.ps_execute_on_noop));
|
ret.ids.push_back(static_cast<uint32_t>(input_info.ps_execute_on_noop));
|
||||||
@@ -1738,10 +1725,7 @@ ShaderId ShaderGetIdCS(const HW::ComputeShaderInfo& regs, const ShaderComputeInp
|
|||||||
ret.ids.push_back(input_info.threads_num[i]);
|
ret.ids.push_back(input_info.threads_num[i]);
|
||||||
ret.ids.push_back(static_cast<uint32_t>(input_info.group_id[i]));
|
ret.ids.push_back(static_cast<uint32_t>(input_info.group_id[i]));
|
||||||
}
|
}
|
||||||
ret.ids.push_back(static_cast<uint32_t>(input_info.dispatch_thread_dimensions));
|
ret.ids.push_back(static_cast<uint32_t>(input_info.tg_size_en));
|
||||||
for (uint32_t dim: input_info.dispatch_threads_num) {
|
|
||||||
ret.ids.push_back(dim);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (include_bind_specialization) {
|
if (include_bind_specialization) {
|
||||||
EXIT_IF(!input_info.stage);
|
EXIT_IF(!input_info.stage);
|
||||||
|
|||||||
@@ -120,7 +120,6 @@ struct ShaderPixelInputInfo {
|
|||||||
uint8_t target_output_mode[8] = {};
|
uint8_t target_output_mode[8] = {};
|
||||||
std::array<Prospero::ColorComponentMapping, 8> target_export_mapping = {};
|
std::array<Prospero::ColorComponentMapping, 8> target_export_mapping = {};
|
||||||
uint32_t mrt_output_mask = 0;
|
uint32_t mrt_output_mask = 0;
|
||||||
uint32_t descriptor_set = 0;
|
|
||||||
uint32_t push_constant_offset = 0;
|
uint32_t push_constant_offset = 0;
|
||||||
uint32_t scratch_size_dwords = 0;
|
uint32_t scratch_size_dwords = 0;
|
||||||
bool ps_pos_x = false;
|
bool ps_pos_x = false;
|
||||||
|
|||||||
@@ -636,23 +636,21 @@ void TestImagesSamplersAndAliases() {
|
|||||||
void TestSampleAdjustSamplerScratch() {
|
void TestSampleAdjustSamplerScratch() {
|
||||||
Fixture fixture(ShaderType::Pixel);
|
Fixture fixture(ShaderType::Pixel);
|
||||||
const auto active = fixture.Emit(ValueOpcode::WqmMask, {Value(true)});
|
const auto active = fixture.Emit(ValueOpcode::WqmMask, {Value(true)});
|
||||||
const auto lane = fixture.Emit(ValueOpcode::SelectU32,
|
const auto lane =
|
||||||
{active, Value(1u), Value(0u)});
|
fixture.Emit(ValueOpcode::SelectU32, {active, Value(1u), Value(0u)});
|
||||||
const auto low = fixture.Emit(ValueOpcode::BitwiseAnd32,
|
const auto low =
|
||||||
{lane, Value(0xffu)});
|
fixture.Emit(ValueOpcode::BitwiseAnd32, {lane, Value(0xffu)});
|
||||||
const auto high = fixture.Emit(ValueOpcode::BitwiseAnd32,
|
const auto high =
|
||||||
{lane, Value(0xffu)});
|
fixture.Emit(ValueOpcode::BitwiseAnd32, {lane, Value(0xffu)});
|
||||||
const auto quads = fixture.Emit(
|
const auto quads = fixture.Emit(
|
||||||
ValueOpcode::BitwiseOr32,
|
ValueOpcode::BitwiseOr32,
|
||||||
{low, fixture.Emit(ValueOpcode::ShiftLeftLogical32,
|
{low, fixture.Emit(ValueOpcode::ShiftLeftLogical32, {high, Value(8u)})});
|
||||||
{high, Value(8u)})});
|
const auto scratch =
|
||||||
const auto scratch = fixture.Emit(ValueOpcode::ShiftLeftLogical32,
|
fixture.Emit(ValueOpcode::ShiftLeftLogical32, {quads, Value(12u)});
|
||||||
{quads, Value(12u)});
|
const auto word3 =
|
||||||
const auto word3 = fixture.Emit(ValueOpcode::BitwiseOr32,
|
fixture.Emit(ValueOpcode::BitwiseOr32, {fixture.UserData(3), scratch});
|
||||||
{fixture.UserData(3), scratch});
|
const auto image = fixture.Image({Value(0u), Value(0u), Value(0u), Value(0u),
|
||||||
const auto image = fixture.Image(
|
Value(0u), Value(0u), Value(0u), Value(0u)},
|
||||||
{Value(0u), Value(0u), Value(0u), Value(0u), Value(0u), Value(0u),
|
|
||||||
Value(0u), Value(0u)},
|
|
||||||
0x1ec);
|
0x1ec);
|
||||||
const auto sampler = fixture.Sampler(
|
const auto sampler = fixture.Sampler(
|
||||||
{fixture.UserData(0), fixture.UserData(1), fixture.UserData(2), word3},
|
{fixture.UserData(0), fixture.UserData(1), fixture.UserData(2), word3},
|
||||||
@@ -685,21 +683,20 @@ void TestSampleAdjustSamplerScratch() {
|
|||||||
const auto CheckRejected = [](uint32_t flags, uint32_t shift,
|
const auto CheckRejected = [](uint32_t flags, uint32_t shift,
|
||||||
const char *message) {
|
const char *message) {
|
||||||
Fixture rejected(ShaderType::Pixel);
|
Fixture rejected(ShaderType::Pixel);
|
||||||
const auto condition =
|
const auto condition = rejected.Emit(ValueOpcode::WqmMask, {Value(true)});
|
||||||
rejected.Emit(ValueOpcode::WqmMask, {Value(true)});
|
|
||||||
const auto bit = rejected.Emit(ValueOpcode::SelectU32,
|
const auto bit = rejected.Emit(ValueOpcode::SelectU32,
|
||||||
{condition, Value(1u), Value(0u)});
|
{condition, Value(1u), Value(0u)});
|
||||||
const auto dynamic = rejected.Emit(ValueOpcode::ShiftLeftLogical32,
|
const auto dynamic =
|
||||||
{bit, Value(shift)});
|
rejected.Emit(ValueOpcode::ShiftLeftLogical32, {bit, Value(shift)});
|
||||||
const auto dynamic_word3 = rejected.Emit(
|
const auto dynamic_word3 = rejected.Emit(ValueOpcode::BitwiseOr32,
|
||||||
ValueOpcode::BitwiseOr32, {rejected.UserData(3), dynamic});
|
{rejected.UserData(3), dynamic});
|
||||||
const auto rejected_image = rejected.Image(
|
const auto rejected_image =
|
||||||
{Value(0u), Value(0u), Value(0u), Value(0u), Value(0u), Value(0u),
|
rejected.Image({Value(0u), Value(0u), Value(0u), Value(0u), Value(0u),
|
||||||
Value(0u), Value(0u)},
|
Value(0u), Value(0u), Value(0u)},
|
||||||
0x200);
|
0x200);
|
||||||
const auto rejected_sampler = rejected.Sampler(
|
const auto rejected_sampler =
|
||||||
{rejected.UserData(0), rejected.UserData(1), rejected.UserData(2),
|
rejected.Sampler({rejected.UserData(0), rejected.UserData(1),
|
||||||
dynamic_word3},
|
rejected.UserData(2), dynamic_word3},
|
||||||
0x200);
|
0x200);
|
||||||
MemoryInfo rejected_memory;
|
MemoryInfo rejected_memory;
|
||||||
rejected_memory.kind = ResourceKind::Image;
|
rejected_memory.kind = ResourceKind::Image;
|
||||||
@@ -789,7 +786,7 @@ void TestDynamicStorageMipTracking() {
|
|||||||
"base-1 through last-3 dynamic storage range was not specialized");
|
"base-1 through last-3 dynamic storage range was not specialized");
|
||||||
ShaderComputeInputInfo compute{};
|
ShaderComputeInputInfo compute{};
|
||||||
Check(CollectShaderInfo(fixture.program, {.compute = &compute}, &error) &&
|
Check(CollectShaderInfo(fixture.program, {.compute = &compute}, &error) &&
|
||||||
AllocateBindings(fixture.program, {}, &error),
|
AllocateBindings(fixture.program, 0, &error),
|
||||||
"dynamic storage mip bindings were not allocated");
|
"dynamic storage mip bindings were not allocated");
|
||||||
const auto *storage_binding =
|
const auto *storage_binding =
|
||||||
FindBinding(fixture.program.bindings, DescriptorBindingKind::Storage2D);
|
FindBinding(fixture.program.bindings, DescriptorBindingKind::Storage2D);
|
||||||
@@ -888,7 +885,7 @@ void TestSrtFlatteningAndRuntimeMemoization() {
|
|||||||
|
|
||||||
ShaderComputeInputInfo compute{};
|
ShaderComputeInputInfo compute{};
|
||||||
Check(CollectShaderInfo(fixture.program, {.compute = &compute}, &error) &&
|
Check(CollectShaderInfo(fixture.program, {.compute = &compute}, &error) &&
|
||||||
AllocateBindings(fixture.program, {}, &error) &&
|
AllocateBindings(fixture.program, 0, &error) &&
|
||||||
FindBinding(fixture.program.bindings,
|
FindBinding(fixture.program.bindings,
|
||||||
DescriptorBindingKind::FlattenedSrt) != nullptr,
|
DescriptorBindingKind::FlattenedSrt) != nullptr,
|
||||||
"flattened typed SRT reads did not receive a binding");
|
"flattened typed SRT reads did not receive a binding");
|
||||||
@@ -933,7 +930,7 @@ void TestDynamicSrtReadRemainsExplicit() {
|
|||||||
|
|
||||||
ShaderComputeInputInfo compute{};
|
ShaderComputeInputInfo compute{};
|
||||||
Check(CollectShaderInfo(fixture.program, {.compute = &compute}, &error) &&
|
Check(CollectShaderInfo(fixture.program, {.compute = &compute}, &error) &&
|
||||||
AllocateBindings(fixture.program, {}, &error) &&
|
AllocateBindings(fixture.program, 0, &error) &&
|
||||||
FindBinding(fixture.program.bindings,
|
FindBinding(fixture.program.bindings,
|
||||||
DescriptorBindingKind::FlattenedSrt) == nullptr &&
|
DescriptorBindingKind::FlattenedSrt) == nullptr &&
|
||||||
FindBinding(fixture.program.bindings,
|
FindBinding(fixture.program.bindings,
|
||||||
@@ -1198,24 +1195,87 @@ void TestShaderInfoAndBindingLayout() {
|
|||||||
StageInputKind::GlobalInvocationId,
|
StageInputKind::GlobalInvocationId,
|
||||||
"typed shader values were not reflected in shader info");
|
"typed shader values were not reflected in shader info");
|
||||||
|
|
||||||
BindingLayoutOptions options;
|
Check(AllocateBindings(fixture.program, 0, &error),
|
||||||
options.descriptor_set = 2;
|
|
||||||
options.max_push_dwords = 1;
|
|
||||||
Check(AllocateBindings(fixture.program, options, &error),
|
|
||||||
"typed binding allocation failed");
|
"typed binding allocation failed");
|
||||||
Check(fixture.program.bindings.descriptor_set == 2 &&
|
Check(FindBinding(fixture.program.bindings, DescriptorBindingKind::Buffers) !=
|
||||||
FindBinding(fixture.program.bindings,
|
nullptr &&
|
||||||
DescriptorBindingKind::Buffers) != nullptr &&
|
|
||||||
FindBinding(fixture.program.bindings, DescriptorBindingKind::Gds) !=
|
FindBinding(fixture.program.bindings, DescriptorBindingKind::Gds) !=
|
||||||
nullptr &&
|
nullptr &&
|
||||||
FindBinding(fixture.program.bindings,
|
FindBinding(fixture.program.bindings,
|
||||||
DescriptorBindingKind::UserData) != nullptr,
|
DescriptorBindingKind::UserData) == nullptr &&
|
||||||
|
fixture.program.bindings.push_constant_size ==
|
||||||
|
fixture.program.bindings.ShaderDataDwords() * sizeof(uint32_t),
|
||||||
"typed resources were not assigned native bindings");
|
"typed resources were not assigned native bindings");
|
||||||
|
Check(NativeBinding(ShaderType::Compute, DescriptorBindingKind::Buffers) ==
|
||||||
|
static_cast<uint32_t>(DescriptorBindingKind::Buffers) &&
|
||||||
|
NativeBinding(ShaderType::Vertex, DescriptorBindingKind::Buffers) ==
|
||||||
|
static_cast<uint32_t>(DescriptorBindingKind::Buffers) &&
|
||||||
|
NativeBinding(ShaderType::Pixel, DescriptorBindingKind::Buffers) ==
|
||||||
|
static_cast<uint32_t>(DescriptorBindingKind::Count) +
|
||||||
|
static_cast<uint32_t>(DescriptorBindingKind::Buffers),
|
||||||
|
"fixed stage binding ranges are inconsistent");
|
||||||
Check(fixture.program.bindings.user_data_registers ==
|
Check(fixture.program.bindings.user_data_registers ==
|
||||||
std::vector<uint32_t>({3u, 4u}),
|
std::vector<uint32_t>({3u, 4u}),
|
||||||
"binding layout did not collect live typed user-data values");
|
"binding layout did not collect live typed user-data values");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void TestGraphicsPushConstantLayout() {
|
||||||
|
const auto AddUserData = [](Fixture &fixture, uint32_t count) {
|
||||||
|
for (uint32_t index = 0; index < count; index++) {
|
||||||
|
fixture.Emit(ValueOpcode::ReferenceU32, {fixture.UserData(index)});
|
||||||
|
}
|
||||||
|
fixture.program.shader_info_complete = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
std::string error;
|
||||||
|
Fixture vertex(ShaderType::Vertex);
|
||||||
|
AddUserData(vertex, 9);
|
||||||
|
Check(AllocateBindings(vertex.program, 0, &error) &&
|
||||||
|
vertex.program.bindings.push_constant_offset == 0 &&
|
||||||
|
vertex.program.bindings.push_constant_size == 9 * sizeof(uint32_t),
|
||||||
|
"vertex shader was not placed at the start of the graphics push bank");
|
||||||
|
|
||||||
|
const auto pixel_offset = vertex.program.bindings.push_constant_size;
|
||||||
|
Fixture pixel(ShaderType::Pixel);
|
||||||
|
AddUserData(pixel, 4);
|
||||||
|
Check(AllocateBindings(pixel.program, pixel_offset, &error) &&
|
||||||
|
pixel.program.bindings.push_constant_offset == pixel_offset &&
|
||||||
|
pixel.program.bindings.push_constant_size == 4 * sizeof(uint32_t) &&
|
||||||
|
FindBinding(pixel.program.bindings, DescriptorBindingKind::UserData) ==
|
||||||
|
nullptr,
|
||||||
|
"pixel shader did not follow the vertex data in the graphics push bank");
|
||||||
|
|
||||||
|
Fixture edge(ShaderType::Pixel);
|
||||||
|
AddUserData(edge, 1);
|
||||||
|
Check(AllocateBindings(edge.program, NativePushConstantSize - sizeof(uint32_t),
|
||||||
|
&error) &&
|
||||||
|
edge.program.bindings.push_constant_size == sizeof(uint32_t),
|
||||||
|
"last aligned push-constant dword did not fit in the graphics bank");
|
||||||
|
|
||||||
|
Fixture spill(ShaderType::Pixel);
|
||||||
|
AddUserData(spill, 32);
|
||||||
|
Check(AllocateBindings(spill.program, pixel_offset, &error) &&
|
||||||
|
spill.program.bindings.push_constant_size == 0 &&
|
||||||
|
FindBinding(spill.program.bindings, DescriptorBindingKind::UserData) !=
|
||||||
|
nullptr,
|
||||||
|
"pixel shader overlapping the vertex push data did not spill to storage");
|
||||||
|
|
||||||
|
Fixture full(ShaderType::Pixel);
|
||||||
|
AddUserData(full, 1);
|
||||||
|
Check(AllocateBindings(full.program, NativePushConstantSize, &error) &&
|
||||||
|
full.program.bindings.push_constant_size == 0 &&
|
||||||
|
FindBinding(full.program.bindings, DescriptorBindingKind::UserData) !=
|
||||||
|
nullptr,
|
||||||
|
"full graphics push bank did not spill pixel user data to storage");
|
||||||
|
|
||||||
|
Fixture invalid(ShaderType::Pixel);
|
||||||
|
AddUserData(invalid, 1);
|
||||||
|
Check(!AllocateBindings(invalid.program,
|
||||||
|
NativePushConstantSize + sizeof(uint32_t), &error) &&
|
||||||
|
!invalid.program.binding_layout_complete,
|
||||||
|
"push-constant placement beyond the graphics bank was accepted");
|
||||||
|
}
|
||||||
|
|
||||||
void TestResourceLimitIsTransactional() {
|
void TestResourceLimitIsTransactional() {
|
||||||
Fixture fixture;
|
Fixture fixture;
|
||||||
MemoryInfo memory;
|
MemoryInfo memory;
|
||||||
@@ -1339,6 +1399,7 @@ int main() {
|
|||||||
Run("exec-masked FLAT address", TestExecMaskedFlatAddressProvenance);
|
Run("exec-masked FLAT address", TestExecMaskedFlatAddressProvenance);
|
||||||
Run("buffer swizzle specialization", TestBufferSwizzleSpecialization);
|
Run("buffer swizzle specialization", TestBufferSwizzleSpecialization);
|
||||||
Run("shader info and bindings", TestShaderInfoAndBindingLayout);
|
Run("shader info and bindings", TestShaderInfoAndBindingLayout);
|
||||||
|
Run("graphics push constants", TestGraphicsPushConstantLayout);
|
||||||
Run("resource limit", TestResourceLimitIsTransactional);
|
Run("resource limit", TestResourceLimitIsTransactional);
|
||||||
Run("malformed memory kinds", TestMalformedMemoryKindsRejected);
|
Run("malformed memory kinds", TestMalformedMemoryKindsRejected);
|
||||||
} catch (const std::exception &exception) {
|
} catch (const std::exception &exception) {
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
+187
-38
@@ -690,6 +690,24 @@ bool SpirvHasDecorationValue(const std::vector<uint32_t> &binary,
|
|||||||
return SpirvDecorationValueCount(binary, decoration, value) != 0;
|
return SpirvDecorationValueCount(binary, decoration, value) != 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool SpirvHasMemberDecorationValue(std::span<const uint32_t> binary,
|
||||||
|
uint32_t decoration, uint32_t value) {
|
||||||
|
for (size_t i = 5; i < binary.size();) {
|
||||||
|
const uint32_t word = binary[i];
|
||||||
|
const uint32_t opcode = word & 0xffffu;
|
||||||
|
const uint32_t word_count = word >> 16u;
|
||||||
|
if (word_count == 0 || i + word_count > binary.size()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (opcode == 72u && word_count >= 5u && binary[i + 3] == decoration &&
|
||||||
|
binary[i + 4] == value) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
i += word_count;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
std::vector<uint32_t>
|
std::vector<uint32_t>
|
||||||
SpirvDecorationValueTargets(const std::vector<uint32_t> &binary,
|
SpirvDecorationValueTargets(const std::vector<uint32_t> &binary,
|
||||||
uint32_t decoration, uint32_t value) {
|
uint32_t decoration, uint32_t value) {
|
||||||
@@ -9868,19 +9886,20 @@ void TestRenderTargetReverseExportMapping() {
|
|||||||
mapped.code_size_bytes = sizeof(shader);
|
mapped.code_size_bytes = sizeof(shader);
|
||||||
ShaderMapUserData(regs.ps_regs.data_addr, mapped);
|
ShaderMapUserData(regs.ps_regs.data_addr, mapped);
|
||||||
HW::ShaderRegisters sh{};
|
HW::ShaderRegisters sh{};
|
||||||
ShaderVertexInputInfo vs_info{};
|
|
||||||
vs_info.stage.program = std::make_shared<ShaderRecompiler::IR::Program>();
|
|
||||||
std::array<Prospero::ColorComponentMapping, 8> mappings{};
|
std::array<Prospero::ColorComponentMapping, 8> mappings{};
|
||||||
mappings[0] = gr32.export_mapping;
|
mappings[0] = gr32.export_mapping;
|
||||||
ShaderPixelInputInfo compiled_info{};
|
ShaderPixelInputInfo compiled_info{};
|
||||||
|
ShaderVertexInputInfo vertex_info{};
|
||||||
std::span<const uint32_t> compiled_spirv;
|
std::span<const uint32_t> compiled_spirv;
|
||||||
Check(ShaderCompileInfoPS(regs, sh, vs_info, mappings, compiled_info,
|
Check(
|
||||||
|
ShaderCompileInfoPS(regs, sh, vertex_info, mappings, compiled_info,
|
||||||
compiled_spirv) &&
|
compiled_spirv) &&
|
||||||
compiled_info.target_export_mapping[0].IsIdentity(),
|
compiled_info.target_export_mapping[0].IsIdentity(),
|
||||||
"inactive reverse MRT mapping was not normalized out of the shader "
|
"inactive reverse MRT mapping was not normalized out of the shader "
|
||||||
"cache key");
|
"cache key");
|
||||||
sh.target_output_mode[0] = 4;
|
sh.target_output_mode[0] = 4;
|
||||||
Check(ShaderCompileInfoPS(regs, sh, vs_info, mappings, compiled_info,
|
Check(
|
||||||
|
ShaderCompileInfoPS(regs, sh, vertex_info, mappings, compiled_info,
|
||||||
compiled_spirv) &&
|
compiled_spirv) &&
|
||||||
compiled_info.target_export_mapping[0] == gr32.export_mapping,
|
compiled_info.target_export_mapping[0] == gr32.export_mapping,
|
||||||
"active reverse MRT mapping was lost before shader specialization");
|
"active reverse MRT mapping was lost before shader specialization");
|
||||||
@@ -9988,15 +10007,21 @@ void TestNewShaderRecompilerNativeBindingPlan() {
|
|||||||
"native binding plan did not allocate the sampler");
|
"native binding plan did not allocate the sampler");
|
||||||
Check(SpirvContainsOpcode(result.spirv, 86),
|
Check(SpirvContainsOpcode(result.spirv, 86),
|
||||||
"SPIR-V binary does not combine separate image/sampler descriptors");
|
"SPIR-V binary does not combine separate image/sampler descriptors");
|
||||||
Check(SpirvHasDecorationValue(result.spirv, 33u, buffers->binding),
|
Check(SpirvHasDecorationValue(result.spirv, 33u,
|
||||||
|
ShaderRecompiler::IR::NativeBinding(
|
||||||
|
result.program.stage, buffers->kind)),
|
||||||
"SPIR-V lacks storage-buffer Binding decoration");
|
"SPIR-V lacks storage-buffer Binding decoration");
|
||||||
Check(SpirvHasDecorationValue(result.spirv, 33u, sampled->binding),
|
Check(SpirvHasDecorationValue(result.spirv, 33u,
|
||||||
|
ShaderRecompiler::IR::NativeBinding(
|
||||||
|
result.program.stage, sampled->kind)),
|
||||||
"SPIR-V lacks sampled-image Binding decoration");
|
"SPIR-V lacks sampled-image Binding decoration");
|
||||||
Check(SpirvHasDecorationValue(result.spirv, 33u, samplers->binding),
|
Check(SpirvHasDecorationValue(result.spirv, 33u,
|
||||||
|
ShaderRecompiler::IR::NativeBinding(
|
||||||
|
result.program.stage, samplers->kind)),
|
||||||
"SPIR-V lacks sampler Binding decoration");
|
"SPIR-V lacks sampler Binding decoration");
|
||||||
Check(SpirvHasDecorationValue(result.spirv, 34u,
|
Check(SpirvDecorationValueCount(result.spirv, 34u, 0u) ==
|
||||||
result.program.bindings.descriptor_set),
|
result.program.bindings.descriptors.size(),
|
||||||
"SPIR-V lacks DescriptorSet decoration");
|
"SPIR-V resources do not all use descriptor set zero");
|
||||||
CheckSpirvBinaryValidates(result.spirv);
|
CheckSpirvBinaryValidates(result.spirv);
|
||||||
|
|
||||||
auto malformed = result.program;
|
auto malformed = result.program;
|
||||||
@@ -10945,6 +10970,31 @@ void TestComputeLdsAllocationIdentity() {
|
|||||||
ShaderGetIdCS(regs, lds_896, true),
|
ShaderGetIdCS(regs, lds_896, true),
|
||||||
"compute pipeline identity omitted the LDS allocation");
|
"compute pipeline identity omitted the LDS allocation");
|
||||||
|
|
||||||
|
auto tg_size_disabled = lds_896;
|
||||||
|
auto tg_size_enabled = lds_896;
|
||||||
|
tg_size_enabled.tg_size_en = true;
|
||||||
|
Check(ShaderGetIdCS(regs, tg_size_disabled, false) !=
|
||||||
|
ShaderGetIdCS(regs, tg_size_enabled, false),
|
||||||
|
"compute shader identity omitted TG_SIZE semantics");
|
||||||
|
|
||||||
|
auto dispatch_disabled = lds_896;
|
||||||
|
dispatch_disabled.dispatch_threads_num[0] = 64;
|
||||||
|
dispatch_disabled.dispatch_threads_num[1] = 32;
|
||||||
|
dispatch_disabled.dispatch_threads_num[2] = 1;
|
||||||
|
auto dispatch_enabled = dispatch_disabled;
|
||||||
|
dispatch_enabled.dispatch_thread_dimensions = true;
|
||||||
|
Check(ShaderGetIdCS(regs, dispatch_disabled, false) ==
|
||||||
|
ShaderGetIdCS(regs, dispatch_enabled, false),
|
||||||
|
"runtime dispatch enable created a compute shader variant");
|
||||||
|
|
||||||
|
auto second_extent = dispatch_enabled;
|
||||||
|
second_extent.dispatch_threads_num[0] = 1920;
|
||||||
|
second_extent.dispatch_threads_num[1] = 1080;
|
||||||
|
second_extent.dispatch_threads_num[2] = 4;
|
||||||
|
Check(ShaderGetIdCS(regs, dispatch_enabled, false) ==
|
||||||
|
ShaderGetIdCS(regs, second_extent, false),
|
||||||
|
"runtime dispatch extent created a compute shader variant");
|
||||||
|
|
||||||
mapped.scratch_size_dwords = 7;
|
mapped.scratch_size_dwords = 7;
|
||||||
ShaderMapUserData(regs.cs_regs.data_addr, mapped);
|
ShaderMapUserData(regs.cs_regs.data_addr, mapped);
|
||||||
regs.cs_regs.lds_size = decode_lds_field(lds_896_rsrc2);
|
regs.cs_regs.lds_size = decode_lds_field(lds_896_rsrc2);
|
||||||
@@ -10981,14 +11031,25 @@ void TestComputeLdsAllocationIdentity() {
|
|||||||
CheckSpirvBinaryValidates(append_result.spirv);
|
CheckSpirvBinaryValidates(append_result.spirv);
|
||||||
}
|
}
|
||||||
|
|
||||||
void TestPixelProgramCacheDescriptorSetIdentity() {
|
void TestPixelProgramCacheBindingIdentity() {
|
||||||
const uint32_t shader_01[] = {0xbf810000u};
|
const uint32_t shader_01[] = {0xbf810000u};
|
||||||
const uint32_t shader_10[] = {0xbf810000u};
|
const uint32_t shader_10[] = {0xbf810000u};
|
||||||
HW::ShaderRegisters sh{};
|
HW::ShaderRegisters sh{};
|
||||||
|
|
||||||
auto check_transition = [&](const uint32_t *shader, uint64_t checksum,
|
HW::PixelShaderInfo identity_regs{};
|
||||||
bool first_has_vs_descriptors,
|
ShaderPixelInputInfo no_depth_export{};
|
||||||
bool second_has_vs_descriptors) {
|
auto depth_export = no_depth_export;
|
||||||
|
depth_export.ps_depth_export_enable = true;
|
||||||
|
Check(ShaderGetIdPS(identity_regs, no_depth_export, false) !=
|
||||||
|
ShaderGetIdPS(identity_regs, depth_export, false),
|
||||||
|
"pixel shader identity omitted depth-export semantics");
|
||||||
|
auto shifted_push = no_depth_export;
|
||||||
|
shifted_push.push_constant_offset = 36;
|
||||||
|
Check(ShaderGetIdPS(identity_regs, no_depth_export, false) !=
|
||||||
|
ShaderGetIdPS(identity_regs, shifted_push, false),
|
||||||
|
"pixel shader identity omitted graphics push-bank placement");
|
||||||
|
|
||||||
|
auto check_cache_identity = [&](const uint32_t *shader, uint64_t checksum) {
|
||||||
HW::PixelShaderInfo regs{};
|
HW::PixelShaderInfo regs{};
|
||||||
regs.ps_regs.data_addr = reinterpret_cast<uint64_t>(shader);
|
regs.ps_regs.data_addr = reinterpret_cast<uint64_t>(shader);
|
||||||
regs.ps_regs.chksum = checksum;
|
regs.ps_regs.chksum = checksum;
|
||||||
@@ -10996,33 +11057,120 @@ void TestPixelProgramCacheDescriptorSetIdentity() {
|
|||||||
mapped.code_size_bytes = sizeof(uint32_t);
|
mapped.code_size_bytes = sizeof(uint32_t);
|
||||||
ShaderMapUserData(regs.ps_regs.data_addr, mapped);
|
ShaderMapUserData(regs.ps_regs.data_addr, mapped);
|
||||||
|
|
||||||
auto compile = [&](bool has_vs_descriptors) {
|
|
||||||
auto vs_program = std::make_shared<ShaderRecompiler::IR::Program>();
|
|
||||||
if (has_vs_descriptors) {
|
|
||||||
vs_program->bindings.descriptors.emplace_back();
|
|
||||||
}
|
|
||||||
ShaderVertexInputInfo vs_info{};
|
|
||||||
vs_info.stage.program = std::move(vs_program);
|
|
||||||
const std::array<Prospero::ColorComponentMapping, 8> identity_mappings{};
|
const std::array<Prospero::ColorComponentMapping, 8> identity_mappings{};
|
||||||
ShaderPixelInputInfo ps_info{};
|
ShaderVertexInputInfo vertex_info{};
|
||||||
std::span<const uint32_t> spirv;
|
ShaderPixelInputInfo first_info{};
|
||||||
Check(ShaderCompileInfoPS(regs, sh, vs_info, identity_mappings, ps_info,
|
std::span<const uint32_t> first_spirv;
|
||||||
spirv),
|
Check(ShaderCompileInfoPS(regs, sh, vertex_info, identity_mappings, first_info,
|
||||||
"pixel program-cache transition failed to compile");
|
first_spirv),
|
||||||
const auto expected_set = has_vs_descriptors ? 1u : 0u;
|
"pixel program-cache fixture failed to compile");
|
||||||
Check(ps_info.descriptor_set == expected_set &&
|
|
||||||
ps_info.stage.program != nullptr &&
|
ShaderPixelInputInfo second_info{};
|
||||||
ps_info.stage.program->bindings.descriptor_set == expected_set,
|
std::span<const uint32_t> second_spirv;
|
||||||
"pixel program cache reused SPIR-V with the paired VS descriptor "
|
Check(ShaderCompileInfoPS(regs, sh, vertex_info, identity_mappings, second_info,
|
||||||
"set");
|
second_spirv),
|
||||||
|
"pixel program-cache lookup failed");
|
||||||
|
Check(
|
||||||
|
first_info.stage.program != nullptr &&
|
||||||
|
second_info.stage.program == first_info.stage.program &&
|
||||||
|
second_spirv.data() == first_spirv.data() &&
|
||||||
|
second_spirv.size() == first_spirv.size(),
|
||||||
|
"pixel program cache did not reuse an identical graphics placement");
|
||||||
};
|
};
|
||||||
|
|
||||||
compile(first_has_vs_descriptors);
|
check_cache_identity(shader_01, 0x91a27e6300000001ull);
|
||||||
compile(second_has_vs_descriptors);
|
check_cache_identity(shader_10, 0x91a27e6300000002ull);
|
||||||
};
|
|
||||||
|
|
||||||
check_transition(shader_01, 0x91a27e6300000001ull, false, true);
|
const uint32_t push_shader[] = {
|
||||||
check_transition(shader_10, 0x91a27e6300000002ull, true, false);
|
EncodeVop1(0x01, 0, 0), EncodeVop1(0x01, 1, 1),
|
||||||
|
EncodeVop1(0x01, 2, 2), EncodeVop1(0x01, 3, 3),
|
||||||
|
EncodeExp0(0x00, 0xf), EncodeExp1(0, 1, 2, 3), 0xbf810000u,
|
||||||
|
};
|
||||||
|
HW::PixelShaderInfo push_regs{};
|
||||||
|
push_regs.ps_regs.data_addr = reinterpret_cast<uint64_t>(push_shader);
|
||||||
|
push_regs.ps_regs.chksum = 0x91a27e6300000003ull;
|
||||||
|
push_regs.ps_regs.rsrc2.user_sgpr = 4;
|
||||||
|
ShaderMappedData push_mapped{};
|
||||||
|
push_mapped.code_size_bytes = sizeof(push_shader);
|
||||||
|
ShaderMapUserData(push_regs.ps_regs.data_addr, push_mapped);
|
||||||
|
HW::ShaderRegisters push_sh{};
|
||||||
|
push_sh.target_output_mode[0] = 4;
|
||||||
|
const std::array<Prospero::ColorComponentMapping, 8> mappings{};
|
||||||
|
const auto MakeVertex = [](uint32_t push_size) {
|
||||||
|
ShaderVertexInputInfo info{};
|
||||||
|
auto program = std::make_shared<ShaderRecompiler::IR::Program>();
|
||||||
|
program->stage = ShaderType::Vertex;
|
||||||
|
program->bindings.push_constant_size = push_size;
|
||||||
|
info.stage.program = std::move(program);
|
||||||
|
return info;
|
||||||
|
};
|
||||||
|
const auto vertex_at_zero = MakeVertex(0);
|
||||||
|
const auto vertex_at_36 = MakeVertex(36);
|
||||||
|
ShaderPixelInputInfo at_zero{};
|
||||||
|
ShaderPixelInputInfo at_36{};
|
||||||
|
ShaderPixelInputInfo at_zero_again{};
|
||||||
|
std::span<const uint32_t> spirv_at_zero;
|
||||||
|
std::span<const uint32_t> spirv_at_36;
|
||||||
|
std::span<const uint32_t> spirv_at_zero_again;
|
||||||
|
Check(ShaderCompileInfoPS(push_regs, push_sh, vertex_at_zero, mappings,
|
||||||
|
at_zero, spirv_at_zero) &&
|
||||||
|
ShaderCompileInfoPS(push_regs, push_sh, vertex_at_36, mappings,
|
||||||
|
at_36, spirv_at_36) &&
|
||||||
|
ShaderCompileInfoPS(push_regs, push_sh, vertex_at_zero, mappings,
|
||||||
|
at_zero_again, spirv_at_zero_again),
|
||||||
|
"pixel program-cache placement fixture failed to compile");
|
||||||
|
Check(at_zero.push_constant_offset == 0 &&
|
||||||
|
at_36.push_constant_offset == 36 &&
|
||||||
|
at_zero.stage.program != at_36.stage.program &&
|
||||||
|
spirv_at_zero.data() != spirv_at_36.data() &&
|
||||||
|
at_zero_again.stage.program == at_zero.stage.program &&
|
||||||
|
spirv_at_zero_again.data() == spirv_at_zero.data() &&
|
||||||
|
SpirvHasMemberDecorationValue(spirv_at_36, 35u, 36u),
|
||||||
|
"pixel program cache did not partition and reuse graphics push placement");
|
||||||
|
}
|
||||||
|
|
||||||
|
void TestGraphicsPushConstantPlacement() {
|
||||||
|
using BindingKind = ShaderRecompiler::IR::DescriptorBindingKind;
|
||||||
|
constexpr uint32_t OffsetDecoration = 35;
|
||||||
|
const uint32_t shader[] = {
|
||||||
|
EncodeVop1(0x01, 0, 0), EncodeVop1(0x01, 1, 1),
|
||||||
|
EncodeVop1(0x01, 2, 2), EncodeVop1(0x01, 3, 3),
|
||||||
|
EncodeExp0(0x00, 0xf), EncodeExp1(0, 1, 2, 3), 0xbf810000u,
|
||||||
|
};
|
||||||
|
const std::array<uint32_t, 4> user_data = {
|
||||||
|
0x3e800000u, 0x3f000000u, 0x3f400000u, 0x3f800000u};
|
||||||
|
ShaderPixelInputInfo pixel_info{};
|
||||||
|
|
||||||
|
auto options = MakeCompileOptions(ShaderType::Pixel);
|
||||||
|
options.input_info.pixel = &pixel_info;
|
||||||
|
options.user_data = user_data.data();
|
||||||
|
options.user_data_count = static_cast<uint32_t>(user_data.size());
|
||||||
|
options.push_constant_offset = 36;
|
||||||
|
|
||||||
|
ShaderRecompiler::CompileResult placed;
|
||||||
|
std::string error;
|
||||||
|
Check(ShaderRecompiler::TryRecompile(shader, options, placed, &error),
|
||||||
|
error.c_str());
|
||||||
|
Check(placed.program.bindings.push_constant_offset == 36 &&
|
||||||
|
placed.program.bindings.push_constant_size == sizeof(user_data) &&
|
||||||
|
ShaderRecompiler::IR::FindBinding(placed.program.bindings,
|
||||||
|
BindingKind::UserData) == nullptr,
|
||||||
|
"pixel push constants did not follow the vertex payload");
|
||||||
|
Check(SpirvHasMemberDecorationValue(placed.spirv, OffsetDecoration, 36),
|
||||||
|
"SPIR-V push block omitted the graphics-bank member offset");
|
||||||
|
CheckSpirvBinaryValidates(placed.spirv);
|
||||||
|
|
||||||
|
options.push_constant_offset = 124;
|
||||||
|
ShaderRecompiler::CompileResult spilled;
|
||||||
|
error.clear();
|
||||||
|
Check(ShaderRecompiler::TryRecompile(shader, options, spilled, &error),
|
||||||
|
error.c_str());
|
||||||
|
Check(spilled.program.bindings.push_constant_offset == 124 &&
|
||||||
|
spilled.program.bindings.push_constant_size == 0 &&
|
||||||
|
ShaderRecompiler::IR::FindBinding(spilled.program.bindings,
|
||||||
|
BindingKind::UserData) != nullptr,
|
||||||
|
"pixel data that crossed the graphics push bank did not spill");
|
||||||
|
CheckSpirvBinaryValidates(spilled.spirv);
|
||||||
}
|
}
|
||||||
|
|
||||||
void TestNewShaderRecompilerUnsupportedMemoryDecode() {
|
void TestNewShaderRecompilerUnsupportedMemoryDecode() {
|
||||||
@@ -11478,7 +11626,8 @@ int main() {
|
|||||||
TestGraphicsCreateInterpolantMapping();
|
TestGraphicsCreateInterpolantMapping();
|
||||||
TestNewShaderRecompilerPixelPipelineEntry();
|
TestNewShaderRecompilerPixelPipelineEntry();
|
||||||
TestComputeLdsAllocationIdentity();
|
TestComputeLdsAllocationIdentity();
|
||||||
TestPixelProgramCacheDescriptorSetIdentity();
|
TestPixelProgramCacheBindingIdentity();
|
||||||
|
TestGraphicsPushConstantPlacement();
|
||||||
TestNewShaderRecompilerUnsupportedMemoryDecode();
|
TestNewShaderRecompilerUnsupportedMemoryDecode();
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|||||||
Reference in New Issue
Block a user