Vulkan: Reduce PipelineDesc size.
This packs the desc into 288 bytes. Down from over 400. The hashing and
memcmp functions are sped up considerably.
Improves performance in the VulkanPipelineCachePerf test by 50% and
also improves performance in the state changing draw call test by 20%.
Bug: angleproject:2522
Change-Id: I72ed191a93721b875684f647f806c09be4e66821
Reviewed-on: https://chromium-review.googlesource.com/c/1308460
Reviewed-by: Yuly Novikov <ynovikov@chromium.org>
Reviewed-by: Shahbaz Youssefi <syoussefi@chromium.org>
Commit-Queue: Jamie Madill <jmadill@chromium.org>
diff --git a/src/libANGLE/renderer/vulkan/VertexArrayVk.cpp b/src/libANGLE/renderer/vulkan/VertexArrayVk.cpp
index baae7ad..37b8c50 100644
--- a/src/libANGLE/renderer/vulkan/VertexArrayVk.cpp
+++ b/src/libANGLE/renderer/vulkan/VertexArrayVk.cpp
@@ -75,6 +75,8 @@
mCurrentElementArrayBufferHandle(VK_NULL_HANDLE),
mCurrentElementArrayBufferOffset(0),
mCurrentElementArrayBufferResource(nullptr),
+ mPackedInputBindings{},
+ mPackedInputAttributes{},
mDynamicVertexData(VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, kDynamicVertexDataSize),
mDynamicIndexData(VK_BUFFER_USAGE_INDEX_BUFFER_BIT, kDynamicIndexDataSize),
mTranslatedByteIndexData(VK_BUFFER_USAGE_INDEX_BUFFER_BIT, kDynamicIndexDataSize),
@@ -85,9 +87,6 @@
mCurrentArrayBufferOffsets.fill(0);
mCurrentArrayBufferResources.fill(nullptr);
- mPackedInputBindings.fill({0, 0});
- mPackedInputAttributes.fill({0, 0, 0});
-
for (vk::DynamicBuffer &buffer : mCurrentArrayBufferConversion)
{
buffer.init(kMaxVertexFormatAlignment, renderer);
@@ -397,10 +396,9 @@
bindingDesc.stride = 0;
bindingDesc.inputRate = VK_VERTEX_INPUT_RATE_VERTEX;
- vk::PackedVertexInputAttributeDesc &attribDesc = mPackedInputAttributes[attribIndex];
- attribDesc.format = static_cast<uint16_t>(VK_FORMAT_R32G32B32A32_SFLOAT);
- attribDesc.location = static_cast<uint16_t>(attribIndex);
- attribDesc.offset = 0;
+ mPackedInputAttributes.formats[attribIndex] =
+ static_cast<uint16_t>(VK_FORMAT_R32G32B32A32_SFLOAT);
+ mPackedInputAttributes.offsets[attribIndex] = 0;
}
}
@@ -424,10 +422,8 @@
UNIMPLEMENTED();
}
- vk::PackedVertexInputAttributeDesc &attribDesc = mPackedInputAttributes[attribIndex];
- attribDesc.format = static_cast<uint16_t>(vkFormat);
- attribDesc.location = static_cast<uint16_t>(attribIndex);
- attribDesc.offset = static_cast<uint32_t>(attrib.relativeOffset);
+ mPackedInputAttributes.formats[attribIndex] = static_cast<uint8_t>(vkFormat);
+ mPackedInputAttributes.offsets[attribIndex] = static_cast<uint16_t>(attrib.relativeOffset);
}
angle::Result VertexArrayVk::updateClientAttribs(const gl::Context *context,
diff --git a/src/libANGLE/renderer/vulkan/vk_cache_utils.cpp b/src/libANGLE/renderer/vulkan/vk_cache_utils.cpp
index ac910e3..51f0fb3 100644
--- a/src/libANGLE/renderer/vulkan/vk_cache_utils.cpp
+++ b/src/libANGLE/renderer/vulkan/vk_cache_utils.cpp
@@ -19,6 +19,8 @@
#include "libANGLE/renderer/vulkan/vk_format_utils.h"
#include "libANGLE/renderer/vulkan/vk_helpers.h"
+#include <type_traits>
+
namespace rx
{
namespace vk
@@ -152,7 +154,9 @@
desc->finalLayout = static_cast<VkImageLayout>(ops.finalLayout);
}
-void UnpackStencilState(const vk::PackedStencilOpState &packedState, VkStencilOpState *stateOut)
+void UnpackStencilState(const vk::PackedStencilOpState &packedState,
+ uint8_t stencilReference,
+ VkStencilOpState *stateOut)
{
stateOut->failOp = static_cast<VkStencilOp>(packedState.failOp);
stateOut->passOp = static_cast<VkStencilOp>(packedState.passOp);
@@ -160,20 +164,18 @@
stateOut->compareOp = static_cast<VkCompareOp>(packedState.compareOp);
stateOut->compareMask = packedState.compareMask;
stateOut->writeMask = packedState.writeMask;
- stateOut->reference = packedState.reference;
+ stateOut->reference = stencilReference;
}
void UnpackBlendAttachmentState(const vk::PackedColorBlendAttachmentState &packedState,
VkPipelineColorBlendAttachmentState *stateOut)
{
- stateOut->blendEnable = static_cast<VkBool32>(packedState.blendEnable);
stateOut->srcColorBlendFactor = static_cast<VkBlendFactor>(packedState.srcColorBlendFactor);
stateOut->dstColorBlendFactor = static_cast<VkBlendFactor>(packedState.dstColorBlendFactor);
stateOut->colorBlendOp = static_cast<VkBlendOp>(packedState.colorBlendOp);
stateOut->srcAlphaBlendFactor = static_cast<VkBlendFactor>(packedState.srcAlphaBlendFactor);
stateOut->dstAlphaBlendFactor = static_cast<VkBlendFactor>(packedState.dstAlphaBlendFactor);
stateOut->alphaBlendOp = static_cast<VkBlendOp>(packedState.alphaBlendOp);
- stateOut->colorWriteMask = static_cast<VkColorComponentFlags>(packedState.colorWriteMask);
}
angle::Result InitializeRenderPassFromDesc(vk::Context *context,
@@ -240,6 +242,45 @@
return angle::Result::Continue();
}
+// Utility for setting a value on a packed 4-bit integer array.
+template <typename SrcT>
+void Int4Array_Set(uint8_t *arrayBytes, uint32_t arrayIndex, SrcT value)
+{
+ uint32_t byteIndex = arrayIndex >> 1;
+ ASSERT(value < 16);
+
+ if ((arrayIndex & 1) == 0)
+ {
+ arrayBytes[byteIndex] &= 0xF0;
+ arrayBytes[byteIndex] |= static_cast<uint8_t>(value);
+ }
+ else
+ {
+ arrayBytes[byteIndex] &= 0x0F;
+ arrayBytes[byteIndex] |= static_cast<uint8_t>(value) << 4;
+ }
+}
+
+// Utility for getting a value from a packed 4-bit integer array.
+template <typename DestT>
+DestT Int4Array_Get(const uint8_t *arrayBytes, uint32_t arrayIndex)
+{
+ uint32_t byteIndex = arrayIndex >> 1;
+
+ if ((arrayIndex & 1) == 0)
+ {
+ return static_cast<DestT>(arrayBytes[byteIndex] & 0xF);
+ }
+ else
+ {
+ return static_cast<DestT>(arrayBytes[byteIndex] >> 4);
+ }
+}
+
+// Helper macro that casts to a bitfield type then verifies no bits were dropped.
+#define SetBitField(lhs, rhs) \
+ lhs = static_cast<typename std::decay<decltype(lhs)>::type>(rhs); \
+ ASSERT(static_cast<decltype(rhs)>(lhs) == rhs);
} // anonymous namespace
// RenderPassDesc implementation.
@@ -255,9 +296,14 @@
memcpy(this, &other, sizeof(RenderPassDesc));
}
+size_t RenderPassDesc::colorAttachmentCount() const
+{
+ return mColorAttachmentCount;
+}
+
size_t RenderPassDesc::attachmentCount() const
{
- return mAttachmentCount;
+ return mColorAttachmentCount + mDepthStencilAttachmentCount;
}
void RenderPassDesc::setSamples(GLint samples)
@@ -268,10 +314,21 @@
void RenderPassDesc::packAttachment(const Format &format)
{
- ASSERT(mAttachmentCount < mAttachmentFormats.size() - 1);
+ ASSERT(attachmentCount() < mAttachmentFormats.size() - 1);
static_assert(angle::kNumANGLEFormats < std::numeric_limits<uint8_t>::max(),
"Too many ANGLE formats to fit in uint8_t");
- mAttachmentFormats[mAttachmentCount++] = static_cast<uint8_t>(format.angleFormatID);
+ uint8_t &packedFormat = mAttachmentFormats[attachmentCount()];
+ SetBitField(packedFormat, format.angleFormatID);
+ if (format.angleFormat().hasDepthOrStencilBits())
+ {
+ mDepthStencilAttachmentCount++;
+ }
+ else
+ {
+ // Force the user to pack the depth/stencil attachment last.
+ ASSERT(mDepthStencilAttachmentCount == 0);
+ mColorAttachmentCount++;
+ }
}
RenderPassDesc &RenderPassDesc::operator=(const RenderPassDesc &other)
@@ -304,7 +361,6 @@
PipelineDesc::PipelineDesc()
{
- ANGLE_UNUSED_VARIABLE(mPadding);
memset(this, 0, sizeof(PipelineDesc));
}
@@ -331,78 +387,85 @@
return (memcmp(this, &other, sizeof(PipelineDesc)) == 0);
}
+// TODO(jmadill): We should prefer using Packed GLenums. http://anglebug.com/2169
+
void PipelineDesc::initDefaults()
{
- mInputAssemblyInfo.topology = static_cast<uint32_t>(VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST);
- mInputAssemblyInfo.primitiveRestartEnable = 0;
+ mRasterizationAndMultisampleStateInfo.depthClampEnable = 0;
+ mRasterizationAndMultisampleStateInfo.rasterizationDiscardEnable = 0;
+ SetBitField(mRasterizationAndMultisampleStateInfo.polygonMode, VK_POLYGON_MODE_FILL);
+ SetBitField(mRasterizationAndMultisampleStateInfo.cullMode, VK_CULL_MODE_NONE);
+ SetBitField(mRasterizationAndMultisampleStateInfo.frontFace, VK_FRONT_FACE_CLOCKWISE);
+ mRasterizationAndMultisampleStateInfo.depthBiasEnable = 0;
+ mRasterizationAndMultisampleStateInfo.depthBiasConstantFactor = 0.0f;
+ mRasterizationAndMultisampleStateInfo.depthBiasClamp = 0.0f;
+ mRasterizationAndMultisampleStateInfo.depthBiasSlopeFactor = 0.0f;
+ mRasterizationAndMultisampleStateInfo.lineWidth = 1.0f;
- mRasterizationStateInfo.depthClampEnable = 0;
- mRasterizationStateInfo.rasterizationDiscardEnable = 0;
- mRasterizationStateInfo.polygonMode = static_cast<uint16_t>(VK_POLYGON_MODE_FILL);
- mRasterizationStateInfo.cullMode = static_cast<uint16_t>(VK_CULL_MODE_NONE);
- mRasterizationStateInfo.frontFace = static_cast<uint16_t>(VK_FRONT_FACE_CLOCKWISE);
- mRasterizationStateInfo.depthBiasEnable = 0;
- mRasterizationStateInfo.depthBiasConstantFactor = 0.0f;
- mRasterizationStateInfo.depthBiasClamp = 0.0f;
- mRasterizationStateInfo.depthBiasSlopeFactor = 0.0f;
- mRasterizationStateInfo.lineWidth = 1.0f;
-
- mMultisampleStateInfo.rasterizationSamples = 1;
- mMultisampleStateInfo.sampleShadingEnable = 0;
- mMultisampleStateInfo.minSampleShading = 0.0f;
- for (uint32_t &sampleMask : mMultisampleStateInfo.sampleMask)
+ mRasterizationAndMultisampleStateInfo.rasterizationSamples = 1;
+ mRasterizationAndMultisampleStateInfo.sampleShadingEnable = 0;
+ mRasterizationAndMultisampleStateInfo.minSampleShading = 0.0f;
+ for (uint32_t &sampleMask : mRasterizationAndMultisampleStateInfo.sampleMask)
{
sampleMask = 0;
}
- mMultisampleStateInfo.alphaToCoverageEnable = 0;
- mMultisampleStateInfo.alphaToOneEnable = 0;
+ mRasterizationAndMultisampleStateInfo.alphaToCoverageEnable = 0;
+ mRasterizationAndMultisampleStateInfo.alphaToOneEnable = 0;
mDepthStencilStateInfo.depthTestEnable = 0;
mDepthStencilStateInfo.depthWriteEnable = 1;
- mDepthStencilStateInfo.depthCompareOp = static_cast<uint8_t>(VK_COMPARE_OP_LESS);
+ SetBitField(mDepthStencilStateInfo.depthCompareOp, VK_COMPARE_OP_LESS);
mDepthStencilStateInfo.depthBoundsTestEnable = 0;
mDepthStencilStateInfo.stencilTestEnable = 0;
mDepthStencilStateInfo.minDepthBounds = 0.0f;
mDepthStencilStateInfo.maxDepthBounds = 0.0f;
- mDepthStencilStateInfo.front.failOp = static_cast<uint8_t>(VK_STENCIL_OP_KEEP);
- mDepthStencilStateInfo.front.passOp = static_cast<uint8_t>(VK_STENCIL_OP_KEEP);
- mDepthStencilStateInfo.front.depthFailOp = static_cast<uint8_t>(VK_STENCIL_OP_KEEP);
- mDepthStencilStateInfo.front.compareOp = static_cast<uint8_t>(VK_COMPARE_OP_ALWAYS);
- mDepthStencilStateInfo.front.compareMask = static_cast<uint32_t>(-1);
- mDepthStencilStateInfo.front.writeMask = static_cast<uint32_t>(-1);
- mDepthStencilStateInfo.front.reference = 0;
- mDepthStencilStateInfo.back.failOp = static_cast<uint8_t>(VK_STENCIL_OP_KEEP);
- mDepthStencilStateInfo.back.passOp = static_cast<uint8_t>(VK_STENCIL_OP_KEEP);
- mDepthStencilStateInfo.back.depthFailOp = static_cast<uint8_t>(VK_STENCIL_OP_KEEP);
- mDepthStencilStateInfo.back.compareOp = static_cast<uint8_t>(VK_COMPARE_OP_ALWAYS);
- mDepthStencilStateInfo.back.compareMask = static_cast<uint32_t>(-1);
- mDepthStencilStateInfo.back.writeMask = static_cast<uint32_t>(-1);
- mDepthStencilStateInfo.back.reference = 0;
+ SetBitField(mDepthStencilStateInfo.front.failOp, VK_STENCIL_OP_KEEP);
+ SetBitField(mDepthStencilStateInfo.front.passOp, VK_STENCIL_OP_KEEP);
+ SetBitField(mDepthStencilStateInfo.front.depthFailOp, VK_STENCIL_OP_KEEP);
+ SetBitField(mDepthStencilStateInfo.front.compareOp, VK_COMPARE_OP_ALWAYS);
+ SetBitField(mDepthStencilStateInfo.front.compareMask, 0xFF);
+ SetBitField(mDepthStencilStateInfo.front.writeMask, 0xFF);
+ mDepthStencilStateInfo.frontStencilReference = 0;
+ SetBitField(mDepthStencilStateInfo.back.failOp, VK_STENCIL_OP_KEEP);
+ SetBitField(mDepthStencilStateInfo.back.passOp, VK_STENCIL_OP_KEEP);
+ SetBitField(mDepthStencilStateInfo.back.depthFailOp, VK_STENCIL_OP_KEEP);
+ SetBitField(mDepthStencilStateInfo.back.compareOp, VK_COMPARE_OP_ALWAYS);
+ SetBitField(mDepthStencilStateInfo.back.compareMask, 0xFF);
+ SetBitField(mDepthStencilStateInfo.back.writeMask, 0xFF);
+ mDepthStencilStateInfo.backStencilReference = 0;
- // TODO(jmadill): Blend state/MRT.
+ PackedInputAssemblyAndColorBlendStateInfo &inputAndBlend =
+ mInputAssembltyAndColorBlendStateInfo;
+ inputAndBlend.logicOpEnable = 0;
+ inputAndBlend.logicOp = static_cast<uint32_t>(VK_LOGIC_OP_CLEAR);
+ inputAndBlend.blendEnableMask = 0;
+ inputAndBlend.blendConstants[0] = 0.0f;
+ inputAndBlend.blendConstants[1] = 0.0f;
+ inputAndBlend.blendConstants[2] = 0.0f;
+ inputAndBlend.blendConstants[3] = 0.0f;
+
+ VkFlags allColorBits = (VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT |
+ VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT);
+
+ for (uint32_t colorIndex = 0; colorIndex < gl::IMPLEMENTATION_MAX_DRAW_BUFFERS; ++colorIndex)
+ {
+ Int4Array_Set(inputAndBlend.colorWriteMaskBits, colorIndex, allColorBits);
+ }
+
PackedColorBlendAttachmentState blendAttachmentState;
- blendAttachmentState.blendEnable = 0;
- blendAttachmentState.srcColorBlendFactor = static_cast<uint8_t>(VK_BLEND_FACTOR_ONE);
- blendAttachmentState.dstColorBlendFactor = static_cast<uint8_t>(VK_BLEND_FACTOR_ONE);
- blendAttachmentState.colorBlendOp = static_cast<uint8_t>(VK_BLEND_OP_ADD);
- blendAttachmentState.srcAlphaBlendFactor = static_cast<uint8_t>(VK_BLEND_FACTOR_ONE);
- blendAttachmentState.dstAlphaBlendFactor = static_cast<uint8_t>(VK_BLEND_FACTOR_ONE);
- blendAttachmentState.alphaBlendOp = static_cast<uint8_t>(VK_BLEND_OP_ADD);
- blendAttachmentState.colorWriteMask =
- static_cast<uint8_t>(VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT |
- VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT);
+ SetBitField(blendAttachmentState.srcColorBlendFactor, VK_BLEND_FACTOR_ONE);
+ SetBitField(blendAttachmentState.dstColorBlendFactor, VK_BLEND_FACTOR_ONE);
+ SetBitField(blendAttachmentState.colorBlendOp, VK_BLEND_OP_ADD);
+ SetBitField(blendAttachmentState.srcAlphaBlendFactor, VK_BLEND_FACTOR_ONE);
+ SetBitField(blendAttachmentState.dstAlphaBlendFactor, VK_BLEND_FACTOR_ONE);
+ SetBitField(blendAttachmentState.alphaBlendOp, VK_BLEND_OP_ADD);
- mColorBlendStateInfo.logicOpEnable = 0;
- mColorBlendStateInfo.logicOp = static_cast<uint32_t>(VK_LOGIC_OP_CLEAR);
- mColorBlendStateInfo.attachmentCount = 1;
- mColorBlendStateInfo.blendConstants[0] = 0.0f;
- mColorBlendStateInfo.blendConstants[1] = 0.0f;
- mColorBlendStateInfo.blendConstants[2] = 0.0f;
- mColorBlendStateInfo.blendConstants[3] = 0.0f;
-
- std::fill(&mColorBlendStateInfo.attachments[0],
- &mColorBlendStateInfo.attachments[gl::IMPLEMENTATION_MAX_DRAW_BUFFERS],
+ std::fill(&inputAndBlend.attachments[0],
+ &inputAndBlend.attachments[gl::IMPLEMENTATION_MAX_DRAW_BUFFERS],
blendAttachmentState);
+
+ inputAndBlend.topology = static_cast<uint16_t>(VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST);
+ inputAndBlend.primitiveRestartEnable = 0;
}
angle::Result PipelineDesc::initializePipeline(vk::Context *context,
@@ -446,23 +509,30 @@
uint32_t vertexAttribCount = 0;
+ size_t unpackedSize = sizeof(shaderStages) + sizeof(vertexInputState) +
+ sizeof(inputAssemblyState) + sizeof(viewportState) + sizeof(rasterState) +
+ sizeof(multisampleState) + sizeof(depthStencilState) +
+ sizeof(blendAttachmentState) + sizeof(blendState) + sizeof(bindingDescs) +
+ sizeof(attributeDescs);
+ ANGLE_UNUSED_VARIABLE(unpackedSize);
+
for (size_t attribIndexSizeT : activeAttribLocationsMask)
{
- const auto attribIndex = static_cast<uint32_t>(attribIndexSizeT);
+ const uint32_t attribIndex = static_cast<uint32_t>(attribIndexSizeT);
VkVertexInputBindingDescription &bindingDesc = bindingDescs[vertexAttribCount];
VkVertexInputAttributeDescription &attribDesc = attributeDescs[vertexAttribCount];
const PackedVertexInputBindingDesc &packedBinding = mVertexInputBindings[attribIndex];
- const PackedVertexInputAttributeDesc &packedAttrib = mVertexInputAttribs[attribIndex];
bindingDesc.binding = attribIndex;
bindingDesc.inputRate = static_cast<VkVertexInputRate>(packedBinding.inputRate);
bindingDesc.stride = static_cast<uint32_t>(packedBinding.stride);
+ // The binding or location might change in future ES versions.
attribDesc.binding = attribIndex;
- attribDesc.format = static_cast<VkFormat>(packedAttrib.format);
- attribDesc.location = static_cast<uint32_t>(packedAttrib.location);
- attribDesc.offset = packedAttrib.offset;
+ attribDesc.format = static_cast<VkFormat>(mVertexInputAttribs.formats[attribIndex]);
+ attribDesc.location = static_cast<uint32_t>(attribIndex);
+ attribDesc.offset = mVertexInputAttribs.offsets[attribIndex];
vertexAttribCount++;
}
@@ -478,9 +548,10 @@
// Primitive topology is filled in at draw time.
inputAssemblyState.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO;
inputAssemblyState.flags = 0;
- inputAssemblyState.topology = static_cast<VkPrimitiveTopology>(mInputAssemblyInfo.topology);
+ inputAssemblyState.topology =
+ static_cast<VkPrimitiveTopology>(mInputAssembltyAndColorBlendStateInfo.topology);
inputAssemblyState.primitiveRestartEnable =
- static_cast<VkBool32>(mInputAssemblyInfo.primitiveRestartEnable);
+ static_cast<VkBool32>(mInputAssembltyAndColorBlendStateInfo.primitiveRestartEnable);
// Set initial viewport and scissor state.
@@ -491,35 +562,35 @@
viewportState.scissorCount = 1;
viewportState.pScissors = &mScissor;
+ const PackedRasterizationAndMultisampleStateInfo &rasterAndMS =
+ mRasterizationAndMultisampleStateInfo;
+
// Rasterizer state.
rasterState.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO;
rasterState.flags = 0;
- rasterState.depthClampEnable = static_cast<VkBool32>(mRasterizationStateInfo.depthClampEnable);
+ rasterState.depthClampEnable = static_cast<VkBool32>(rasterAndMS.depthClampEnable);
rasterState.rasterizerDiscardEnable =
- static_cast<VkBool32>(mRasterizationStateInfo.rasterizationDiscardEnable);
- rasterState.polygonMode = static_cast<VkPolygonMode>(mRasterizationStateInfo.polygonMode);
- rasterState.cullMode = static_cast<VkCullModeFlags>(mRasterizationStateInfo.cullMode);
- rasterState.frontFace = static_cast<VkFrontFace>(mRasterizationStateInfo.frontFace);
- rasterState.depthBiasEnable = static_cast<VkBool32>(mRasterizationStateInfo.depthBiasEnable);
- rasterState.depthBiasConstantFactor = mRasterizationStateInfo.depthBiasConstantFactor;
- rasterState.depthBiasClamp = mRasterizationStateInfo.depthBiasClamp;
- rasterState.depthBiasSlopeFactor = mRasterizationStateInfo.depthBiasSlopeFactor;
- rasterState.lineWidth = mRasterizationStateInfo.lineWidth;
+ static_cast<VkBool32>(rasterAndMS.rasterizationDiscardEnable);
+ rasterState.polygonMode = static_cast<VkPolygonMode>(rasterAndMS.polygonMode);
+ rasterState.cullMode = static_cast<VkCullModeFlags>(rasterAndMS.cullMode);
+ rasterState.frontFace = static_cast<VkFrontFace>(rasterAndMS.frontFace);
+ rasterState.depthBiasEnable = static_cast<VkBool32>(rasterAndMS.depthBiasEnable);
+ rasterState.depthBiasConstantFactor = rasterAndMS.depthBiasConstantFactor;
+ rasterState.depthBiasClamp = rasterAndMS.depthBiasClamp;
+ rasterState.depthBiasSlopeFactor = rasterAndMS.depthBiasSlopeFactor;
+ rasterState.lineWidth = rasterAndMS.lineWidth;
// Multisample state.
multisampleState.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO;
multisampleState.flags = 0;
- multisampleState.rasterizationSamples =
- gl_vk::GetSamples(mMultisampleStateInfo.rasterizationSamples);
- multisampleState.sampleShadingEnable =
- static_cast<VkBool32>(mMultisampleStateInfo.sampleShadingEnable);
- multisampleState.minSampleShading = mMultisampleStateInfo.minSampleShading;
+ multisampleState.rasterizationSamples = gl_vk::GetSamples(rasterAndMS.rasterizationSamples);
+ multisampleState.sampleShadingEnable = static_cast<VkBool32>(rasterAndMS.sampleShadingEnable);
+ multisampleState.minSampleShading = rasterAndMS.minSampleShading;
// TODO(jmadill): sample masks
multisampleState.pSampleMask = nullptr;
multisampleState.alphaToCoverageEnable =
- static_cast<VkBool32>(mMultisampleStateInfo.alphaToCoverageEnable);
- multisampleState.alphaToOneEnable =
- static_cast<VkBool32>(mMultisampleStateInfo.alphaToOneEnable);
+ static_cast<VkBool32>(rasterAndMS.alphaToCoverageEnable);
+ multisampleState.alphaToOneEnable = static_cast<VkBool32>(rasterAndMS.alphaToOneEnable);
// Depth/stencil state.
depthStencilState.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO;
@@ -534,27 +605,38 @@
static_cast<VkBool32>(mDepthStencilStateInfo.depthBoundsTestEnable);
depthStencilState.stencilTestEnable =
static_cast<VkBool32>(mDepthStencilStateInfo.stencilTestEnable);
- UnpackStencilState(mDepthStencilStateInfo.front, &depthStencilState.front);
- UnpackStencilState(mDepthStencilStateInfo.back, &depthStencilState.back);
+ UnpackStencilState(mDepthStencilStateInfo.front, mDepthStencilStateInfo.frontStencilReference,
+ &depthStencilState.front);
+ UnpackStencilState(mDepthStencilStateInfo.back, mDepthStencilStateInfo.backStencilReference,
+ &depthStencilState.back);
depthStencilState.minDepthBounds = mDepthStencilStateInfo.minDepthBounds;
depthStencilState.maxDepthBounds = mDepthStencilStateInfo.maxDepthBounds;
+ const PackedInputAssemblyAndColorBlendStateInfo &inputAndBlend =
+ mInputAssembltyAndColorBlendStateInfo;
+
blendState.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO;
blendState.flags = 0;
- blendState.logicOpEnable = static_cast<VkBool32>(mColorBlendStateInfo.logicOpEnable);
- blendState.logicOp = static_cast<VkLogicOp>(mColorBlendStateInfo.logicOp);
- blendState.attachmentCount = mColorBlendStateInfo.attachmentCount;
+ blendState.logicOpEnable = static_cast<VkBool32>(inputAndBlend.logicOpEnable);
+ blendState.logicOp = static_cast<VkLogicOp>(inputAndBlend.logicOp);
+ blendState.attachmentCount = mRenderPassDesc.colorAttachmentCount();
blendState.pAttachments = blendAttachmentState.data();
for (int i = 0; i < 4; i++)
{
- blendState.blendConstants[i] = mColorBlendStateInfo.blendConstants[i];
+ blendState.blendConstants[i] = inputAndBlend.blendConstants[i];
}
+ const gl::DrawBufferMask blendEnableMask(inputAndBlend.blendEnableMask);
+
for (uint32_t colorIndex = 0; colorIndex < blendState.attachmentCount; ++colorIndex)
{
- UnpackBlendAttachmentState(mColorBlendStateInfo.attachments[colorIndex],
- &blendAttachmentState[colorIndex]);
+ VkPipelineColorBlendAttachmentState &state = blendAttachmentState[colorIndex];
+
+ state.blendEnable = blendEnableMask[colorIndex] ? VK_TRUE : VK_FALSE;
+ state.colorWriteMask =
+ Int4Array_Get<VkColorComponentFlags>(inputAndBlend.colorWriteMaskBits, colorIndex);
+ UnpackBlendAttachmentState(inputAndBlend.attachments[colorIndex], &state);
}
// TODO(jmadill): Dynamic state.
@@ -636,23 +718,25 @@
void PipelineDesc::updateTopology(gl::PrimitiveMode drawMode)
{
- mInputAssemblyInfo.topology = static_cast<uint32_t>(gl_vk::GetPrimitiveTopology(drawMode));
+ mInputAssembltyAndColorBlendStateInfo.topology =
+ static_cast<uint32_t>(gl_vk::GetPrimitiveTopology(drawMode));
}
void PipelineDesc::updateCullMode(const gl::RasterizerState &rasterState)
{
- mRasterizationStateInfo.cullMode = static_cast<uint16_t>(gl_vk::GetCullMode(rasterState));
+ mRasterizationAndMultisampleStateInfo.cullMode =
+ static_cast<uint16_t>(gl_vk::GetCullMode(rasterState));
}
void PipelineDesc::updateFrontFace(const gl::RasterizerState &rasterState, bool invertFrontFace)
{
- mRasterizationStateInfo.frontFace =
+ mRasterizationAndMultisampleStateInfo.frontFace =
static_cast<uint16_t>(gl_vk::GetFrontFace(rasterState.frontFace, invertFrontFace));
}
void PipelineDesc::updateLineWidth(float lineWidth)
{
- mRasterizationStateInfo.lineWidth = lineWidth;
+ mRasterizationAndMultisampleStateInfo.lineWidth = lineWidth;
}
const RenderPassDesc &PipelineDesc::getRenderPassDesc() const
@@ -662,23 +746,25 @@
void PipelineDesc::updateBlendColor(const gl::ColorF &color)
{
- mColorBlendStateInfo.blendConstants[0] = color.red;
- mColorBlendStateInfo.blendConstants[1] = color.green;
- mColorBlendStateInfo.blendConstants[2] = color.blue;
- mColorBlendStateInfo.blendConstants[3] = color.alpha;
+ mInputAssembltyAndColorBlendStateInfo.blendConstants[0] = color.red;
+ mInputAssembltyAndColorBlendStateInfo.blendConstants[1] = color.green;
+ mInputAssembltyAndColorBlendStateInfo.blendConstants[2] = color.blue;
+ mInputAssembltyAndColorBlendStateInfo.blendConstants[3] = color.alpha;
}
void PipelineDesc::updateBlendEnabled(bool isBlendEnabled)
{
- for (auto &blendAttachmentState : mColorBlendStateInfo.attachments)
- {
- blendAttachmentState.blendEnable = isBlendEnabled;
- }
+ gl::DrawBufferMask blendEnabled;
+ if (isBlendEnabled)
+ blendEnabled.set();
+ mInputAssembltyAndColorBlendStateInfo.blendEnableMask =
+ static_cast<uint8_t>(blendEnabled.bits());
}
void PipelineDesc::updateBlendEquations(const gl::BlendState &blendState)
{
- for (auto &blendAttachmentState : mColorBlendStateInfo.attachments)
+ for (PackedColorBlendAttachmentState &blendAttachmentState :
+ mInputAssembltyAndColorBlendStateInfo.attachments)
{
blendAttachmentState.colorBlendOp = PackGLBlendOp(blendState.blendEquationRGB);
blendAttachmentState.alphaBlendOp = PackGLBlendOp(blendState.blendEquationAlpha);
@@ -687,7 +773,8 @@
void PipelineDesc::updateBlendFuncs(const gl::BlendState &blendState)
{
- for (auto &blendAttachmentState : mColorBlendStateInfo.attachments)
+ for (PackedColorBlendAttachmentState &blendAttachmentState :
+ mInputAssembltyAndColorBlendStateInfo.attachments)
{
blendAttachmentState.srcColorBlendFactor = PackGLBlendFactor(blendState.sourceBlendRGB);
blendAttachmentState.dstColorBlendFactor = PackGLBlendFactor(blendState.destBlendRGB);
@@ -699,12 +786,14 @@
void PipelineDesc::updateColorWriteMask(VkColorComponentFlags colorComponentFlags,
const gl::DrawBufferMask &alphaMask)
{
+ PackedInputAssemblyAndColorBlendStateInfo &inputAndBlend =
+ mInputAssembltyAndColorBlendStateInfo;
uint8_t colorMask = static_cast<uint8_t>(colorComponentFlags);
for (size_t colorIndex = 0; colorIndex < gl::IMPLEMENTATION_MAX_DRAW_BUFFERS; colorIndex++)
{
- mColorBlendStateInfo.attachments[colorIndex].colorWriteMask =
- alphaMask[colorIndex] ? (colorMask & ~VK_COLOR_COMPONENT_A_BIT) : colorMask;
+ uint8_t mask = alphaMask[colorIndex] ? (colorMask & ~VK_COLOR_COMPONENT_A_BIT) : colorMask;
+ Int4Array_Set(inputAndBlend.colorWriteMaskBits, colorIndex, mask);
}
}
@@ -742,17 +831,17 @@
void PipelineDesc::updateStencilFrontFuncs(GLint ref,
const gl::DepthStencilState &depthStencilState)
{
- mDepthStencilStateInfo.front.reference = ref;
+ mDepthStencilStateInfo.frontStencilReference = static_cast<uint8_t>(ref);
mDepthStencilStateInfo.front.compareOp = PackGLCompareFunc(depthStencilState.stencilFunc);
- mDepthStencilStateInfo.front.compareMask = static_cast<uint32_t>(depthStencilState.stencilMask);
+ mDepthStencilStateInfo.front.compareMask = static_cast<uint8_t>(depthStencilState.stencilMask);
}
void PipelineDesc::updateStencilBackFuncs(GLint ref, const gl::DepthStencilState &depthStencilState)
{
- mDepthStencilStateInfo.back.reference = ref;
+ mDepthStencilStateInfo.backStencilReference = static_cast<uint8_t>(ref);
mDepthStencilStateInfo.back.compareOp = PackGLCompareFunc(depthStencilState.stencilBackFunc);
mDepthStencilStateInfo.back.compareMask =
- static_cast<uint32_t>(depthStencilState.stencilBackMask);
+ static_cast<uint8_t>(depthStencilState.stencilBackMask);
}
void PipelineDesc::updateStencilFrontOps(const gl::DepthStencilState &depthStencilState)
@@ -776,7 +865,7 @@
const gl::Framebuffer *drawFramebuffer)
{
// Don't write to stencil buffers that should not exist
- mDepthStencilStateInfo.front.writeMask = static_cast<uint32_t>(
+ mDepthStencilStateInfo.front.writeMask = static_cast<uint8_t>(
drawFramebuffer->hasStencil() ? depthStencilState.stencilWritemask : 0);
}
@@ -784,19 +873,19 @@
const gl::Framebuffer *drawFramebuffer)
{
// Don't write to stencil buffers that should not exist
- mDepthStencilStateInfo.back.writeMask = static_cast<uint32_t>(
+ mDepthStencilStateInfo.back.writeMask = static_cast<uint8_t>(
drawFramebuffer->hasStencil() ? depthStencilState.stencilBackWritemask : 0);
}
void PipelineDesc::updatePolygonOffsetFillEnabled(bool enabled)
{
- mRasterizationStateInfo.depthBiasEnable = enabled;
+ mRasterizationAndMultisampleStateInfo.depthBiasEnable = enabled;
}
void PipelineDesc::updatePolygonOffset(const gl::RasterizerState &rasterState)
{
- mRasterizationStateInfo.depthBiasSlopeFactor = rasterState.polygonOffsetFactor;
- mRasterizationStateInfo.depthBiasConstantFactor = rasterState.polygonOffsetUnits;
+ mRasterizationAndMultisampleStateInfo.depthBiasSlopeFactor = rasterState.polygonOffsetFactor;
+ mRasterizationAndMultisampleStateInfo.depthBiasConstantFactor = rasterState.polygonOffsetUnits;
}
void PipelineDesc::updateRenderPassDesc(const RenderPassDesc &renderPassDesc)
diff --git a/src/libANGLE/renderer/vulkan/vk_cache_utils.h b/src/libANGLE/renderer/vulkan/vk_cache_utils.h
index 1698981..a857a49 100644
--- a/src/libANGLE/renderer/vulkan/vk_cache_utils.h
+++ b/src/libANGLE/renderer/vulkan/vk_cache_utils.h
@@ -56,10 +56,12 @@
RenderPassDesc(const RenderPassDesc &other);
RenderPassDesc &operator=(const RenderPassDesc &other);
+ // The caller must pack the depth/stencil attachment last.
void packAttachment(const Format &format);
size_t hash() const;
+ size_t colorAttachmentCount() const;
size_t attachmentCount() const;
void setSamples(GLint samples);
@@ -68,19 +70,21 @@
angle::FormatID operator[](size_t index) const
{
- ASSERT(index < mAttachmentFormats.size());
+ ASSERT(index < gl::IMPLEMENTATION_MAX_DRAW_BUFFERS + 1);
return static_cast<angle::FormatID>(mAttachmentFormats[index]);
}
private:
uint8_t mSamples;
- uint8_t mAttachmentCount;
+ uint8_t mColorAttachmentCount : 4;
+ uint8_t mDepthStencilAttachmentCount : 4;
gl::AttachmentArray<uint8_t> mAttachmentFormats;
};
bool operator==(const RenderPassDesc &lhs, const RenderPassDesc &rhs);
-static_assert(sizeof(RenderPassDesc) == 12, "Size check failed");
+constexpr size_t kRenderPassDescSize = sizeof(RenderPassDesc);
+static_assert(kRenderPassDescSize == 12, "Size check failed");
struct alignas(8) PackedAttachmentOpsDesc final
{
@@ -120,7 +124,7 @@
static_assert(sizeof(AttachmentOpsArray) == 80, "Size check failed");
-struct alignas(8) PackedShaderStageInfo final
+struct PackedShaderStageInfo final
{
uint32_t stage;
uint32_t moduleSerial;
@@ -129,7 +133,7 @@
static_assert(sizeof(PackedShaderStageInfo) == 8, "Size check failed");
-struct alignas(4) PackedVertexInputBindingDesc final
+struct PackedVertexInputBindingDesc final
{
// Although techncially stride can be any value in ES 2.0, in practice supporting stride
// greater than MAX_USHORT should not be that helpful. Note that stride limits are
@@ -140,32 +144,21 @@
static_assert(sizeof(PackedVertexInputBindingDesc) == 4, "Size check failed");
-struct alignas(8) PackedVertexInputAttributeDesc final
-{
- uint16_t location;
- uint16_t format;
- uint32_t offset;
-};
-
-static_assert(sizeof(PackedVertexInputAttributeDesc) == 8, "Size check failed");
-
-struct alignas(8) PackedInputAssemblyInfo
-{
- uint32_t topology;
- uint32_t primitiveRestartEnable;
-};
-
-static_assert(sizeof(PackedInputAssemblyInfo) == 8, "Size check failed");
-
-struct alignas(32) PackedRasterizationStateInfo
+struct PackedRasterizationAndMultisampleStateInfo final
{
// Padded to ensure there's no gaps in this structure or those that use it.
- uint32_t depthClampEnable;
- uint32_t rasterizationDiscardEnable;
- uint16_t polygonMode;
- uint16_t cullMode;
- uint16_t frontFace;
- uint16_t depthBiasEnable;
+ uint32_t depthClampEnable : 4;
+ uint32_t rasterizationDiscardEnable : 4;
+ uint32_t polygonMode : 4;
+ uint32_t cullMode : 4;
+ uint32_t frontFace : 4;
+ uint32_t depthBiasEnable : 4;
+ uint32_t rasterizationSamples : 4;
+ uint32_t sampleShadingEnable : 1;
+ uint32_t alphaToCoverageEnable : 1;
+ uint32_t alphaToOneEnable : 2;
+ float minSampleShading;
+ uint32_t sampleMask[gl::MAX_SAMPLE_MASK_WORDS];
float depthBiasConstantFactor;
// Note: depth bias clamp is only exposed in a 3.1 extension, but left here for completeness.
float depthBiasClamp;
@@ -173,79 +166,82 @@
float lineWidth;
};
-static_assert(sizeof(PackedRasterizationStateInfo) == 32, "Size check failed");
+static constexpr size_t kPackedRasterizationAndMultisampleStateSize =
+ sizeof(PackedRasterizationAndMultisampleStateInfo);
+static_assert(kPackedRasterizationAndMultisampleStateSize == 32, "Size check failed");
-struct alignas(16) PackedMultisampleStateInfo final
+struct PackedStencilOpState final
{
- uint8_t rasterizationSamples;
- uint8_t sampleShadingEnable;
- uint8_t alphaToCoverageEnable;
- uint8_t alphaToOneEnable;
- float minSampleShading;
- uint32_t sampleMask[gl::MAX_SAMPLE_MASK_WORDS];
+ uint8_t failOp : 4;
+ uint8_t passOp : 4;
+ uint8_t depthFailOp : 4;
+ uint8_t compareOp : 4;
+ uint8_t compareMask;
+ uint8_t writeMask;
};
-static_assert(sizeof(PackedMultisampleStateInfo) == 16, "Size check failed");
-
-struct alignas(16) PackedStencilOpState final
-{
- uint8_t failOp;
- uint8_t passOp;
- uint8_t depthFailOp;
- uint8_t compareOp;
- uint32_t compareMask;
- uint32_t writeMask;
- uint32_t reference;
-};
-
-static_assert(sizeof(PackedStencilOpState) == 16, "Size check failed");
+static constexpr size_t kPackedStencilOpSize = sizeof(PackedStencilOpState);
+static_assert(sizeof(PackedStencilOpState) == 4, "Size check failed");
struct PackedDepthStencilStateInfo final
{
- uint8_t depthTestEnable;
- uint8_t depthWriteEnable;
- uint8_t depthCompareOp;
- uint8_t depthBoundsTestEnable;
- // 32-bits to pad the alignments.
- uint32_t stencilTestEnable;
+ uint8_t depthTestEnable : 1;
+ uint8_t depthWriteEnable : 1;
+ uint8_t depthCompareOp : 4;
+ uint8_t depthBoundsTestEnable : 1;
+ uint8_t stencilTestEnable : 1;
+ uint8_t frontStencilReference;
+ uint8_t backStencilReference;
+ uint8_t padding;
float minDepthBounds;
float maxDepthBounds;
PackedStencilOpState front;
PackedStencilOpState back;
};
-static_assert(sizeof(PackedDepthStencilStateInfo) == 48, "Size check failed");
+constexpr size_t kPackedDepthStencilStateSize = sizeof(PackedDepthStencilStateInfo);
+static_assert(kPackedDepthStencilStateSize == 20, "Size check failed");
-struct alignas(8) PackedColorBlendAttachmentState final
+struct PackedColorBlendAttachmentState final
{
- uint8_t blendEnable;
- uint8_t srcColorBlendFactor;
- uint8_t dstColorBlendFactor;
- uint8_t colorBlendOp;
- uint8_t srcAlphaBlendFactor;
- uint8_t dstAlphaBlendFactor;
- uint8_t alphaBlendOp;
- uint8_t colorWriteMask;
+ uint16_t srcColorBlendFactor : 5;
+ uint16_t dstColorBlendFactor : 5;
+ uint16_t colorBlendOp : 6;
+ uint16_t srcAlphaBlendFactor : 5;
+ uint16_t dstAlphaBlendFactor : 5;
+ uint16_t alphaBlendOp : 6;
};
-static_assert(sizeof(PackedColorBlendAttachmentState) == 8, "Size check failed");
+static_assert(sizeof(PackedColorBlendAttachmentState) == 4, "Size check failed");
-struct PackedColorBlendStateInfo final
+struct PackedInputAssemblyAndColorBlendStateInfo final
{
- // Padded to round the struct size.
- uint32_t logicOpEnable;
- uint32_t logicOp;
- uint32_t attachmentCount;
- uint32_t padding;
- float blendConstants[4];
+ uint8_t logicOpEnable : 1;
+ uint8_t logicOp : 7;
+ uint8_t blendEnableMask;
+ uint8_t colorWriteMaskBits[gl::IMPLEMENTATION_MAX_DRAW_BUFFERS / 2];
PackedColorBlendAttachmentState attachments[gl::IMPLEMENTATION_MAX_DRAW_BUFFERS];
+ uint16_t topology : 15;
+ uint16_t primitiveRestartEnable : 1;
+ float blendConstants[4];
};
-static_assert(sizeof(PackedColorBlendStateInfo) == 96, "Size check failed");
+constexpr size_t kPackedInputAssemblyAndColorBlendStateSize =
+ sizeof(PackedInputAssemblyAndColorBlendStateInfo);
+static_assert(kPackedInputAssemblyAndColorBlendStateSize == 56, "Size check failed");
using ShaderStageInfo = vk::ShaderMap<PackedShaderStageInfo>;
using VertexInputBindings = gl::AttribArray<PackedVertexInputBindingDesc>;
-using VertexInputAttributes = gl::AttribArray<PackedVertexInputAttributeDesc>;
+
+struct VertexInputAttributes final
+{
+ uint8_t formats[gl::MAX_VERTEX_ATTRIBS];
+ uint16_t offsets[gl::MAX_VERTEX_ATTRIBS]; // can only take 11 bits on NV
+};
+
+constexpr size_t kShaderStageInfoSize = sizeof(ShaderStageInfo);
+constexpr size_t kVertexInputBindingsSize = sizeof(VertexInputBindings);
+constexpr size_t kVertexInputAttributesSize = sizeof(VertexInputAttributes);
class PipelineDesc final
{
@@ -340,18 +336,15 @@
ShaderStageInfo mShaderStageInfo;
VertexInputBindings mVertexInputBindings;
VertexInputAttributes mVertexInputAttribs;
- PackedInputAssemblyInfo mInputAssemblyInfo;
+ RenderPassDesc mRenderPassDesc;
+ PackedRasterizationAndMultisampleStateInfo mRasterizationAndMultisampleStateInfo;
+ PackedDepthStencilStateInfo mDepthStencilStateInfo;
+ PackedInputAssemblyAndColorBlendStateInfo mInputAssembltyAndColorBlendStateInfo;
// TODO(jmadill): Consider using dynamic state for viewport/scissor.
VkViewport mViewport;
VkRect2D mScissor;
- PackedRasterizationStateInfo mRasterizationStateInfo;
- PackedMultisampleStateInfo mMultisampleStateInfo;
- PackedDepthStencilStateInfo mDepthStencilStateInfo;
- PackedColorBlendStateInfo mColorBlendStateInfo;
// TODO(jmadill): Dynamic state.
// TODO(jmadill): Pipeline layout
- RenderPassDesc mRenderPassDesc;
- uint8_t mPadding[20];
};
// Verify the packed pipeline description has no gaps in the packing.
@@ -359,11 +352,10 @@
// No gaps or padding at the end ensures that hashing and memcmp checks will not run
// into uninitialized memory regions.
constexpr size_t kPipelineDescSumOfSizes =
- sizeof(ShaderStageInfo) + sizeof(VertexInputBindings) + sizeof(VertexInputAttributes) +
- sizeof(PackedInputAssemblyInfo) + sizeof(VkViewport) + sizeof(VkRect2D) +
- sizeof(PackedRasterizationStateInfo) + sizeof(PackedMultisampleStateInfo) +
- sizeof(PackedDepthStencilStateInfo) + sizeof(PackedColorBlendStateInfo) +
- sizeof(RenderPassDesc) + 20;
+ kShaderStageInfoSize + kVertexInputBindingsSize + kVertexInputAttributesSize +
+ kPackedInputAssemblyAndColorBlendStateSize + sizeof(VkViewport) + sizeof(VkRect2D) +
+ kPackedRasterizationAndMultisampleStateSize + kPackedDepthStencilStateSize +
+ kRenderPassDescSize;
static constexpr size_t kPipelineDescSize = sizeof(PipelineDesc);
static_assert(kPipelineDescSize == kPipelineDescSumOfSizes, "Size mismatch");