Support VK_EXT_graphics_pipeline_library
And VK_KHR_pipeline_library as corollary.
Bug: b/245568070
Tests: dEQP-VK.*library*
Change-Id: I10afe16ca19fa0a274a5d8c7887480a7c5b4c129
Reviewed-on: https://swiftshader-review.googlesource.com/c/SwiftShader/+/68028
Reviewed-by: Nicolas Capens <nicolascapens@google.com>
Kokoro-Result: kokoro <noreply+kokoro@google.com>
Presubmit-Ready: Shahbaz Youssefi <syoussefi@google.com>
Tested-by: Shahbaz Youssefi <syoussefi@google.com>
Commit-Queue: Shahbaz Youssefi <syoussefi@google.com>
diff --git a/src/Device/Context.cpp b/src/Device/Context.cpp
index 9024abf..ef80f03 100644
--- a/src/Device/Context.cpp
+++ b/src/Device/Context.cpp
@@ -17,6 +17,7 @@
#include "Vulkan/VkBuffer.hpp"
#include "Vulkan/VkDevice.hpp"
#include "Vulkan/VkImageView.hpp"
+#include "Vulkan/VkPipeline.hpp"
#include "Vulkan/VkRenderPass.hpp"
#include "Vulkan/VkStringify.hpp"
@@ -197,7 +198,6 @@
return dynamicStateFlags;
}
-
} // namespace
namespace vk {
@@ -269,7 +269,7 @@
}
}
-Inputs::Inputs(const VkPipelineVertexInputStateCreateInfo *vertexInputState)
+void Inputs::initialize(const VkPipelineVertexInputStateCreateInfo *vertexInputState)
{
if(vertexInputState->flags != 0)
{
@@ -528,12 +528,14 @@
}
void PreRasterizationState::initialize(const vk::Device *device,
+ const PipelineLayout *layout,
const VkPipelineViewportStateCreateInfo *viewportState,
const VkPipelineRasterizationStateCreateInfo *rasterizationState,
const vk::RenderPass *renderPass, uint32_t subpassIndex,
const VkPipelineRenderingCreateInfo *rendering,
const DynamicStateFlags &allDynamicStateFlags)
{
+ pipelineLayout = layout;
dynamicStateFlags = allDynamicStateFlags.preRasterization;
if(rasterizationState->flags != 0)
@@ -717,11 +719,14 @@
}
}
-void FragmentState::initialize(const VkPipelineDepthStencilStateCreateInfo *depthStencilState,
- const vk::RenderPass *renderPass, uint32_t subpassIndex,
- const VkPipelineRenderingCreateInfo *rendering,
- const DynamicStateFlags &allDynamicStateFlags)
+void FragmentState::initialize(
+ const PipelineLayout *layout,
+ const VkPipelineDepthStencilStateCreateInfo *depthStencilState,
+ const vk::RenderPass *renderPass, uint32_t subpassIndex,
+ const VkPipelineRenderingCreateInfo *rendering,
+ const DynamicStateFlags &allDynamicStateFlags)
{
+ pipelineLayout = layout;
dynamicStateFlags = allDynamicStateFlags.fragment;
if(renderPass)
@@ -1198,14 +1203,16 @@
GraphicsState::GraphicsState(const Device *device, const VkGraphicsPipelineCreateInfo *pCreateInfo,
const PipelineLayout *layout)
- : pipelineLayout(layout)
{
if((pCreateInfo->flags &
~(VK_PIPELINE_CREATE_DISABLE_OPTIMIZATION_BIT |
VK_PIPELINE_CREATE_DERIVATIVE_BIT |
VK_PIPELINE_CREATE_ALLOW_DERIVATIVES_BIT |
VK_PIPELINE_CREATE_EARLY_RETURN_ON_FAILURE_BIT_EXT |
- VK_PIPELINE_CREATE_FAIL_ON_PIPELINE_COMPILE_REQUIRED_BIT_EXT)) != 0)
+ VK_PIPELINE_CREATE_FAIL_ON_PIPELINE_COMPILE_REQUIRED_BIT_EXT |
+ VK_PIPELINE_CREATE_LIBRARY_BIT_KHR |
+ VK_PIPELINE_CREATE_RETAIN_LINK_TIME_OPTIMIZATION_INFO_BIT_EXT |
+ VK_PIPELINE_CREATE_LINK_TIME_OPTIMIZATION_BIT_EXT)) != 0)
{
UNSUPPORTED("pCreateInfo->flags 0x%08X", int(pCreateInfo->flags));
}
@@ -1213,24 +1220,46 @@
DynamicStateFlags dynamicStateFlags = ParseDynamicStateFlags(pCreateInfo->pDynamicState);
const auto *rendering = GetExtendedStruct<VkPipelineRenderingCreateInfo>(pCreateInfo, VK_STRUCTURE_TYPE_PIPELINE_RENDERING_CREATE_INFO);
- vertexInputInterfaceState.initialize(pCreateInfo->pVertexInputState,
- pCreateInfo->pInputAssemblyState,
- dynamicStateFlags);
- preRasterizationState.initialize(device,
- pCreateInfo->pViewportState,
- pCreateInfo->pRasterizationState,
- vk::Cast(pCreateInfo->renderPass),
- pCreateInfo->subpass,
- rendering,
- dynamicStateFlags);
+ // First, get the subset of state specified in pCreateInfo itself.
+ validSubset = GraphicsPipeline::GetGraphicsPipelineSubset(pCreateInfo);
- if(!pCreateInfo->pRasterizationState->rasterizerDiscardEnable || dynamicStateFlags.preRasterization.dynamicRasterizerDiscardEnable)
+ // If rasterizer discard is enabled (and not dynamically overridable), ignore the fragment
+ // and fragment output subsets, as they will not be used.
+ if((validSubset & VK_GRAPHICS_PIPELINE_LIBRARY_PRE_RASTERIZATION_SHADERS_BIT_EXT) != 0 &&
+ pCreateInfo->pRasterizationState->rasterizerDiscardEnable &&
+ !dynamicStateFlags.preRasterization.dynamicRasterizerDiscardEnable)
{
- fragmentState.initialize(pCreateInfo->pDepthStencilState,
+ validSubset &= ~(VK_GRAPHICS_PIPELINE_LIBRARY_FRAGMENT_SHADER_BIT_EXT | VK_GRAPHICS_PIPELINE_LIBRARY_FRAGMENT_OUTPUT_INTERFACE_BIT_EXT);
+ }
+
+ if((validSubset & VK_GRAPHICS_PIPELINE_LIBRARY_VERTEX_INPUT_INTERFACE_BIT_EXT) != 0)
+ {
+ vertexInputInterfaceState.initialize(pCreateInfo->pVertexInputState,
+ pCreateInfo->pInputAssemblyState,
+ dynamicStateFlags);
+ }
+ if((validSubset & VK_GRAPHICS_PIPELINE_LIBRARY_PRE_RASTERIZATION_SHADERS_BIT_EXT) != 0)
+ {
+ preRasterizationState.initialize(device,
+ layout,
+ pCreateInfo->pViewportState,
+ pCreateInfo->pRasterizationState,
+ vk::Cast(pCreateInfo->renderPass),
+ pCreateInfo->subpass,
+ rendering,
+ dynamicStateFlags);
+ }
+ if((validSubset & VK_GRAPHICS_PIPELINE_LIBRARY_FRAGMENT_SHADER_BIT_EXT) != 0)
+ {
+ fragmentState.initialize(layout,
+ pCreateInfo->pDepthStencilState,
vk::Cast(pCreateInfo->renderPass),
pCreateInfo->subpass,
rendering,
dynamicStateFlags);
+ }
+ if((validSubset & VK_GRAPHICS_PIPELINE_LIBRARY_FRAGMENT_OUTPUT_INTERFACE_BIT_EXT) != 0)
+ {
fragmentOutputInterfaceState.initialize(pCreateInfo->pColorBlendState,
pCreateInfo->pMultisampleState,
vk::Cast(pCreateInfo->renderPass),
@@ -1238,6 +1267,48 @@
rendering,
dynamicStateFlags);
}
+
+ // Then, apply state coming from pipeline libraries.
+ const auto *libraryCreateInfo = vk::GetExtendedStruct<VkPipelineLibraryCreateInfoKHR>(pCreateInfo->pNext, VK_STRUCTURE_TYPE_PIPELINE_LIBRARY_CREATE_INFO_KHR);
+ if(libraryCreateInfo)
+ {
+ for(uint32_t i = 0; i < libraryCreateInfo->libraryCount; ++i)
+ {
+ const auto *library = static_cast<const GraphicsPipeline *>(vk::Cast(libraryCreateInfo->pLibraries[i]));
+ const GraphicsState &libraryState = library->getState();
+ const VkGraphicsPipelineLibraryFlagsEXT librarySubset = libraryState.validSubset;
+
+ // The library subsets should be disjoint
+ ASSERT((libraryState.validSubset & validSubset) == 0);
+
+ if((librarySubset & VK_GRAPHICS_PIPELINE_LIBRARY_VERTEX_INPUT_INTERFACE_BIT_EXT) != 0)
+ {
+ vertexInputInterfaceState = libraryState.vertexInputInterfaceState;
+ }
+ if((librarySubset & VK_GRAPHICS_PIPELINE_LIBRARY_PRE_RASTERIZATION_SHADERS_BIT_EXT) != 0)
+ {
+ preRasterizationState = libraryState.preRasterizationState;
+ if (layout)
+ {
+ preRasterizationState.overridePipelineLayout(layout);
+ }
+ }
+ if((librarySubset & VK_GRAPHICS_PIPELINE_LIBRARY_FRAGMENT_SHADER_BIT_EXT) != 0)
+ {
+ fragmentState = libraryState.fragmentState;
+ if (layout)
+ {
+ fragmentState.overridePipelineLayout(layout);
+ }
+ }
+ if((librarySubset & VK_GRAPHICS_PIPELINE_LIBRARY_FRAGMENT_OUTPUT_INTERFACE_BIT_EXT) != 0)
+ {
+ fragmentOutputInterfaceState = libraryState.fragmentOutputInterfaceState;
+ }
+
+ validSubset |= libraryState.validSubset;
+ }
+ }
}
GraphicsState GraphicsState::combineStates(const DynamicState &dynamicState) const
diff --git a/src/Device/Context.hpp b/src/Device/Context.hpp
index 17a5188..55257d4 100644
--- a/src/Device/Context.hpp
+++ b/src/Device/Context.hpp
@@ -65,7 +65,7 @@
struct Inputs
{
- Inputs(const VkPipelineVertexInputStateCreateInfo *vertexInputState);
+ void initialize(const VkPipelineVertexInputStateCreateInfo *vertexInputState);
void updateDescriptorSets(const DescriptorSet::Array &dso,
const DescriptorSet::Bindings &ds,
@@ -216,8 +216,8 @@
struct VertexInputInterfaceState
{
void initialize(const VkPipelineVertexInputStateCreateInfo *vertexInputState,
- const VkPipelineInputAssemblyStateCreateInfo *inputAssemblyState,
- const DynamicStateFlags &allDynamicStateFlags);
+ const VkPipelineInputAssemblyStateCreateInfo *inputAssemblyState,
+ const DynamicStateFlags &allDynamicStateFlags);
void applyState(const DynamicState &dynamicState);
@@ -243,11 +243,15 @@
struct PreRasterizationState
{
void initialize(const vk::Device *device,
- const VkPipelineViewportStateCreateInfo *viewportState,
- const VkPipelineRasterizationStateCreateInfo *rasterizationState,
- const vk::RenderPass *renderPass, uint32_t subpassIndex,
- const VkPipelineRenderingCreateInfo *rendering,
- const DynamicStateFlags &allDynamicStateFlags);
+ const PipelineLayout *layout,
+ const VkPipelineViewportStateCreateInfo *viewportState,
+ const VkPipelineRasterizationStateCreateInfo *rasterizationState,
+ const vk::RenderPass *renderPass, uint32_t subpassIndex,
+ const VkPipelineRenderingCreateInfo *rendering,
+ const DynamicStateFlags &allDynamicStateFlags);
+
+ inline const PipelineLayout *getPipelineLayout() const { return pipelineLayout; }
+ inline void overridePipelineLayout(const PipelineLayout *linkedLayout) { pipelineLayout = linkedLayout; }
void applyState(const DynamicState &dynamicState);
@@ -274,6 +278,8 @@
inline const VkViewport &getViewport() const { return viewport; }
private:
+ const PipelineLayout *pipelineLayout = nullptr;
+
PreRasterizationDynamicStateFlags dynamicStateFlags = {};
bool rasterizerDiscard = false;
@@ -301,10 +307,14 @@
struct FragmentState
{
- void initialize(const VkPipelineDepthStencilStateCreateInfo *depthStencilState,
- const vk::RenderPass *renderPass, uint32_t subpassIndex,
- const VkPipelineRenderingCreateInfo *rendering,
- const DynamicStateFlags &allDynamicStateFlags);
+ void initialize(const PipelineLayout *layout,
+ const VkPipelineDepthStencilStateCreateInfo *depthStencilState,
+ const vk::RenderPass *renderPass, uint32_t subpassIndex,
+ const VkPipelineRenderingCreateInfo *rendering,
+ const DynamicStateFlags &allDynamicStateFlags);
+
+ inline const PipelineLayout *getPipelineLayout() const { return pipelineLayout; }
+ inline void overridePipelineLayout(const PipelineLayout *linkedLayout) { pipelineLayout = linkedLayout; }
void applyState(const DynamicState &dynamicState);
@@ -324,6 +334,8 @@
private:
void setDepthStencilState(const VkPipelineDepthStencilStateCreateInfo *depthStencilState);
+ const PipelineLayout *pipelineLayout = nullptr;
+
FragmentDynamicStateFlags dynamicStateFlags = {};
bool depthTestEnable = false;
@@ -355,10 +367,10 @@
struct FragmentOutputInterfaceState
{
void initialize(const VkPipelineColorBlendStateCreateInfo *colorBlendState,
- const VkPipelineMultisampleStateCreateInfo *multisampleState,
- const vk::RenderPass *renderPass, uint32_t subpassIndex,
- const VkPipelineRenderingCreateInfo *rendering,
- const DynamicStateFlags &allDynamicStateFlags);
+ const VkPipelineMultisampleStateCreateInfo *multisampleState,
+ const vk::RenderPass *renderPass, uint32_t subpassIndex,
+ const VkPipelineRenderingCreateInfo *rendering,
+ const DynamicStateFlags &allDynamicStateFlags);
void applyState(const DynamicState &dynamicState);
@@ -399,22 +411,53 @@
GraphicsState combineStates(const DynamicState &dynamicState) const;
- inline const PipelineLayout *getPipelineLayout() const { return pipelineLayout; }
+ bool hasVertexInputInterfaceState() const
+ {
+ return (validSubset & VK_GRAPHICS_PIPELINE_LIBRARY_VERTEX_INPUT_INTERFACE_BIT_EXT) != 0;
+ }
+ bool hasPreRasterizationState() const
+ {
+ return (validSubset & VK_GRAPHICS_PIPELINE_LIBRARY_PRE_RASTERIZATION_SHADERS_BIT_EXT) != 0;
+ }
+ bool hasFragmentState() const
+ {
+ return (validSubset & VK_GRAPHICS_PIPELINE_LIBRARY_FRAGMENT_SHADER_BIT_EXT) != 0;
+ }
+ bool hasFragmentOutputInterfaceState() const
+ {
+ return (validSubset & VK_GRAPHICS_PIPELINE_LIBRARY_FRAGMENT_OUTPUT_INTERFACE_BIT_EXT) != 0;
+ }
- const VertexInputInterfaceState &getVertexInputInterfaceState() const { return vertexInputInterfaceState; }
- const PreRasterizationState &getPreRasterizationState() const { return preRasterizationState; }
- const FragmentState &getFragmentState() const { return fragmentState; }
- const FragmentOutputInterfaceState &getFragmentOutputInterfaceState() const { return fragmentOutputInterfaceState; }
+ const VertexInputInterfaceState &getVertexInputInterfaceState() const
+ {
+ ASSERT(hasVertexInputInterfaceState());
+ return vertexInputInterfaceState;
+ }
+ const PreRasterizationState &getPreRasterizationState() const
+ {
+ ASSERT(hasPreRasterizationState());
+ return preRasterizationState;
+ }
+ const FragmentState &getFragmentState() const
+ {
+ ASSERT(hasFragmentState());
+ return fragmentState;
+ }
+ const FragmentOutputInterfaceState &getFragmentOutputInterfaceState() const
+ {
+ ASSERT(hasFragmentOutputInterfaceState());
+ return fragmentOutputInterfaceState;
+ }
private:
- const PipelineLayout *pipelineLayout = nullptr;
-
// The four subsets of a graphics pipeline as described in the spec. With
// VK_EXT_graphics_pipeline_library, a number of these may be valid.
VertexInputInterfaceState vertexInputInterfaceState;
PreRasterizationState preRasterizationState;
FragmentState fragmentState;
FragmentOutputInterfaceState fragmentOutputInterfaceState;
+
+ VkGraphicsPipelineLibraryFlagsEXT validSubset = 0;
};
} // namespace vk
diff --git a/src/Device/PixelProcessor.cpp b/src/Device/PixelProcessor.cpp
index ba61c2f..afedb55 100644
--- a/src/Device/PixelProcessor.cpp
+++ b/src/Device/PixelProcessor.cpp
@@ -86,7 +86,7 @@
if(fragmentShader)
{
state.shaderID = fragmentShader->getIdentifier();
- state.pipelineLayoutIdentifier = pipelineState.getPipelineLayout()->identifier;
+ state.pipelineLayoutIdentifier = fragmentState.getPipelineLayout()->identifier;
}
else
{
diff --git a/src/Device/Renderer.cpp b/src/Device/Renderer.cpp
index 365cef3..4c3919b 100644
--- a/src/Device/Renderer.cpp
+++ b/src/Device/Renderer.cpp
@@ -196,7 +196,7 @@
}
draw->id = id;
- const vk::GraphicsState &pipelineState = pipeline->getState(dynamicState);
+ const vk::GraphicsState &pipelineState = pipeline->getCombinedState(dynamicState);
// A graphics pipeline must always be "complete" before it can be used for drawing. A
// complete graphics pipeline always includes the vertex input interface and
@@ -208,13 +208,16 @@
// enabled, these functions and state for the latter two states are not set.
const vk::VertexInputInterfaceState &vertexInputInterfaceState = pipelineState.getVertexInputInterfaceState();
const vk::PreRasterizationState &preRasterizationState = pipelineState.getPreRasterizationState();
- const vk::FragmentState &fragmentState = pipelineState.getFragmentState();
- const vk::FragmentOutputInterfaceState &fragmentOutputInterfaceState = pipelineState.getFragmentOutputInterfaceState();
+ const vk::FragmentState *fragmentState = nullptr;
+ const vk::FragmentOutputInterfaceState *fragmentOutputInterfaceState = nullptr;
const bool hasRasterizerDiscard = preRasterizationState.hasRasterizerDiscard();
if(!hasRasterizerDiscard)
{
- pixelProcessor.setBlendConstant(fragmentOutputInterfaceState.getBlendConstants());
+ fragmentState = &pipelineState.getFragmentState();
+ fragmentOutputInterfaceState = &pipelineState.getFragmentOutputInterfaceState();
+
+ pixelProcessor.setBlendConstant(fragmentOutputInterfaceState->getBlendConstants());
}
const vk::Inputs &inputs = pipeline->getInputs();
@@ -229,7 +232,7 @@
const vk::Attachments attachments = pipeline->getAttachments();
vertexState = vertexProcessor.update(pipelineState, vertexShader, inputs);
- vertexRoutine = vertexProcessor.routine(vertexState, pipelineState.getPipelineLayout(), vertexShader, inputs.getDescriptorSets());
+ vertexRoutine = vertexProcessor.routine(vertexState, preRasterizationState.getPipelineLayout(), vertexShader, inputs.getDescriptorSets());
if(!hasRasterizerDiscard)
{
@@ -237,7 +240,7 @@
setupRoutine = setupProcessor.routine(setupState);
pixelState = pixelProcessor.update(pipelineState, fragmentShader, vertexShader, attachments, hasOcclusionQuery());
- pixelRoutine = pixelProcessor.routine(pixelState, pipelineState.getPipelineLayout(), fragmentShader, inputs.getDescriptorSets());
+ pixelRoutine = pixelProcessor.routine(pixelState, fragmentState->getPipelineLayout(), fragmentShader, inputs.getDescriptorSets());
}
}
@@ -246,7 +249,7 @@
// The sample count affects the batch size even if rasterization is disabled.
// TODO(b/147812380): Eliminate the dependency between multisampling and batch size.
- int ms = hasRasterizerDiscard ? 1 : fragmentOutputInterfaceState.getSampleCount();
+ int ms = hasRasterizerDiscard ? 1 : fragmentOutputInterfaceState->getSampleCount();
ASSERT(ms > 0);
unsigned int numPrimitivesPerBatch = MaxBatchSize / ms;
@@ -262,7 +265,7 @@
draw->indexType = pipeline->getIndexBuffer().getIndexType();
draw->lineRasterizationMode = preRasterizationState.getLineRasterizationMode();
draw->descriptorSetObjects = inputs.getDescriptorSetObjects();
- draw->pipelineLayout = pipelineState.getPipelineLayout();
+ draw->preRasterizationPipelineLayout = preRasterizationState.getPipelineLayout();
draw->depthClipEnable = preRasterizationState.getDepthClipEnable();
draw->depthClipNegativeOneToOne = preRasterizationState.getDepthClipNegativeOneToOne();
data->lineWidth = preRasterizationState.getLineWidth();
@@ -286,6 +289,8 @@
draw->vertexRoutine = vertexRoutine;
+ vk::DescriptorSet::PrepareForSampling(draw->descriptorSetObjects, draw->preRasterizationPipelineLayout, device);
+
// Viewport
{
const VkViewport &viewport = preRasterizationState.getViewport();
@@ -372,11 +377,12 @@
draw->setupRoutine = setupRoutine;
draw->pixelRoutine = pixelRoutine;
draw->setupPrimitives = setupPrimitives;
+ draw->fragmentPipelineLayout = fragmentState->getPipelineLayout();
if(pixelState.stencilActive)
{
- data->stencil[0].set(fragmentState.getFrontStencil().reference, fragmentState.getFrontStencil().compareMask, fragmentState.getFrontStencil().writeMask);
- data->stencil[1].set(fragmentState.getBackStencil().reference, fragmentState.getBackStencil().compareMask, fragmentState.getBackStencil().writeMask);
+ data->stencil[0].set(fragmentState->getFrontStencil().reference, fragmentState->getFrontStencil().compareMask, fragmentState->getFrontStencil().writeMask);
+ data->stencil[1].set(fragmentState->getBackStencil().reference, fragmentState->getBackStencil().compareMask, fragmentState->getBackStencil().writeMask);
}
data->factor = pixelProcessor.factor;
@@ -465,6 +471,11 @@
data->stencilSliceB = attachments.stencilBuffer->slicePitchBytes(VK_IMAGE_ASPECT_STENCIL_BIT, 0);
}
}
+
+ if(draw->fragmentPipelineLayout != draw->preRasterizationPipelineLayout)
+ {
+ vk::DescriptorSet::PrepareForSampling(draw->descriptorSetObjects, draw->fragmentPipelineLayout, device);
+ }
}
// Push constants
@@ -474,8 +485,6 @@
draw->events = events;
- vk::DescriptorSet::PrepareForSampling(draw->descriptorSetObjects, draw->pipelineLayout, device);
-
DrawCall::run(device, draw, &drawTickets, clusterQueues);
}
@@ -506,7 +515,7 @@
if(preRasterizationContainsImageWrite)
{
- vk::DescriptorSet::ContentsChanged(descriptorSetObjects, pipelineLayout, device);
+ vk::DescriptorSet::ContentsChanged(descriptorSetObjects, preRasterizationPipelineLayout, device);
}
if(!data->rasterizerDiscard)
@@ -528,10 +537,12 @@
}
}
- // If pre-rasterization also contains image writes, don't double-notify the descriptor set.
- if(fragmentContainsImageWrite && !preRasterizationContainsImageWrite)
+ // If pre-rasterization and fragment use the same pipeline, and pre-rasterization
+ // also contains image writes, don't double-notify the descriptor set.
+ const bool descSetAlreadyNotified = preRasterizationContainsImageWrite && fragmentPipelineLayout == preRasterizationPipelineLayout;
+ if(fragmentContainsImageWrite && !descSetAlreadyNotified)
{
- vk::DescriptorSet::ContentsChanged(descriptorSetObjects, pipelineLayout, device);
+ vk::DescriptorSet::ContentsChanged(descriptorSetObjects, fragmentPipelineLayout, device);
}
}
}
diff --git a/src/Device/Renderer.hpp b/src/Device/Renderer.hpp
index 47dd0b1..0e5afb4 100644
--- a/src/Device/Renderer.hpp
+++ b/src/Device/Renderer.hpp
@@ -170,7 +170,8 @@
vk::ImageView *depthBuffer;
vk::ImageView *stencilBuffer;
vk::DescriptorSet::Array descriptorSetObjects;
- const vk::PipelineLayout *pipelineLayout;
+ const vk::PipelineLayout *preRasterizationPipelineLayout;
+ const vk::PipelineLayout *fragmentPipelineLayout;
sw::CountedEvent *events;
vk::Query *occlusionQuery;
diff --git a/src/Device/VertexProcessor.cpp b/src/Device/VertexProcessor.cpp
index 931b50b..e5562cb 100644
--- a/src/Device/VertexProcessor.cpp
+++ b/src/Device/VertexProcessor.cpp
@@ -73,7 +73,7 @@
State state;
state.shaderID = vertexShader->getIdentifier();
- state.pipelineLayoutIdentifier = pipelineState.getPipelineLayout()->identifier;
+ state.pipelineLayoutIdentifier = preRasterizationState.getPipelineLayout()->identifier;
state.robustBufferAccess = vertexShader->getRobustBufferAccess();
state.isPoint = vertexInputInterfaceState.getTopology() == VK_PRIMITIVE_TOPOLOGY_POINT_LIST;
state.depthClipEnable = preRasterizationState.getDepthClipEnable();
diff --git a/src/Vulkan/VkPhysicalDevice.cpp b/src/Vulkan/VkPhysicalDevice.cpp
index 598d4b8..24c616c 100644
--- a/src/Vulkan/VkPhysicalDevice.cpp
+++ b/src/Vulkan/VkPhysicalDevice.cpp
@@ -383,6 +383,12 @@
}
template<typename T>
+static void getPhysicalDeviceGraphicsPipelineLibraryFeatures(T *features)
+{
+ features->graphicsPipelineLibrary = VK_TRUE;
+}
+
+template<typename T>
static void getPhysicalDeviceVulkan12Features(T *features)
{
features->samplerMirrorClampToEdge = VK_TRUE;
@@ -620,6 +626,9 @@
case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DEPTH_CLIP_CONTROL_FEATURES_EXT:
getPhysicalDeviceDepthClipControlFeaturesExt(reinterpret_cast<struct VkPhysicalDeviceDepthClipControlFeaturesEXT *>(curExtension));
break;
+ case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_GRAPHICS_PIPELINE_LIBRARY_FEATURES_EXT:
+ getPhysicalDeviceGraphicsPipelineLibraryFeatures(reinterpret_cast<struct VkPhysicalDeviceGraphicsPipelineLibraryFeaturesEXT *>(curExtension));
+ break;
case VK_STRUCTURE_TYPE_MAX_ENUM: // TODO(b/176893525): This may not be legal. dEQP tests that this value is ignored.
break;
default:
@@ -1324,6 +1333,21 @@
}
template<typename T>
+static void getGraphicsPipelineLibraryProperties(T *properties)
+{
+ // Library linking is currently fast in SwiftShader, because all the pipeline creation cost
+ // is actually paid at draw time.
+ properties->graphicsPipelineLibraryFastLinking = VK_TRUE;
+ // TODO: check this
+ properties->graphicsPipelineLibraryIndependentInterpolationDecoration = VK_FALSE;
+}
+
+void PhysicalDevice::getProperties(VkPhysicalDeviceGraphicsPipelineLibraryPropertiesEXT *properties) const
+{
+ getGraphicsPipelineLibraryProperties(properties);
+}
+
+template<typename T>
static void getSamplerFilterMinmaxProperties(T *properties)
{
properties->filterMinmaxSingleComponentFormats = VK_FALSE;
@@ -1620,6 +1644,13 @@
CheckFeature(requested, supported, primitiveTopologyPatchListRestart);
}
+bool PhysicalDevice::hasExtendedFeatures(const VkPhysicalDeviceGraphicsPipelineLibraryFeaturesEXT *requested) const
+{
+ auto supported = getSupportedFeatures(requested);
+
+ return CheckFeature(requested, supported, graphicsPipelineLibrary);
+}
+
bool PhysicalDevice::hasExtendedFeatures(const VkPhysicalDeviceDescriptorIndexingFeatures *requested) const
{
auto supported = getSupportedFeatures(requested);
diff --git a/src/Vulkan/VkPhysicalDevice.hpp b/src/Vulkan/VkPhysicalDevice.hpp
index 7f68803..8739533 100644
--- a/src/Vulkan/VkPhysicalDevice.hpp
+++ b/src/Vulkan/VkPhysicalDevice.hpp
@@ -58,6 +58,7 @@
bool hasExtendedFeatures(const VkPhysicalDeviceDescriptorIndexingFeatures *requested) const;
bool hasExtendedFeatures(const VkPhysicalDevicePipelineRobustnessFeaturesEXT *requested) const;
bool hasExtendedFeatures(const VkPhysicalDeviceProtectedMemoryFeatures *requested) const;
+ bool hasExtendedFeatures(const VkPhysicalDeviceGraphicsPipelineLibraryFeaturesEXT *requested) const;
const VkPhysicalDeviceProperties &getProperties() const;
void getProperties(VkPhysicalDeviceIDProperties *properties) const;
@@ -93,6 +94,7 @@
void getProperties(VkPhysicalDeviceTexelBufferAlignmentProperties *properties) const;
void getProperties(VkPhysicalDeviceShaderIntegerDotProductProperties *properties) const;
void getProperties(VkPhysicalDevicePipelineRobustnessPropertiesEXT *properties) const;
+ void getProperties(VkPhysicalDeviceGraphicsPipelineLibraryPropertiesEXT *properties) const;
void getProperties(VkPhysicalDeviceVulkan11Properties *properties) const;
void getProperties(VkPhysicalDeviceVulkan12Properties *properties) const;
void getProperties(VkPhysicalDeviceVulkan13Properties *properties) const;
diff --git a/src/Vulkan/VkPipeline.cpp b/src/Vulkan/VkPipeline.cpp
index 5e0902e..99c6580 100644
--- a/src/Vulkan/VkPipeline.cpp
+++ b/src/Vulkan/VkPipeline.cpp
@@ -320,21 +320,54 @@
, device(device)
, robustBufferAccess(robustBufferAccess)
{
- layout->incRefCount();
+ if(layout)
+ {
+ layout->incRefCount();
+ }
}
void Pipeline::destroy(const VkAllocationCallbacks *pAllocator)
{
destroyPipeline(pAllocator);
- vk::release(static_cast<VkPipelineLayout>(*layout), pAllocator);
+ if(layout)
+ {
+ vk::release(static_cast<VkPipelineLayout>(*layout), pAllocator);
+ }
}
GraphicsPipeline::GraphicsPipeline(const VkGraphicsPipelineCreateInfo *pCreateInfo, void *mem, Device *device)
: Pipeline(vk::Cast(pCreateInfo->layout), device, getPipelineRobustBufferAccess(pCreateInfo->pNext, device))
, state(device, pCreateInfo, layout)
- , inputs(pCreateInfo->pVertexInputState)
{
+ // Either the vertex input interface comes from a pipeline library, or the
+ // VkGraphicsPipelineCreateInfo itself. Same with shaders.
+ const auto *libraryCreateInfo = GetExtendedStruct<VkPipelineLibraryCreateInfoKHR>(pCreateInfo->pNext, VK_STRUCTURE_TYPE_PIPELINE_LIBRARY_CREATE_INFO_KHR);
+ bool vertexInputInterfaceInLibraries = false;
+ if(libraryCreateInfo)
+ {
+ for(uint32_t i = 0; i < libraryCreateInfo->libraryCount; ++i)
+ {
+ const auto *library = static_cast<const vk::GraphicsPipeline *>(vk::Cast(libraryCreateInfo->pLibraries[i]));
+ if(library->state.hasVertexInputInterfaceState())
+ {
+ inputs = library->inputs;
+ vertexInputInterfaceInLibraries = true;
+ }
+ if(library->state.hasPreRasterizationState())
+ {
+ vertexShader = library->vertexShader;
+ }
+ if(library->state.hasFragmentState())
+ {
+ fragmentShader = library->fragmentShader;
+ }
+ }
+ }
+ if(state.hasVertexInputInterfaceState() && !vertexInputInterfaceInLibraries)
+ {
+ inputs.initialize(pCreateInfo->pVertexInputState);
+ }
}
void GraphicsPipeline::destroyPipeline(const VkAllocationCallbacks *pAllocator)
@@ -348,6 +381,43 @@
return 0;
}
+VkGraphicsPipelineLibraryFlagsEXT GraphicsPipeline::GetGraphicsPipelineSubset(const VkGraphicsPipelineCreateInfo *pCreateInfo)
+{
+ const auto *libraryCreateInfo = vk::GetExtendedStruct<VkPipelineLibraryCreateInfoKHR>(pCreateInfo->pNext, VK_STRUCTURE_TYPE_PIPELINE_LIBRARY_CREATE_INFO_KHR);
+ const auto *graphicsLibraryCreateInfo = vk::GetExtendedStruct<VkGraphicsPipelineLibraryCreateInfoEXT>(pCreateInfo->pNext, VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_LIBRARY_CREATE_INFO_EXT);
+
+ if(graphicsLibraryCreateInfo)
+ {
+ return graphicsLibraryCreateInfo->flags;
+ }
+
+ // > If this structure is omitted, and either VkGraphicsPipelineCreateInfo::flags
+ // > includes VK_PIPELINE_CREATE_LIBRARY_BIT_KHR or the
+ // > VkGraphicsPipelineCreateInfo::pNext chain includes a VkPipelineLibraryCreateInfoKHR
+ // > structure with a libraryCount greater than 0, it is as if flags is 0. Otherwise if
+ // > this structure is omitted, it is as if flags includes all possible subsets of the
+ // > graphics pipeline (i.e. a complete graphics pipeline).
+ //
+ // The above basically says that when a pipeline is created:
+ // - If not a library and not created from libraries, it's a complete pipeline (i.e.
+ // Vulkan 1.0 pipelines)
+ // - If only created from other libraries, no state is taken from
+ // VkGraphicsPipelineCreateInfo.
+ //
+ // Otherwise the behavior when creating a library from other libraries is that some
+ // state is taken from VkGraphicsPipelineCreateInfo and some from the libraries.
+ const bool isLibrary = (pCreateInfo->flags & VK_PIPELINE_CREATE_LIBRARY_BIT_KHR) != 0;
+ if(isLibrary || (libraryCreateInfo && libraryCreateInfo->libraryCount > 0))
+ {
+ return 0;
+ }
+
+ return VK_GRAPHICS_PIPELINE_LIBRARY_VERTEX_INPUT_INTERFACE_BIT_EXT |
+ VK_GRAPHICS_PIPELINE_LIBRARY_PRE_RASTERIZATION_SHADERS_BIT_EXT |
+ VK_GRAPHICS_PIPELINE_LIBRARY_FRAGMENT_SHADER_BIT_EXT |
+ VK_GRAPHICS_PIPELINE_LIBRARY_FRAGMENT_OUTPUT_INTERFACE_BIT_EXT;
+}
+
void GraphicsPipeline::getIndexBuffers(const vk::DynamicState &dynamicState, uint32_t count, uint32_t first, bool indexed, std::vector<std::pair<uint32_t, void *>> *indexBuffers) const
{
const vk::VertexInputInterfaceState &vertexInputInterfaceState = state.getVertexInputInterfaceState();
@@ -404,11 +474,21 @@
VkResult GraphicsPipeline::compileShaders(const VkAllocationCallbacks *pAllocator, const VkGraphicsPipelineCreateInfo *pCreateInfo, PipelineCache *pPipelineCache)
{
PipelineCreationFeedback pipelineCreationFeedback(pCreateInfo);
+ VkGraphicsPipelineLibraryFlagsEXT pipelineSubset = GetGraphicsPipelineSubset(pCreateInfo);
+ const bool expectVertexShader = (pipelineSubset & VK_GRAPHICS_PIPELINE_LIBRARY_PRE_RASTERIZATION_SHADERS_BIT_EXT) != 0;
+ const bool expectFragmentShader = (pipelineSubset & VK_GRAPHICS_PIPELINE_LIBRARY_FRAGMENT_SHADER_BIT_EXT) != 0;
for(uint32_t stageIndex = 0; stageIndex < pCreateInfo->stageCount; stageIndex++)
{
const VkPipelineShaderStageCreateInfo &stageInfo = pCreateInfo->pStages[stageIndex];
+ // Ignore stages that don't exist in the pipeline library.
+ if((stageInfo.stage == VK_SHADER_STAGE_VERTEX_BIT && !expectVertexShader) ||
+ (stageInfo.stage == VK_SHADER_STAGE_FRAGMENT_BIT && !expectFragmentShader))
+ {
+ continue;
+ }
+
pipelineCreationFeedback.stageCreationBegins(stageIndex);
if((stageInfo.flags &
@@ -425,6 +505,25 @@
const bool optimize = !dbgctx;
const ShaderModule *module = vk::Cast(stageInfo.module);
+
+ // VK_EXT_graphics_pipeline_library allows VkShaderModuleCreateInfo to be chained to
+ // VkPipelineShaderStageCreateInfo, which is used if stageInfo.module is
+ // VK_NULL_HANDLE.
+ VkShaderModule tempModule = {};
+ if(stageInfo.module == VK_NULL_HANDLE)
+ {
+ const auto *moduleCreateInfo = vk::GetExtendedStruct<VkShaderModuleCreateInfo>(stageInfo.pNext,
+ VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO);
+ ASSERT(moduleCreateInfo);
+ VkResult createResult = vk::ShaderModule::Create(nullptr, moduleCreateInfo, &tempModule);
+ if(createResult != VK_SUCCESS)
+ {
+ return createResult;
+ }
+
+ module = vk::Cast(tempModule);
+ }
+
const PipelineCache::SpirvBinaryKey key(module->getBinary(), stageInfo.pSpecializationInfo, optimize);
if((pCreateInfo->flags & VK_PIPELINE_CREATE_FAIL_ON_PIPELINE_COMPILE_REQUIRED_BIT_EXT) &&
@@ -463,6 +562,11 @@
setShader(stageInfo.stage, shader);
pipelineCreationFeedback.stageCreationEnds(stageIndex);
+
+ if(tempModule != VK_NULL_HANDLE)
+ {
+ vk::destroy(tempModule, nullptr);
+ }
}
return VK_SUCCESS;
diff --git a/src/Vulkan/VkPipeline.hpp b/src/Vulkan/VkPipeline.hpp
index 0110d7d..c3322cd 100644
--- a/src/Vulkan/VkPipeline.hpp
+++ b/src/Vulkan/VkPipeline.hpp
@@ -92,10 +92,12 @@
#endif
static size_t ComputeRequiredAllocationSize(const VkGraphicsPipelineCreateInfo *pCreateInfo);
+ static VkGraphicsPipelineLibraryFlagsEXT GetGraphicsPipelineSubset(const VkGraphicsPipelineCreateInfo *pCreateInfo);
VkResult compileShaders(const VkAllocationCallbacks *pAllocator, const VkGraphicsPipelineCreateInfo *pCreateInfo, PipelineCache *pipelineCache);
- const GraphicsState getState(const DynamicState &ds) const { return state.combineStates(ds); }
+ GraphicsState getCombinedState(const DynamicState &ds) const { return state.combineStates(ds); }
+ const GraphicsState &getState() const { return state; }
void getIndexBuffers(const vk::DynamicState &dynamicState, uint32_t count, uint32_t first, bool indexed, std::vector<std::pair<uint32_t, void *>> *indexBuffers) const;
bool hasDynamicVertexStride() const { return state.getVertexInputInterfaceState().hasDynamicVertexStride(); }
diff --git a/src/Vulkan/VkPipelineLayout.cpp b/src/Vulkan/VkPipelineLayout.cpp
index e89cb75..f6d1917 100644
--- a/src/Vulkan/VkPipelineLayout.cpp
+++ b/src/Vulkan/VkPipelineLayout.cpp
@@ -33,6 +33,10 @@
for(uint32_t i = 0; i < pCreateInfo->setLayoutCount; i++)
{
+ if(pCreateInfo->pSetLayouts[i] == VK_NULL_HANDLE)
+ {
+ continue;
+ }
const vk::DescriptorSetLayout *setLayout = vk::Cast(pCreateInfo->pSetLayouts[i]);
uint32_t bindingsArraySize = setLayout->getBindingsArraySize();
descriptorSets[i].bindings = bindingStorage;
@@ -80,6 +84,10 @@
uint32_t bindingsCount = 0;
for(uint32_t i = 0; i < pCreateInfo->setLayoutCount; i++)
{
+ if(pCreateInfo->pSetLayouts[i] == VK_NULL_HANDLE)
+ {
+ continue;
+ }
bindingsCount += vk::Cast(pCreateInfo->pSetLayouts[i])->getBindingsArraySize();
}
diff --git a/src/Vulkan/libVulkan.cpp b/src/Vulkan/libVulkan.cpp
index 65103ca..b55891b 100644
--- a/src/Vulkan/libVulkan.cpp
+++ b/src/Vulkan/libVulkan.cpp
@@ -409,6 +409,8 @@
{ { VK_KHR_VULKAN_MEMORY_MODEL_EXTENSION_NAME, VK_KHR_VULKAN_MEMORY_MODEL_SPEC_VERSION } },
{ { VK_KHR_SAMPLER_MIRROR_CLAMP_TO_EDGE_EXTENSION_NAME, VK_KHR_SAMPLER_MIRROR_CLAMP_TO_EDGE_SPEC_VERSION } },
{ { VK_KHR_SWAPCHAIN_MUTABLE_FORMAT_EXTENSION_NAME, VK_KHR_SWAPCHAIN_MUTABLE_FORMAT_SPEC_VERSION } },
+ { { VK_KHR_PIPELINE_LIBRARY_EXTENSION_NAME, VK_KHR_PIPELINE_LIBRARY_SPEC_VERSION } },
+ { { VK_EXT_GRAPHICS_PIPELINE_LIBRARY_EXTENSION_NAME, VK_EXT_GRAPHICS_PIPELINE_LIBRARY_SPEC_VERSION } },
{ { VK_EXT_DESCRIPTOR_INDEXING_EXTENSION_NAME, VK_EXT_DESCRIPTOR_INDEXING_SPEC_VERSION } },
{ { VK_EXT_DEPTH_CLIP_ENABLE_EXTENSION_NAME, VK_EXT_DEPTH_CLIP_ENABLE_SPEC_VERSION } },
{ { VK_EXT_CUSTOM_BORDER_COLOR_EXTENSION_NAME, VK_EXT_CUSTOM_BORDER_COLOR_SPEC_VERSION } },
@@ -1084,6 +1086,7 @@
case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_MEMORY_MODEL_FEATURES:
case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MAINTENANCE_4_FEATURES:
case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_RASTERIZATION_ORDER_ATTACHMENT_ACCESS_FEATURES_EXT:
+ case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_GRAPHICS_PIPELINE_LIBRARY_FEATURES_EXT:
case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DEPTH_CLIP_CONTROL_FEATURES_EXT:
case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PIPELINE_ROBUSTNESS_FEATURES_EXT:
break;
@@ -2283,9 +2286,8 @@
TRACE("(VkDevice device = %p, const VkPipelineLayoutCreateInfo* pCreateInfo = %p, const VkAllocationCallbacks* pAllocator = %p, VkPipelineLayout* pPipelineLayout = %p)",
device, pCreateInfo, pAllocator, pPipelineLayout);
- if(pCreateInfo->flags != 0)
+ if(pCreateInfo->flags != 0 && pCreateInfo->flags != VK_PIPELINE_LAYOUT_CREATE_INDEPENDENT_SETS_BIT_EXT)
{
- // Vulkan 1.2: "flags is reserved for future use." "flags must be 0"
UNSUPPORTED("pCreateInfo->flags 0x%08X", int(pCreateInfo->flags));
}
@@ -3701,6 +3703,12 @@
vk::Cast(physicalDevice)->getProperties(properties);
}
break;
+ case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_GRAPHICS_PIPELINE_LIBRARY_PROPERTIES_EXT:
+ {
+ auto *properties = reinterpret_cast<VkPhysicalDeviceGraphicsPipelineLibraryPropertiesEXT *>(extensionProperties);
+ vk::Cast(physicalDevice)->getProperties(properties);
+ }
+ break;
default:
// "the [driver] must skip over, without processing (other than reading the sType and pNext members) any structures in the chain with sType values not defined by [supported extenions]"
UNSUPPORTED("pProperties->pNext sType = %s", vk::Stringify(extensionProperties->sType).c_str());