venus: Refactor pipeline fixup into two stages

Function vn_fix_graphics_pipeline_create_infos() had two interleaved
phases: discovery of needed fixes, and application of those fixes.
Move the discovery phase into new function
vn_find_graphics_pipeline_create_info_fixes(). The two-phase approach
will be simplify the implementation of VK_EXT_graphics_pipeline_library.

No intended change in behavior.

Signed-off-by: Lina Versace <linyaa@google.com>
Reviewed-by: Yiwei Zhang <zzyiwei@chromium.org>
Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/22419>
This commit is contained in:
Lina Versace
2023-08-03 14:11:38 -07:00
parent 732c8aa2b7
commit 258f04a426

View File

@@ -61,6 +61,17 @@ static_assert(
sizeof(((struct vn_graphics_pipeline_create_info_fields){}).mask),
"vn_graphics_pipeline_create_info_fields::mask is too small");
/**
* Description of fixes needed for a single VkGraphicsPipelineCreateInfo
* pNext chain.
*/
struct vn_graphics_pipeline_fix_desc {
/** Erase these fields to prevent the Venus encoder from reading invalid
* memory.
*/
struct vn_graphics_pipeline_create_info_fields erase;
};
/**
* Temporary storage for fixes in vkCreateGraphicsPipelines.
*
@@ -430,307 +441,308 @@ vn_graphics_pipeline_fix_tmp_alloc(const VkAllocationCallbacks *alloc,
return tmp;
}
static void
vn_find_graphics_pipeline_create_info_fixes(
const VkGraphicsPipelineCreateInfo *info,
struct vn_graphics_pipeline_fix_desc *fix_desc)
{
memset(fix_desc, 0, sizeof(*fix_desc));
const VkPipelineRenderingCreateInfo *rendering_info =
vk_find_struct_const(info, PIPELINE_RENDERING_CREATE_INFO);
VkShaderStageFlags stages = 0;
for (uint32_t j = 0; j < info->stageCount; j++) {
stages |= info->pStages[j].stage;
}
/* VkDynamicState */
struct {
bool rasterizer_discard_enable;
bool viewport;
bool viewport_with_count;
bool scissor;
bool scissor_with_count;
bool vertex_input;
} has_dynamic_state = { 0 };
if (info->pDynamicState) {
for (uint32_t j = 0; j < info->pDynamicState->dynamicStateCount; j++) {
switch (info->pDynamicState->pDynamicStates[j]) {
case VK_DYNAMIC_STATE_RASTERIZER_DISCARD_ENABLE:
has_dynamic_state.rasterizer_discard_enable = true;
break;
case VK_DYNAMIC_STATE_VIEWPORT:
has_dynamic_state.viewport = true;
break;
case VK_DYNAMIC_STATE_VIEWPORT_WITH_COUNT:
has_dynamic_state.viewport_with_count = true;
break;
case VK_DYNAMIC_STATE_SCISSOR:
has_dynamic_state.scissor = true;
break;
case VK_DYNAMIC_STATE_SCISSOR_WITH_COUNT:
has_dynamic_state.scissor_with_count = true;
break;
case VK_DYNAMIC_STATE_VERTEX_INPUT_EXT:
has_dynamic_state.vertex_input = true;
break;
default:
break;
}
}
}
const struct vn_render_pass *pass =
vn_render_pass_from_handle(info->renderPass);
const struct vn_subpass *subpass = NULL;
if (pass)
subpass = &pass->subpasses[info->subpass];
/* TODO: Ignore VkPipelineRenderingCreateInfo when not using dynamic
* rendering. This requires either a deep rewrite of
* VkGraphicsPipelineCreateInfo::pNext or a fix in the generated
* protocol code.
*
* The Vulkan spec (1.3.223) says about VkPipelineRenderingCreateInfo:
* If a graphics pipeline is created with a valid VkRenderPass,
* parameters of this structure are ignored.
*/
const bool has_dynamic_rendering = !pass && rendering_info;
/* For each pipeline state category, we define a bool.
*
* The Vulkan spec (1.3.223) says:
* The state required for a graphics pipeline is divided into vertex
* input state, pre-rasterization shader state, fragment shader
* state, and fragment output state.
*
* Without VK_EXT_graphics_pipeline_library, most states are
* unconditionally included in the pipeline. Despite that, we still
* reference the state bools in the ignore rules because (a) it makes
* the ignore condition easier to validate against the text of the
* relevant VUs; and (b) it makes it easier to enable
* VK_EXT_graphics_pipeline_library because we won't need to carefully
* revisit the text of each VU to untangle the missing pipeline state
* bools.
*/
/* The spec does not assign a name to this state. We define it just to
* deduplicate code.
*
* The Vulkan spec (1.3.223) says:
* If the value of [...]rasterizerDiscardEnable in the
* pre-rasterization shader state is VK_FALSE or the
* VK_DYNAMIC_STATE_RASTERIZER_DISCARD_ENABLE dynamic state is
* enabled fragment shader state and fragment output interface state
* is included in a complete graphics pipeline.
*/
const bool has_raster_state =
has_dynamic_state.rasterizer_discard_enable ||
(info->pRasterizationState &&
info->pRasterizationState->rasterizerDiscardEnable == VK_FALSE);
/* VK_GRAPHICS_PIPELINE_LIBRARY_FRAGMENT_SHADER_BIT_EXT */
const bool has_fragment_shader_state = has_raster_state;
/* VK_GRAPHICS_PIPELINE_LIBRARY_FRAGMENT_OUTPUT_INTERFACE_BIT_EXT */
const bool has_fragment_output_state = has_raster_state;
/* Ignore pTessellationState?
* VUID-VkGraphicsPipelineCreateInfo-pStages-00731
*/
if (info->pTessellationState &&
(!(stages & VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT) ||
!(stages & VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT))) {
fix_desc->erase.tessellation_state = true;
}
/* Ignore pViewportState?
* VUID-VkGraphicsPipelineCreateInfo-rasterizerDiscardEnable-00750
* VUID-VkGraphicsPipelineCreateInfo-pViewportState-04892
*/
if (info->pViewportState && !has_raster_state) {
fix_desc->erase.viewport_state = true;
}
/* Ignore pViewports?
* VUID-VkGraphicsPipelineCreateInfo-pDynamicStates-04130
*
* If viewportCount is 0, then venus encoder will ignore pViewports and we
* do not need to erase it.
*/
if (!fix_desc->erase.viewport_state && info->pViewportState &&
info->pViewportState->pViewports &&
info->pViewportState->viewportCount) {
const bool has_dynamic_viewport =
has_dynamic_state.viewport || has_dynamic_state.viewport_with_count;
if (has_dynamic_viewport) {
fix_desc->erase.viewport_state_viewports = true;
}
}
/* Ignore pScissors?
* VUID-VkGraphicsPipelineCreateInfo-pDynamicStates-04131
*
* If scissorCount is 0, then venus encoder will ignore pScissors and we
* do not need to erase it.
*/
if (!fix_desc->erase.viewport_state && info->pViewportState &&
info->pViewportState->pScissors &&
info->pViewportState->scissorCount) {
const bool has_dynamic_scissor =
has_dynamic_state.scissor || has_dynamic_state.scissor_with_count;
if (has_dynamic_scissor) {
fix_desc->erase.viewport_state_scissors = true;
}
}
/* Ignore pMultisampleState?
* VUID-VkGraphicsPipelineCreateInfo-rasterizerDiscardEnable-00751
*/
if (info->pMultisampleState && !has_fragment_output_state) {
fix_desc->erase.multisample_state = true;
}
/* Ignore pDepthStencilState? */
if (info->pDepthStencilState) {
const bool has_static_attachment =
subpass &&
(subpass->attachment_aspects &
(VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT));
/* VUID-VkGraphicsPipelineCreateInfo-renderPass-06043 */
bool require_state = has_fragment_shader_state && has_static_attachment;
if (!require_state) {
const bool has_dynamic_attachment =
has_dynamic_rendering &&
(rendering_info->depthAttachmentFormat != VK_FORMAT_UNDEFINED ||
rendering_info->stencilAttachmentFormat != VK_FORMAT_UNDEFINED);
/* VUID-VkGraphicsPipelineCreateInfo-renderPass-06053 */
require_state = has_fragment_shader_state &&
has_fragment_output_state && has_dynamic_attachment;
}
fix_desc->erase.depth_stencil_state = !require_state;
}
/* Ignore pColorBlendState? */
if (info->pColorBlendState) {
const bool has_static_attachment =
subpass && (subpass->attachment_aspects & VK_IMAGE_ASPECT_COLOR_BIT);
/* VUID-VkGraphicsPipelineCreateInfo-renderPass-06044 */
bool require_state = has_fragment_output_state && has_static_attachment;
if (!require_state) {
const bool has_dynamic_attachment =
has_dynamic_rendering && rendering_info->colorAttachmentCount;
/* VUID-VkGraphicsPipelineCreateInfo-renderPass-06054 */
require_state = has_fragment_output_state && has_dynamic_attachment;
}
fix_desc->erase.color_blend_state = !require_state;
}
/* Ignore pVertexInputState?
* The Vulkan spec (1.3.264) says:
* VK_DYNAMIC_STATE_VERTEX_INPUT_EXT specifies that the
* pVertexInputState state will be ignored and must be set dynamically
* with vkCmdSetVertexInputEXT before any drawing commands
*/
if (info->pVertexInputState && has_dynamic_state.vertex_input) {
fix_desc->erase.vertex_input_state = true;
}
/* Ignore basePipelineHandle?
* VUID-VkGraphicsPipelineCreateInfo-flags-00722
* VUID-VkGraphicsPipelineCreateInfo-flags-00724
* VUID-VkGraphicsPipelineCreateInfo-flags-00725
*/
if (info->basePipelineHandle != VK_NULL_HANDLE &&
!(info->flags & VK_PIPELINE_CREATE_DERIVATIVE_BIT)) {
fix_desc->erase.base_pipeline_handle = true;
}
}
static const VkGraphicsPipelineCreateInfo *
vn_fix_graphics_pipeline_create_infos(
struct vn_device *dev,
uint32_t info_count,
const VkGraphicsPipelineCreateInfo *infos,
const VkAllocationCallbacks *alloc,
struct vn_graphics_pipeline_fix_tmp **out_fix_tmp)
const struct vn_graphics_pipeline_fix_desc fix_descs[info_count],
struct vn_graphics_pipeline_fix_tmp **out_fix_tmp,
const VkAllocationCallbacks *alloc)
{
VN_TRACE_FUNC();
VN_TRACE_SCOPE("apply_fixes");
/* Defer allocation until we need a fix. */
struct vn_graphics_pipeline_fix_tmp *fix_tmp = NULL;
*out_fix_tmp = NULL;
uint32_t erase_mask = 0;
for (uint32_t i = 0; i < info_count; i++) {
erase_mask |= fix_descs[i].erase.mask;
}
if (!erase_mask) {
/* No fix is needed. */
return infos;
}
struct vn_graphics_pipeline_fix_tmp *fix_tmp =
vn_graphics_pipeline_fix_tmp_alloc(alloc, info_count);
if (!fix_tmp)
return NULL;
memcpy(fix_tmp->infos, infos, info_count * sizeof(infos[0]));
for (uint32_t i = 0; i < info_count; i++) {
/* Erase these fields to prevent the Venus encoder from reading invalid
* memory.
*/
struct vn_graphics_pipeline_create_info_fields erase = { 0 };
const VkGraphicsPipelineCreateInfo *info = &infos[i];
const VkPipelineRenderingCreateInfo *rendering_info =
vk_find_struct_const(info, PIPELINE_RENDERING_CREATE_INFO);
VkShaderStageFlags stages = 0;
for (uint32_t j = 0; j < info->stageCount; j++) {
stages |= info->pStages[j].stage;
}
/* VkDynamicState */
struct {
bool rasterizer_discard_enable;
bool viewport;
bool viewport_with_count;
bool scissor;
bool scissor_with_count;
bool vertex_input;
} has_dynamic_state = { 0 };
if (info->pDynamicState) {
for (uint32_t j = 0; j < info->pDynamicState->dynamicStateCount;
j++) {
switch (info->pDynamicState->pDynamicStates[j]) {
case VK_DYNAMIC_STATE_RASTERIZER_DISCARD_ENABLE:
has_dynamic_state.rasterizer_discard_enable = true;
break;
case VK_DYNAMIC_STATE_VIEWPORT:
has_dynamic_state.viewport = true;
break;
case VK_DYNAMIC_STATE_VIEWPORT_WITH_COUNT:
has_dynamic_state.viewport_with_count = true;
break;
case VK_DYNAMIC_STATE_SCISSOR:
has_dynamic_state.scissor = true;
break;
case VK_DYNAMIC_STATE_SCISSOR_WITH_COUNT:
has_dynamic_state.scissor_with_count = true;
break;
case VK_DYNAMIC_STATE_VERTEX_INPUT_EXT:
has_dynamic_state.vertex_input = true;
break;
default:
break;
}
}
}
const struct vn_render_pass *pass =
vn_render_pass_from_handle(info->renderPass);
const struct vn_subpass *subpass = NULL;
if (pass)
subpass = &pass->subpasses[info->subpass];
/* TODO: Ignore VkPipelineRenderingCreateInfo when not using dynamic
* rendering. This requires either a deep rewrite of
* VkGraphicsPipelineCreateInfo::pNext or a fix in the generated
* protocol code.
*
* The Vulkan spec (1.3.223) says about VkPipelineRenderingCreateInfo:
* If a graphics pipeline is created with a valid VkRenderPass,
* parameters of this structure are ignored.
*/
const bool has_dynamic_rendering = !pass && rendering_info;
/* For each pipeline state category, we define a bool.
*
* The Vulkan spec (1.3.223) says:
* The state required for a graphics pipeline is divided into vertex
* input state, pre-rasterization shader state, fragment shader
* state, and fragment output state.
*
* Without VK_EXT_graphics_pipeline_library, most states are
* unconditionally included in the pipeline. Despite that, we still
* reference the state bools in the ignore rules because (a) it makes
* the ignore condition easier to validate against the text of the
* relevant VUs; and (b) it makes it easier to enable
* VK_EXT_graphics_pipeline_library because we won't need to carefully
* revisit the text of each VU to untangle the missing pipeline state
* bools.
*/
/* The spec does not assign a name to this state. We define it just to
* deduplicate code.
*
* The Vulkan spec (1.3.223) says:
* If the value of [...]rasterizerDiscardEnable in the
* pre-rasterization shader state is VK_FALSE or the
* VK_DYNAMIC_STATE_RASTERIZER_DISCARD_ENABLE dynamic state is
* enabled fragment shader state and fragment output interface state
* is included in a complete graphics pipeline.
*/
const bool has_raster_state =
has_dynamic_state.rasterizer_discard_enable ||
(info->pRasterizationState &&
info->pRasterizationState->rasterizerDiscardEnable == VK_FALSE);
/* VK_GRAPHICS_PIPELINE_LIBRARY_FRAGMENT_SHADER_BIT_EXT */
const bool has_fragment_shader_state = has_raster_state;
/* VK_GRAPHICS_PIPELINE_LIBRARY_FRAGMENT_OUTPUT_INTERFACE_BIT_EXT */
const bool has_fragment_output_state = has_raster_state;
/* Ignore pTessellationState?
* VUID-VkGraphicsPipelineCreateInfo-pStages-00731
*/
if (info->pTessellationState &&
(!(stages & VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT) ||
!(stages & VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT))) {
erase.tessellation_state = true;
}
/* Ignore pViewportState?
* VUID-VkGraphicsPipelineCreateInfo-rasterizerDiscardEnable-00750
* VUID-VkGraphicsPipelineCreateInfo-pViewportState-04892
*/
if (info->pViewportState && has_raster_state) {
erase.viewport_state = true;
}
/* Ignore pViewports?
* VUID-VkGraphicsPipelineCreateInfo-pDynamicStates-04130
*
* If viewportCount is 0, then venus encoder will ignore pViewports and
* we do not need to erase it.
*/
if (!erase.viewport_state && info->pViewportState &&
info->pViewportState->pViewports &&
info->pViewportState->viewportCount) {
const bool has_dynamic_viewport =
has_dynamic_state.viewport ||
has_dynamic_state.viewport_with_count;
if (has_dynamic_viewport) {
erase.viewport_state_viewports = true;
}
}
/* Ignore pScissors?
* VUID-VkGraphicsPipelineCreateInfo-pDynamicStates-04131
*
* If scissorCount is 0, then venus encoder will ignore pScissors and we
* do not need to erase it.
*/
if (!erase.viewport_state && info->pViewportState &&
info->pViewportState->pScissors &&
info->pViewportState->scissorCount) {
const bool has_dynamic_scissor =
has_dynamic_state.scissor || has_dynamic_state.scissor_with_count;
if (has_dynamic_scissor) {
erase.viewport_state_scissors = true;
}
}
/* Ignore pMultisampleState?
* VUID-VkGraphicsPipelineCreateInfo-rasterizerDiscardEnable-00751
*/
if (info->pMultisampleState && !has_fragment_output_state) {
erase.multisample_state = true;
}
/* Ignore pDepthStencilState? */
if (info->pDepthStencilState) {
const bool has_static_attachment =
subpass &&
(subpass->attachment_aspects &
(VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT));
/* VUID-VkGraphicsPipelineCreateInfo-renderPass-06043 */
bool require_state =
has_fragment_shader_state && has_static_attachment;
if (!require_state) {
const bool has_dynamic_attachment =
has_dynamic_rendering &&
(rendering_info->depthAttachmentFormat !=
VK_FORMAT_UNDEFINED ||
rendering_info->stencilAttachmentFormat !=
VK_FORMAT_UNDEFINED);
/* VUID-VkGraphicsPipelineCreateInfo-renderPass-06053 */
require_state = has_fragment_shader_state &&
has_fragment_output_state &&
has_dynamic_attachment;
}
erase.depth_stencil_state = !require_state;
}
/* Ignore pColorBlendState? */
if (info->pColorBlendState) {
const bool has_static_attachment =
subpass &&
(subpass->attachment_aspects & VK_IMAGE_ASPECT_COLOR_BIT);
/* VUID-VkGraphicsPipelineCreateInfo-renderPass-06044 */
bool require_state =
has_fragment_output_state && has_static_attachment;
if (!require_state) {
const bool has_dynamic_attachment =
has_dynamic_rendering && rendering_info->colorAttachmentCount;
/* VUID-VkGraphicsPipelineCreateInfo-renderPass-06054 */
require_state =
has_fragment_output_state && has_dynamic_attachment;
}
erase.color_blend_state = !require_state;
}
/* Ignore pVertexInputState?
* The Vulkan spec (1.3.264) says:
* VK_DYNAMIC_STATE_VERTEX_INPUT_EXT specifies that the
* pVertexInputState state will be ignored and must be set dynamically
* with vkCmdSetVertexInputEXT before any drawing commands
*/
if (info->pVertexInputState && has_dynamic_state.vertex_input)
erase.vertex_input_state = true;
/* Ignore basePipelineHandle?
* VUID-VkGraphicsPipelineCreateInfo-flags-00722
* VUID-VkGraphicsPipelineCreateInfo-flags-00724
* VUID-VkGraphicsPipelineCreateInfo-flags-00725
*/
if (info->basePipelineHandle != VK_NULL_HANDLE &&
!(info->flags & VK_PIPELINE_CREATE_DERIVATIVE_BIT)) {
erase.base_pipeline_handle = true;
}
if (erase.mask != 0)
if (!fix_descs[i].erase.mask) {
/* No fix is needed for this VkGraphicsPipelineCreateInfo chain. */
continue;
if (!fix_tmp) {
fix_tmp = vn_graphics_pipeline_fix_tmp_alloc(alloc, info_count);
if (!fix_tmp)
return NULL;
memcpy(fix_tmp->infos, infos, info_count * sizeof(infos[0]));
}
if (erase.tessellation_state)
if (fix_descs[i].erase.tessellation_state)
fix_tmp->infos[i].pTessellationState = NULL;
if (erase.viewport_state)
if (fix_descs[i].erase.viewport_state)
fix_tmp->infos[i].pViewportState = NULL;
if (fix_tmp->infos[i].pViewportState) {
if (erase.viewport_state_viewports ||
erase.viewport_state_scissors) {
fix_tmp->viewport_state_infos[i] = *info->pViewportState;
if (fix_descs[i].erase.viewport_state_viewports ||
fix_descs[i].erase.viewport_state_scissors) {
fix_tmp->viewport_state_infos[i] = *infos[i].pViewportState;
fix_tmp->infos[i].pViewportState =
&fix_tmp->viewport_state_infos[i];
}
if (erase.viewport_state_viewports)
if (fix_descs[i].erase.viewport_state_viewports)
fix_tmp->viewport_state_infos[i].pViewports = NULL;
if (erase.viewport_state_scissors)
if (fix_descs[i].erase.viewport_state_scissors)
fix_tmp->viewport_state_infos[i].pScissors = NULL;
}
if (erase.multisample_state)
if (fix_descs[i].erase.multisample_state)
fix_tmp->infos[i].pMultisampleState = NULL;
if (erase.depth_stencil_state)
if (fix_descs[i].erase.depth_stencil_state)
fix_tmp->infos[i].pDepthStencilState = NULL;
if (erase.color_blend_state)
if (fix_descs[i].erase.color_blend_state)
fix_tmp->infos[i].pColorBlendState = NULL;
if (erase.vertex_input_state)
if (fix_descs[i].erase.vertex_input_state)
fix_tmp->infos[i].pVertexInputState = NULL;
if (erase.base_pipeline_handle)
if (fix_descs[i].erase.base_pipeline_handle)
fix_tmp->infos[i].basePipelineHandle = VK_NULL_HANDLE;
}
if (!fix_tmp)
return infos;
*out_fix_tmp = fix_tmp;
return fix_tmp->infos;
}
@@ -772,20 +784,33 @@ vn_CreateGraphicsPipelines(VkDevice device,
struct vn_device *dev = vn_device_from_handle(device);
const VkAllocationCallbacks *alloc =
pAllocator ? pAllocator : &dev->base.base.alloc;
struct vn_graphics_pipeline_fix_tmp *fix_tmp = NULL;
bool want_sync = false;
VkResult result;
STACK_ARRAY(struct vn_graphics_pipeline_fix_desc, fix_descs,
createInfoCount);
struct vn_graphics_pipeline_fix_tmp *fix_tmp = NULL;
memset(pPipelines, 0, sizeof(*pPipelines) * createInfoCount);
VN_TRACE_BEGIN("find_fixes");
for (uint32_t i = 0; i < createInfoCount; i++) {
vn_find_graphics_pipeline_create_info_fixes(&pCreateInfos[i],
&fix_descs[i]);
}
VN_TRACE_END();
pCreateInfos = vn_fix_graphics_pipeline_create_infos(
dev, createInfoCount, pCreateInfos, alloc, &fix_tmp);
if (!pCreateInfos)
dev, createInfoCount, pCreateInfos, fix_descs, &fix_tmp, alloc);
if (!pCreateInfos) {
STACK_ARRAY_FINISH(fix_descs);
return vn_error(dev->instance, VK_ERROR_OUT_OF_HOST_MEMORY);
}
if (!vn_create_pipeline_handles(dev, VN_PIPELINE_TYPE_GRAPHICS,
createInfoCount, pPipelines, alloc)) {
vk_free(alloc, fix_tmp);
STACK_ARRAY_FINISH(fix_descs);
return vn_error(dev->instance, VK_ERROR_OUT_OF_HOST_MEMORY);
}
@@ -823,6 +848,7 @@ vn_CreateGraphicsPipelines(VkDevice device,
}
vk_free(alloc, fix_tmp);
STACK_ARRAY_FINISH(fix_descs);
return vn_result(dev->instance, result);
}