Merge pull request #4456 from Mouhand-Kaddo/fix/agc-library-used-descriptor-sets
fix(agc): retain used descriptor sets in pipeline libraries
This commit is contained in:
commit
8a67eb2e68
|
|
@ -21,6 +21,8 @@ struct PipelineLibraryKeys {
|
|||
std::vector<std::byte> layout;
|
||||
std::vector<std::byte> preRasterizationLayout;
|
||||
std::vector<std::byte> fragmentLayout;
|
||||
std::uint32_t preRasterizationSets = 0;
|
||||
std::uint32_t fragmentSets = 0;
|
||||
};
|
||||
|
||||
std::span<const VkDynamicState> PipelineLibraryDynamicStates();
|
||||
|
|
|
|||
|
|
@ -182,6 +182,13 @@ bool libraryKeys(PipelineLibraryKeys& keys, const Context& context, const State&
|
|||
appendKey(key, shader.stage);
|
||||
appendKey(key, shader.program->PipelineVariantId());
|
||||
appendKey(key, shader.pushConstantOffset);
|
||||
if (resources.split) {
|
||||
auto& sets = shader.stage == Stage::Fragment ? keys.fragmentSets : keys.preRasterizationSets;
|
||||
for (const auto& binding : shader.program->bindings) {
|
||||
Require(binding.descriptorSet < 2u, "split pipeline library descriptor set exceeds two slots");
|
||||
sets |= 1u << binding.descriptorSet;
|
||||
}
|
||||
}
|
||||
}
|
||||
for (const auto& binding : input.bindings) {
|
||||
appendKey(keys.vertexInput, binding.binding);
|
||||
|
|
@ -246,8 +253,8 @@ bool libraryKeys(PipelineLibraryKeys& keys, const Context& context, const State&
|
|||
}
|
||||
};
|
||||
splitKey(keys.layout, 3u);
|
||||
splitKey(keys.preRasterizationLayout, 1u);
|
||||
splitKey(keys.fragmentLayout, 2u);
|
||||
splitKey(keys.preRasterizationLayout, keys.preRasterizationSets);
|
||||
splitKey(keys.fragmentLayout, keys.fragmentSets);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -104,6 +104,8 @@ std::span<const VkDynamicState> PipelineLibraryDynamicStates() {
|
|||
|
||||
VkPipeline LinkPipelineFromLibraries(const Context& context, const VkGraphicsPipelineCreateInfo& info, const VkPipelineRenderingCreateInfoKHR& rendering, const VkPipelineLayoutCreateInfo& layout, const PipelineLibraryKeys& keys, std::shared_ptr<OptimizedPipeline>* optimized, bool partsOnly, std::span<VkShaderModule> modules) {
|
||||
Require(info.renderPass == VK_NULL_HANDLE && info.pDynamicState != nullptr, "pipeline library parts need dynamic rendering and the library dynamic states");
|
||||
const bool split = (layout.flags & VK_PIPELINE_LAYOUT_CREATE_INDEPENDENT_SETS_BIT_EXT) != 0;
|
||||
Require(!split || (layout.setLayoutCount == 2u && layout.pSetLayouts != nullptr && ((keys.preRasterizationSets | keys.fragmentSets) & ~3u) == 0u), "split pipeline library layout requires two descriptor set slots and a valid mask");
|
||||
auto& store = Libraries();
|
||||
std::unique_lock lock(store.mutex);
|
||||
auto& device = store.devices[context.device];
|
||||
|
|
@ -112,22 +114,23 @@ VkPipeline LinkPipelineFromLibraries(const Context& context, const VkGraphicsPip
|
|||
device.destroyLayout = context.Function<PFN_vkDestroyPipelineLayout>("vkDestroyPipelineLayout");
|
||||
device.destroyModule = context.Function<PFN_vkDestroyShaderModule>("vkDestroyShaderModule");
|
||||
}
|
||||
const auto layoutFor = [&](const Key& key, std::optional<std::uint32_t> set) {
|
||||
const auto layoutFor = [&](const Key& key, std::optional<std::uint32_t> mask) {
|
||||
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];
|
||||
if (mask.has_value()) {
|
||||
for (std::uint32_t set = 0; set < sets.size(); ++set) {
|
||||
if ((*mask & (1u << set)) != 0u) sets[set] = layout.pSetLayouts[set];
|
||||
}
|
||||
info.pSetLayouts = sets.data();
|
||||
}
|
||||
VkPipelineLayout handle = VK_NULL_HANDLE;
|
||||
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};
|
||||
const std::array<VkPipelineLayout, 4> partLayouts{VK_NULL_HANDLE, split ? layoutFor(keys.preRasterizationLayout, keys.preRasterizationSets) : fullLayout, split ? layoutFor(keys.fragmentLayout, keys.fragmentSets) : fullLayout, VK_NULL_HANDLE};
|
||||
std::vector<VkPipelineShaderStageCreateInfo> preRasterization;
|
||||
const VkPipelineShaderStageCreateInfo* fragment = nullptr;
|
||||
bool mesh = false;
|
||||
|
|
|
|||
|
|
@ -2257,6 +2257,10 @@ struct MockVulkan {
|
|||
std::uint32_t boundFirst = 0;
|
||||
VkPipelineBindPoint boundPoint = VK_PIPELINE_BIND_POINT_MAX_ENUM;
|
||||
std::map<VkPipelineLayout, VkDeviceSize> pipelineLayoutPushConstantSize;
|
||||
std::map<VkPipelineLayout, std::vector<VkDescriptorSetLayout>> pipelineLayoutSets;
|
||||
std::map<VkPipelineLayout, VkPipelineLayoutCreateFlags> pipelineLayoutFlags;
|
||||
std::map<VkPipelineLayout, std::vector<VkPushConstantRange>> pipelineLayoutPushRanges;
|
||||
std::map<VkGraphicsPipelineLibraryFlagsEXT, VkPipelineLayout> libraryLayouts;
|
||||
std::uint32_t pipelineCreateCount = 0;
|
||||
std::vector<std::array<std::uint32_t, 3>> pipelineSpecializations;
|
||||
VkPipeline boundPipeline = VK_NULL_HANDLE;
|
||||
|
|
@ -2415,6 +2419,19 @@ VKAPI_ATTR VkDeviceAddress VKAPI_CALL mockGetBufferDeviceAddress(VkDevice, const
|
|||
VKAPI_ATTR VkResult VKAPI_CALL mockCreatePipelineLayout(VkDevice, const VkPipelineLayoutCreateInfo* info, const VkAllocationCallbacks*, VkPipelineLayout* layout) {
|
||||
*layout = makeHandle<VkPipelineLayout>();
|
||||
mock.pipelineLayoutPushConstantSize[*layout] = info->pushConstantRangeCount > 0 ? info->pPushConstantRanges[0].size : 0;
|
||||
if (info->setLayoutCount != 0) mock.pipelineLayoutSets[*layout].assign(info->pSetLayouts, info->pSetLayouts + info->setLayoutCount);
|
||||
mock.pipelineLayoutFlags[*layout] = info->flags;
|
||||
if (info->pushConstantRangeCount != 0) mock.pipelineLayoutPushRanges[*layout].assign(info->pPushConstantRanges, info->pPushConstantRanges + info->pushConstantRangeCount);
|
||||
++mock.live;
|
||||
return VK_SUCCESS;
|
||||
}
|
||||
|
||||
VKAPI_ATTR VkResult VKAPI_CALL mockCreateGraphicsPipelines(VkDevice, VkPipelineCache, std::uint32_t count, const VkGraphicsPipelineCreateInfo* infos, const VkAllocationCallbacks*, VkPipeline* pipelines) {
|
||||
Require(count == 1 && (infos[0].flags & VK_PIPELINE_CREATE_LIBRARY_BIT_KHR) != 0, "mock expects a graphics pipeline library");
|
||||
const auto* library = static_cast<const VkGraphicsPipelineLibraryCreateInfoEXT*>(infos[0].pNext);
|
||||
Require(library != nullptr && library->sType == VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_LIBRARY_CREATE_INFO_EXT, "missing graphics pipeline library description");
|
||||
mock.libraryLayouts[library->flags] = infos[0].layout;
|
||||
*pipelines = makeHandle<VkPipeline>();
|
||||
++mock.live;
|
||||
return VK_SUCCESS;
|
||||
}
|
||||
|
|
@ -2429,8 +2446,8 @@ VKAPI_ATTR VkResult VKAPI_CALL mockCreateShaderModule(VkDevice, const VkShaderMo
|
|||
return VK_SUCCESS;
|
||||
}
|
||||
|
||||
VKAPI_ATTR void VKAPI_CALL mockDestroyShaderModule(VkDevice, VkShaderModule, const VkAllocationCallbacks*) {
|
||||
--mock.live;
|
||||
VKAPI_ATTR void VKAPI_CALL mockDestroyShaderModule(VkDevice, VkShaderModule module, const VkAllocationCallbacks*) {
|
||||
if (module != VK_NULL_HANDLE) --mock.live;
|
||||
}
|
||||
|
||||
VKAPI_ATTR VkResult VKAPI_CALL mockCreateComputePipelines(VkDevice, VkPipelineCache, std::uint32_t count, const VkComputePipelineCreateInfo* infos, const VkAllocationCallbacks*, VkPipeline* pipelines) {
|
||||
|
|
@ -2528,6 +2545,7 @@ PFN_vkVoidFunction VKAPI_CALL mockProc(VkDevice, const char* name) {
|
|||
{"vkResetDescriptorPool", reinterpret_cast<PFN_vkVoidFunction>(mockResetDescriptorPool)},
|
||||
{"vkUpdateDescriptorSets", reinterpret_cast<PFN_vkVoidFunction>(mockUpdateDescriptorSets)},
|
||||
{"vkCmdBindDescriptorSets", reinterpret_cast<PFN_vkVoidFunction>(mockCmdBindDescriptorSets)},
|
||||
{"vkCreateGraphicsPipelines", reinterpret_cast<PFN_vkVoidFunction>(mockCreateGraphicsPipelines)},
|
||||
{"vkCreatePipelineLayout", reinterpret_cast<PFN_vkVoidFunction>(mockCreatePipelineLayout)},
|
||||
{"vkDestroyPipelineLayout", reinterpret_cast<PFN_vkVoidFunction>(mockDestroyPipelineLayout)},
|
||||
{"vkCreateShaderModule", reinterpret_cast<PFN_vkVoidFunction>(mockCreateShaderModule)},
|
||||
|
|
@ -3384,6 +3402,61 @@ std::vector<std::uint32_t> makeModule(const ModuleShape& shape) {
|
|||
return words;
|
||||
}
|
||||
|
||||
void pipelineLibrarySetTests() {
|
||||
using namespace AgcDriver::Graphics;
|
||||
for (const auto mask : {0u, 1u, 2u, 3u}) {
|
||||
mock = MockVulkan{};
|
||||
auto context = mockContext();
|
||||
context.graphicsPipelineLibrary = true;
|
||||
context.limits.framebufferNoAttachmentsSampleCounts = VK_SAMPLE_COUNT_1_BIT;
|
||||
State state{};
|
||||
ShaderRecompiler::RecompileResult vertex, fragment;
|
||||
vertex.spirv = makeModule({});
|
||||
fragment.spirv = makeModule({.fragment = true});
|
||||
vertex.variantId = 1;
|
||||
fragment.variantId = 2;
|
||||
for (std::uint32_t set = 0; set < 2; ++set) {
|
||||
ShaderRecompiler::DescriptorBinding binding{};
|
||||
binding.descriptorSet = set;
|
||||
if ((mask & (1u << set)) != 0u) fragment.bindings.push_back(binding);
|
||||
else vertex.bindings.push_back(binding);
|
||||
}
|
||||
const std::array<CompiledShader, 2> shaders{{{ShaderRecompiler::ShaderStage::Vertex, &vertex, 0}, {ShaderRecompiler::ShaderStage::Fragment, &fragment, PipelinePushSlotBytes}}};
|
||||
std::array<std::uint32_t, 1> mainKey{11}, fragmentKey{22};
|
||||
PipelineSetLayouts resources{makeHandle<VkDescriptorSetLayout>(), makeHandle<VkDescriptorSetLayout>(), true, mainKey, fragmentKey};
|
||||
const auto prebuild = [&] { Require(Pipeline::Prebuild(context, state, VertexInputLayout{}, resources, shaders), "graphics pipeline library prebuild was skipped"); };
|
||||
prebuild();
|
||||
for (const auto part : {VK_GRAPHICS_PIPELINE_LIBRARY_PRE_RASTERIZATION_SHADERS_BIT_EXT, VK_GRAPHICS_PIPELINE_LIBRARY_FRAGMENT_SHADER_BIT_EXT}) {
|
||||
const auto layout = mock.libraryLayouts.at(part);
|
||||
const auto expectedMask = part == VK_GRAPHICS_PIPELINE_LIBRARY_FRAGMENT_SHADER_BIT_EXT ? mask : mask ^ 3u;
|
||||
const std::vector<VkDescriptorSetLayout> expected{(expectedMask & 1u) != 0u ? resources.main : VK_NULL_HANDLE, (expectedMask & 2u) != 0u ? resources.fragment : VK_NULL_HANDLE};
|
||||
Require(mock.pipelineLayoutSets.at(layout) == expected, "shader library lost a used set or retained an unused set");
|
||||
Require(mock.pipelineLayoutFlags.at(layout) == VK_PIPELINE_LAYOUT_CREATE_INDEPENDENT_SETS_BIT_EXT, "shader library lost independent sets");
|
||||
const auto& pushes = mock.pipelineLayoutPushRanges.at(layout);
|
||||
Require(pushes.size() == 2 && pushes[0].offset == 0 && pushes[0].size == PipelinePushSlotBytes && pushes[0].stageFlags == VK_SHADER_STAGE_VERTEX_BIT && pushes[1].offset == PipelinePushSlotBytes && pushes[1].size == PipelinePushSlotBytes && pushes[1].stageFlags == VK_SHADER_STAGE_FRAGMENT_BIT, "shader library changed push constant slots");
|
||||
}
|
||||
auto before = PipelineLibraryCountersOf(context.device).built;
|
||||
prebuild();
|
||||
Require(PipelineLibraryCountersOf(context.device).built == before, "identical shader libraries were rebuilt");
|
||||
for (std::uint32_t set = 0; set < 2; ++set) {
|
||||
if (set == 0) { ++mainKey[0]; resources.main = makeHandle<VkDescriptorSetLayout>(); }
|
||||
else { ++fragmentKey[0]; resources.fragment = makeHandle<VkDescriptorSetLayout>(); }
|
||||
prebuild();
|
||||
const auto after = PipelineLibraryCountersOf(context.device).built;
|
||||
Require(after[0] == before[0] && after[3] == before[3], "descriptor layout change rebuilt interface libraries");
|
||||
Require(after[1] == before[1] + ((mask & (1u << set)) == 0u ? 1u : 0u), "pre-rasterization library reuse ignored descriptor set usage");
|
||||
Require(after[2] == before[2] + ((mask & (1u << set)) != 0u ? 1u : 0u), "fragment library reuse ignored descriptor set usage");
|
||||
before = after;
|
||||
}
|
||||
ShaderRecompiler::DescriptorBinding invalid{};
|
||||
invalid.descriptorSet = 32;
|
||||
fragment.bindings.push_back(invalid);
|
||||
expectFailure(prebuild, "descriptor set exceeds two slots");
|
||||
ClearPipelineLibraries(context.device);
|
||||
Require(mock.live == 0, "shader library tests leaked Vulkan objects");
|
||||
}
|
||||
}
|
||||
|
||||
void barycentricEmulationTests() {
|
||||
using namespace ShaderRecompiler;
|
||||
using namespace AgcDriver::Graphics;
|
||||
|
|
@ -4983,6 +5056,7 @@ int main(int argc, char** argv) {
|
|||
}
|
||||
RunGuestLeaseWaitTests();
|
||||
stateTests();
|
||||
pipelineLibrarySetTests();
|
||||
hdrComponentSwapTests();
|
||||
depthMaintenanceTests();
|
||||
hardwareScreenOffsetTests();
|
||||
|
|
|
|||
Loading…
Reference in New Issue