perf(agc): give the fragment stage its own descriptor set when modules are built at registration
Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
baf3437465
commit
4d0b5c7898
|
|
@ -19,6 +19,8 @@ struct PipelineLibraryKeys {
|
|||
std::vector<std::byte> fragmentOutput;
|
||||
std::vector<std::byte> renderPass;
|
||||
std::vector<std::byte> layout;
|
||||
std::vector<std::byte> preRasterizationLayout;
|
||||
std::vector<std::byte> fragmentLayout;
|
||||
};
|
||||
|
||||
std::span<const VkDynamicState> PipelineLibraryDynamicStates();
|
||||
|
|
|
|||
|
|
@ -117,6 +117,8 @@ public:
|
|||
std::vector<VkDescriptorSetLayoutBinding> LayoutBindings() const;
|
||||
bool LayoutUpdateAfterBind() const;
|
||||
std::span<const VkDescriptorBindingFlags> LayoutBindingFlags() const { return layoutBindingFlags; }
|
||||
VkDescriptorSetLayout FragmentLayout() const { return fragment.layout; }
|
||||
bool SplitSets() const { return splitSets; }
|
||||
void Bind(VkCommandBuffer commands, VkPipelineBindPoint bindPoint, VkPipelineLayout layout) const;
|
||||
struct DrawBindings {
|
||||
DrawBindings() = default;
|
||||
|
|
@ -127,6 +129,7 @@ public:
|
|||
std::shared_ptr<Buffer> buffer;
|
||||
};
|
||||
DescriptorCache* cache = nullptr;
|
||||
DescriptorCache::SetAllocation fragmentAllocation;
|
||||
DescriptorCache::SetAllocation allocation;
|
||||
std::vector<Snapshot> snapshots;
|
||||
~DrawBindings();
|
||||
|
|
@ -165,6 +168,7 @@ public:
|
|||
bool ReadsImage(const StorageTexture* image) const;
|
||||
bool HasCachedStorageImages() const;
|
||||
const std::vector<std::uint32_t>& LayoutKey() const { return layoutKey; }
|
||||
const std::vector<std::uint32_t>& FragmentLayoutKey() const { return fragmentLayoutKey; }
|
||||
// Debug aid: each bound guest resource with the fraction of sampled bytes that are nonzero.
|
||||
std::string Describe() const;
|
||||
|
||||
|
|
@ -428,6 +432,17 @@ private:
|
|||
VkDescriptorPool pool = VK_NULL_HANDLE;
|
||||
// The cache pool the set was allocated from, freed back to it on release.
|
||||
DescriptorCache::SetAllocation cacheAllocation;
|
||||
struct SetObjects {
|
||||
VkDescriptorSetLayout layout = VK_NULL_HANDLE;
|
||||
bool ownsLayout = false;
|
||||
VkDescriptorSet set = VK_NULL_HANDLE;
|
||||
VkDescriptorPool pool = VK_NULL_HANDLE;
|
||||
DescriptorCache::SetAllocation cacheAllocation;
|
||||
};
|
||||
bool splitSets = false;
|
||||
std::vector<std::uint32_t> fragmentLayoutKey;
|
||||
SetObjects fragment;
|
||||
std::uint32_t setOf(const Binding& binding) const;
|
||||
std::vector<Allocation> allocations;
|
||||
std::vector<VkDescriptorBufferInfo> descriptorBuffers;
|
||||
std::vector<VkDescriptorImageInfo> descriptorImages;
|
||||
|
|
@ -479,6 +494,7 @@ private:
|
|||
// the descriptor counts the set was sized for, and the compute stage of a deferred build.
|
||||
std::vector<Binding> bindings;
|
||||
std::vector<VkDescriptorBindingFlags> layoutBindingFlags;
|
||||
std::vector<VkDescriptorBindingFlags> fragmentBindingFlags;
|
||||
std::vector<std::uint32_t> refreshResourceKey;
|
||||
struct DeferredImages {
|
||||
const ShaderRecompiler::DescriptorBinding* binding;
|
||||
|
|
|
|||
|
|
@ -1764,7 +1764,9 @@ void recordDraw(const Context& context, const State& state, const Pm4::DrawParam
|
|||
APS5_LOG_CHARS_OUT_DEBUG("Pipeline Begin OK");
|
||||
if (drawBindings != nullptr) {
|
||||
const auto set = drawBindings->allocation.set;
|
||||
context.Resolved(&DeviceFunctions::cmdBindDescriptorSets, "vkCmdBindDescriptorSets")(commands, VK_PIPELINE_BIND_POINT_GRAPHICS, record.pipeline->Layout(), 0, 1, &set, 0, nullptr);
|
||||
const auto fragmentSet = drawBindings->fragmentAllocation.set;
|
||||
if (set != VK_NULL_HANDLE) context.Resolved(&DeviceFunctions::cmdBindDescriptorSets, "vkCmdBindDescriptorSets")(commands, VK_PIPELINE_BIND_POINT_GRAPHICS, record.pipeline->Layout(), 0, 1, &set, 0, nullptr);
|
||||
if (fragmentSet != VK_NULL_HANDLE) context.Resolved(&DeviceFunctions::cmdBindDescriptorSets, "vkCmdBindDescriptorSets")(commands, VK_PIPELINE_BIND_POINT_GRAPHICS, record.pipeline->Layout(), ShaderRecompiler::RuntimeAbi::FragmentDescriptorSet, 1, &fragmentSet, 0, nullptr);
|
||||
} else {
|
||||
resources.Bind(commands, VK_PIPELINE_BIND_POINT_GRAPHICS, record.pipeline->Layout());
|
||||
}
|
||||
|
|
|
|||
|
|
@ -217,8 +217,28 @@ bool libraryKeys(PipelineLibraryKeys& keys, const Context& context, const State&
|
|||
}
|
||||
appendKey(keys.renderPass, state.depth.has_value());
|
||||
if (state.depth) appendKey(keys.renderPass, state.depth->format);
|
||||
appendKey(keys.layout, pushStagesOf(context));
|
||||
for (const auto word : resources.LayoutKey()) appendKey(keys.layout, word);
|
||||
if (!resources.SplitSets()) {
|
||||
appendKey(keys.layout, pushStagesOf(context));
|
||||
for (const auto word : resources.LayoutKey()) appendKey(keys.layout, word);
|
||||
keys.preRasterizationLayout = keys.fragmentLayout = keys.layout;
|
||||
return true;
|
||||
}
|
||||
const auto splitKey = [&](std::vector<std::byte>& key, std::uint32_t sets) {
|
||||
appendKey(key, ~0u);
|
||||
appendKey(key, sets);
|
||||
appendKey(key, pushStagesOf(context));
|
||||
if ((sets & 1u) != 0u) {
|
||||
appendKey(key, resources.LayoutKey().size());
|
||||
for (const auto word : resources.LayoutKey()) appendKey(key, word);
|
||||
}
|
||||
if ((sets & 2u) != 0u) {
|
||||
appendKey(key, resources.FragmentLayoutKey().size());
|
||||
for (const auto word : resources.FragmentLayoutKey()) appendKey(key, word);
|
||||
}
|
||||
};
|
||||
splitKey(keys.layout, 3u);
|
||||
splitKey(keys.preRasterizationLayout, 1u);
|
||||
splitKey(keys.fragmentLayout, 2u);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -280,12 +300,13 @@ Pipeline::Pipeline(const Context& context, const State& state, const VertexInput
|
|||
}
|
||||
// A descriptor set layout with the same bindings as this one is compatible with the pipeline
|
||||
// layout, so later draws bind their own ShaderResources' set under it.
|
||||
const auto setLayout = resources.Layout();
|
||||
const std::array<VkDescriptorSetLayout, 2> setLayouts{resources.Layout(), resources.FragmentLayout()};
|
||||
const VkPushConstantRange push{pushStages, 0, PipelinePushSlotBytes};
|
||||
const std::array<VkPushConstantRange, 2> slots{{{pushStagesOf(context), 0, PipelinePushSlotBytes}, {VK_SHADER_STAGE_FRAGMENT_BIT, PipelinePushSlotBytes, PipelinePushSlotBytes}}};
|
||||
VkPipelineLayoutCreateInfo layoutInfo{VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO};
|
||||
layoutInfo.setLayoutCount = 1;
|
||||
layoutInfo.pSetLayouts = &setLayout;
|
||||
layoutInfo.flags = resources.SplitSets() ? VK_PIPELINE_LAYOUT_CREATE_INDEPENDENT_SETS_BIT_EXT : 0;
|
||||
layoutInfo.setLayoutCount = resources.SplitSets() ? 2u : 1u;
|
||||
layoutInfo.pSetLayouts = setLayouts.data();
|
||||
layoutInfo.pushConstantRangeCount = dynamicRendering ? static_cast<std::uint32_t>(slots.size()) : pushStages != 0 ? 1 : 0;
|
||||
layoutInfo.pPushConstantRanges = dynamicRendering ? slots.data() : pushStages != 0 ? &push : nullptr;
|
||||
Check(context.Function<PFN_vkCreatePipelineLayout>("vkCreatePipelineLayout")(context.device, &layoutInfo, nullptr, &layout), "vkCreatePipelineLayout graphics");
|
||||
|
|
@ -709,6 +730,8 @@ void pipelineKey(std::vector<std::byte>& key, const Context& context, const Stat
|
|||
}
|
||||
append(key, resources.LayoutKey().size());
|
||||
for (const auto word : resources.LayoutKey()) append(key, word);
|
||||
append(key, resources.FragmentLayoutKey().size());
|
||||
for (const auto word : resources.FragmentLayoutKey()) append(key, word);
|
||||
append(key, state.hasColorTarget);
|
||||
append(key, state.rectList);
|
||||
append(key, state.topology);
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@
|
|||
#include <thread>
|
||||
#include <map>
|
||||
#include <mutex>
|
||||
#include <optional>
|
||||
#include <string_view>
|
||||
|
||||
namespace AgcDriver::Graphics {
|
||||
|
|
@ -111,12 +112,22 @@ VkPipeline LinkPipelineFromLibraries(const Context& context, const VkGraphicsPip
|
|||
device.destroyLayout = context.Function<PFN_vkDestroyPipelineLayout>("vkDestroyPipelineLayout");
|
||||
device.destroyModule = context.Function<PFN_vkDestroyShaderModule>("vkDestroyShaderModule");
|
||||
}
|
||||
auto pipelineLayout = device.layouts.find(keys.layout);
|
||||
if (pipelineLayout == device.layouts.end()) {
|
||||
const auto layoutFor = [&](const Key& key, std::optional<std::uint32_t> set) {
|
||||
auto found = device.layouts.find(key);
|
||||
if (found != device.layouts.end()) return found->second;
|
||||
auto info = layout;
|
||||
std::array<VkDescriptorSetLayout, 2> sets{VK_NULL_HANDLE, VK_NULL_HANDLE};
|
||||
if (set) {
|
||||
sets.at(*set) = layout.pSetLayouts[*set];
|
||||
info.pSetLayouts = sets.data();
|
||||
}
|
||||
VkPipelineLayout handle = VK_NULL_HANDLE;
|
||||
Check(context.Function<PFN_vkCreatePipelineLayout>("vkCreatePipelineLayout")(context.device, &layout, nullptr, &handle), "vkCreatePipelineLayout library");
|
||||
pipelineLayout = device.layouts.emplace(keys.layout, handle).first;
|
||||
}
|
||||
Check(context.Function<PFN_vkCreatePipelineLayout>("vkCreatePipelineLayout")(context.device, &info, nullptr, &handle), "vkCreatePipelineLayout library");
|
||||
return device.layouts.emplace(key, handle).first->second;
|
||||
};
|
||||
const bool split = (layout.flags & VK_PIPELINE_LAYOUT_CREATE_INDEPENDENT_SETS_BIT_EXT) != 0;
|
||||
const auto fullLayout = layoutFor(keys.layout, std::nullopt);
|
||||
const std::array<VkPipelineLayout, 4> partLayouts{VK_NULL_HANDLE, split ? layoutFor(keys.preRasterizationLayout, 0u) : fullLayout, split ? layoutFor(keys.fragmentLayout, 1u) : fullLayout, VK_NULL_HANDLE};
|
||||
std::vector<VkPipelineShaderStageCreateInfo> preRasterization;
|
||||
const VkPipelineShaderStageCreateInfo* fragment = nullptr;
|
||||
bool mesh = false;
|
||||
|
|
@ -135,7 +146,7 @@ VkPipeline LinkPipelineFromLibraries(const Context& context, const VkGraphicsPip
|
|||
};
|
||||
const std::array<const Key*, 4> partKeys{&keys.vertexInput, &keys.preRasterization, &keys.fragmentShader, &keys.fragmentOutput};
|
||||
const Key none;
|
||||
const std::array<const Key*, 4> contextKeys{&none, &keys.layout, &keys.layout, &keys.renderPass};
|
||||
const std::array<const Key*, 4> contextKeys{&none, &keys.preRasterizationLayout, &keys.fragmentLayout, &keys.renderPass};
|
||||
const VkPipelineRenderingCreateInfoKHR shaderRendering{VK_STRUCTURE_TYPE_PIPELINE_RENDERING_CREATE_INFO_KHR};
|
||||
VkPipelineDepthStencilStateCreateInfo dynamicDepth{VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO};
|
||||
static const bool backgroundLinking = std::getenv("APS5_NO_PIPELINE_LTO") == nullptr;
|
||||
|
|
@ -155,7 +166,7 @@ VkPipeline LinkPipelineFromLibraries(const Context& context, const VkGraphicsPip
|
|||
VkGraphicsPipelineCreateInfo create{VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO, &library};
|
||||
create.flags = VK_PIPELINE_CREATE_LIBRARY_BIT_KHR | VK_PIPELINE_CREATE_RETAIN_LINK_TIME_OPTIMIZATION_INFO_BIT_EXT;
|
||||
create.pDynamicState = info.pDynamicState;
|
||||
create.layout = pipelineLayout->second;
|
||||
create.layout = partLayouts[part];
|
||||
switch (flags[part]) {
|
||||
case VK_GRAPHICS_PIPELINE_LIBRARY_VERTEX_INPUT_INTERFACE_BIT_EXT:
|
||||
create.pVertexInputState = info.pVertexInputState;
|
||||
|
|
@ -212,7 +223,7 @@ VkPipeline LinkPipelineFromLibraries(const Context& context, const VkGraphicsPip
|
|||
*optimized = slot;
|
||||
const auto createPipelines = context.Function<PFN_vkCreateGraphicsPipelines>("vkCreateGraphicsPipelines");
|
||||
const auto cache = context.pipelineCache;
|
||||
const auto libraryLayout = pipelineLayout->second;
|
||||
const auto libraryLayout = fullLayout;
|
||||
enqueue(store, context.device, [slot, createPipelines, cache, libraryLayout, libraries, &store, devicePointer = &device] {
|
||||
if (slot->released.load()) return;
|
||||
VkPipelineLibraryCreateInfoKHR linked{VK_STRUCTURE_TYPE_PIPELINE_LIBRARY_CREATE_INFO_KHR};
|
||||
|
|
|
|||
|
|
@ -1166,6 +1166,14 @@ VkDescriptorSetLayout CreateSetLayout(const Context& context, std::span<const Vk
|
|||
return layout;
|
||||
}
|
||||
|
||||
std::vector<VkDescriptorPoolSize> PoolSizes(std::span<const VkDescriptorSetLayoutBinding> bindings) {
|
||||
std::map<VkDescriptorType, std::uint32_t> counts;
|
||||
for (const auto& binding : bindings) counts[binding.descriptorType] += binding.descriptorCount;
|
||||
std::vector<VkDescriptorPoolSize> sizes;
|
||||
for (const auto& [type, count] : counts) sizes.push_back({type, count});
|
||||
return sizes;
|
||||
}
|
||||
|
||||
VkDescriptorPool CreateSetPool(const Context& context, std::span<const VkDescriptorPoolSize> sizes, bool updateAfterBind) {
|
||||
VkDescriptorPoolCreateInfo info{VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO};
|
||||
info.flags = updateAfterBind ? VK_DESCRIPTOR_POOL_CREATE_UPDATE_AFTER_BIND_BIT : 0;
|
||||
|
|
@ -1339,7 +1347,7 @@ void ShaderResources::buildPrepare(std::span<const CompiledShader> shaders, cons
|
|||
updateAfterBind = true;
|
||||
};
|
||||
for (const auto& binding : shader.program->bindings) {
|
||||
Require(binding.descriptorSet == 0, "unexpected descriptor set: every shader resource must use descriptor set zero");
|
||||
Require(binding.descriptorSet == ShaderRecompiler::RuntimeAbi::DescriptorSet || (shader.stage == ShaderRecompiler::ShaderStage::Fragment && binding.descriptorSet == ShaderRecompiler::RuntimeAbi::FragmentDescriptorSet), "unexpected descriptor set: a shader resource must use its stage's descriptor set");
|
||||
Require(occupied.insert(binding.binding).second, "duplicate shader binding");
|
||||
const bool addressRole = binding.role == ShaderRecompiler::DescriptorRole::BdaPagetable || binding.role == ShaderRecompiler::DescriptorRole::FaultBuffer;
|
||||
const bool bufferRole = addressRole || binding.role == ShaderRecompiler::DescriptorRole::GuestBuffers || binding.role == ShaderRecompiler::DescriptorRole::ShaderData || binding.role == ShaderRecompiler::DescriptorRole::FlattenedSrt || binding.role == ShaderRecompiler::DescriptorRole::Gds;
|
||||
|
|
@ -1416,23 +1424,62 @@ void ShaderResources::buildPrepare(std::span<const CompiledShader> shaders, cons
|
|||
if (drawBuild && !usesBda && std::all_of(allocations.begin(), allocations.end(), [&](const Allocation& allocation) { return !allocation.guest || allocation.pushByte >= 0 || (allocation.dataAllocation >= 0 && allocation.dataByte < allocations[static_cast<std::size_t>(allocation.dataAllocation)].size); })) guestMemory.AllowAdjustedRegions();
|
||||
guestMemory.UploadPrepare(usesBda);
|
||||
timing.uploadMs = phase(BuildPhase::Upload);
|
||||
std::vector<VkDescriptorSetLayoutBinding> description;
|
||||
splitSets = std::any_of(shaders.begin(), shaders.end(), [](const CompiledShader& shader) { return shader.program != nullptr && std::any_of(shader.program->bindings.begin(), shader.program->bindings.end(), [](const auto& binding) { return binding.descriptorSet == ShaderRecompiler::RuntimeAbi::FragmentDescriptorSet; }); });
|
||||
std::array<std::vector<VkDescriptorSetLayoutBinding>, 2> descriptions;
|
||||
for (const auto& binding : bindings) {
|
||||
description.push_back(binding.layout);
|
||||
layoutBindingFlags.push_back(updateAfterBind && binding.layout.descriptorType == VK_DESCRIPTOR_TYPE_STORAGE_BUFFER ? VK_DESCRIPTOR_BINDING_UPDATE_AFTER_BIND_BIT : 0);
|
||||
layoutKey.insert(layoutKey.end(), {binding.layout.binding, static_cast<std::uint32_t>(binding.layout.descriptorType), binding.layout.descriptorCount, binding.layout.stageFlags});
|
||||
const auto set = setOf(binding);
|
||||
descriptions[set].push_back(binding.layout);
|
||||
(set == 0 ? layoutBindingFlags : fragmentBindingFlags).push_back(updateAfterBind && binding.layout.descriptorType == VK_DESCRIPTOR_TYPE_STORAGE_BUFFER ? VK_DESCRIPTOR_BINDING_UPDATE_AFTER_BIND_BIT : 0);
|
||||
auto& key = set == 0 ? layoutKey : fragmentLayoutKey;
|
||||
key.insert(key.end(), {binding.layout.binding, static_cast<std::uint32_t>(binding.layout.descriptorType), binding.layout.descriptorCount, binding.layout.stageFlags});
|
||||
}
|
||||
if (updateAfterBind) {
|
||||
layoutKey.push_back(VK_DESCRIPTOR_SET_LAYOUT_CREATE_UPDATE_AFTER_BIND_POOL_BIT);
|
||||
if (splitSets) fragmentLayoutKey.push_back(VK_DESCRIPTOR_SET_LAYOUT_CREATE_UPDATE_AFTER_BIND_POOL_BIT);
|
||||
}
|
||||
if (updateAfterBind) layoutKey.push_back(VK_DESCRIPTOR_SET_LAYOUT_CREATE_UPDATE_AFTER_BIND_POOL_BIT);
|
||||
static const bool noLayoutCache = std::getenv("APS5_NO_LAYOUT_CACHE") != nullptr;
|
||||
static const bool noPoolCache = std::getenv("APS5_NO_POOL_CACHE") != nullptr;
|
||||
if (context.descriptorCache != nullptr && !noLayoutCache) {
|
||||
_layout = context.descriptorCache->Layout(layoutKey, description, updateAfterBind, layoutBindingFlags);
|
||||
} else {
|
||||
const auto createLayout = [&](const std::vector<std::uint32_t>& key, const std::vector<VkDescriptorSetLayoutBinding>& description, const std::vector<VkDescriptorBindingFlags>& flags, bool& owns) {
|
||||
if (context.descriptorCache != nullptr && !noLayoutCache) return context.descriptorCache->Layout(key, description, updateAfterBind, flags);
|
||||
ValidateDescriptorLayout(context, description, updateAfterBind);
|
||||
_layout = CreateSetLayout(context, description, updateAfterBind, layoutBindingFlags);
|
||||
ownsLayout = true;
|
||||
owns = true;
|
||||
return CreateSetLayout(context, description, updateAfterBind, flags);
|
||||
};
|
||||
_layout = createLayout(layoutKey, descriptions[0], layoutBindingFlags, ownsLayout);
|
||||
if (splitSets) {
|
||||
fragment.layout = createLayout(fragmentLayoutKey, descriptions[1], fragmentBindingFlags, fragment.ownsLayout);
|
||||
if (!descriptions[1].empty()) {
|
||||
const auto sizes = PoolSizes(descriptions[1]);
|
||||
if (context.descriptorCache != nullptr && !noPoolCache) {
|
||||
fragment.cacheAllocation = context.descriptorCache->Allocate(fragment.layout, sizes, updateAfterBind);
|
||||
fragment.set = fragment.cacheAllocation.set;
|
||||
}
|
||||
if (fragment.set == VK_NULL_HANDLE) {
|
||||
fragment.pool = CreateSetPool(context, sizes, updateAfterBind);
|
||||
VkDescriptorSetAllocateInfo allocation{VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO};
|
||||
allocation.descriptorPool = fragment.pool;
|
||||
allocation.descriptorSetCount = 1;
|
||||
allocation.pSetLayouts = &fragment.layout;
|
||||
Check(context.Function<PFN_vkAllocateDescriptorSets>("vkAllocateDescriptorSets")(context.device, &allocation, &fragment.set), "vkAllocateDescriptorSets");
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!bindings.empty()) {
|
||||
if (splitSets && !descriptions[0].empty()) {
|
||||
const auto sizes = PoolSizes(descriptions[0]);
|
||||
if (context.descriptorCache != nullptr && !noPoolCache) {
|
||||
cacheAllocation = context.descriptorCache->Allocate(_layout, sizes, updateAfterBind);
|
||||
_set = cacheAllocation.set;
|
||||
}
|
||||
if (_set == VK_NULL_HANDLE) {
|
||||
pool = CreateSetPool(context, sizes, updateAfterBind);
|
||||
VkDescriptorSetAllocateInfo allocation{VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO};
|
||||
allocation.descriptorPool = pool;
|
||||
allocation.descriptorSetCount = 1;
|
||||
allocation.pSetLayouts = &_layout;
|
||||
Check(context.Function<PFN_vkAllocateDescriptorSets>("vkAllocateDescriptorSets")(context.device, &allocation, &_set), "vkAllocateDescriptorSets");
|
||||
}
|
||||
}
|
||||
if (!splitSets && !bindings.empty()) {
|
||||
// The set is sized from the plan: every image element becomes exactly one descriptor
|
||||
// when stage B looks it up.
|
||||
std::vector<VkDescriptorPoolSize> sizes;
|
||||
|
|
@ -1492,7 +1539,7 @@ void ShaderResources::buildComplete() {
|
|||
if (usesBda) bda = std::make_unique<BdaResources>(context, guestMemory);
|
||||
else if (usesFaultBuffer) bda = std::make_unique<BdaResources>(context);
|
||||
phase(BuildPhase::Bda);
|
||||
if (_set != VK_NULL_HANDLE) {
|
||||
if (_set != VK_NULL_HANDLE || fragment.set != VK_NULL_HANDLE) {
|
||||
// One update call for the whole set: the info arrays are sized up front so every write's
|
||||
// pointer into them stays valid until the call.
|
||||
std::size_t bufferCount = 0;
|
||||
|
|
@ -1514,7 +1561,7 @@ void ShaderResources::buildComplete() {
|
|||
};
|
||||
for (const auto& binding : bindings) {
|
||||
VkWriteDescriptorSet write{VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET};
|
||||
write.dstSet = _set;
|
||||
write.dstSet = setOf(binding) == 0 ? _set : fragment.set;
|
||||
write.dstBinding = binding.layout.binding;
|
||||
write.descriptorCount = binding.layout.descriptorCount;
|
||||
write.descriptorType = binding.layout.descriptorType;
|
||||
|
|
@ -3334,10 +3381,19 @@ void ShaderResources::release() noexcept {
|
|||
if (cacheAllocation.pool && context.descriptorCache != nullptr) context.descriptorCache->Free(cacheAllocation);
|
||||
if (pool) context.Function<PFN_vkDestroyDescriptorPool>("vkDestroyDescriptorPool")(context.device, pool, nullptr);
|
||||
if (_layout && ownsLayout) context.Function<PFN_vkDestroyDescriptorSetLayout>("vkDestroyDescriptorSetLayout")(context.device, _layout, nullptr);
|
||||
if (fragment.cacheAllocation.pool && context.descriptorCache != nullptr) context.descriptorCache->Free(fragment.cacheAllocation);
|
||||
if (fragment.pool) context.Function<PFN_vkDestroyDescriptorPool>("vkDestroyDescriptorPool")(context.device, fragment.pool, nullptr);
|
||||
if (fragment.layout && fragment.ownsLayout) context.Function<PFN_vkDestroyDescriptorSetLayout>("vkDestroyDescriptorSetLayout")(context.device, fragment.layout, nullptr);
|
||||
cacheAllocation = {};
|
||||
pool = VK_NULL_HANDLE;
|
||||
_set = VK_NULL_HANDLE;
|
||||
_layout = VK_NULL_HANDLE;
|
||||
fragment = {};
|
||||
}
|
||||
|
||||
std::uint32_t ShaderResources::setOf(const Binding& binding) const {
|
||||
constexpr auto count = static_cast<std::uint32_t>(ShaderRecompiler::RuntimeAbi::Binding::Count);
|
||||
return splitSets && binding.layout.binding / count == static_cast<std::uint32_t>(ShaderRecompiler::RuntimeAbi::Stage::Fragment) ? 1u : 0u;
|
||||
}
|
||||
|
||||
VkDescriptorSetLayout ShaderResources::Layout() const {
|
||||
|
|
@ -3356,11 +3412,12 @@ bool ShaderResources::LayoutUpdateAfterBind() const {
|
|||
|
||||
ShaderResources::DrawBindings::~DrawBindings() {
|
||||
if (cache != nullptr && allocation.set != VK_NULL_HANDLE) cache->Free(allocation);
|
||||
if (cache != nullptr && fragmentAllocation.set != VK_NULL_HANDLE) cache->Free(fragmentAllocation);
|
||||
}
|
||||
|
||||
std::optional<std::vector<ShaderResources::MovedBuffer>> ShaderResources::MovedReadOnlyBuffers(std::span<const CompiledShader> shaders, Recorder& recorder) const {
|
||||
std::vector<MovedBuffer> moved;
|
||||
if (_set == VK_NULL_HANDLE || usesBda) return moved;
|
||||
if ((_set == VK_NULL_HANDLE && fragment.set == VK_NULL_HANDLE) || usesBda) return moved;
|
||||
for (const auto& shader : shaders) {
|
||||
if (shader.program == nullptr) return std::nullopt;
|
||||
for (const auto& binding : shader.program->bindings) {
|
||||
|
|
@ -3413,7 +3470,7 @@ std::size_t ShaderResources::MaxDrawSnapshotBytes() {
|
|||
}
|
||||
|
||||
std::shared_ptr<ShaderResources::DrawBindings> ShaderResources::PrepareDrawBindings(Recorder& recorder, std::span<const MovedBuffer> moved) const {
|
||||
if (_set == VK_NULL_HANDLE || usesBda) return {};
|
||||
if ((_set == VK_NULL_HANDLE && fragment.set == VK_NULL_HANDLE) || usesBda) return {};
|
||||
const auto reads = guestMemory.InPlaceReads();
|
||||
const auto maxSnapshotBytes = MaxDrawSnapshotBytes();
|
||||
std::size_t totalSnapshotBytes = 0;
|
||||
|
|
@ -3460,13 +3517,17 @@ std::shared_ptr<ShaderResources::DrawBindings> ShaderResources::PrepareDrawBindi
|
|||
}
|
||||
if (selected.empty()) return {};
|
||||
Require(context.descriptorCache != nullptr, "draw snapshots require a descriptor cache");
|
||||
std::map<VkDescriptorType, std::uint32_t> counts;
|
||||
for (const auto& binding : bindings) counts[binding.layout.descriptorType] += binding.layout.descriptorCount;
|
||||
std::vector<VkDescriptorPoolSize> sizes;
|
||||
for (const auto& [type, count] : counts) sizes.push_back({type, count});
|
||||
std::array<std::vector<VkDescriptorSetLayoutBinding>, 2> descriptions;
|
||||
for (const auto& binding : bindings) descriptions[setOf(binding)].push_back(binding.layout);
|
||||
result->cache = context.descriptorCache;
|
||||
result->allocation = result->cache->Allocate(_layout, sizes);
|
||||
Require(result->allocation.set != VK_NULL_HANDLE, "draw snapshot descriptor allocation failed");
|
||||
if (!descriptions[0].empty()) {
|
||||
result->allocation = result->cache->Allocate(_layout, PoolSizes(descriptions[0]));
|
||||
Require(result->allocation.set != VK_NULL_HANDLE, "draw snapshot descriptor allocation failed");
|
||||
}
|
||||
if (!descriptions[1].empty()) {
|
||||
result->fragmentAllocation = result->cache->Allocate(fragment.layout, PoolSizes(descriptions[1]));
|
||||
Require(result->fragmentAllocation.set != VK_NULL_HANDLE, "draw snapshot descriptor allocation failed");
|
||||
}
|
||||
auto buffers = descriptorBuffers;
|
||||
auto writes = descriptorWrites;
|
||||
std::vector<std::size_t> snapshotIndices(allocations.size(), selected.size());
|
||||
|
|
@ -3474,7 +3535,7 @@ std::shared_ptr<ShaderResources::DrawBindings> ShaderResources::PrepareDrawBindi
|
|||
for (std::size_t index = 0; index < bindings.size(); ++index) {
|
||||
const auto& binding = bindings[index];
|
||||
auto& write = writes[index];
|
||||
write.dstSet = result->allocation.set;
|
||||
write.dstSet = setOf(binding) == 0 ? result->allocation.set : result->fragmentAllocation.set;
|
||||
if (binding.allocations.empty()) continue;
|
||||
const auto offset = static_cast<std::size_t>(write.pBufferInfo - descriptorBuffers.data());
|
||||
write.pBufferInfo = buffers.data() + offset;
|
||||
|
|
@ -3492,8 +3553,8 @@ std::shared_ptr<ShaderResources::DrawBindings> ShaderResources::PrepareDrawBindi
|
|||
}
|
||||
|
||||
void ShaderResources::Bind(VkCommandBuffer commands, VkPipelineBindPoint bindPoint, VkPipelineLayout layout) const {
|
||||
if (_set == VK_NULL_HANDLE) return;
|
||||
context.Resolved(&DeviceFunctions::cmdBindDescriptorSets, "vkCmdBindDescriptorSets")(commands, bindPoint, layout, 0, 1, &_set, 0, nullptr);
|
||||
if (_set != VK_NULL_HANDLE) context.Resolved(&DeviceFunctions::cmdBindDescriptorSets, "vkCmdBindDescriptorSets")(commands, bindPoint, layout, 0, 1, &_set, 0, nullptr);
|
||||
if (fragment.set != VK_NULL_HANDLE) context.Resolved(&DeviceFunctions::cmdBindDescriptorSets, "vkCmdBindDescriptorSets")(commands, bindPoint, layout, ShaderRecompiler::RuntimeAbi::FragmentDescriptorSet, 1, &fragment.set, 0, nullptr);
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
|
|
|||
|
|
@ -574,7 +574,7 @@ std::set<std::uint32_t> ValidateShaders(std::span<const CompiledShader> shaders,
|
|||
const auto expected = state.rectList ? (i == 0 ? Stage::Vertex : tessStages[i]) : tessellation ? tessStages[i] : geometry && i == 1 ? Stage::Geometry : i == shaders.size() - 1 ? Stage::Fragment : state.stages.path == ShaderPath::Geometry ? Stage::Mesh : Stage::Vertex;
|
||||
Require(shaders[i].program != nullptr, "missing compiled shader");
|
||||
Require(shaders[i].stage == expected, "graphics stage order disagrees");
|
||||
for (const auto& binding : shaders[i].program->bindings) Require(binding.descriptorSet == 0, "graphics resource uses a descriptor set other than zero");
|
||||
for (const auto& binding : shaders[i].program->bindings) Require(binding.descriptorSet == ShaderRecompiler::RuntimeAbi::DescriptorSet || (shaders[i].stage == ShaderRecompiler::ShaderStage::Fragment && binding.descriptorSet == ShaderRecompiler::RuntimeAbi::FragmentDescriptorSet), "graphics resource uses a descriptor set other than its stage's");
|
||||
const auto current = Inspect(shaders[i], state, subgroup, fragmentShaderBarycentric, descriptorIndexing, imageInt64Atomics, geometryShader, sampleRateShading, bufferInt64Atomics, shaderClipDistance);
|
||||
if (i != 0) {
|
||||
for (const auto& [location, signature] : current.inputs) {
|
||||
|
|
|
|||
|
|
@ -4388,7 +4388,7 @@ void validationTests() {
|
|||
{
|
||||
auto vertex = pushed({.push = true, .bufferArray = 2});
|
||||
vertex.bindings.front().descriptorSet = 1;
|
||||
expectFailure([&] { validate(vertex, 8); }, "descriptor set other than zero");
|
||||
expectFailure([&] { validate(vertex, 8); }, "descriptor set other than its stage's");
|
||||
}
|
||||
{
|
||||
auto vertex = pushed({.push = true, .bufferArray = 2});
|
||||
|
|
|
|||
|
|
@ -103,7 +103,7 @@ BindingAllocationResult BindingAllocator::Allocate(IrProgram& program, const Bin
|
|||
if (info.buffers.size() > RuntimeAbi::BufferCapacity || info.images.size() > RuntimeAbi::ImageCapacity || info.samplers.size() > RuntimeAbi::SamplerHeapCapacity) fail("shader binding layout exceeds runtime metadata capacity");
|
||||
if (std::ranges::any_of(next.userDataRegisters, [](auto reg) { return reg >= RuntimeAbi::UserDataCapacity; })) fail("shader user data exceeds runtime ABI capacity");
|
||||
if (registrationModules || std::ranges::any_of(info.images, [](const auto& image) { return image.indirectRoot != ImageResource::NoIndirectImage; })) next.runtimeImageCount = static_cast<std::uint32_t>(info.images.size());
|
||||
if (registrationModules && (program.Resources().stage == IrShaderStage::Pixel || std::ranges::any_of(info.samplers, [](const auto& sampler) { return FoldsTexelOffsets(sampler); }))) next.runtimeStateDwords = RuntimeAbi::StateDwords;
|
||||
if (registrationModules && program.Resources().stage == IrShaderStage::Pixel) next.runtimeStateDwords = RuntimeAbi::StateDwords;
|
||||
const auto pushSize = layout.pushConstantSizeBytes / 4u;
|
||||
next.pushDataStartDword = next.runtimeImageCount == 0u && next.ShaderDataDwords() != 0u && next.ShaderDataDwords() <= pushSize ? layout.pushConstantOffsetBytes / 4u : PushData::NoStart;
|
||||
|
||||
|
|
|
|||
|
|
@ -257,11 +257,8 @@ DescriptorBindingPlan DescriptorBindingBuilder::Prepare(const IrBindingLayout& l
|
|||
if (info.images.size() > ShaderInfo::MaxImages) fail("runtime image count exceeds the static capacity");
|
||||
DescriptorBindingPlan plan;
|
||||
const auto unnormalized = ProveUnnormalized(info, snapshot);
|
||||
std::uint32_t unnormalizedSamplers = 0u;
|
||||
for (std::uint32_t index = 0; index < info.samplers.size(); ++index) {
|
||||
if (!FoldsTexelOffsets(info.samplers[index])) continue;
|
||||
if (!registrationModules) plan.specialization.push_back({PipelineSpecialization::FoldOffsetBase + index, unnormalized.samplers[index] ? 1u : 0u});
|
||||
else if (unnormalized.samplers[index]) unnormalizedSamplers |= 1u << index;
|
||||
if (FoldsTexelOffsets(info.samplers[index])) plan.specialization.push_back({PipelineSpecialization::FoldOffsetBase + index, unnormalized.samplers[index] ? 1u : 0u});
|
||||
}
|
||||
std::vector<std::uint32_t> compareStates(info.images.size());
|
||||
for (std::uint32_t index = 0; index < info.images.size(); ++index) {
|
||||
|
|
@ -382,7 +379,7 @@ DescriptorBindingPlan DescriptorBindingBuilder::Prepare(const IrBindingLayout& l
|
|||
if ((imageHeap || samplerHeap) && compact.resources.empty()) continue;
|
||||
PreparedDescriptorBinding entry{};
|
||||
auto& physical = entry.descriptor;
|
||||
physical.descriptorSet = RuntimeAbi::DescriptorSet;
|
||||
physical.descriptorSet = registrationModules && stage == IrShaderStage::Pixel ? RuntimeAbi::FragmentDescriptorSet : RuntimeAbi::DescriptorSet;
|
||||
physical.binding = NativeBinding(stage, compact.kind);
|
||||
physical.count = compact.resources.empty() ? 1u : static_cast<std::uint32_t>(compact.resources.size());
|
||||
physical.kind = PhysicalKindFor(compact.kind);
|
||||
|
|
@ -459,7 +456,6 @@ DescriptorBindingPlan DescriptorBindingBuilder::Prepare(const IrBindingLayout& l
|
|||
}
|
||||
if (layout.runtimeStateDwords != 0u) {
|
||||
plan.runtimeState.assign(layout.runtimeStateDwords, 0u);
|
||||
plan.runtimeState.at(RuntimeAbi::StateUnnormalizedSamplers) = unnormalizedSamplers;
|
||||
for (std::uint32_t target = 0; target < exportMappings.size(); ++target) plan.runtimeState.at(RuntimeAbi::StateExportMappings + target / 4u) |= static_cast<std::uint32_t>(exportMappings[target]) << (target % 4u * 8u);
|
||||
}
|
||||
std::vector<std::uint32_t> samplerMasks(info.samplers.size());
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ namespace ShaderRecompiler::RuntimeAbi {
|
|||
|
||||
inline constexpr std::uint32_t Version = 11u;
|
||||
inline constexpr std::uint32_t DescriptorSet = 0u;
|
||||
inline constexpr std::uint32_t FragmentDescriptorSet = 1u;
|
||||
inline constexpr std::uint32_t StageCount = 4u;
|
||||
inline constexpr std::uint32_t PushConstantDwords = 32u;
|
||||
inline constexpr std::uint32_t FirstImageBinding = 1u;
|
||||
|
|
@ -39,9 +40,8 @@ inline constexpr std::uint32_t BindlessTableSlots = 16u;
|
|||
inline constexpr std::uint32_t StorageMipSlots = 4u;
|
||||
inline constexpr std::uint32_t StorageHeapCapacity = 16u;
|
||||
inline constexpr std::uint32_t SamplerHeapCapacity = 32u;
|
||||
inline constexpr std::uint32_t StateUnnormalizedSamplers = 0u;
|
||||
inline constexpr std::uint32_t StateExportMappings = 1u;
|
||||
inline constexpr std::uint32_t StateDwords = 3u;
|
||||
inline constexpr std::uint32_t StateExportMappings = 0u;
|
||||
inline constexpr std::uint32_t StateDwords = 2u;
|
||||
inline constexpr std::uint32_t ImageModeMask = 0xffu;
|
||||
inline constexpr std::uint32_t CompareStateShift = 9u;
|
||||
inline constexpr std::uint32_t CompareStateMask = 0x1fffu;
|
||||
|
|
|
|||
|
|
@ -1494,7 +1494,7 @@ void EmitSampleOp(SpirvValueEmitContext& ctx, const ImageEmitAccess& access, con
|
|||
} else if (!offsetOperand) {
|
||||
sample = emitSample(FoldedOffsetCoord(ctx, access, setup), foldedMask, foldedOperands);
|
||||
} else {
|
||||
const auto selector = state.registrationModules ? EmitAndConstant(state, Binary(state, spv::OpShiftRightLogical, TypeU32(state), EmitShaderDataDwordLoad(state, state.program.Metadata().bindings.RuntimeStateDword() + RuntimeAbi::StateUnnormalizedSamplers), ConstantU32(state, mem.sampler)), 1u) : state.module.SpecializationConstant(TypeU32(state), PipelineSpecialization::FoldOffsetBase + mem.sampler, 0u);
|
||||
const auto selector = state.module.SpecializationConstant(TypeU32(state), PipelineSpecialization::FoldOffsetBase + mem.sampler, 0u);
|
||||
const auto folded = Binary(state, spv::OpINotEqual, TypeBool(state), selector, ConstantU32(state, 0u));
|
||||
const auto foldLabel = state.module.AllocateId();
|
||||
const auto operandLabel = state.module.AllocateId();
|
||||
|
|
|
|||
|
|
@ -391,7 +391,7 @@ void DefineDescriptors(SpirvEmitterState& state) {
|
|||
const auto Define = [&](std::uint32_t type, const char* name, std::uint32_t storage = spv::StorageClassStorageBuffer) {
|
||||
const auto variable = state.module.DefineGlobalVariable(TypePointer(state, storage, type), storage);
|
||||
state.module.AddName(variable, name);
|
||||
state.module.AddAnnotation(spv::OpDecorate, variable, spv::DecorationDescriptorSet, 0u);
|
||||
state.module.AddAnnotation(spv::OpDecorate, variable, spv::DecorationDescriptorSet, state.registrationModules && stage == IrShaderStage::Pixel ? RuntimeAbi::FragmentDescriptorSet : RuntimeAbi::DescriptorSet);
|
||||
state.module.AddAnnotation(spv::OpDecorate, variable, spv::DecorationBinding, NativeBinding(stage, binding.kind));
|
||||
return variable;
|
||||
};
|
||||
|
|
|
|||
Loading…
Reference in New Issue