diff --git a/BUILD.gn b/BUILD.gn index 12c2257204c..3de98ed8e99 100644 --- a/BUILD.gn +++ b/BUILD.gn @@ -108,6 +108,7 @@ vvl_sources = [ "layers/core_checks/cc_descriptor.cpp", "layers/core_checks/cc_device.cpp", "layers/core_checks/cc_device_memory.cpp", + "layers/core_checks/cc_device_generated_commands.cpp", "layers/core_checks/cc_drawdispatch.cpp", "layers/core_checks/cc_external_object.cpp", "layers/core_checks/cc_image.cpp", @@ -225,6 +226,8 @@ vvl_sources = [ "layers/state_tracker/cmd_buffer_state.h", "layers/state_tracker/descriptor_sets.cpp", "layers/state_tracker/descriptor_sets.h", + "layers/state_tracker/device_generated_commands_state.cpp", + "layers/state_tracker/device_generated_commands_state.h", "layers/state_tracker/device_memory_state.cpp", "layers/state_tracker/device_memory_state.h", "layers/state_tracker/device_state.cpp", @@ -270,6 +273,7 @@ vvl_sources = [ "layers/stateless/sl_cmd_buffer.cpp", "layers/stateless/sl_cmd_buffer_dynamic.cpp", "layers/stateless/sl_descriptor.cpp", + "layers/stateless/sl_device_generated_commands.cpp", "layers/stateless/sl_device_memory.cpp", "layers/stateless/sl_external_object.cpp", "layers/stateless/sl_framebuffer.cpp", diff --git a/layers/CMakeLists.txt b/layers/CMakeLists.txt index d4e706b4c98..9e49f5b3cd2 100644 --- a/layers/CMakeLists.txt +++ b/layers/CMakeLists.txt @@ -157,6 +157,7 @@ target_sources(vvl PRIVATE core_checks/cc_descriptor.cpp core_checks/cc_device.cpp core_checks/cc_device_memory.cpp + core_checks/cc_device_generated_commands.cpp core_checks/cc_drawdispatch.cpp core_checks/cc_external_object.cpp core_checks/cc_image.cpp @@ -266,6 +267,8 @@ target_sources(vvl PRIVATE state_tracker/cmd_buffer_state.h state_tracker/descriptor_sets.cpp state_tracker/descriptor_sets.h + state_tracker/device_generated_commands_state.cpp + state_tracker/device_generated_commands_state.h state_tracker/device_memory_state.cpp state_tracker/device_memory_state.h state_tracker/device_state.cpp @@ -311,6 +314,7 @@ target_sources(vvl PRIVATE stateless/sl_cmd_buffer_dynamic.cpp stateless/sl_cmd_buffer.cpp stateless/sl_descriptor.cpp + stateless/sl_device_generated_commands.cpp stateless/sl_device_memory.cpp stateless/sl_external_object.cpp stateless/sl_framebuffer.cpp diff --git a/layers/chassis/layer_chassis_dispatch_manual.cpp b/layers/chassis/layer_chassis_dispatch_manual.cpp index 934cb64dbbe..f07d08d17fb 100644 --- a/layers/chassis/layer_chassis_dispatch_manual.cpp +++ b/layers/chassis/layer_chassis_dispatch_manual.cpp @@ -1714,4 +1714,4 @@ VkResult DispatchCreateIndirectExecutionSetEXT(VkDevice device, const VkIndirect *pIndirectExecutionSet = layer_data->WrapNew(*pIndirectExecutionSet); } return result; -} \ No newline at end of file +} diff --git a/layers/core_checks/cc_device_generated_commands.cpp b/layers/core_checks/cc_device_generated_commands.cpp new file mode 100644 index 00000000000..9196d412e07 --- /dev/null +++ b/layers/core_checks/cc_device_generated_commands.cpp @@ -0,0 +1,1050 @@ +/* Copyright (c) 2024 The Khronos Group Inc. + * Copyright (c) 2024 Valve Corporation + * Copyright (c) 2024 LunarG, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include "generated/chassis.h" +#include "core_validation.h" +#include "error_message/error_strings.h" +#include "state_tracker/device_generated_commands_state.h" +#include "state_tracker/pipeline_layout_state.h" +#include "state_tracker/descriptor_sets.h" +#include "state_tracker/render_pass_state.h" +#include "state_tracker/shader_object_state.h" +#include "state_tracker/shader_module.h" +#include "cc_buffer_address.h" + +static inline bool IsActionCommand(VkIndirectCommandsTokenTypeEXT type) { + return IsValueIn( + type, {VK_INDIRECT_COMMANDS_TOKEN_TYPE_DRAW_INDEXED_EXT, VK_INDIRECT_COMMANDS_TOKEN_TYPE_DRAW_EXT, + VK_INDIRECT_COMMANDS_TOKEN_TYPE_DRAW_INDEXED_COUNT_EXT, VK_INDIRECT_COMMANDS_TOKEN_TYPE_DRAW_COUNT_EXT, + VK_INDIRECT_COMMANDS_TOKEN_TYPE_DISPATCH_EXT, VK_INDIRECT_COMMANDS_TOKEN_TYPE_DRAW_MESH_TASKS_NV_EXT, + VK_INDIRECT_COMMANDS_TOKEN_TYPE_DRAW_MESH_TASKS_COUNT_NV_EXT, VK_INDIRECT_COMMANDS_TOKEN_TYPE_DRAW_MESH_TASKS_EXT, + VK_INDIRECT_COMMANDS_TOKEN_TYPE_DRAW_MESH_TASKS_COUNT_EXT, VK_INDIRECT_COMMANDS_TOKEN_TYPE_TRACE_RAYS2_EXT}); +} + +// Make sure sub_range is contained inside the full_range +bool PushConstantRangesContained(VkPushConstantRange full_range, VkPushConstantRange sub_range) { + const uint32_t start = full_range.offset; + const uint32_t end = start + full_range.size; + return (start >= sub_range.offset && end <= (sub_range.offset + sub_range.size)); +} + +bool CoreChecks::PreCallValidateCreateIndirectCommandsLayoutEXT(VkDevice device, + const VkIndirectCommandsLayoutCreateInfoEXT* pCreateInfo, + const VkAllocationCallbacks* pAllocator, + VkIndirectCommandsLayoutEXT* pIndirectCommandsLayout, + const ErrorObject& error_obj) const { + bool skip = false; + + const Location create_info_loc = error_obj.location.dot(Field::pCreateInfo); + + auto pipeline_layout_state = Get(pCreateInfo->pipelineLayout); + auto* dynamic_layout_create = vku::FindStructInPNextChain(pCreateInfo->pNext); + + const uint32_t kNotFound = vvl::kU32Max; + uint32_t execution_set_token_index = kNotFound; + uint32_t vertex_buffer_token_index = kNotFound; + uint32_t index_buffer_token_index = kNotFound; + uint32_t sequence_index_token_index = kNotFound; + + vvl::unordered_map vertex_binding_unit_unique; + vvl::unordered_map token_ranges; + + for (uint32_t i = 0; i < pCreateInfo->tokenCount; i++) { + const Location token_loc = create_info_loc.dot(Field::pTokens, i); + const auto& token = pCreateInfo->pTokens[i]; + + // Validate contents of VkIndirectCommandsTokenDataEXT + const Location data_loc = token_loc.dot(Field::data); + if (token.type == VK_INDIRECT_COMMANDS_TOKEN_TYPE_PUSH_CONSTANT_EXT) { + const VkIndirectCommandsPushConstantTokenEXT* push_constant_token = token.data.pPushConstant; + const Location pc_token_loc = data_loc.dot(Field::pPushConstant); + const Location update_range_loc = pc_token_loc.dot(Field::updateRange); + const VkPushConstantRange& token_range = push_constant_token->updateRange; + + if (!dynamic_layout_create && pipeline_layout_state) { + const auto& layout_ranges = *pipeline_layout_state->push_constant_ranges_layout; + for (const auto& layout_range : layout_ranges) { + if (!PushConstantRangesContained(token_range, layout_range)) { + skip |= LogError("VUID-VkIndirectCommandsPushConstantTokenEXT-updateRange-11132", + pipeline_layout_state->Handle(), update_range_loc, + "is in the range %s but the push constant range in " + "VkIndirectCommandsLayoutCreateInfoEXT::pipelineLayout is %s.", + string_VkPushConstantRange(token_range).c_str(), + string_VkPushConstantRange(layout_range).c_str()); + } + break; + } + + } else if (dynamic_layout_create) { + // TODO - Because of custom PushConstantRangesId, can't share logic when using dynamicGeneratedPipelineLayout + for (uint32_t pc_index = 0; pc_index < dynamic_layout_create->pushConstantRangeCount; pc_index++) { + const VkPushConstantRange& layout_range = dynamic_layout_create->pPushConstantRanges[pc_index]; + if (!PushConstantRangesContained(token_range, layout_range)) { + skip |= LogError("VUID-VkIndirectCommandsPushConstantTokenEXT-updateRange-11132", device, update_range_loc, + "is in the range %s but the push constant range in " + "VkPipelineLayoutCreateInfo::pPushConstantRanges[%" PRIu32 "] is %s.", + string_VkPushConstantRange(token_range).c_str(), pc_index, + string_VkPushConstantRange(layout_range).c_str()); + } + break; + } + } + } else if (token.type == VK_INDIRECT_COMMANDS_TOKEN_TYPE_VERTEX_BUFFER_EXT) { + const VkIndirectCommandsVertexBufferTokenEXT* vertex_buffer_token = token.data.pVertexBuffer; + const uint32_t vertex_binding_uint = vertex_buffer_token->vertexBindingUnit; + if (vertex_binding_unit_unique.find(vertex_binding_uint) != vertex_binding_unit_unique.end()) { + skip |= LogError("VUID-VkIndirectCommandsLayoutCreateInfoEXT-pTokens-11097", device, + data_loc.dot(Field::pVertexBuffer).dot(Field::vertexBindingUnit), + "(%" PRIu32 ") is the same as pTokens[%" PRIu32 "].data.pVertexBuffer.vertexBindingUnit", + vertex_binding_uint, vertex_binding_unit_unique[vertex_binding_uint]); + } else { + vertex_binding_unit_unique[vertex_binding_uint] = i; + } + } + + // Check for duplicate tokens + if (token.type == VK_INDIRECT_COMMANDS_TOKEN_TYPE_EXECUTION_SET_EXT) { + // currently these 2 VUs overlap but can be helpful to catch in different times + if (execution_set_token_index == kNotFound) { + execution_set_token_index = i; + if (i != 0) { + skip |= LogError( + "VUID-VkIndirectCommandsLayoutCreateInfoEXT-pTokens-11139", device, token_loc.dot(Field::type), + "is VK_INDIRECT_COMMANDS_TOKEN_TYPE_EXECUTION_SET_EXT but it can only be the first token at pTokens[0]."); + } + } else { + skip |= LogError("VUID-VkIndirectCommandsLayoutCreateInfoEXT-pTokens-11093", device, token_loc.dot(Field::type), + "is VK_INDIRECT_COMMANDS_TOKEN_TYPE_EXECUTION_SET_EXT but also was pTokens[%" PRIu32 + "] (can only list token once).", + execution_set_token_index); + } + } else if (token.type == VK_INDIRECT_COMMANDS_TOKEN_TYPE_INDEX_BUFFER_EXT) { + if (index_buffer_token_index == kNotFound) { + index_buffer_token_index = i; + } else { + skip |= LogError("VUID-VkIndirectCommandsLayoutCreateInfoEXT-pTokens-11094", device, token_loc.dot(Field::type), + "is VK_INDIRECT_COMMANDS_TOKEN_TYPE_INDEX_BUFFER_EXT but also was pTokens[%" PRIu32 + "] (can only list token once).", + index_buffer_token_index); + } + } else if (token.type == VK_INDIRECT_COMMANDS_TOKEN_TYPE_SEQUENCE_INDEX_EXT) { + if (sequence_index_token_index == kNotFound) { + sequence_index_token_index = i; + } else { + skip |= LogError("VUID-VkIndirectCommandsLayoutCreateInfoEXT-pTokens-11145", device, token_loc.dot(Field::type), + "is VK_INDIRECT_COMMANDS_TOKEN_TYPE_SEQUENCE_INDEX_EXT but also was pTokens[%" PRIu32 + "] (can only list token once).", + sequence_index_token_index); + } + } else if (token.type == VK_INDIRECT_COMMANDS_TOKEN_TYPE_VERTEX_BUFFER_EXT) { + vertex_buffer_token_index = i; + } + + if (token.type == VK_INDIRECT_COMMANDS_TOKEN_TYPE_PUSH_CONSTANT_EXT || + token.type == VK_INDIRECT_COMMANDS_TOKEN_TYPE_SEQUENCE_INDEX_EXT) { + if (pCreateInfo->pipelineLayout == VK_NULL_HANDLE) { + if (!enabled_features.dynamicGeneratedPipelineLayout) { + skip |= LogError("VUID-VkIndirectCommandsLayoutCreateInfoEXT-pTokens-11101", device, token_loc.dot(Field::type), + "is %s, pipelineLayout is VK_NULL_HANDLE, but the " + "dynamicGeneratedPipelineLayout feature was not enabled.", + string_VkIndirectCommandsTokenTypeEXT(token.type)); + } else if (!dynamic_layout_create) { + skip |= LogError("VUID-VkIndirectCommandsLayoutCreateInfoEXT-pTokens-11102", device, token_loc.dot(Field::type), + "is %s, pipelineLayout is VK_NULL_HANDLE, but no " + "there is no VkPipelineLayoutCreateInfo structure attached to the pNext.", + string_VkIndirectCommandsTokenTypeEXT(token.type)); + } + } + + const VkIndirectCommandsPushConstantTokenEXT* push_constant_token = token.data.pPushConstant; + const VkPushConstantRange& token_range = push_constant_token->updateRange; + for (auto past_range : token_ranges) { + if (RangesIntersect(past_range.second.offset, past_range.second.size, token_range.offset, token_range.size)) { + skip |= + LogError("VUID-VkIndirectCommandsLayoutCreateInfoEXT-pTokens-11099", device, + data_loc.dot(Field::pPushConstant).dot(Field::updateRange), + "is in the range %s which overlaps with pTokens[%" PRIu32 "].data.pPushConstant->updateRange %s.", + string_VkPushConstantRange(token_range).c_str(), past_range.first, + string_VkPushConstantRange(past_range.second).c_str()); + break; + } + } + token_ranges[i] = token_range; + } + } + + const uint32_t final_token_index = pCreateInfo->tokenCount - 1; + const VkIndirectCommandsTokenTypeEXT final_token_type = pCreateInfo->pTokens[final_token_index].type; + if (!IsActionCommand(final_token_type)) { + skip |= LogError("VUID-VkIndirectCommandsLayoutCreateInfoEXT-pTokens-11100", device, + create_info_loc.dot(Field::pTokens, final_token_index).dot(Field::type), + "is %s, but needs to be an action command (ex. Draw, Dispatch, Trace Rays, etc ).", + string_VkIndirectCommandsTokenTypeEXT(final_token_type)); + } else { + if (!IsValueIn(final_token_type, {VK_INDIRECT_COMMANDS_TOKEN_TYPE_DRAW_INDEXED_EXT, + VK_INDIRECT_COMMANDS_TOKEN_TYPE_DRAW_INDEXED_COUNT_EXT}) && + index_buffer_token_index != kNotFound) { + skip |= LogError("VUID-VkIndirectCommandsLayoutCreateInfoEXT-pTokens-11095", device, + create_info_loc.dot(Field::pTokens, final_token_index).dot(Field::type), + "is %s (not an index draw token), but pTokens[%" PRIu32 + "].type is VK_INDIRECT_COMMANDS_TOKEN_TYPE_INDEX_BUFFER_EXT.", + string_VkIndirectCommandsTokenTypeEXT(final_token_type), index_buffer_token_index); + } + if (!IsValueIn(final_token_type, + {VK_INDIRECT_COMMANDS_TOKEN_TYPE_DRAW_INDEXED_EXT, VK_INDIRECT_COMMANDS_TOKEN_TYPE_DRAW_EXT, + VK_INDIRECT_COMMANDS_TOKEN_TYPE_DRAW_INDEXED_COUNT_EXT, VK_INDIRECT_COMMANDS_TOKEN_TYPE_DRAW_COUNT_EXT}) && + vertex_buffer_token_index != kNotFound) { + skip |= LogError("VUID-VkIndirectCommandsLayoutCreateInfoEXT-pTokens-11096", device, + create_info_loc.dot(Field::pTokens, final_token_index).dot(Field::type), + "is %s (not a non-mesh draw token), but pTokens[%" PRIu32 + "].type is VK_INDIRECT_COMMANDS_TOKEN_TYPE_VERTEX_BUFFER_EXT.", + string_VkIndirectCommandsTokenTypeEXT(final_token_type), vertex_buffer_token_index); + } + } + + return skip; +} + +bool CoreChecks::PreCallValidateDestroyIndirectCommandsLayoutEXT(VkDevice device, + VkIndirectCommandsLayoutEXT indirectCommandsLayout, + const VkAllocationCallbacks* pAllocator, + const ErrorObject& error_obj) const { + bool skip = false; + if (auto indirect_commands_layout = Get(indirectCommandsLayout)) { + skip |= ValidateObjectNotInUse(indirect_commands_layout.get(), error_obj.location, + "VUID-vkDestroyIndirectCommandsLayoutEXT-indirectCommandsLayout-11114"); + } + return skip; +} + +bool CoreChecks::ValidateIndirectExecutionSetPipelineInfo(const VkIndirectExecutionSetPipelineInfoEXT& pipeline_info, + const Location& pipeline_info_loc) const { + bool skip = false; + + const auto initial_pipeline = Get(pipeline_info.initialPipeline); + ASSERT_AND_RETURN_SKIP(initial_pipeline); + + const auto& props = phys_dev_ext_props.device_generated_commands_props; + + if ((initial_pipeline->create_flags & VK_PIPELINE_CREATE_2_INDIRECT_BINDABLE_BIT_EXT) == 0) { + skip |= LogError("VUID-VkIndirectExecutionSetPipelineInfoEXT-initialPipeline-11153", initial_pipeline->Handle(), + pipeline_info_loc.dot(Field::initialPipeline), + "is missing VK_PIPELINE_CREATE_2_INDIRECT_BINDABLE_BIT_EXT, was created with flags %s. (Make sure you " + "set it with VkPipelineCreateFlags2CreateInfoKHR)", + string_VkPipelineCreateFlags2KHR(initial_pipeline->create_flags).c_str()); + } + + if (initial_pipeline->pipeline_type == VK_PIPELINE_BIND_POINT_COMPUTE && + ((props.supportedIndirectCommandsShaderStagesPipelineBinding & VK_SHADER_STAGE_COMPUTE_BIT) == 0)) { + skip |= LogError( + "VUID-VkIndirectExecutionSetPipelineInfoEXT-supportedIndirectCommandsShaderStagesPipelineBinding-11015", + initial_pipeline->Handle(), pipeline_info_loc.dot(Field::initialPipeline), + "is type VK_PIPELINE_BIND_POINT_COMPUTE, but supportedIndirectCommandsShaderStagesPipelineBinding (%s) doesn't " + "contain compute stage support.", + string_VkShaderStageFlags(props.supportedIndirectCommandsShaderStagesPipelineBinding).c_str()); + } else if (initial_pipeline->pipeline_type == VK_PIPELINE_BIND_POINT_GRAPHICS && + ((props.supportedIndirectCommandsShaderStagesPipelineBinding & VK_SHADER_STAGE_FRAGMENT_BIT) == 0)) { + skip |= LogError( + "VUID-VkIndirectExecutionSetPipelineInfoEXT-supportedIndirectCommandsShaderStagesPipelineBinding-11016", + initial_pipeline->Handle(), pipeline_info_loc.dot(Field::initialPipeline), + "is type VK_PIPELINE_BIND_POINT_GRAPHICS, but supportedIndirectCommandsShaderStagesPipelineBinding (%s) doesn't " + "contain VK_SHADER_STAGE_FRAGMENT_BIT support.", + string_VkShaderStageFlags(props.supportedIndirectCommandsShaderStagesPipelineBinding).c_str()); + } else if (initial_pipeline->pipeline_type == VK_PIPELINE_BIND_POINT_RAY_TRACING_KHR && + ((props.supportedIndirectCommandsShaderStagesPipelineBinding & kShaderStageAllRayTracing) == 0)) { + skip |= LogError( + "VUID-VkIndirectExecutionSetPipelineInfoEXT-supportedIndirectCommandsShaderStagesPipelineBinding-11017", + initial_pipeline->Handle(), pipeline_info_loc.dot(Field::initialPipeline), + "is type VK_PIPELINE_BIND_POINT_RAY_TRACING_KHR, but supportedIndirectCommandsShaderStagesPipelineBinding (%s) doesn't " + "contain ray tracing stage support.", + string_VkShaderStageFlags(props.supportedIndirectCommandsShaderStagesPipelineBinding).c_str()); + } + + auto pipeline_layout = initial_pipeline->PipelineLayoutState(); + ASSERT_AND_RETURN_SKIP(pipeline_layout); + for (uint32_t i = 0; i < pipeline_layout->set_layouts.size(); i++) { + if (pipeline_layout->set_layouts[i] == nullptr) continue; + const auto& bindings = pipeline_layout->set_layouts[i]->GetBindings(); + for (uint32_t j = 0; j < bindings.size(); j++) { + if (bindings[j].descriptorType == VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC || + bindings[j].descriptorType == VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC) { + const LogObjectList objlist(pipeline_layout->Handle(), pipeline_layout->set_layouts[i]->Handle()); + skip |= LogError("VUID-VkIndirectExecutionSetPipelineInfoEXT-initialPipeline-11019", objlist, + pipeline_info_loc.dot(Field::initialPipeline), + "was created with a VkPipelineLayout that contains a descriptor type of %s in pSetLayouts[%" PRIu32 + "], VkDescriptorSetLayoutCreateInfo::pBindings[%" PRIu32 "].", + string_VkDescriptorType(bindings[j].descriptorType), i, j); + } + } + } + + return skip; +} + +bool CoreChecks::ValidateIndirectExecutionSetShaderInfo(const VkIndirectExecutionSetShaderInfoEXT& shader_info, + const Location& shader_info_loc) const { + bool skip = false; + + const auto& props = phys_dev_ext_props.device_generated_commands_props; + vvl::unordered_map unique_stages; + for (uint32_t i = 0; i < shader_info.shaderCount; i++) { + const VkShaderEXT shader_handle = shader_info.pInitialShaders[i]; + const auto shader_object = Get(shader_handle); + ASSERT_AND_CONTINUE(shader_object); + if ((shader_object->create_info.flags & VK_SHADER_CREATE_INDIRECT_BINDABLE_BIT_EXT) == 0) { + skip |= LogError("VUID-VkIndirectExecutionSetShaderInfoEXT-pInitialShaders-11154", shader_object->Handle(), + shader_info_loc.dot(Field::pInitialShaders, i), + "is missing VK_SHADER_CREATE_INDIRECT_BINDABLE_BIT_EXT, was created with flags %s.", + string_VkShaderCreateFlagsEXT(shader_object->create_info.flags).c_str()); + } + + const VkShaderStageFlagBits stage = shader_object->create_info.stage; + if ((stage & props.supportedIndirectCommandsShaderStagesShaderBinding) == 0) { + skip |= LogError( + "VUID-VkIndirectExecutionSetShaderInfoEXT-pInitialShaders-11020", shader_handle, + shader_info_loc.dot(Field::pInitialShaders, i), + "was created with stage %s, but supportedIndirectCommandsShaderStagesShaderBinding (%s) doesn't indicate support.", + string_VkShaderStageFlagBits(stage), + string_VkShaderStageFlags(props.supportedIndirectCommandsShaderStagesShaderBinding).c_str()); + } + + if (unique_stages.find(stage) != unique_stages.end()) { + skip |= + LogError("VUID-VkIndirectExecutionSetShaderInfoEXT-stage-11023", shader_handle, + shader_info_loc.dot(Field::pInitialShaders, i), "is the same stage as pInitialShaders[%" PRIu32 "] (%s)", + unique_stages[stage], string_VkShaderStageFlagBits(stage)); + } else { + unique_stages[stage] = i; + } + } + + return skip; +} + +bool CoreChecks::PreCallValidateCreateIndirectExecutionSetEXT(VkDevice device, + const VkIndirectExecutionSetCreateInfoEXT* pCreateInfo, + const VkAllocationCallbacks* pAllocator, + VkIndirectExecutionSetEXT* pIndirectExecutionSet, + const ErrorObject& error_obj) const { + bool skip = false; + + const Location create_info_loc = error_obj.location.dot(Field::pCreateInfo); + const Location info_loc = create_info_loc.dot(Field::info); + + if (pCreateInfo->type == VK_INDIRECT_EXECUTION_SET_INFO_TYPE_PIPELINES_EXT) { + ASSERT_AND_RETURN_SKIP(pCreateInfo->info.pPipelineInfo); // caught in stateless validation + skip |= ValidateIndirectExecutionSetPipelineInfo(*pCreateInfo->info.pPipelineInfo, info_loc.dot(Field::pPipelineInfo)); + } else if (pCreateInfo->type == VK_INDIRECT_EXECUTION_SET_INFO_TYPE_SHADER_OBJECTS_EXT) { + ASSERT_AND_RETURN_SKIP(pCreateInfo->info.pShaderInfo); // caught in stateless validation + skip |= ValidateIndirectExecutionSetShaderInfo(*pCreateInfo->info.pShaderInfo, info_loc.dot(Field::pShaderInfo)); + } + + return skip; +} + +bool CoreChecks::PreCallValidateDestroyIndirectExecutionSetEXT(VkDevice device, VkIndirectExecutionSetEXT indirectExecutionSet, + const VkAllocationCallbacks* pAllocator, + const ErrorObject& error_obj) const { + bool skip = false; + if (auto indirect_execution_set = Get(indirectExecutionSet)) { + skip |= ValidateObjectNotInUse(indirect_execution_set.get(), error_obj.location, + "VUID-vkDestroyIndirectExecutionSetEXT-indirectExecutionSet-11025"); + } + return skip; +} + +bool CoreChecks::ValidateGeneratedCommandsInfo(const vvl::CommandBuffer& cb_state, + const vvl::IndirectCommandsLayout& indirect_commands_layout, + const VkGeneratedCommandsInfoEXT& generated_commands_info, bool preprocessed, + const Location& info_loc) const { + bool skip = false; + + if (indirect_commands_layout.has_vertex_buffer_token) { + // If had vertex buffer token, it had to be graphic bind point (else would hit error earlier) + const auto pipeline = cb_state.GetCurrentPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS); + if (pipeline && !pipeline->IsDynamic(CB_DYNAMIC_STATE_VERTEX_INPUT_BINDING_STRIDE)) { + const LogObjectList objlist(cb_state.Handle(), pipeline->Handle()); + skip |= LogError("VUID-VkGeneratedCommandsInfoEXT-indirectCommandsLayout-11079", objlist, + info_loc.dot(Field::indirectCommandsLayout), + "contains a VK_INDIRECT_COMMANDS_TOKEN_TYPE_VERTEX_BUFFER_EXT token but the last bound graphics " + "pipeline did not have VK_DYNAMIC_STATE_VERTEX_INPUT_BINDING_STRIDE set."); + } + } + + if (indirect_commands_layout.has_multi_draw_count_token) { + // Use 64-bit to catch overflow + const uint64_t limit = 1 << 24; + const uint64_t count = (uint64_t)generated_commands_info.maxDrawCount * (uint64_t)generated_commands_info.maxSequenceCount; + if (count >= limit) { + skip |= LogError( + "VUID-VkGeneratedCommandsInfoEXT-maxDrawCount-11078", cb_state.Handle(), info_loc.dot(Field::maxDrawCount), + "(%" PRIu32 ") time maxSequenceCount (%" PRIu32 ") is %" PRIu64 " which is over the limit of 2^24 (16777216)", + generated_commands_info.maxDrawCount, generated_commands_info.maxSequenceCount, count); + } + } + + auto* pipeline_info = vku::FindStructInPNextChain(generated_commands_info.pNext); + auto* shader_info = vku::FindStructInPNextChain(generated_commands_info.pNext); + if (generated_commands_info.indirectExecutionSet == VK_NULL_HANDLE && !pipeline_info && !shader_info) { + skip |= LogError("VUID-VkGeneratedCommandsInfoEXT-indirectExecutionSet-11080", cb_state.Handle(), + info_loc.dot(Field::indirectExecutionSet), + "is VK_NULL_HANDLE but the pNext is missing a VkGeneratedCommandsPipelineInfoEXT or " + "VkGeneratedCommandsShaderInfoEXT."); + } else if (generated_commands_info.indirectExecutionSet == VK_NULL_HANDLE && indirect_commands_layout.has_execution_set_token) { + skip |= LogError("VUID-VkGeneratedCommandsInfoEXT-indirectCommandsLayout-11083", indirect_commands_layout.Handle(), + info_loc.dot(Field::indirectExecutionSet), + "is VK_NULL_HANDLE but indirectCommandsLayout was created with a " + "VK_INDIRECT_COMMANDS_TOKEN_TYPE_EXECUTION_SET_EXT token."); + } else { + // Only dispatch if we know this is valid + VkGeneratedCommandsMemoryRequirementsInfoEXT req_info = vku::InitStructHelper(); + req_info.maxSequenceCount = generated_commands_info.maxSequenceCount; + req_info.indirectCommandsLayout = generated_commands_info.indirectCommandsLayout; + req_info.indirectExecutionSet = generated_commands_info.indirectExecutionSet; + if (generated_commands_info.indirectExecutionSet == VK_NULL_HANDLE) { + req_info.pNext = pipeline_info ? (void*)pipeline_info : (void*)shader_info; + } + VkMemoryRequirements2 mem_reqs = vku::InitStructHelper(); + DispatchGetGeneratedCommandsMemoryRequirementsEXT(device, &req_info, &mem_reqs); + + if (generated_commands_info.preprocessAddress == 0 && mem_reqs.memoryRequirements.size != 0) { + skip |= LogError("VUID-VkGeneratedCommandsInfoEXT-preprocessAddress-11063", cb_state.Handle(), + info_loc.dot(Field::preprocessAddress), + "is NULL but vkGetGeneratedCommandsMemoryRequirementsEXT returned a non-zero size of %" PRIu64 ".", + mem_reqs.memoryRequirements.size); + } + if (generated_commands_info.preprocessSize < mem_reqs.memoryRequirements.size) { + skip |= LogError( + "VUID-VkGeneratedCommandsInfoEXT-preprocessSize-11071", cb_state.Handle(), info_loc.dot(Field::preprocessSize), + "(%" PRIu64 ") is less then the size returned from vkGetGeneratedCommandsMemoryRequirementsEXT (%" PRIu64 ").", + generated_commands_info.preprocessSize, mem_reqs.memoryRequirements.size); + } + } + + if (generated_commands_info.maxSequenceCount > phys_dev_ext_props.device_generated_commands_props.maxIndirectSequenceCount) { + skip |= LogError( + "VUID-VkGeneratedCommandsInfoEXT-maxSequenceCount-11067", cb_state.Handle(), info_loc.dot(Field::maxSequenceCount), + "(%" PRIu32 ") is larger than maxIndirectSequenceCount (%" PRIu32 ").", generated_commands_info.maxSequenceCount, + phys_dev_ext_props.device_generated_commands_props.maxIndirectSequenceCount); + } + + if (generated_commands_info.indirectExecutionSet != VK_NULL_HANDLE && indirect_commands_layout.has_execution_set_token) { + auto indirect_execution_set = Get(generated_commands_info.indirectExecutionSet); + ASSERT_AND_RETURN_SKIP(indirect_execution_set); + if (indirect_execution_set->shader_stage_flags != indirect_commands_layout.execution_set_token_shader_stage_flags) { + skip |= LogError("VUID-VkGeneratedCommandsInfoEXT-indirectCommandsLayout-11002", indirect_commands_layout.Handle(), + info_loc.dot(Field::indirectExecutionSet), + "was created with shader stage %s but indirectCommandsLayout was created with shader stage %s.", + string_VkShaderStageFlags(indirect_execution_set->shader_stage_flags).c_str(), + string_VkShaderStageFlags(indirect_commands_layout.execution_set_token_shader_stage_flags).c_str()); + } + } + + const auto preprocess_buffer_states = GetBuffersByAddress(generated_commands_info.preprocessAddress); + if (!preprocess_buffer_states.empty()) { + BufferAddressValidation<2> buffer_address_validator = {{{ + {"VUID-VkGeneratedCommandsInfoEXT-preprocessAddress-11069", + [](vvl::Buffer* const buffer_state, std::string* out_error_msg) { + if ((buffer_state->usage & VK_BUFFER_USAGE_2_PREPROCESS_BUFFER_BIT_EXT) == 0) { + if (out_error_msg) { + *out_error_msg += "buffer has usage " + string_VkBufferUsageFlags2KHR(buffer_state->usage); + } + return false; + } + return true; + }, + []() { return "The following buffers are missing VK_BUFFER_USAGE_2_PREPROCESS_BUFFER_BIT_EXT"; }}, + {"VUID-VkGeneratedCommandsInfoEXT-preprocessAddress-11070", + [this](vvl::Buffer* const buffer_state, std::string* out_error_msg) { + return BufferAddressValidation<1>::ValidateMemoryBoundToBuffer(*this, buffer_state, out_error_msg); + }, + []() { return BufferAddressValidation<1>::ValidateMemoryBoundToBufferErrorMsgHeader(); }}, + }}}; + + skip |= buffer_address_validator.LogErrorsIfNoValidBuffer( + *this, preprocess_buffer_states, info_loc.dot(Field::preprocessAddress), LogObjectList(cb_state.Handle()), + generated_commands_info.preprocessAddress); + } + + const auto sequence_buffer_states = GetBuffersByAddress(generated_commands_info.sequenceCountAddress); + if (!sequence_buffer_states.empty()) { + BufferAddressValidation<2> buffer_address_validator = {{{ + {"VUID-VkGeneratedCommandsInfoEXT-sequenceCountAddress-11072", + [](vvl::Buffer* const buffer_state, std::string* out_error_msg) { + if ((buffer_state->usage & VK_BUFFER_USAGE_2_INDIRECT_BUFFER_BIT_KHR) == 0) { + if (out_error_msg) { + *out_error_msg += "buffer has usage " + string_VkBufferUsageFlags2KHR(buffer_state->usage); + } + return false; + } + return true; + }, + []() { return "The following buffers are missing VK_BUFFER_USAGE_2_INDIRECT_BUFFER_BIT_KHR"; }}, + {"VUID-VkGeneratedCommandsInfoEXT-sequenceCountAddress-11075", + [this](vvl::Buffer* const buffer_state, std::string* out_error_msg) { + return BufferAddressValidation<1>::ValidateMemoryBoundToBuffer(*this, buffer_state, out_error_msg); + }, + []() { return BufferAddressValidation<1>::ValidateMemoryBoundToBufferErrorMsgHeader(); }}, + }}}; + + skip |= buffer_address_validator.LogErrorsIfNoValidBuffer( + *this, sequence_buffer_states, info_loc.dot(Field::sequenceCountAddress), LogObjectList(cb_state.Handle()), + generated_commands_info.sequenceCountAddress); + } + + return skip; +} + +bool CoreChecks::PreCallValidateCmdExecuteGeneratedCommandsEXT(VkCommandBuffer commandBuffer, VkBool32 isPreprocessed, + const VkGeneratedCommandsInfoEXT* pGeneratedCommandsInfo, + const ErrorObject& error_obj) const { + bool skip = false; + + const auto& cb_state = *GetRead(commandBuffer); + skip |= ValidateCmd(cb_state, error_obj.location); + if (!cb_state.unprotected) { + skip |= LogError("VUID-vkCmdExecuteGeneratedCommandsEXT-commandBuffer-11045", commandBuffer, + error_obj.location.dot(Field::commandBuffer), "is protected."); + } + + const auto& props = phys_dev_ext_props.device_generated_commands_props; + if (cb_state.transform_feedback_active) { + if (!props.deviceGeneratedCommandsTransformFeedback) { + skip |= LogError("VUID-vkCmdExecuteGeneratedCommandsEXT-deviceGeneratedCommandsTransformFeedback-11057", commandBuffer, + error_obj.location.dot(Field::commandBuffer), + "has Transform Feedback active, but deviceGeneratedCommandsTransformFeedback is not supported."); + } + if (pGeneratedCommandsInfo->indirectExecutionSet != VK_NULL_HANDLE) { + skip |= + LogError("VUID-vkCmdExecuteGeneratedCommandsEXT-indirectExecutionSet-11058", commandBuffer, + error_obj.location.dot(Field::commandBuffer), + "has Transform Feedback active, but pGeneratedCommandsInfo->indirectExecutionSet is not VK_NULL_HANDLE."); + } + } + + if (cb_state.beginInfo.flags & VK_COMMAND_BUFFER_USAGE_SIMULTANEOUS_USE_BIT) { + LogError("VUID-vkCmdExecuteGeneratedCommandsEXT-commandBuffer-11143", commandBuffer, + error_obj.location.dot(Field::commandBuffer), "was created with VK_COMMAND_BUFFER_USAGE_SIMULTANEOUS_USE_BIT."); + } + + const Location info_loc = error_obj.location.dot(Field::pGeneratedCommandsInfo); + const auto indirect_commands_layout = Get(pGeneratedCommandsInfo->indirectCommandsLayout); + ASSERT_AND_RETURN_SKIP(indirect_commands_layout); + + const bool preprocess_usage_flag = + (indirect_commands_layout->create_info.flags & VK_INDIRECT_COMMANDS_LAYOUT_USAGE_EXPLICIT_PREPROCESS_BIT_EXT) != 0; + if (isPreprocessed && !preprocess_usage_flag) { + const LogObjectList objlist(commandBuffer, indirect_commands_layout->Handle()); + skip |= LogError( + "VUID-vkCmdExecuteGeneratedCommandsEXT-isPreprocessed-11047", objlist, info_loc.dot(Field::indirectCommandsLayout), + "was not created with VK_INDIRECT_COMMANDS_LAYOUT_USAGE_EXPLICIT_PREPROCESS_BIT_EXT but isPreprocessed is VK_TRUE."); + } else if (!isPreprocessed && preprocess_usage_flag) { + const LogObjectList objlist(commandBuffer, indirect_commands_layout->Handle()); + skip |= LogError( + "VUID-vkCmdExecuteGeneratedCommandsEXT-indirectCommandsLayout-11141", objlist, + info_loc.dot(Field::indirectCommandsLayout), + "was created with VK_INDIRECT_COMMANDS_LAYOUT_USAGE_EXPLICIT_PREPROCESS_BIT_EXT but isPreprocessed is VK_FALSE."); + } + + if (cb_state.activeRenderPass) { + uint32_t view_mask = 0; + if (cb_state.activeRenderPass->UsesDynamicRendering()) { + view_mask = cb_state.activeRenderPass->dynamic_rendering_begin_rendering_info.viewMask; + } else { + const auto* render_pass_info = cb_state.activeRenderPass->create_info.ptr(); + const auto subpass_desc = render_pass_info->pSubpasses[cb_state.GetActiveSubpass()]; + view_mask = subpass_desc.viewMask; + } + if (view_mask != 0) { + skip |= LogError("VUID-vkCmdExecuteGeneratedCommandsEXT-None-11062", commandBuffer, error_obj.location, + "The active render pass contains a non-zero view mask (%" PRIu32 ").", view_mask); + } + } + + if (auto indirect_execution_set = Get(pGeneratedCommandsInfo->indirectExecutionSet)) { + const LogObjectList objlist(commandBuffer, indirect_commands_layout->Handle(), indirect_execution_set->Handle()); + skip |= ValidateGeneratedCommandsInitialShaderState(cb_state, *indirect_commands_layout, *indirect_execution_set, + pGeneratedCommandsInfo->shaderStages, objlist, + error_obj.location.dot(Field::commandBuffer)); + } + + const auto lv_bind_point = ConvertToLvlBindPoint(indirect_commands_layout->bind_point); + const auto& last_bound_state = cb_state.lastBound[lv_bind_point]; + VkShaderStageFlags bound_stages = last_bound_state.GetAllActiveBoundStages(); + if ((bound_stages | props.supportedIndirectCommandsShaderStages) != props.supportedIndirectCommandsShaderStages) { + skip |= LogError("VUID-vkCmdExecuteGeneratedCommandsEXT-supportedIndirectCommandsShaderStages-11060", + cb_state.GetObjectList(indirect_commands_layout->bind_point), error_obj.location.dot(Field::commandBuffer), + "is using stages (%s) but supportedIndirectCommandsShaderStages only supports %s.", + string_VkShaderStageFlags(bound_stages).c_str(), + string_VkShaderStageFlags(props.supportedIndirectCommandsShaderStages).c_str()); + } + + skip |= ValidateGeneratedCommandsInfo(cb_state, *indirect_commands_layout, *pGeneratedCommandsInfo, isPreprocessed, info_loc); + + return skip; +} + +bool CoreChecks::ValidateGeneratedCommandsInitialShaderState(const vvl::CommandBuffer& cb_state, + const vvl::IndirectCommandsLayout& indirect_commands_layout, + const vvl::IndirectExecutionSet& indirect_execution_set, + VkShaderStageFlags shader_stage_flags, const LogObjectList& objlist, + const Location cb_loc) const { + bool skip = false; + if (!indirect_commands_layout.has_execution_set_token) return skip; + const char* vuid = (cb_loc.function == Func::vkCmdPreprocessGeneratedCommandsEXT) + ? "VUID-vkCmdPreprocessGeneratedCommandsEXT-indirectCommandsLayout-11084" + : "VUID-vkCmdExecuteGeneratedCommandsEXT-indirectCommandsLayout-11053"; + + const VkPipelineBindPoint bind_point = ConvertToPipelineBindPoint(shader_stage_flags); + const auto lv_bind_point = ConvertToLvlBindPoint(bind_point); + const auto& last_bound = cb_state.lastBound[lv_bind_point]; + + if (indirect_execution_set.is_pipeline) { + const vvl::Pipeline* pipeline = last_bound.pipeline_state; + if (!pipeline) { + skip |= LogError(vuid, objlist, cb_loc, "has not had a pipeline bound for %s.", string_VkPipelineBindPoint(bind_point)); + } else if (pipeline->Handle() != indirect_execution_set.initial_pipeline->Handle()) { + skip |= LogError(vuid, objlist, cb_loc, + "bound %s at %s does not match the VkIndirectExecutionSetPipelineInfoEXT::initialPipeline (%s).", + FormatHandle(*pipeline).c_str(), string_VkPipelineBindPoint(bind_point), + FormatHandle(*indirect_execution_set.initial_pipeline).c_str()); + } + } else if (indirect_execution_set.is_shader_objects) { + // Shader Objects only has compute or graphics + if (bind_point == VK_PIPELINE_BIND_POINT_COMPUTE) { + if (!last_bound.IsValidShaderBound(ShaderObjectStage::COMPUTE)) { + skip |= LogError(vuid, objlist, cb_loc, "has not had a compute VkShaderEXT bound yet."); + } + } else if (bind_point == VK_PIPELINE_BIND_POINT_GRAPHICS) { + if (!last_bound.IsAnyGraphicsShaderBound()) { + skip |= LogError(vuid, objlist, cb_loc, "has not had a graphics VkShaderEXT bound yet."); + } + } + } + + return skip; +} + +// To prevent having "cb_state" and "state_cb_state", we isolate the logic here +bool CoreChecks::ValidatePreprocessGeneratedCommandsStateCommandBuffer(const vvl::CommandBuffer& command_buffer, + const vvl::CommandBuffer& state_command_buffer, + const vvl::IndirectCommandsLayout& indirect_commands_layout, + const VkGeneratedCommandsInfoEXT& generated_commands_info, + const Location loc) const { + bool skip = false; + + if (state_command_buffer.state == CbState::InvalidComplete || state_command_buffer.state == CbState::InvalidIncomplete) { + skip |= ReportInvalidCommandBuffer(state_command_buffer, loc.dot(Field::stateCommandBuffer), + "VUID-vkCmdPreprocessGeneratedCommandsEXT-stateCommandBuffer-11138"); + } else if (CbState::Recording != state_command_buffer.state) { + const LogObjectList objlist(command_buffer.Handle(), state_command_buffer.Handle()); + skip |= LogError("VUID-vkCmdPreprocessGeneratedCommandsEXT-stateCommandBuffer-11138", objlist, + loc.dot(Field::stateCommandBuffer), "is not in a recording state."); + } + + if (auto indirect_execution_set = Get(generated_commands_info.indirectExecutionSet)) { + const LogObjectList objlist(command_buffer.Handle(), state_command_buffer.Handle(), indirect_commands_layout.Handle(), + indirect_execution_set->Handle()); + skip |= ValidateGeneratedCommandsInitialShaderState(state_command_buffer, indirect_commands_layout, *indirect_execution_set, + generated_commands_info.shaderStages, objlist, + loc.dot(Field::stateCommandBuffer)); + } + + return skip; +} + +bool CoreChecks::PreCallValidateCmdPreprocessGeneratedCommandsEXT(VkCommandBuffer commandBuffer, + const VkGeneratedCommandsInfoEXT* pGeneratedCommandsInfo, + VkCommandBuffer stateCommandBuffer, + const ErrorObject& error_obj) const { + bool skip = false; + + const auto& cb_state = *GetRead(commandBuffer); + skip |= ValidateCmd(cb_state, error_obj.location); + if (!cb_state.unprotected) { + skip |= LogError("VUID-vkCmdPreprocessGeneratedCommandsEXT-commandBuffer-11081", commandBuffer, + error_obj.location.dot(Field::commandBuffer), "is protected."); + } + + const Location info_loc = error_obj.location.dot(Field::pGeneratedCommandsInfo); + const auto indirect_commands_layout = Get(pGeneratedCommandsInfo->indirectCommandsLayout); + ASSERT_AND_RETURN_SKIP(indirect_commands_layout); + + if ((indirect_commands_layout->create_info.flags & VK_INDIRECT_COMMANDS_LAYOUT_USAGE_EXPLICIT_PREPROCESS_BIT_EXT) == 0) { + const LogObjectList objlist(commandBuffer, indirect_commands_layout->Handle()); + skip |= LogError("VUID-vkCmdPreprocessGeneratedCommandsEXT-pGeneratedCommandsInfo-11082", objlist, + info_loc.dot(Field::indirectCommandsLayout), + "was not created with VK_INDIRECT_COMMANDS_LAYOUT_USAGE_EXPLICIT_PREPROCESS_BIT_EXT."); + } + + // Unfortunate variable name, but it is in line with the spec + const auto state_cb_state = GetRead(stateCommandBuffer); + ASSERT_AND_RETURN_SKIP(state_cb_state); + skip |= ValidatePreprocessGeneratedCommandsStateCommandBuffer(cb_state, *state_cb_state, *indirect_commands_layout, + *pGeneratedCommandsInfo, error_obj.location); + + skip |= ValidateGeneratedCommandsInfo(cb_state, *indirect_commands_layout, *pGeneratedCommandsInfo, false, info_loc); + + return skip; +} + +bool CoreChecks::PreCallValidateGetGeneratedCommandsMemoryRequirementsEXT(VkDevice device, + const VkGeneratedCommandsMemoryRequirementsInfoEXT* pInfo, + VkMemoryRequirements2* pMemoryRequirements, + const ErrorObject& error_obj) const { + bool skip = false; + + const Location info_loc = error_obj.location.dot(Field::pInfo); + if (pInfo->maxSequenceCount > phys_dev_ext_props.device_generated_commands_props.maxIndirectSequenceCount) { + skip |= + LogError("VUID-VkGeneratedCommandsMemoryRequirementsInfoEXT-maxSequencesCount-11009", device, + info_loc.dot(Field::maxSequenceCount), "(%" PRIu32 ") is larger than maxIndirectSequenceCount (%" PRIu32 ").", + pInfo->maxSequenceCount, phys_dev_ext_props.device_generated_commands_props.maxIndirectSequenceCount); + } + + const auto indirect_commands_layout = Get(pInfo->indirectCommandsLayout); + ASSERT_AND_RETURN_SKIP(indirect_commands_layout); + + if (indirect_commands_layout->has_multi_draw_count_token) { + // Use 64-bit to catch overflow + const uint64_t limit = 1 << 24; + const uint64_t count = (uint64_t)pInfo->maxDrawCount * (uint64_t)pInfo->maxSequenceCount; + if (count >= limit) { + skip |= LogError( + "VUID-VkGeneratedCommandsMemoryRequirementsInfoEXT-maxDrawCount-11146", device, info_loc.dot(Field::maxDrawCount), + "(%" PRIu32 ") time maxSequenceCount (%" PRIu32 ") is %" PRIu64 " which is over the limit of 2^24 (16777216)", + pInfo->maxDrawCount, pInfo->maxSequenceCount, count); + } + } + + if (pInfo->indirectExecutionSet == VK_NULL_HANDLE) { + if (indirect_commands_layout->has_execution_set_token) { + skip |= LogError("VUID-VkGeneratedCommandsMemoryRequirementsInfoEXT-indirectCommandsLayout-11010", + indirect_commands_layout->Handle(), info_loc.dot(Field::indirectExecutionSet), + "is VK_NULL_HANDLE but indirectCommandsLayout was created with a " + "VK_INDIRECT_COMMANDS_TOKEN_TYPE_EXECUTION_SET_EXT token."); + } + + auto* pipeline_info = vku::FindStructInPNextChain(pInfo->pNext); + auto* shader_info = vku::FindStructInPNextChain(pInfo->pNext); + if (!pipeline_info && !shader_info) { + skip |= LogError("VUID-VkGeneratedCommandsMemoryRequirementsInfoEXT-indirectExecutionSet-11012", + indirect_commands_layout->Handle(), info_loc.dot(Field::indirectExecutionSet), + "is VK_NULL_HANDLE but the pNext is missing a VkGeneratedCommandsPipelineInfoEXT or " + "VkGeneratedCommandsShaderInfoEXT."); + } + } else { + if (!indirect_commands_layout->has_execution_set_token) { + skip |= LogError("VUID-VkGeneratedCommandsMemoryRequirementsInfoEXT-indirectCommandsLayout-11011", + indirect_commands_layout->Handle(), info_loc.dot(Field::indirectExecutionSet), + "is not VK_NULL_HANDLE but indirectCommandsLayout was not created with a " + "VK_INDIRECT_COMMANDS_TOKEN_TYPE_EXECUTION_SET_EXT token."); + } else { + auto indirect_execution_set = Get(pInfo->indirectExecutionSet); + ASSERT_AND_RETURN_SKIP(indirect_execution_set); + if (indirect_execution_set->shader_stage_flags != indirect_commands_layout->execution_set_token_shader_stage_flags) { + skip |= + LogError("VUID-VkGeneratedCommandsMemoryRequirementsInfoEXT-indirectCommandsLayout-11151", + indirect_commands_layout->Handle(), info_loc.dot(Field::indirectExecutionSet), + "was created with shader stage %s but indirectCommandsLayout was created with shader stage %s.", + string_VkShaderStageFlags(indirect_execution_set->shader_stage_flags).c_str(), + string_VkShaderStageFlags(indirect_commands_layout->execution_set_token_shader_stage_flags).c_str()); + } + } + } + + return skip; +} + +bool CoreChecks::PreCallValidateUpdateIndirectExecutionSetPipelineEXT( + VkDevice device, VkIndirectExecutionSetEXT indirectExecutionSet, uint32_t executionSetWriteCount, + const VkWriteIndirectExecutionSetPipelineEXT* pExecutionSetWrites, const ErrorObject& error_obj) const { + bool skip = false; + auto indirect_execution_set = Get(indirectExecutionSet); + ASSERT_AND_RETURN_SKIP(indirect_execution_set); + if (!indirect_execution_set->is_pipeline) { + skip |= LogError("VUID-vkUpdateIndirectExecutionSetPipelineEXT-indirectExecutionSet-11035", + indirect_execution_set->Handle(), error_obj.location.dot(Field::indirectExecutionSet), + "was not created with type VK_INDIRECT_EXECUTION_SET_INFO_TYPE_PIPELINES_EXT."); + return skip; // something is wrong and the rest will probably be garbage + } + + if (executionSetWriteCount > indirect_execution_set->max_pipeline_count) { + skip |= LogError("VUID-vkUpdateIndirectExecutionSetPipelineEXT-executionSetWriteCount-11037", + indirect_execution_set->Handle(), error_obj.location.dot(Field::executionSetWriteCount), + "(%" PRIu32 ") is larger than the maxPipelineCount used to create indirectExecutionSet (%" PRIu32 ").", + executionSetWriteCount, indirect_execution_set->max_pipeline_count); + } + + vvl::unordered_map unique_indexes; + const auto& props = phys_dev_ext_props.device_generated_commands_props; + for (uint32_t i = 0; i < executionSetWriteCount; i++) { + const VkWriteIndirectExecutionSetPipelineEXT& set_pipeline = pExecutionSetWrites[i]; + const Location set_write_loc = error_obj.location.dot(Field::pExecutionSetWrites, i); + + if (unique_indexes.find(set_pipeline.index) != unique_indexes.end()) { + skip |= + LogError("VUID-vkUpdateIndirectExecutionSetPipelineEXT-pExecutionSetWrites-11042", device, + set_write_loc.dot(Field::index), "(%" PRIu32 ") has same value as pExecutionSetWrites[%" PRIu32 "].index", + set_pipeline.index, unique_indexes[set_pipeline.index]); + } else { + unique_indexes[set_pipeline.index] = i; + } + + if (set_pipeline.index >= indirect_execution_set->max_pipeline_count) { + skip |= + LogError("VUID-VkWriteIndirectExecutionSetPipelineEXT-index-11026", indirect_execution_set->Handle(), + set_write_loc.dot(Field::index), + "(%" PRIu32 ") is not less than the maxPipelineCount used to create indirectExecutionSet (%" PRIu32 ").", + set_pipeline.index, indirect_execution_set->max_pipeline_count); + } + + auto update_pipeline = Get(set_pipeline.pipeline); + ASSERT_AND_CONTINUE(update_pipeline); + if ((update_pipeline->create_flags & VK_PIPELINE_CREATE_2_INDIRECT_BINDABLE_BIT_EXT) == 0) { + // TODO - This seems to not be possible to hit without first hitting 11153 + skip |= LogError("VUID-VkWriteIndirectExecutionSetPipelineEXT-pipeline-11027", update_pipeline->Handle(), + set_write_loc.dot(Field::pipeline), + "is missing VK_PIPELINE_CREATE_2_INDIRECT_BINDABLE_BIT_EXT, was created with flags %s. (Make sure you " + "set it with VkPipelineCreateFlags2CreateInfoKHR)", + string_VkPipelineCreateFlags2KHR(update_pipeline->create_flags).c_str()); + } + + if ((update_pipeline->active_shaders | props.supportedIndirectCommandsShaderStagesShaderBinding) != + props.supportedIndirectCommandsShaderStagesShaderBinding) { + skip |= LogError("VUID-VkWriteIndirectExecutionSetPipelineEXT-pipeline-11030", update_pipeline->Handle(), + set_write_loc.dot(Field::pipeline), + "is using stages (%s) but supportedIndirectCommandsShaderStagesShaderBinding only supports %s.", + string_VkShaderStageFlags(update_pipeline->active_shaders).c_str(), + string_VkShaderStageFlags(props.supportedIndirectCommandsShaderStagesShaderBinding).c_str()); + } + + if (const auto initial_pipeline = indirect_execution_set->initial_pipeline) { + // Want to throw this error first, in case one is missing a fragment shader, might have extra errors follow below + if (initial_pipeline->active_shaders != update_pipeline->active_shaders) { + LogObjectList objlist(initial_pipeline->Handle(), update_pipeline->Handle()); + skip |= LogError("VUID-vkUpdateIndirectExecutionSetPipelineEXT-initialPipeline-11152", objlist, + set_write_loc.dot(Field::pipeline), + "was created with shader stages different from the ones specified in " + "initialPipeline.\ninitialPipeline: %s\nupdate " + "pipeline: %s\n", + string_VkShaderStageFlags(initial_pipeline->active_shaders).c_str(), + string_VkShaderStageFlags(update_pipeline->active_shaders).c_str()); + } + + if (initial_pipeline->dynamic_state != update_pipeline->dynamic_state) { + LogObjectList objlist(initial_pipeline->Handle(), update_pipeline->Handle()); + skip |= + LogError("VUID-vkUpdateIndirectExecutionSetPipelineEXT-None-11040", objlist, set_write_loc.dot(Field::pipeline), + "was created with dynamic states different from the ones specified in " + "initialPipeline.\ninitialPipeline: %s\nupdate " + "pipeline: %s\n", + DynamicStatesToString(initial_pipeline->dynamic_state).c_str(), + DynamicStatesToString(update_pipeline->dynamic_state).c_str()); + } + + if (initial_pipeline->fragmentShader_writable_output_location_list != + update_pipeline->fragmentShader_writable_output_location_list) { + LogObjectList objlist(initial_pipeline->Handle(), update_pipeline->Handle()); + skip |= LogError("VUID-vkUpdateIndirectExecutionSetPipelineEXT-initialPipeline-11147", objlist, + set_write_loc.dot(Field::pipeline), + "was created with a fragment shader interface different from the one specified in " + "initialPipeline. The fragment shaders statically write to different locations."); + } + + // Default to false incase there is not fragment shader + bool initial_pipeline_frag_depth = false; + bool initial_pipeline_sample_mask = false; + bool initial_pipeline_stencil_export = false; + if (initial_pipeline->fragment_shader_state && initial_pipeline->fragment_shader_state->fragment_entry_point) { + auto shader_state = initial_pipeline->fragment_shader_state; + initial_pipeline_frag_depth = shader_state->fragment_entry_point->HasBuiltIn(spv::BuiltInFragDepth); + initial_pipeline_sample_mask = shader_state->fragment_entry_point->HasBuiltIn(spv::BuiltInSampleMask); + if (shader_state->fragment_shader && shader_state->fragment_shader->spirv) { + initial_pipeline_stencil_export = + shader_state->fragment_shader->spirv->HasCapability(spv::CapabilityStencilExportEXT); + } + } + + // Indirect Execution Set + bool ies_pipeline_frag_depth = false; + bool ies_pipeline_sample_mask = false; + bool ies_pipeline_stencil_export = false; + if (update_pipeline->fragment_shader_state && update_pipeline->fragment_shader_state->fragment_entry_point) { + auto shader_state = update_pipeline->fragment_shader_state; + ies_pipeline_frag_depth = shader_state->fragment_entry_point->HasBuiltIn(spv::BuiltInFragDepth); + ies_pipeline_sample_mask = shader_state->fragment_entry_point->HasBuiltIn(spv::BuiltInSampleMask); + if (shader_state->fragment_shader && shader_state->fragment_shader->spirv) { + ies_pipeline_stencil_export = + shader_state->fragment_shader->spirv->HasCapability(spv::CapabilityStencilExportEXT); + } + } + + if (initial_pipeline_frag_depth != ies_pipeline_frag_depth) { + LogObjectList objlist(initial_pipeline->Handle(), update_pipeline->Handle()); + skip |= LogError("VUID-vkUpdateIndirectExecutionSetPipelineEXT-initialPipeline-11098", objlist, + set_write_loc.dot(Field::pipeline), + "was %screated with FragDepth, but the initialPipeline was %screated with FragDepth.", + ies_pipeline_frag_depth ? "" : "not ", initial_pipeline_frag_depth ? "" : "not "); + } + if (initial_pipeline_sample_mask != ies_pipeline_sample_mask) { + LogObjectList objlist(initial_pipeline->Handle(), update_pipeline->Handle()); + skip |= LogError("VUID-vkUpdateIndirectExecutionSetPipelineEXT-initialPipeline-11086", objlist, + set_write_loc.dot(Field::pipeline), + "was %screated with SampleMask, but the initialPipeline was %screated with SampleMask.", + ies_pipeline_sample_mask ? "" : "not ", initial_pipeline_sample_mask ? "" : "not "); + } + if (initial_pipeline_stencil_export != ies_pipeline_stencil_export) { + LogObjectList objlist(initial_pipeline->Handle(), update_pipeline->Handle()); + skip |= + LogError("VUID-vkUpdateIndirectExecutionSetPipelineEXT-initialPipeline-11085", objlist, + set_write_loc.dot(Field::pipeline), + "was %screated with StencilExportEXT, but the initialPipeline was %screated with StencilExportEXT.", + ies_pipeline_stencil_export ? "" : "not ", initial_pipeline_stencil_export ? "" : "not "); + } + } + } + + return skip; +} + +bool CoreChecks::PreCallValidateUpdateIndirectExecutionSetShaderEXT(VkDevice device, VkIndirectExecutionSetEXT indirectExecutionSet, + uint32_t executionSetWriteCount, + const VkWriteIndirectExecutionSetShaderEXT* pExecutionSetWrites, + const ErrorObject& error_obj) const { + bool skip = false; + auto indirect_execution_set = Get(indirectExecutionSet); + ASSERT_AND_RETURN_SKIP(indirect_execution_set); + if (!indirect_execution_set->is_shader_objects) { + skip |= LogError("VUID-vkUpdateIndirectExecutionSetShaderEXT-indirectExecutionSet-11041", indirect_execution_set->Handle(), + error_obj.location.dot(Field::indirectExecutionSet), + "was not created with type VK_INDIRECT_EXECUTION_SET_INFO_TYPE_SHADER_OBJECTS_EXT."); + return skip; // something is wrong and the rest will probably be garbage + } + + vvl::unordered_map unique_indexes; + for (uint32_t i = 0; i < executionSetWriteCount; i++) { + const VkWriteIndirectExecutionSetShaderEXT& set_shader = pExecutionSetWrites[i]; + const Location set_write_loc = error_obj.location.dot(Field::pExecutionSetWrites, i); + + if (unique_indexes.find(set_shader.index) != unique_indexes.end()) { + skip |= + LogError("VUID-vkUpdateIndirectExecutionSetShaderEXT-pExecutionSetWrites-11043", device, + set_write_loc.dot(Field::index), "(%" PRIu32 ") has same value as pExecutionSetWrites[%" PRIu32 "].index", + set_shader.index, unique_indexes[set_shader.index]); + } else { + unique_indexes[set_shader.index] = i; + } + + if (set_shader.index >= indirect_execution_set->max_shader_count) { + skip |= LogError("VUID-VkWriteIndirectExecutionSetShaderEXT-index-11031", device, set_write_loc.dot(Field::index), + "(%" PRIu32 + ") is not less then the sum of VkIndirectExecutionSetShaderInfoEXT::maxShaderCount (%" PRIu32 ").", + set_shader.index, indirect_execution_set->max_shader_count); + } + + auto update_shader_object = Get(set_shader.shader); + ASSERT_AND_CONTINUE(update_shader_object); + if ((update_shader_object->create_info.flags & VK_SHADER_CREATE_INDIRECT_BINDABLE_BIT_EXT) == 0) { + // TODO - This seems to not be possible to hit without first hitting 11154 + skip |= LogError("VUID-VkWriteIndirectExecutionSetShaderEXT-shader-11032", update_shader_object->Handle(), + set_write_loc.dot(Field::shader), + "is missing VK_SHADER_CREATE_INDIRECT_BINDABLE_BIT_EXT, was created with flags %s.", + string_VkShaderCreateFlagsEXT(update_shader_object->create_info.flags).c_str()); + } + + if ((update_shader_object->create_info.stage & indirect_execution_set->shader_stage_flags) == 0) { + skip |= LogError( + "VUID-VkWriteIndirectExecutionSetShaderEXT-pInitialShaders-11033", update_shader_object->Handle(), + set_write_loc.dot(Field::shader), + "was created with %s but none of the VkIndirectExecutionSetShaderInfoEXT::pShaderStages contained that stage (%s).", + string_VkShaderStageFlagBits(update_shader_object->create_info.stage), + string_VkShaderStageFlags(indirect_execution_set->shader_stage_flags).c_str()); + } + + const auto initial_fragment_shader_object = indirect_execution_set->initial_fragment_shader_object; + if (initial_fragment_shader_object && update_shader_object->create_info.stage == VK_SHADER_STAGE_FRAGMENT_BIT) { + if (update_shader_object->entrypoint && initial_fragment_shader_object->entrypoint) { + vvl::unordered_set update_shader_output_locations; + vvl::unordered_set initial_shader_output_locations; + + // From GetFSOutputLocations() + for (const auto* variable : update_shader_object->entrypoint->user_defined_interface_variables) { + if ((variable->storage_class != spv::StorageClassOutput) || variable->interface_slots.empty()) { + continue; // not an output interface + } + update_shader_output_locations.insert(variable->interface_slots[0].Location()); + } + for (const auto* variable : initial_fragment_shader_object->entrypoint->user_defined_interface_variables) { + if ((variable->storage_class != spv::StorageClassOutput) || variable->interface_slots.empty()) { + continue; // not an output interface + } + initial_shader_output_locations.insert(variable->interface_slots[0].Location()); + } + + if (update_shader_output_locations != initial_shader_output_locations) { + LogObjectList objlist(initial_fragment_shader_object->Handle(), update_shader_object->Handle()); + skip |= + LogError("VUID-vkUpdateIndirectExecutionSetShaderEXT-None-11148", objlist, set_write_loc.dot(Field::shader), + "was created with a fragment shader interface different from the initial Fragment VkShaderEXT. " + "The fragment shaders statically write to different locations."); + } + + const bool initial_shader_frag_depth = + initial_fragment_shader_object->entrypoint->HasBuiltIn(spv::BuiltInFragDepth); + const bool initial_shader_sample_mask = + initial_fragment_shader_object->entrypoint->HasBuiltIn(spv::BuiltInSampleMask); + const bool initial_shader_stencil_export = + initial_fragment_shader_object->spirv && + initial_fragment_shader_object->spirv->HasCapability(spv::CapabilityStencilExportEXT); + + const bool ies_shader_frag_depth = update_shader_object->entrypoint->HasBuiltIn(spv::BuiltInFragDepth); + const bool ies_shader_sample_mask = update_shader_object->entrypoint->HasBuiltIn(spv::BuiltInSampleMask); + const bool ies_shader_stencil_export = + update_shader_object->spirv && update_shader_object->spirv->HasCapability(spv::CapabilityStencilExportEXT); + + if (initial_shader_frag_depth != ies_shader_frag_depth) { + LogObjectList objlist(initial_fragment_shader_object->Handle(), update_shader_object->Handle()); + skip |= LogError( + "VUID-vkUpdateIndirectExecutionSetShaderEXT-FragDepth-11054", objlist, set_write_loc.dot(Field::shader), + "was %screated with FragDepth, but the initial Fragment VkShaderEXT was %screated with FragDepth.", + ies_shader_frag_depth ? "" : "not ", initial_shader_frag_depth ? "" : "not "); + } + if (initial_shader_sample_mask != ies_shader_sample_mask) { + LogObjectList objlist(initial_fragment_shader_object->Handle(), update_shader_object->Handle()); + skip |= LogError( + "VUID-vkUpdateIndirectExecutionSetShaderEXT-SampleMask-11050", objlist, set_write_loc.dot(Field::shader), + "was %screated with SampleMask, but the initial Fragment VkShaderEXT was %screated with SampleMask.", + ies_shader_sample_mask ? "" : "not ", initial_shader_sample_mask ? "" : "not "); + } + if (initial_shader_stencil_export != ies_shader_stencil_export) { + LogObjectList objlist(initial_fragment_shader_object->Handle(), update_shader_object->Handle()); + skip |= LogError("VUID-vkUpdateIndirectExecutionSetShaderEXT-StencilExportEXT-11003", objlist, + set_write_loc.dot(Field::shader), + "was %screated with StencilExportEXT, but the initial Fragment VkShaderEXT was %screated with " + "StencilExportEXT.", + ies_shader_stencil_export ? "" : "not ", initial_shader_stencil_export ? "" : "not "); + } + } + } + } + + return skip; +} \ No newline at end of file diff --git a/layers/core_checks/cc_render_pass.cpp b/layers/core_checks/cc_render_pass.cpp index db9407bf756..e4e8add874d 100644 --- a/layers/core_checks/cc_render_pass.cpp +++ b/layers/core_checks/cc_render_pass.cpp @@ -5060,7 +5060,7 @@ bool CoreChecks::ValidateRenderingAttachmentLocationsKHR(const VkRenderingAttach if (unique.find(location) != unique.end()) { skip |= LogError("VUID-VkRenderingAttachmentLocationInfoKHR-pColorAttachmentLocations-09513", objlist, loc, - "= %" PRIu32 " have same value as pColorAttachmentLocations[%" PRIu32 "] = %" PRIu32, location, + "= %" PRIu32 " has same value as pColorAttachmentLocations[%" PRIu32 "] = %" PRIu32, location, unique[location], location); } else unique[location] = i; @@ -5164,7 +5164,7 @@ bool CoreChecks::ValidateRenderingInputAttachmentIndicesKHR(const VkRenderingInp skip |= LogError("VUID-VkRenderingInputAttachmentIndexInfoKHR-pColorAttachmentInputIndices-09522", objlist, loc_info.dot(Struct::VkRenderingInputAttachmentIndexInfoKHR, Field::pColorAttachmentInputIndices, i), - "= %" PRIu32 " have same value as in pColorAttachmentInputIndices[%" PRIu32 "] = %" PRIu32, index, + "= %" PRIu32 " has same value as in pColorAttachmentInputIndices[%" PRIu32 "] = %" PRIu32, index, unique[index], index_info.pColorAttachmentInputIndices[unique[index]]); } else unique[index] = i; @@ -5173,7 +5173,7 @@ bool CoreChecks::ValidateRenderingInputAttachmentIndicesKHR(const VkRenderingInp unique.find(*index_info.pDepthInputAttachmentIndex) != unique.end()) { const Location loc = loc_info.dot(Struct::VkRenderingInputAttachmentIndexInfoKHR, Field::pDepthInputAttachmentIndex, 0); skip |= LogError("VUID-VkRenderingInputAttachmentIndexInfoKHR-pColorAttachmentInputIndices-09523", objlist, loc, - "= %" PRIu32 " have same value as in pColorAttachmentInputIndices[%" PRIu32 "] = %" PRIu32, + "= %" PRIu32 " has same value as in pColorAttachmentInputIndices[%" PRIu32 "] = %" PRIu32, *index_info.pDepthInputAttachmentIndex, unique[*index_info.pDepthInputAttachmentIndex], index_info.pColorAttachmentInputIndices[unique[*index_info.pDepthInputAttachmentIndex]]); } @@ -5182,7 +5182,7 @@ bool CoreChecks::ValidateRenderingInputAttachmentIndicesKHR(const VkRenderingInp const Location loc = loc_info.dot(Struct::VkRenderingInputAttachmentIndexInfoKHR, Field::pStencilInputAttachmentIndex, 0); skip |= LogError("VUID-VkRenderingInputAttachmentIndexInfoKHR-pColorAttachmentInputIndices-09524", objlist, loc, - "= %" PRIu32 " have same value as in pColorAttachmentInputIndices[%" PRIu32 "] = %" PRIu32, + "= %" PRIu32 " has same value as in pColorAttachmentInputIndices[%" PRIu32 "] = %" PRIu32, *index_info.pStencilInputAttachmentIndex, unique[*index_info.pStencilInputAttachmentIndex], index_info.pColorAttachmentInputIndices[unique[*index_info.pStencilInputAttachmentIndex]]); } diff --git a/layers/core_checks/cc_shader_object.cpp b/layers/core_checks/cc_shader_object.cpp index 29d2c57e505..54c2055ccb0 100644 --- a/layers/core_checks/cc_shader_object.cpp +++ b/layers/core_checks/cc_shader_object.cpp @@ -219,6 +219,12 @@ bool CoreChecks::ValidateCreateShadersLinking(uint32_t createInfoCount, const Vk "VUID-VkShaderCreateInfoEXT-flags-09405", device, create_info_loc.dot(Field::flags), "contains VK_SHADER_CREATE_REQUIRE_FULL_SUBGROUPS_BIT_EXT, but computeFullSubgroups feature is not enabled."); } + if ((create_info.flags & VK_SHADER_CREATE_INDIRECT_BINDABLE_BIT_EXT) != 0 && + enabled_features.deviceGeneratedCommands == VK_FALSE) { + skip |= LogError( + " VUID-VkShaderCreateInfoEXT-flags-11005", device, create_info_loc.dot(Field::flags), + "contains VK_SHADER_CREATE_INDIRECT_BINDABLE_BIT_EXT, but deviceGeneratedCommands feature is not enabled."); + } } if (linked_stage != invalid && non_linked_graphics_stage != invalid) { diff --git a/layers/core_checks/cc_spirv.cpp b/layers/core_checks/cc_spirv.cpp index 82c12d94d4b..f301bfbfa03 100644 --- a/layers/core_checks/cc_spirv.cpp +++ b/layers/core_checks/cc_spirv.cpp @@ -1844,11 +1844,18 @@ bool CoreChecks::ValidateTransformFeedbackPipeline(const spirv::Module &module_s string_VkShaderStageFlags(pipeline.create_info_shaders).c_str()); } - if (pipeline.pre_raster_state && (entrypoint.stage != pipeline.pre_raster_state->last_stage)) { - skip |= LogError("VUID-VkGraphicsPipelineCreateInfo-pStages-02318", module_state.handle(), loc, - "SPIR-V has OpExecutionMode of Xfb in %s, but %s is the last last pre-rasterization shader stage.", - string_VkShaderStageFlagBits(entrypoint.stage), - string_VkShaderStageFlagBits(pipeline.pre_raster_state->last_stage)); + if (pipeline.pre_raster_state) { + if (entrypoint.stage != pipeline.pre_raster_state->last_stage) { + skip |= LogError("VUID-VkGraphicsPipelineCreateInfo-pStages-02318", module_state.handle(), loc, + "SPIR-V has OpExecutionMode of Xfb in %s, but %s is the last last pre-rasterization shader stage.", + string_VkShaderStageFlagBits(entrypoint.stage), + string_VkShaderStageFlagBits(pipeline.pre_raster_state->last_stage)); + } + if ((pipeline.create_flags & VK_PIPELINE_CREATE_2_INDIRECT_BINDABLE_BIT_EXT) != 0) { + skip |= LogError("VUID-VkGraphicsPipelineCreateInfo-flags-11001", module_state.handle(), loc, + "SPIR-V has OpExecutionMode of Xfb but this pipeline is being created with " + "VK_PIPELINE_CREATE_2_INDIRECT_BINDABLE_BIT_EXT."); + } } } diff --git a/layers/core_checks/core_validation.h b/layers/core_checks/core_validation.h index e3e33fb46d9..8c254da268e 100644 --- a/layers/core_checks/core_validation.h +++ b/layers/core_checks/core_validation.h @@ -2503,6 +2503,61 @@ class CoreChecks : public ValidationStateTracker { bool PreCallValidateGetCalibratedTimestampsKHR(VkDevice device, uint32_t timestampCount, const VkCalibratedTimestampInfoEXT* pTimestampInfos, uint64_t* pTimestamps, uint64_t* pMaxDeviation, const ErrorObject& error_obj) const override; + + bool PreCallValidateCreateIndirectCommandsLayoutEXT(VkDevice device, const VkIndirectCommandsLayoutCreateInfoEXT* pCreateInfo, + const VkAllocationCallbacks* pAllocator, + VkIndirectCommandsLayoutEXT* pIndirectCommandsLayout, + const ErrorObject& error_obj) const override; + bool PreCallValidateDestroyIndirectCommandsLayoutEXT(VkDevice device, VkIndirectCommandsLayoutEXT indirectCommandsLayout, + const VkAllocationCallbacks* pAllocator, + const ErrorObject& error_obj) const override; + + bool ValidateIndirectExecutionSetPipelineInfo(const VkIndirectExecutionSetPipelineInfoEXT& pipeline_info, + const Location& pipeline_info_loc) const; + bool ValidateIndirectExecutionSetShaderInfo(const VkIndirectExecutionSetShaderInfoEXT& shader_info, + const Location& shader_info_loc) const; + bool PreCallValidateCreateIndirectExecutionSetEXT(VkDevice device, const VkIndirectExecutionSetCreateInfoEXT* pCreateInfo, + const VkAllocationCallbacks* pAllocator, + VkIndirectExecutionSetEXT* pIndirectExecutionSet, + const ErrorObject& error_obj) const override; + bool PreCallValidateDestroyIndirectExecutionSetEXT(VkDevice device, VkIndirectExecutionSetEXT indirectExecutionSet, + const VkAllocationCallbacks* pAllocator, + const ErrorObject& error_obj) const override; + + bool PreCallValidateCmdExecuteGeneratedCommandsEXT(VkCommandBuffer commandBuffer, VkBool32 isPreprocessed, + const VkGeneratedCommandsInfoEXT* pGeneratedCommandsInfo, + const ErrorObject& error_obj) const override; + bool ValidateGeneratedCommandsInitialShaderState(const vvl::CommandBuffer& cb_state, + const vvl::IndirectCommandsLayout& indirect_commands_layout, + const vvl::IndirectExecutionSet& indirect_execution_set, + VkShaderStageFlags shader_stage_flags, const LogObjectList& objlist, + const Location cb_loc) const; + bool ValidatePreprocessGeneratedCommandsStateCommandBuffer(const vvl::CommandBuffer& command_buffer, + const vvl::CommandBuffer& state_command_buffer, + const vvl::IndirectCommandsLayout& indirect_commands_layout, + const VkGeneratedCommandsInfoEXT& generated_commands_info, + const Location loc) const; + bool PreCallValidateCmdPreprocessGeneratedCommandsEXT(VkCommandBuffer commandBuffer, + const VkGeneratedCommandsInfoEXT* pGeneratedCommandsInfo, + VkCommandBuffer stateCommandBuffer, + const ErrorObject& error_obj) const override; + bool PreCallValidateGetGeneratedCommandsMemoryRequirementsEXT(VkDevice device, + const VkGeneratedCommandsMemoryRequirementsInfoEXT* pInfo, + VkMemoryRequirements2* pMemoryRequirements, + const ErrorObject& error_obj) const override; + bool PreCallValidateUpdateIndirectExecutionSetPipelineEXT(VkDevice device, VkIndirectExecutionSetEXT indirectExecutionSet, + uint32_t executionSetWriteCount, + const VkWriteIndirectExecutionSetPipelineEXT* pExecutionSetWrites, + const ErrorObject& error_obj) const override; + bool PreCallValidateUpdateIndirectExecutionSetShaderEXT(VkDevice device, VkIndirectExecutionSetEXT indirectExecutionSet, + uint32_t executionSetWriteCount, + const VkWriteIndirectExecutionSetShaderEXT* pExecutionSetWrites, + const ErrorObject& error_obj) const override; + bool ValidateGeneratedCommandsInfo(const vvl::CommandBuffer& cb_state, + const vvl::IndirectCommandsLayout& indirect_commands_layout, + const VkGeneratedCommandsInfoEXT& generated_commands_info, bool preprocessed, + const Location& info_loc) const; + bool ValidateRenderingAttachmentLocationsKHR(const VkRenderingAttachmentLocationInfoKHR& location_info, const LogObjectList objlist, const Location& loc_info) const; bool PreCallValidateCmdSetRenderingAttachmentLocationsKHR(VkCommandBuffer commandBuffer, diff --git a/layers/error_message/error_strings.h b/layers/error_message/error_strings.h index f1907b831b3..1956f5e00ec 100644 --- a/layers/error_message/error_strings.h +++ b/layers/error_message/error_strings.h @@ -80,3 +80,9 @@ } return ss.str(); } + +[[maybe_unused]] static std::string string_VkPushConstantRange(VkPushConstantRange range) { + std::stringstream ss; + ss << "[" << range.offset << ", " << (range.offset + range.size) << "]"; + return ss.str(); +} diff --git a/layers/error_message/unimplementable_validation.h b/layers/error_message/unimplementable_validation.h index 499fc02019f..34f49b09d1b 100644 --- a/layers/error_message/unimplementable_validation.h +++ b/layers/error_message/unimplementable_validation.h @@ -72,6 +72,9 @@ const char* unimplementable_validation[] = { "VUID-VkPipelineCacheHeaderVersionOne-headerVersion-04968", "VUID-VkPipelineCacheHeaderVersionOne-headerSize-08990", + // https://gitlab.khronos.org/vulkan/vulkan/-/merge_requests/6639#note_468463 + "VUID-VkIndirectCommandsVertexBufferTokenEXT-vertexBindingUnit-11134", + // These implicit VUs ask to check for a valid structure that has no sType, // there is nothing that can actually be validated // diff --git a/layers/gpu/core/gpuav.h b/layers/gpu/core/gpuav.h index 8942c5f7d02..f1b32d64a4d 100644 --- a/layers/gpu/core/gpuav.h +++ b/layers/gpu/core/gpuav.h @@ -298,6 +298,12 @@ class Validator : public gpu::GpuShaderInstrumentor { const RecordObject& record_obj) final; void PostCallRecordCmdTraceRaysIndirect2KHR(VkCommandBuffer commandBuffer, VkDeviceAddress indirectDeviceAddress, const RecordObject& record_obj) final; + void PreCallRecordCmdExecuteGeneratedCommandsEXT(VkCommandBuffer commandBuffer, VkBool32 isPreprocessed, + const VkGeneratedCommandsInfoEXT* pGeneratedCommandsInfo, + const RecordObject& record_obj) final; + void PostCallRecordCmdExecuteGeneratedCommandsEXT(VkCommandBuffer commandBuffer, VkBool32 isPreprocessed, + const VkGeneratedCommandsInfoEXT* pGeneratedCommandsInfo, + const RecordObject& record_obj) final; void PostCallRecordGetPhysicalDeviceProperties2(VkPhysicalDevice physicalDevice, VkPhysicalDeviceProperties2* pPhysicalDeviceProperties2, diff --git a/layers/gpu/core/gpuav_record.cpp b/layers/gpu/core/gpuav_record.cpp index 8e93896d947..74f6160dfe5 100644 --- a/layers/gpu/core/gpuav_record.cpp +++ b/layers/gpu/core/gpuav_record.cpp @@ -981,4 +981,32 @@ void Validator::PostCallRecordCmdTraceRaysIndirect2KHR(VkCommandBuffer commandBu PostCallSetupShaderInstrumentationResources(*this, *cb_state, VK_PIPELINE_BIND_POINT_RAY_TRACING_KHR, record_obj.location); } +void Validator::PreCallRecordCmdExecuteGeneratedCommandsEXT(VkCommandBuffer commandBuffer, VkBool32 isPreprocessed, + const VkGeneratedCommandsInfoEXT *pGeneratedCommandsInfo, + const RecordObject &record_obj) { + BaseClass::PreCallRecordCmdExecuteGeneratedCommandsEXT(commandBuffer, isPreprocessed, pGeneratedCommandsInfo, record_obj); + + auto cb_state = GetWrite(commandBuffer); + if (!cb_state) { + InternalError(commandBuffer, record_obj.location, "Unrecognized command buffer."); + return; + } + const VkPipelineBindPoint bind_point = ConvertToPipelineBindPoint(pGeneratedCommandsInfo->shaderStages); + PreCallSetupShaderInstrumentationResources(*this, *cb_state, bind_point, record_obj.location); +}; + +void Validator::PostCallRecordCmdExecuteGeneratedCommandsEXT(VkCommandBuffer commandBuffer, VkBool32 isPreprocessed, + const VkGeneratedCommandsInfoEXT *pGeneratedCommandsInfo, + const RecordObject &record_obj) { + BaseClass::PostCallRecordCmdExecuteGeneratedCommandsEXT(commandBuffer, isPreprocessed, pGeneratedCommandsInfo, record_obj); + + auto cb_state = GetWrite(commandBuffer); + if (!cb_state) { + InternalError(commandBuffer, record_obj.location, "Unrecognized command buffer."); + return; + } + const VkPipelineBindPoint bind_point = ConvertToPipelineBindPoint(pGeneratedCommandsInfo->shaderStages); + PostCallSetupShaderInstrumentationResources(*this, *cb_state, bind_point, record_obj.location); +} + } // namespace gpuav diff --git a/layers/gpu/debug_printf/debug_printf.cpp b/layers/gpu/debug_printf/debug_printf.cpp index a958da492f8..a1f3c1201a1 100644 --- a/layers/gpu/debug_printf/debug_printf.cpp +++ b/layers/gpu/debug_printf/debug_printf.cpp @@ -568,6 +568,13 @@ void Validator::PreCallRecordCmdTraceRaysIndirect2KHR(VkCommandBuffer commandBuf AllocateDebugPrintfResources(commandBuffer, VK_PIPELINE_BIND_POINT_RAY_TRACING_KHR, record_obj.location); } +void Validator::PreCallRecordCmdExecuteGeneratedCommandsEXT(VkCommandBuffer commandBuffer, VkBool32 isPreprocessed, + const VkGeneratedCommandsInfoEXT *pGeneratedCommandsInfo, + const RecordObject &record_obj) { + const VkPipelineBindPoint bind_point = ConvertToPipelineBindPoint(pGeneratedCommandsInfo->shaderStages); + AllocateDebugPrintfResources(commandBuffer, bind_point, record_obj.location); +}; + void Validator::AllocateDebugPrintfResources(const VkCommandBuffer cmd_buffer, const VkPipelineBindPoint bind_point, const Location &loc) { assert(bind_point == VK_PIPELINE_BIND_POINT_GRAPHICS || bind_point == VK_PIPELINE_BIND_POINT_COMPUTE || diff --git a/layers/gpu/debug_printf/debug_printf.h b/layers/gpu/debug_printf/debug_printf.h index 3fee6c10db1..328715a5e66 100644 --- a/layers/gpu/debug_printf/debug_printf.h +++ b/layers/gpu/debug_printf/debug_printf.h @@ -149,6 +149,9 @@ class Validator : public gpu::GpuShaderInstrumentor { VkDeviceAddress indirectDeviceAddress, const RecordObject& record_obj) override; void PreCallRecordCmdTraceRaysIndirect2KHR(VkCommandBuffer commandBuffer, VkDeviceAddress indirectDeviceAddress, const RecordObject& record_obj) override; + void PreCallRecordCmdExecuteGeneratedCommandsEXT(VkCommandBuffer commandBuffer, VkBool32 isPreprocessed, + const VkGeneratedCommandsInfoEXT* pGeneratedCommandsInfo, + const RecordObject& record_obj) override; void AllocateDebugPrintfResources(const VkCommandBuffer cmd_buffer, const VkPipelineBindPoint bind_point, const Location& loc); std::shared_ptr CreateCmdBufferState(VkCommandBuffer cb, const VkCommandBufferAllocateInfo* create_info, diff --git a/layers/gpu/error_message/gpuav_vuids.cpp b/layers/gpu/error_message/gpuav_vuids.cpp index 140b3a3d1ed..b61529e5173 100644 --- a/layers/gpu/error_message/gpuav_vuids.cpp +++ b/layers/gpu/error_message/gpuav_vuids.cpp @@ -292,6 +292,16 @@ struct GpuVuidsCmdDispatchBase : GpuVuid { } }; +struct GpuVuidsCmdExecuteGeneratedCommandsEXT : GpuVuid { + GpuVuidsCmdExecuteGeneratedCommandsEXT() : GpuVuid() { + uniform_access_oob_06935 = "VUID-vkCmdExecuteGeneratedCommandsEXT-uniformBuffers-06935"; + storage_access_oob_06936 = "VUID-vkCmdExecuteGeneratedCommandsEXT-storageBuffers-06936"; + uniform_access_oob_08612 = "VUID-vkCmdExecuteGeneratedCommandsEXT-None-08612"; + storage_access_oob_08613 = "VUID-vkCmdExecuteGeneratedCommandsEXT-None-08613"; + invalid_descriptor = "VUID-vkCmdExecuteGeneratedCommandsEXT-None-08114"; + } +}; + using Func = vvl::Func; static const std::map &GetGpuVuidsMap() { @@ -322,6 +332,7 @@ static const std::map gpu_vuid = { {Func::vkCmdDrawIndirectByteCountEXT, GpuVuidsCmdDrawIndirectByteCountEXT()}, {Func::vkCmdDispatchBase, GpuVuidsCmdDispatchBase()}, {Func::vkCmdDispatchBaseKHR, GpuVuidsCmdDispatchBase()}, + {Func::vkCmdExecuteGeneratedCommandsEXT, GpuVuidsCmdExecuteGeneratedCommandsEXT()}, // Used if invalid function is used {Func::Empty, GpuVuid()} }; diff --git a/layers/state_tracker/device_generated_commands_state.cpp b/layers/state_tracker/device_generated_commands_state.cpp new file mode 100644 index 00000000000..fdbd6f82b3d --- /dev/null +++ b/layers/state_tracker/device_generated_commands_state.cpp @@ -0,0 +1,78 @@ +/* Copyright (c) 2015-2024 The Khronos Group Inc. + * Copyright (c) 2015-2024 Valve Corporation + * Copyright (c) 2015-2024 LunarG, Inc. + * Copyright (C) 2015-2024 Google Inc. + * Modifications Copyright (C) 2020 Advanced Micro Devices, Inc. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "state_tracker/device_generated_commands_state.h" + +vvl::IndirectExecutionSet::IndirectExecutionSet(ValidationStateTracker &dev, VkIndirectExecutionSetEXT handle, + const VkIndirectExecutionSetCreateInfoEXT *pCreateInfo) + : StateObject(handle, kVulkanObjectTypeIndirectExecutionSetEXT), + safe_create_info(pCreateInfo), + create_info(*safe_create_info.ptr()), + is_pipeline(pCreateInfo->type == VK_INDIRECT_EXECUTION_SET_INFO_TYPE_PIPELINES_EXT), + is_shader_objects(pCreateInfo->type == VK_INDIRECT_EXECUTION_SET_INFO_TYPE_SHADER_OBJECTS_EXT) { + if (is_pipeline && pCreateInfo->info.pPipelineInfo) { + const VkIndirectExecutionSetPipelineInfoEXT &pipeline_info = *pCreateInfo->info.pPipelineInfo; + max_pipeline_count = pipeline_info.maxPipelineCount; + } else if (is_shader_objects && pCreateInfo->info.pShaderInfo) { + const VkIndirectExecutionSetShaderInfoEXT &shader_info = *pCreateInfo->info.pShaderInfo; + max_shader_count = shader_info.maxShaderCount; + } +} + +vvl::IndirectCommandsLayout::IndirectCommandsLayout(ValidationStateTracker &dev, VkIndirectCommandsLayoutEXT handle, + const VkIndirectCommandsLayoutCreateInfoEXT *pCreateInfo) + : StateObject(handle, kVulkanObjectTypeIndirectCommandsLayoutEXT), + safe_create_info(pCreateInfo), + create_info(*safe_create_info.ptr()), + // default to graphics as it is most common and has most cases + bind_point(VK_PIPELINE_BIND_POINT_GRAPHICS) { + for (uint32_t i = 0; i < pCreateInfo->tokenCount; i++) { + const VkIndirectCommandsLayoutTokenEXT &token = pCreateInfo->pTokens[i]; + switch (token.type) { + case VK_INDIRECT_COMMANDS_TOKEN_TYPE_EXECUTION_SET_EXT: + has_execution_set_token = true; + execution_set_token_shader_stage_flags = token.data.pExecutionSet->shaderStages; + break; + case VK_INDIRECT_COMMANDS_TOKEN_TYPE_VERTEX_BUFFER_EXT: + has_vertex_buffer_token = true; + break; + case VK_INDIRECT_COMMANDS_TOKEN_TYPE_DRAW_INDEXED_COUNT_EXT: + case VK_INDIRECT_COMMANDS_TOKEN_TYPE_DRAW_COUNT_EXT: + case VK_INDIRECT_COMMANDS_TOKEN_TYPE_DRAW_MESH_TASKS_COUNT_EXT: + case VK_INDIRECT_COMMANDS_TOKEN_TYPE_DRAW_MESH_TASKS_COUNT_NV_EXT: + has_draw_token = true; + has_multi_draw_count_token = true; + break; + case VK_INDIRECT_COMMANDS_TOKEN_TYPE_DRAW_INDEXED_EXT: + case VK_INDIRECT_COMMANDS_TOKEN_TYPE_DRAW_EXT: + case VK_INDIRECT_COMMANDS_TOKEN_TYPE_DRAW_MESH_TASKS_NV_EXT: + case VK_INDIRECT_COMMANDS_TOKEN_TYPE_DRAW_MESH_TASKS_EXT: + has_draw_token = true; + break; + case VK_INDIRECT_COMMANDS_TOKEN_TYPE_DISPATCH_EXT: + bind_point = VK_PIPELINE_BIND_POINT_COMPUTE; + break; + case VK_INDIRECT_COMMANDS_TOKEN_TYPE_TRACE_RAYS2_EXT: + bind_point = VK_PIPELINE_BIND_POINT_RAY_TRACING_KHR; + break; + default: + break; + } + } +} diff --git a/layers/state_tracker/device_generated_commands_state.h b/layers/state_tracker/device_generated_commands_state.h new file mode 100644 index 00000000000..4fbe65bb7de --- /dev/null +++ b/layers/state_tracker/device_generated_commands_state.h @@ -0,0 +1,71 @@ +/* Copyright (c) 2024 The Khronos Group Inc. + * Copyright (c) 2024 Valve Corporation + * Copyright (c) 2024 LunarG, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "state_tracker/state_object.h" +#include + +class ValidationStateTracker; + +namespace vvl { +class Pipeline; +struct ShaderObject; + +class IndirectExecutionSet : public StateObject { + public: + const vku::safe_VkIndirectExecutionSetCreateInfoEXT safe_create_info; + const VkIndirectExecutionSetCreateInfoEXT &create_info; + + IndirectExecutionSet(ValidationStateTracker &dev, VkIndirectExecutionSetEXT handle, + const VkIndirectExecutionSetCreateInfoEXT *pCreateInfo); + VkIndirectExecutionSetEXT VkHandle() const { return handle_.Cast(); } + + const bool is_pipeline; + const bool is_shader_objects; + + // Need to keep a smart pointer around because from spec: + // "The characteristics of initialPipeline will be used to validate all pipelines added to the set even if they are removed from + // the set or destroyed" + std::shared_ptr initial_pipeline; + std::shared_ptr initial_fragment_shader_object; + + uint32_t max_pipeline_count = 0; + uint32_t max_shader_count = 0; + VkShaderStageFlags shader_stage_flags = 0; +}; + +class IndirectCommandsLayout : public StateObject { + public: + const vku::safe_VkIndirectCommandsLayoutCreateInfoEXT safe_create_info; + const VkIndirectCommandsLayoutCreateInfoEXT &create_info; + + IndirectCommandsLayout(ValidationStateTracker &dev, VkIndirectCommandsLayoutEXT handle, + const VkIndirectCommandsLayoutCreateInfoEXT *pCreateInfo); + VkIndirectCommandsLayoutEXT VkHandle() const { return handle_.Cast(); } + + VkPipelineBindPoint bind_point; + + bool has_execution_set_token = false; // VK_INDIRECT_COMMANDS_TOKEN_TYPE_EXECUTION_SET_EXT + bool has_vertex_buffer_token = false; // VK_INDIRECT_COMMANDS_TOKEN_TYPE_VERTEX_BUFFER_EXT + bool has_draw_token = false; // VK_INDIRECT_COMMANDS_TOKEN_TYPE_DRAW_* + bool has_multi_draw_count_token = false; // VK_INDIRECT_COMMANDS_TOKEN_TYPE_DRAW_*_COUNT_* + + VkShaderStageFlags execution_set_token_shader_stage_flags = 0; +}; + +} // namespace vvl diff --git a/layers/state_tracker/pipeline_state.cpp b/layers/state_tracker/pipeline_state.cpp index 83de3ca9609..bc5ac964bf8 100644 --- a/layers/state_tracker/pipeline_state.cpp +++ b/layers/state_tracker/pipeline_state.cpp @@ -1264,6 +1264,39 @@ bool LastBound::IsAnyGraphicsShaderBound() const { IsValidShaderBound(ShaderObjectStage::MESH); } +VkShaderStageFlags LastBound::GetAllActiveBoundStages() const { + if (pipeline_state) { + return pipeline_state->active_shaders; + } + // else shader object + VkShaderStageFlags stages = 0; + if (IsValidShaderBound(ShaderObjectStage::VERTEX)) { + stages |= VK_SHADER_STAGE_VERTEX_BIT; + } + if (IsValidShaderBound(ShaderObjectStage::TESSELLATION_CONTROL)) { + stages |= VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT; + } + if (IsValidShaderBound(ShaderObjectStage::TESSELLATION_EVALUATION)) { + stages |= VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT; + } + if (IsValidShaderBound(ShaderObjectStage::GEOMETRY)) { + stages |= VK_SHADER_STAGE_GEOMETRY_BIT; + } + if (IsValidShaderBound(ShaderObjectStage::FRAGMENT)) { + stages |= VK_SHADER_STAGE_FRAGMENT_BIT; + } + if (IsValidShaderBound(ShaderObjectStage::COMPUTE)) { + stages |= VK_SHADER_STAGE_COMPUTE_BIT; + } + if (IsValidShaderBound(ShaderObjectStage::TASK)) { + stages |= VK_SHADER_STAGE_TASK_BIT_EXT; + } + if (IsValidShaderBound(ShaderObjectStage::MESH)) { + stages |= VK_SHADER_STAGE_MESH_BIT_EXT; + } + return stages; +} + bool LastBound::IsBoundSetCompatible(uint32_t set, const vvl::PipelineLayout &pipeline_layout) const { if ((set >= per_set.size()) || (set >= pipeline_layout.set_compat_ids.size())) { return false; diff --git a/layers/state_tracker/pipeline_state.h b/layers/state_tracker/pipeline_state.h index be6bd282834..d3cbe27afc8 100644 --- a/layers/state_tracker/pipeline_state.h +++ b/layers/state_tracker/pipeline_state.h @@ -26,6 +26,7 @@ #include "utils/shader_utils.h" #include "state_tracker/state_tracker.h" #include "state_tracker/shader_stage_state.h" +#include "utils/vk_layer_utils.h" // Fwd declarations -- including descriptor_set.h creates an ugly include loop namespace vvl { @@ -739,6 +740,7 @@ struct LastBound { bool IsValidShaderOrNullBound(ShaderObjectStage stage) const; std::vector GetAllBoundGraphicsShaders(); bool IsAnyGraphicsShaderBound() const; + VkShaderStageFlags GetAllActiveBoundStages() const; bool IsBoundSetCompatible(uint32_t set, const vvl::PipelineLayout &pipeline_layout) const; bool IsBoundSetCompatible(uint32_t set, const vvl::ShaderObject &shader_object_state) const; @@ -810,6 +812,19 @@ static VkPipelineBindPoint inline ConvertToPipelineBindPoint(VkShaderStageFlagBi return VK_PIPELINE_BIND_POINT_MAX_ENUM; } +static VkPipelineBindPoint inline ConvertToPipelineBindPoint(VkShaderStageFlags stage) { + // Assumes the call has checked stages have not been mixed + if (stage & kShaderStageAllGraphics) { + return VK_PIPELINE_BIND_POINT_GRAPHICS; + } else if (stage & VK_SHADER_STAGE_COMPUTE_BIT) { + return VK_PIPELINE_BIND_POINT_COMPUTE; + } else if (stage & kShaderStageAllRayTracing) { + return VK_PIPELINE_BIND_POINT_RAY_TRACING_KHR; + } else { + assert(false); + return VK_PIPELINE_BIND_POINT_MAX_ENUM; + } +} static LvlBindPoint inline ConvertToLvlBindPoint(VkShaderStageFlagBits stage) { switch (stage) { case VK_SHADER_STAGE_VERTEX_BIT: diff --git a/layers/state_tracker/shader_module.cpp b/layers/state_tracker/shader_module.cpp index e465939b546..aeca332bbed 100644 --- a/layers/state_tracker/shader_module.cpp +++ b/layers/state_tracker/shader_module.cpp @@ -446,6 +446,15 @@ bool EntryPoint::IsBuiltInWritten(spv::BuiltIn built_in, const Module& module_st return false; } +bool EntryPoint::HasBuiltIn(spv::BuiltIn built_in) const { + for (const auto* variable : built_in_variables) { + if (variable->decorations.builtin == built_in) { + return true; + } + } + return false; +} + vvl::unordered_set EntryPoint::GetAccessibleIds(const Module& module_state, EntryPoint& entrypoint) { vvl::unordered_set result_ids; diff --git a/layers/state_tracker/shader_module.h b/layers/state_tracker/shader_module.h index b34e28b99e2..78ded095e73 100644 --- a/layers/state_tracker/shader_module.h +++ b/layers/state_tracker/shader_module.h @@ -536,6 +536,8 @@ struct EntryPoint { const AccessChainVariableMap &access_chain_map, const VariableAccessMap &variable_access_map, const DebugNameMap &debug_name_map); + bool HasBuiltIn(spv::BuiltIn built_in) const; + protected: static vvl::unordered_set GetAccessibleIds(const Module &module_state, EntryPoint &entrypoint); static std::vector GetStageInterfaceVariables(const Module &module_state, const EntryPoint &entrypoint, diff --git a/layers/state_tracker/state_tracker.cpp b/layers/state_tracker/state_tracker.cpp index a88b4c14915..fce93d20d02 100644 --- a/layers/state_tracker/state_tracker.cpp +++ b/layers/state_tracker/state_tracker.cpp @@ -21,6 +21,7 @@ #include #include +#include #include #include "containers/custom_containers.h" @@ -39,6 +40,7 @@ #include "state_tracker/render_pass_state.h" #include "state_tracker/ray_tracing_state.h" #include "state_tracker/shader_object_state.h" +#include "state_tracker/device_generated_commands_state.h" #include "chassis/chassis_modification_state.h" #include "spirv-tools/optimizer.hpp" @@ -1004,6 +1006,8 @@ void ValidationStateTracker::PostCreateDevice(const VkDeviceCreateInfo *pCreateI GetPhysicalDeviceExtProperties(physical_device, dev_ext.vk_khr_maintenance7, &phys_dev_props->maintenance7_props); GetPhysicalDeviceExtProperties(physical_device, dev_ext.vk_ext_descriptor_buffer, &phys_dev_props->descriptor_buffer_props); GetPhysicalDeviceExtProperties(physical_device, dev_ext.vk_ext_descriptor_buffer, &phys_dev_props->descriptor_buffer_density_props); + GetPhysicalDeviceExtProperties(physical_device, dev_ext.vk_ext_device_generated_commands, + &phys_dev_props->device_generated_commands_props); GetPhysicalDeviceExtProperties(physical_device, dev_ext.vk_ext_host_image_copy, &phys_dev_props->host_image_copy_props); GetPhysicalDeviceExtProperties(physical_device, dev_ext.vk_ext_map_memory_placed, &phys_dev_props->map_memory_placed_props); GetPhysicalDeviceExtProperties(physical_device, dev_ext.vk_khr_pipeline_binary, &phys_dev_props->pipeline_binary_props); @@ -4800,6 +4804,20 @@ void ValidationStateTracker::PostCallRecordCmdTraceRaysIndirect2KHR(VkCommandBuf cb_state->UpdateTraceRayCmd(record_obj.location.function); } +void ValidationStateTracker::PostCallRecordCmdExecuteGeneratedCommandsEXT(VkCommandBuffer commandBuffer, VkBool32 isPreprocessed, + const VkGeneratedCommandsInfoEXT *pGeneratedCommandsInfo, + const RecordObject &record_obj) { + auto cb_state = GetWrite(commandBuffer); + const VkPipelineBindPoint bind_point = ConvertToPipelineBindPoint(pGeneratedCommandsInfo->shaderStages); + if (bind_point == VK_PIPELINE_BIND_POINT_GRAPHICS) { + cb_state->UpdateDrawCmd(record_obj.location.function); + } else if (bind_point == VK_PIPELINE_BIND_POINT_COMPUTE) { + cb_state->UpdateDispatchCmd(record_obj.location.function); + } else if (bind_point == VK_PIPELINE_BIND_POINT_RAY_TRACING_KHR) { + cb_state->UpdateTraceRayCmd(record_obj.location.function); + } +} + void ValidationStateTracker::PreCallRecordCreateShaderModule(VkDevice device, const VkShaderModuleCreateInfo *pCreateInfo, const VkAllocationCallbacks *pAllocator, VkShaderModule *pShaderModule, const RecordObject &record_obj, @@ -5695,3 +5713,56 @@ void ValidationStateTracker::PostCallRecordLatencySleepNV(VkDevice device, VkSwa semaphore_state->RetireWait(nullptr, pSleepInfo->value, record_obj.location); } } + +void ValidationStateTracker::PostCallRecordCreateIndirectExecutionSetEXT(VkDevice device, + const VkIndirectExecutionSetCreateInfoEXT *pCreateInfo, + const VkAllocationCallbacks *pAllocator, + VkIndirectExecutionSetEXT *pIndirectExecutionSet, + const RecordObject &record_obj) { + if (VK_SUCCESS != record_obj.result) return; + + std::shared_ptr indirect_execution_state = + std::make_shared(*this, *pIndirectExecutionSet, pCreateInfo); + + if (indirect_execution_state->is_pipeline && pCreateInfo->info.pPipelineInfo) { + const VkIndirectExecutionSetPipelineInfoEXT &pipeline_info = *pCreateInfo->info.pPipelineInfo; + indirect_execution_state->initial_pipeline = Get(pipeline_info.initialPipeline); + indirect_execution_state->shader_stage_flags = indirect_execution_state->initial_pipeline->active_shaders; + } else if (indirect_execution_state->is_shader_objects && pCreateInfo->info.pShaderInfo) { + const VkIndirectExecutionSetShaderInfoEXT &shader_info = *pCreateInfo->info.pShaderInfo; + for (uint32_t i = 0; i < shader_info.shaderCount; i++) { + const VkShaderEXT shader_handle = shader_info.pInitialShaders[i]; + const auto shader_object = Get(shader_handle); + ASSERT_AND_CONTINUE(shader_object); + indirect_execution_state->shader_stage_flags |= shader_object->create_info.stage; + if (shader_object->create_info.stage == VK_SHADER_STAGE_FRAGMENT_BIT) { + indirect_execution_state->initial_fragment_shader_object = shader_object; + } + } + } + + Add(std::move(indirect_execution_state)); +} + +void ValidationStateTracker::PostCallRecordDestroyIndirectExecutionSetEXT(VkDevice device, + VkIndirectExecutionSetEXT indirectExecutionSet, + const VkAllocationCallbacks *pAllocator, + const RecordObject &record_obj) { + Destroy(indirectExecutionSet); +} + +void ValidationStateTracker::PostCallRecordCreateIndirectCommandsLayoutEXT(VkDevice device, + const VkIndirectCommandsLayoutCreateInfoEXT *pCreateInfo, + const VkAllocationCallbacks *pAllocator, + VkIndirectCommandsLayoutEXT *pIndirectCommandsLayout, + const RecordObject &record_obj) { + if (VK_SUCCESS != record_obj.result) return; + Add(std::make_shared(*this, *pIndirectCommandsLayout, pCreateInfo)); +} + +void ValidationStateTracker::PostCallRecordDestroyIndirectCommandsLayoutEXT(VkDevice device, + VkIndirectCommandsLayoutEXT indirectCommandsLayout, + const VkAllocationCallbacks *pAllocator, + const RecordObject &record_obj) { + Destroy(indirectCommandsLayout); +} diff --git a/layers/state_tracker/state_tracker.h b/layers/state_tracker/state_tracker.h index 818ad47cacb..3a3a888371f 100644 --- a/layers/state_tracker/state_tracker.h +++ b/layers/state_tracker/state_tracker.h @@ -65,6 +65,8 @@ class Pipeline; class DeviceMemory; class AccelerationStructureNV; class AccelerationStructureKHR; +class IndirectExecutionSet; +class IndirectCommandsLayout; class QueryPool; struct DedicatedBinding; struct ShaderModule; @@ -247,6 +249,8 @@ VALSTATETRACK_STATE_OBJECT(VkAccelerationStructureKHR, vvl::AccelerationStructur VALSTATETRACK_STATE_OBJECT(VkSurfaceKHR, vvl::Surface) VALSTATETRACK_STATE_OBJECT(VkDisplayModeKHR, vvl::DisplayMode) VALSTATETRACK_STATE_OBJECT(VkPhysicalDevice, vvl::PhysicalDevice) +VALSTATETRACK_STATE_OBJECT(VkIndirectExecutionSetEXT, vvl::IndirectExecutionSet) +VALSTATETRACK_STATE_OBJECT(VkIndirectCommandsLayoutEXT, vvl::IndirectCommandsLayout) class ValidationStateTracker : public ValidationObject { using Func = vvl::Func; @@ -1173,6 +1177,9 @@ class ValidationStateTracker : public ValidationObject { VkDeviceAddress indirectDeviceAddress, const RecordObject& record_obj) override; void PostCallRecordCmdTraceRaysIndirect2KHR(VkCommandBuffer commandBuffer, VkDeviceAddress indirectDeviceAddress, const RecordObject& record_obj) override; + void PostCallRecordCmdExecuteGeneratedCommandsEXT(VkCommandBuffer commandBuffer, VkBool32 isPreprocessed, + const VkGeneratedCommandsInfoEXT* pGeneratedCommandsInfo, + const RecordObject& record_obj) override; void PostCallRecordCmdBeginDebugUtilsLabelEXT(VkCommandBuffer commandBuffer, const VkDebugUtilsLabelEXT* pLabelInfo, const RecordObject& record_obj) override; void PostCallRecordCmdEndDebugUtilsLabelEXT(VkCommandBuffer commandBuffer, const RecordObject& record_obj) override; @@ -1649,6 +1656,21 @@ class ValidationStateTracker : public ValidationObject { const VkDeviceSize* pOffsets, const VkDeviceSize* pSizes, const RecordObject& record_obj) override; + void PostCallRecordCreateIndirectExecutionSetEXT(VkDevice device, const VkIndirectExecutionSetCreateInfoEXT* pCreateInfo, + const VkAllocationCallbacks* pAllocator, + VkIndirectExecutionSetEXT* pIndirectExecutionSet, + const RecordObject& record_obj) override; + void PostCallRecordDestroyIndirectExecutionSetEXT(VkDevice device, VkIndirectExecutionSetEXT indirectExecutionSet, + const VkAllocationCallbacks* pAllocator, + const RecordObject& record_obj) override; + void PostCallRecordCreateIndirectCommandsLayoutEXT(VkDevice device, const VkIndirectCommandsLayoutCreateInfoEXT* pCreateInfo, + const VkAllocationCallbacks* pAllocator, + VkIndirectCommandsLayoutEXT* pIndirectCommandsLayout, + const RecordObject& record_ob) override; + void PostCallRecordDestroyIndirectCommandsLayoutEXT(VkDevice device, VkIndirectCommandsLayoutEXT indirectCommandsLayout, + const VkAllocationCallbacks* pAllocator, + const RecordObject& record_obj) override; + template void GetPhysicalDeviceExtProperties(VkPhysicalDevice gpu, ExtEnabled enabled, ExtProp* ext_prop) { assert(ext_prop); @@ -1825,6 +1847,7 @@ class ValidationStateTracker : public ValidationObject { VkPhysicalDeviceNestedCommandBufferPropertiesEXT nested_command_buffer_props; VkPhysicalDeviceDescriptorBufferPropertiesEXT descriptor_buffer_props; VkPhysicalDeviceDescriptorBufferDensityMapPropertiesEXT descriptor_buffer_density_props; + VkPhysicalDeviceDeviceGeneratedCommandsPropertiesEXT device_generated_commands_props; VkPhysicalDeviceHostImageCopyPropertiesEXT host_image_copy_props; VkPhysicalDevicePipelineBinaryPropertiesKHR pipeline_binary_props; VkPhysicalDeviceMapMemoryPlacedPropertiesEXT map_memory_placed_props; @@ -1924,6 +1947,8 @@ class ValidationStateTracker : public ValidationObject { VALSTATETRACK_MAP_AND_TRAITS(VkVideoSessionKHR, vvl::VideoSession, video_session_map_) VALSTATETRACK_MAP_AND_TRAITS(VkVideoSessionParametersKHR, vvl::VideoSessionParameters, video_session_parameters_map_) VALSTATETRACK_MAP_AND_TRAITS(VkAccelerationStructureKHR, vvl::AccelerationStructureKHR, acceleration_structure_khr_map_) + VALSTATETRACK_MAP_AND_TRAITS(VkIndirectExecutionSetEXT, vvl::IndirectExecutionSet, indirect_execution_set_ext_map_) + VALSTATETRACK_MAP_AND_TRAITS(VkIndirectCommandsLayoutEXT, vvl::IndirectCommandsLayout, indirect_commands_layout_ext_map_) VALSTATETRACK_MAP_AND_TRAITS_INSTANCE_SCOPE(VkSurfaceKHR, vvl::Surface, surface_map_) VALSTATETRACK_MAP_AND_TRAITS_INSTANCE_SCOPE(VkDisplayModeKHR, vvl::DisplayMode, display_mode_map_) VALSTATETRACK_MAP_AND_TRAITS_INSTANCE_SCOPE(VkPhysicalDevice, vvl::PhysicalDevice, physical_device_map_) diff --git a/layers/stateless/sl_device_generated_commands.cpp b/layers/stateless/sl_device_generated_commands.cpp new file mode 100644 index 00000000000..90044da076b --- /dev/null +++ b/layers/stateless/sl_device_generated_commands.cpp @@ -0,0 +1,546 @@ +/* Copyright (c) 2015-2024 The Khronos Group Inc. + * Copyright (c) 2015-2024 Valve Corporation + * Copyright (c) 2015-2024 LunarG, Inc. + * Copyright (C) 2015-2024 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "stateless/stateless_validation.h" +#include "generated/enum_flag_bits.h" +#include "utils/vk_layer_utils.h" + +static inline bool IsMeshCommand(VkIndirectCommandsTokenTypeEXT type) { + return IsValueIn( + type, + {VK_INDIRECT_COMMANDS_TOKEN_TYPE_DRAW_MESH_TASKS_EXT, VK_INDIRECT_COMMANDS_TOKEN_TYPE_DRAW_MESH_TASKS_COUNT_EXT, + VK_INDIRECT_COMMANDS_TOKEN_TYPE_DRAW_MESH_TASKS_NV_EXT, VK_INDIRECT_COMMANDS_TOKEN_TYPE_DRAW_MESH_TASKS_COUNT_NV_EXT}); +} + +static inline bool IsComputeCommand(VkIndirectCommandsTokenTypeEXT type) { + return IsValueIn(type, {VK_INDIRECT_COMMANDS_TOKEN_TYPE_DISPATCH_EXT, VK_INDIRECT_COMMANDS_TOKEN_TYPE_EXECUTION_SET_EXT, + VK_INDIRECT_COMMANDS_TOKEN_TYPE_PUSH_CONSTANT_EXT, VK_INDIRECT_COMMANDS_TOKEN_TYPE_SEQUENCE_INDEX_EXT}); +} + +static inline bool IsRayTracingCommand(VkIndirectCommandsTokenTypeEXT type) { + return IsValueIn(type, {VK_INDIRECT_COMMANDS_TOKEN_TYPE_TRACE_RAYS2_EXT, VK_INDIRECT_COMMANDS_TOKEN_TYPE_EXECUTION_SET_EXT, + VK_INDIRECT_COMMANDS_TOKEN_TYPE_PUSH_CONSTANT_EXT, VK_INDIRECT_COMMANDS_TOKEN_TYPE_SEQUENCE_INDEX_EXT}); +} + +bool StatelessValidation::ValidateIndirectExecutionSetPipelineInfo(const VkIndirectExecutionSetPipelineInfoEXT& pipeline_info, + const Location& pipeline_info_loc) const { + bool skip = false; + + const auto& props = phys_dev_ext_props.device_generated_commands_props; + if (pipeline_info.maxPipelineCount == 0) { + skip |= LogError("VUID-VkIndirectExecutionSetPipelineInfoEXT-maxPipelineCount-11018", device, + pipeline_info_loc.dot(Field::maxPipelineCount), "is zero."); + } else if (pipeline_info.maxPipelineCount > props.maxIndirectPipelineCount) { + skip |= LogError("VUID-VkIndirectExecutionSetPipelineInfoEXT-maxPipelineCount-11018", device, + pipeline_info_loc.dot(Field::maxPipelineCount), + "(%" PRIu32 ") is larger than maxIndirectPipelineCount (%" PRIu32 ").", pipeline_info.maxPipelineCount, + props.maxIndirectPipelineCount); + } + + // implicit checks + skip |= ValidateRequiredHandle(pipeline_info_loc.dot(Field::initialPipeline), pipeline_info.initialPipeline); + + return skip; +} + +bool StatelessValidation::ValidateIndirectExecutionSetShaderInfo(const VkIndirectExecutionSetShaderInfoEXT& shader_info, + const Location& shader_info_loc) const { + bool skip = false; + + const auto& props = phys_dev_ext_props.device_generated_commands_props; + if (shader_info.maxShaderCount == 0) { + skip |= LogError("VUID-VkIndirectExecutionSetShaderInfoEXT-maxShaderCount-11021", device, + shader_info_loc.dot(Field::maxShaderCount), "is zero."); + } else if (shader_info.maxShaderCount > props.maxIndirectShaderObjectCount) { + skip |= LogError("VUID-VkIndirectExecutionSetShaderInfoEXT-maxShaderCount-11022", device, + shader_info_loc.dot(Field::maxShaderCount), + "(%" PRIu32 ") is larger than maxIndirectShaderObjectCount (%" PRIu32 ").", shader_info.maxShaderCount, + props.maxIndirectShaderObjectCount); + } else if (shader_info.maxShaderCount < shader_info.shaderCount) { + skip |= LogError("VUID-VkIndirectExecutionSetShaderInfoEXT-maxShaderCount-11036", device, + shader_info_loc.dot(Field::maxShaderCount), "(%" PRIu32 ") is less than shaderCount (%" PRIu32 ").", + shader_info.maxShaderCount, shader_info.shaderCount); + } + + // implicit checks + skip |= ValidateStructTypeArray(shader_info_loc.dot(Field::shaderCount), shader_info_loc.dot(Field::pSetLayoutInfos), + shader_info.shaderCount, shader_info.pSetLayoutInfos, + VK_STRUCTURE_TYPE_INDIRECT_EXECUTION_SET_SHADER_LAYOUT_INFO_EXT, true, false, + "VUID-VkIndirectExecutionSetShaderLayoutInfoEXT-sType-sType", + "VUID-VkIndirectExecutionSetShaderInfoEXT-pSetLayoutInfos-parameter", + "VUID-VkIndirectExecutionSetShaderInfoEXT-shaderCount-arraylength"); + + // Validate shaderCount once above + skip |= ValidateArray(shader_info_loc.dot(Field::shaderCount), shader_info_loc.dot(Field::pInitialShaders), + shader_info.shaderCount, &shader_info.pInitialShaders, false, true, kVUIDUndefined, + "VUID-VkIndirectExecutionSetShaderInfoEXT-pInitialShaders-parameter"); + skip |= ValidateArray(shader_info_loc.dot(Field::pushConstantRangeCount), shader_info_loc.dot(Field::pPushConstantRanges), + shader_info.pushConstantRangeCount, &shader_info.pPushConstantRanges, false, true, kVUIDUndefined, + "VUID-VkIndirectExecutionSetShaderInfoEXT-pPushConstantRanges-parameter"); + + if (shader_info.pPushConstantRanges != nullptr) { + for (uint32_t i = 0; i < shader_info.pushConstantRangeCount; ++i) { + const Location pc_range_loc = shader_info_loc.dot(Field::pPushConstantRanges, i); + skip |= ValidateFlags(pc_range_loc.dot(Field::stageFlags), vvl::FlagBitmask::VkShaderStageFlagBits, + AllVkShaderStageFlagBits, shader_info.pPushConstantRanges[i].stageFlags, kRequiredFlags, + VK_NULL_HANDLE, "VUID-VkPushConstantRange-stageFlags-parameter", + "VUID-VkPushConstantRange-stageFlags-requiredbitmask"); + } + } + + return skip; +} + +bool StatelessValidation::manual_PreCallValidateCreateIndirectExecutionSetEXT( + VkDevice device, const VkIndirectExecutionSetCreateInfoEXT* pCreateInfo, const VkAllocationCallbacks* pAllocator, + VkIndirectExecutionSetEXT* pIndirectExecutionSet, const ErrorObject& error_obj) const { + bool skip = false; + if (!enabled_features.deviceGeneratedCommands) { + skip |= LogError("VUID-vkCreateIndirectExecutionSetEXT-deviceGeneratedCommands-11013", device, error_obj.location, + "deviceGeneratedCommands feature was not enabled."); + } + + const Location create_info_loc = error_obj.location.dot(Field::pCreateInfo); + const Location info_loc = create_info_loc.dot(Field::info); + + if (pCreateInfo->type == VK_INDIRECT_EXECUTION_SET_INFO_TYPE_PIPELINES_EXT) { + if (!pCreateInfo->info.pPipelineInfo) { + skip |= LogError("VUID-VkIndirectExecutionSetCreateInfoEXT-pPipelineInfo-parameter", device, + create_info_loc.dot(Field::type), + "is VK_INDIRECT_EXECUTION_SET_INFO_TYPE_PIPELINES_EXT, but info.pPipelineInfo is null."); + } else { + skip |= ValidateIndirectExecutionSetPipelineInfo(*pCreateInfo->info.pPipelineInfo, info_loc.dot(Field::pPipelineInfo)); + } + } else if (pCreateInfo->type == VK_INDIRECT_EXECUTION_SET_INFO_TYPE_SHADER_OBJECTS_EXT) { + if (!enabled_features.shaderObject) { + skip |= + LogError("VUID-VkIndirectExecutionSetCreateInfoEXT-maxIndirectShaderObjectCount-11014", device, + create_info_loc.dot(Field::type), + "is VK_INDIRECT_EXECUTION_SET_INFO_TYPE_SHADER_OBJECTS_EXT but the shaderObject feature was not enabled."); + } else if (phys_dev_ext_props.device_generated_commands_props.maxIndirectShaderObjectCount == 0) { + skip |= LogError("VUID-VkIndirectExecutionSetCreateInfoEXT-maxIndirectShaderObjectCount-11014", device, + create_info_loc.dot(Field::type), + "is VK_INDIRECT_EXECUTION_SET_INFO_TYPE_SHADER_OBJECTS_EXT but maxIndirectShaderObjectCount is zero " + "(so is no support for device generated commands via shader object)."); + } + + if (!pCreateInfo->info.pShaderInfo) { + skip |= + LogError("VUID-VkIndirectExecutionSetCreateInfoEXT-pShaderInfo-parameter", device, create_info_loc.dot(Field::type), + "is VK_INDIRECT_EXECUTION_SET_INFO_TYPE_SHADER_OBJECTS_EXT, but info.pShaderInfo is null."); + } else { + skip |= ValidateIndirectExecutionSetShaderInfo(*pCreateInfo->info.pShaderInfo, info_loc.dot(Field::pShaderInfo)); + } + } + + return skip; +}; + +bool StatelessValidation::ValidateIndirectCommandsPushConstantToken( + const VkIndirectCommandsPushConstantTokenEXT& push_constant_token, VkIndirectCommandsTokenTypeEXT token_type, + const Location& push_constant_token_loc) const { + bool skip = false; + skip |= ValidateFlags(push_constant_token_loc.dot(Field::updateRange).dot(Field::stageFlags), + vvl::FlagBitmask::VkShaderStageFlagBits, AllVkShaderStageFlagBits, + push_constant_token.updateRange.stageFlags, kRequiredFlags, VK_NULL_HANDLE, + "VUID-VkPushConstantRange-stageFlags-parameter", "VUID-VkPushConstantRange-stageFlags-requiredbitmask"); + + if (token_type == VK_INDIRECT_COMMANDS_TOKEN_TYPE_SEQUENCE_INDEX_EXT && push_constant_token.updateRange.size != 4) { + skip |= LogError("VUID-VkIndirectCommandsPushConstantTokenEXT-size-11133", device, + push_constant_token_loc.dot(Field::updateRange).dot(Field::size), + "is %" PRIu32 ", but needs to be 4 when using VK_INDIRECT_COMMANDS_TOKEN_TYPE_SEQUENCE_INDEX_EXT.", + push_constant_token.updateRange.size); + } + + return skip; +}; + +bool StatelessValidation::ValidateIndirectCommandsIndexBufferToken(const VkIndirectCommandsIndexBufferTokenEXT& index_buffer_token, + const Location& index_buffer_token_loc) const { + bool skip = false; + skip |= ValidateFlags(index_buffer_token_loc.dot(Field::mode), vvl::FlagBitmask::VkIndirectCommandsInputModeFlagBitsEXT, + AllVkIndirectCommandsInputModeFlagBitsEXT, index_buffer_token.mode, kRequiredSingleBit, VK_NULL_HANDLE, + "VUID-VkIndirectCommandsIndexBufferTokenEXT-mode-parameter", + "VUID-VkIndirectCommandsIndexBufferTokenEXT-mode-11135"); + + const auto& props = phys_dev_ext_props.device_generated_commands_props; + if ((index_buffer_token.mode & props.supportedIndirectCommandsInputModes) == 0) { + skip |= LogError("VUID-VkIndirectCommandsIndexBufferTokenEXT-mode-11136", device, index_buffer_token_loc.dot(Field::mode), + "is %s, but that is not supported by supportedIndirectCommandsInputModes (%s).", + string_VkIndirectCommandsInputModeFlagBitsEXT(index_buffer_token.mode), + string_VkIndirectCommandsInputModeFlagsEXT(props.supportedIndirectCommandsInputModes).c_str()); + } + return skip; +}; + +bool StatelessValidation::ValidateIndirectCommandsExecutionSetToken(const VkIndirectCommandsExecutionSetTokenEXT& exe_set_token, + const Location& exe_set_token_loc) const { + bool skip = false; + skip |= ValidateRangedEnum(exe_set_token_loc.dot(Field::type), vvl::Enum::VkIndirectExecutionSetInfoTypeEXT, exe_set_token.type, + "VUID-VkIndirectCommandsExecutionSetTokenEXT-type-parameter"); + + skip |= ValidateFlags(exe_set_token_loc.dot(Field::shaderStages), vvl::FlagBitmask::VkShaderStageFlagBits, + AllVkShaderStageFlagBits, exe_set_token.shaderStages, kRequiredFlags, VK_NULL_HANDLE, + "VUID-VkIndirectCommandsExecutionSetTokenEXT-shaderStages-parameter", + "VUID-VkIndirectCommandsExecutionSetTokenEXT-shaderStages-requiredbitmask"); + + const auto& props = phys_dev_ext_props.device_generated_commands_props; + if ((exe_set_token.shaderStages & (props.supportedIndirectCommandsShaderStagesPipelineBinding | + props.supportedIndirectCommandsShaderStagesShaderBinding)) == 0) { + skip |= LogError("VUID-VkIndirectCommandsExecutionSetTokenEXT-shaderStages-11137", device, + exe_set_token_loc.dot(Field::shaderStages), + "is %s, but that is not supported by supportedIndirectCommandsShaderStagesPipelineBinding (%s) or " + "supportedIndirectCommandsShaderStagesShaderBinding (%s).", + string_VkShaderStageFlags(exe_set_token.shaderStages).c_str(), + string_VkShaderStageFlags(props.supportedIndirectCommandsShaderStagesPipelineBinding).c_str(), + string_VkShaderStageFlags(props.supportedIndirectCommandsShaderStagesShaderBinding).c_str()); + } + + return skip; +}; + +bool StatelessValidation::ValidateIndirectCommandsLayoutToken(const VkIndirectCommandsLayoutTokenEXT& token, + const Location& token_loc) const { + bool skip = false; + + const Location data_loc = token_loc.dot(Field::data); + switch (token.type) { + case VK_INDIRECT_COMMANDS_TOKEN_TYPE_PUSH_CONSTANT_EXT: + case VK_INDIRECT_COMMANDS_TOKEN_TYPE_SEQUENCE_INDEX_EXT: + if (!token.data.pPushConstant) { + skip |= + LogError("VUID-VkIndirectCommandsLayoutTokenEXT-pPushConstant-parameter", device, token_loc.dot(Field::type), + "is %s, but data.pPushConstant is null.", string_VkIndirectCommandsTokenTypeEXT(token.type)); + } else { + skip |= ValidateIndirectCommandsPushConstantToken(*token.data.pPushConstant, token.type, + data_loc.dot(Field::pPushConstant)); + } + break; + case VK_INDIRECT_COMMANDS_TOKEN_TYPE_VERTEX_BUFFER_EXT: + if (!token.data.pVertexBuffer) { + skip |= + LogError("VUID-VkIndirectCommandsLayoutTokenEXT-pVertexBuffer-parameter", device, token_loc.dot(Field::type), + "is VK_INDIRECT_COMMANDS_TOKEN_TYPE_VERTEX_BUFFER_EXT, but data.pVertexBuffer is null."); + } + break; + case VK_INDIRECT_COMMANDS_TOKEN_TYPE_INDEX_BUFFER_EXT: + if (!token.data.pIndexBuffer) { + skip |= LogError("VUID-VkIndirectCommandsLayoutTokenEXT-pIndexBuffer-parameter", device, token_loc.dot(Field::type), + "is VK_INDIRECT_COMMANDS_TOKEN_TYPE_INDEX_BUFFER_EXT, but data.pIndexBuffer is null."); + } else { + skip |= ValidateIndirectCommandsIndexBufferToken(*token.data.pIndexBuffer, data_loc.dot(Field::pIndexBuffer)); + } + break; + case VK_INDIRECT_COMMANDS_TOKEN_TYPE_EXECUTION_SET_EXT: + if (!token.data.pExecutionSet) { + skip |= + LogError("VUID-VkIndirectCommandsLayoutTokenEXT-pExecutionSet-parameter", device, token_loc.dot(Field::type), + "is VK_INDIRECT_COMMANDS_TOKEN_TYPE_EXECUTION_SET_EXT, but data.pExecutionSet is null."); + } else { + skip |= ValidateIndirectCommandsExecutionSetToken(*token.data.pExecutionSet, data_loc.dot(Field::pExecutionSet)); + } + break; + default: + break; + } + + const auto& props = phys_dev_ext_props.device_generated_commands_props; + if (IsMeshCommand(token.type)) { + if (!enabled_features.meshShader || !enabled_features.taskShader) { + skip |= LogError("VUID-VkIndirectCommandsLayoutTokenEXT-meshShader-11126", device, token_loc.dot(Field::type), + "is %s but meshShader and taskShader features are disabled.", + string_VkIndirectCommandsTokenTypeEXT(token.type)); + } else if (!props.deviceGeneratedCommandsMultiDrawIndirectCount) { + if (token.type == VK_INDIRECT_COMMANDS_TOKEN_TYPE_DRAW_MESH_TASKS_COUNT_EXT) { + skip |= LogError("VUID-VkIndirectCommandsLayoutTokenEXT-deviceGeneratedCommandsMultiDrawIndirectCount-11130", + device, token_loc.dot(Field::type), + "is VK_INDIRECT_COMMANDS_TOKEN_TYPE_DRAW_MESH_TASKS_COUNT_EXT but " + "deviceGeneratedCommandsMultiDrawIndirectCount is not supported."); + } else if (token.type == VK_INDIRECT_COMMANDS_TOKEN_TYPE_DRAW_MESH_TASKS_COUNT_NV_EXT) { + skip |= LogError("VUID-VkIndirectCommandsLayoutTokenEXT-deviceGeneratedCommandsMultiDrawIndirectCount-11131", + device, token_loc.dot(Field::type), + "is VK_INDIRECT_COMMANDS_TOKEN_TYPE_DRAW_MESH_TASKS_COUNT_NV_EXT but " + "deviceGeneratedCommandsMultiDrawIndirectCount is not supported."); + } + } + } else if (token.type == VK_INDIRECT_COMMANDS_TOKEN_TYPE_DRAW_INDEXED_COUNT_EXT || + token.type == VK_INDIRECT_COMMANDS_TOKEN_TYPE_DRAW_COUNT_EXT) { + if (!props.deviceGeneratedCommandsMultiDrawIndirectCount) { + skip |= + LogError("VUID-VkIndirectCommandsLayoutTokenEXT-deviceGeneratedCommandsMultiDrawIndirectCount-11129", device, + token_loc.dot(Field::type), "is %s but deviceGeneratedCommandsMultiDrawIndirectCount is not supported.", + string_VkIndirectCommandsTokenTypeEXT(token.type)); + } + } else if (token.type == VK_INDIRECT_COMMANDS_TOKEN_TYPE_TRACE_RAYS2_EXT && !enabled_features.rayTracingMaintenance1) { + skip |= LogError("VUID-VkIndirectCommandsLayoutTokenEXT-rayTracingMaintenance1-11128", device, token_loc.dot(Field::type), + "is VK_INDIRECT_COMMANDS_TOKEN_TYPE_TRACE_RAYS2_EXT but rayTracingMaintenance1 was not enabled."); + } + + // offset is ignored for SEQUENCE_INDEX + if (token.type != VK_INDIRECT_COMMANDS_TOKEN_TYPE_SEQUENCE_INDEX_EXT) { + if (token.offset > props.maxIndirectCommandsTokenOffset) { + skip |= LogError("VUID-VkIndirectCommandsLayoutTokenEXT-offset-11124", device, token_loc.dot(Field::offset), + "(%" PRIu32 ") is greater than maxIndirectCommandsTokenOffset (%" PRIu32 ")", token.offset, + props.maxIndirectCommandsTokenOffset); + } + if (SafeModulo(token.offset, 4) != 0) { + skip |= LogError("VUID-VkIndirectCommandsLayoutTokenEXT-offset-11125", device, token_loc.dot(Field::offset), + "(%" PRIu32 ") is not aligned to 4.", token.offset); + } + } + + return skip; +} + +bool StatelessValidation::ValidateIndirectCommandsLayoutStage(const VkIndirectCommandsLayoutTokenEXT& token, + const Location& token_loc, VkShaderStageFlags shader_stages, + bool has_stage_graphics, bool has_stage_compute, + bool has_stage_ray_tracing, bool has_stage_mesh) const { + bool skip = false; + // Check if type has supported shaderStage + if (!has_stage_graphics && + IsValueIn( + token.type, + {VK_INDIRECT_COMMANDS_TOKEN_TYPE_INDEX_BUFFER_EXT, VK_INDIRECT_COMMANDS_TOKEN_TYPE_VERTEX_BUFFER_EXT, + VK_INDIRECT_COMMANDS_TOKEN_TYPE_DRAW_INDEXED_EXT, VK_INDIRECT_COMMANDS_TOKEN_TYPE_DRAW_EXT, + VK_INDIRECT_COMMANDS_TOKEN_TYPE_DRAW_INDEXED_COUNT_EXT, VK_INDIRECT_COMMANDS_TOKEN_TYPE_DRAW_COUNT_EXT, + VK_INDIRECT_COMMANDS_TOKEN_TYPE_DRAW_MESH_TASKS_NV_EXT, VK_INDIRECT_COMMANDS_TOKEN_TYPE_DRAW_MESH_TASKS_COUNT_NV_EXT, + VK_INDIRECT_COMMANDS_TOKEN_TYPE_DRAW_MESH_TASKS_EXT, VK_INDIRECT_COMMANDS_TOKEN_TYPE_DRAW_MESH_TASKS_COUNT_EXT})) { + skip |= LogError("VUID-VkIndirectCommandsLayoutCreateInfoEXT-pTokens-11104", device, token_loc.dot(Field::type), + "is %s, but shaderStages (%s) doesn't contain graphics stages.", + string_VkIndirectCommandsTokenTypeEXT(token.type), string_VkShaderStageFlags(shader_stages).c_str()); + } + if (!has_stage_compute && token.type == VK_INDIRECT_COMMANDS_TOKEN_TYPE_DISPATCH_EXT) { + skip |= LogError("VUID-VkIndirectCommandsLayoutCreateInfoEXT-pTokens-11105", device, token_loc.dot(Field::type), + "is VK_INDIRECT_COMMANDS_TOKEN_TYPE_DISPATCH_EXT, but shaderStages (%s) doesn't contain " + "VK_SHADER_STAGE_COMPUTE_BIT.", + string_VkShaderStageFlags(shader_stages).c_str()); + } + if (!has_stage_mesh && IsValueIn(token.type, {VK_INDIRECT_COMMANDS_TOKEN_TYPE_DRAW_MESH_TASKS_EXT, + VK_INDIRECT_COMMANDS_TOKEN_TYPE_DRAW_MESH_TASKS_COUNT_EXT})) { + skip |= LogError("VUID-VkIndirectCommandsLayoutCreateInfoEXT-pTokens-11106", device, token_loc.dot(Field::type), + "is %s, but shaderStages (%s) doesn't contain mesh stages.", + string_VkIndirectCommandsTokenTypeEXT(token.type), string_VkShaderStageFlags(shader_stages).c_str()); + } + if (!has_stage_mesh && IsValueIn(token.type, {VK_INDIRECT_COMMANDS_TOKEN_TYPE_DRAW_MESH_TASKS_NV_EXT, + VK_INDIRECT_COMMANDS_TOKEN_TYPE_DRAW_MESH_TASKS_COUNT_NV_EXT})) { + skip |= LogError("VUID-VkIndirectCommandsLayoutCreateInfoEXT-pTokens-11107", device, token_loc.dot(Field::type), + "is %s, but shaderStages (%s) doesn't contain mesh stages.", + string_VkIndirectCommandsTokenTypeEXT(token.type), string_VkShaderStageFlags(shader_stages).c_str()); + } + if (!has_stage_ray_tracing && token.type == VK_INDIRECT_COMMANDS_TOKEN_TYPE_TRACE_RAYS2_EXT) { + skip |= LogError( + "VUID-VkIndirectCommandsLayoutCreateInfoEXT-pTokens-11108", device, token_loc.dot(Field::type), + "is VK_INDIRECT_COMMANDS_TOKEN_TYPE_TRACE_RAYS2_EXT, but shaderStages (%s) doesn't contain ray tracing stages.", + string_VkShaderStageFlags(shader_stages).c_str()); + } + + // Check if certain stages, it is limited to certain tokens + if (has_stage_graphics && + IsValueIn(token.type, {VK_INDIRECT_COMMANDS_TOKEN_TYPE_TRACE_RAYS2_EXT, VK_INDIRECT_COMMANDS_TOKEN_TYPE_DISPATCH_EXT})) { + skip |= LogError("VUID-VkIndirectCommandsLayoutCreateInfoEXT-shaderStages-11109", device, token_loc.dot(Field::type), + "is %s but shaderStages is graphics (%s).", string_VkIndirectCommandsTokenTypeEXT(token.type), + string_VkShaderStageFlags(shader_stages).c_str()); + } else if (has_stage_compute && !IsComputeCommand(token.type)) { + skip |= + LogError("VUID-VkIndirectCommandsLayoutCreateInfoEXT-shaderStages-11110", device, token_loc.dot(Field::type), + "is %s but shaderStages is VK_SHADER_STAGE_COMPUTE_BIT.", string_VkIndirectCommandsTokenTypeEXT(token.type)); + } else if (has_stage_ray_tracing && !IsRayTracingCommand(token.type)) { + skip |= LogError("VUID-VkIndirectCommandsLayoutCreateInfoEXT-shaderStages-11111", device, token_loc.dot(Field::type), + "is %s but shaderStages is ray tracing (%s).", string_VkIndirectCommandsTokenTypeEXT(token.type), + string_VkShaderStageFlags(shader_stages).c_str()); + } + + return skip; +} + +bool StatelessValidation::manual_PreCallValidateCreateIndirectCommandsLayoutEXT( + VkDevice device, const VkIndirectCommandsLayoutCreateInfoEXT* pCreateInfo, const VkAllocationCallbacks* pAllocator, + VkIndirectCommandsLayoutEXT* pIndirectCommandsLayout, const ErrorObject& error_obj) const { + bool skip = false; + + if (!enabled_features.deviceGeneratedCommands) { + skip |= LogError("VUID-vkCreateIndirectCommandsLayoutEXT-deviceGeneratedCommands-11089", device, error_obj.location, + "deviceGeneratedCommands feature was not enabled."); + } + + const Location create_info_loc = error_obj.location.dot(Field::pCreateInfo); + const auto& props = phys_dev_ext_props.device_generated_commands_props; + if (pCreateInfo->indirectStride > props.maxIndirectCommandsIndirectStride) { + skip |= LogError("VUID-VkIndirectCommandsLayoutCreateInfoEXT-indirectStride-11090", device, + create_info_loc.dot(Field::indirectStride), + "(%" PRIu32 ") is greater than maxIndirectCommandsIndirectStride (%" PRIu32 ")", + pCreateInfo->indirectStride, props.maxIndirectCommandsIndirectStride); + } + const VkShaderStageFlags shader_stages = pCreateInfo->shaderStages; + if (shader_stages & ~props.supportedIndirectCommandsShaderStages) { + skip |= LogError("VUID-VkIndirectCommandsLayoutCreateInfoEXT-shaderStages-11091", device, + create_info_loc.dot(Field::shaderStages), + "is %s which contain stages not found in supportedIndirectCommandsShaderStages (%s)", + string_VkShaderStageFlags(shader_stages).c_str(), + string_VkShaderStageFlags(props.supportedIndirectCommandsShaderStages).c_str()); + } + if (pCreateInfo->tokenCount > props.maxIndirectCommandsTokenCount) { + skip |= + LogError("VUID-VkIndirectCommandsLayoutCreateInfoEXT-tokenCount-11092", device, create_info_loc.dot(Field::tokenCount), + "(%" PRIu32 ") is greater than maxIndirectCommandsTokenCount (%" PRIu32 ")", pCreateInfo->tokenCount, + props.maxIndirectCommandsTokenCount); + } + + if (shader_stages & VK_SHADER_STAGE_FRAGMENT_BIT) { + if (!(shader_stages & (VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_MESH_BIT_EXT))) { + skip |= LogError("VUID-VkIndirectCommandsLayoutCreateInfoEXT-shaderStages-11113", device, + create_info_loc.dot(Field::shaderStages), + "(%s) contains VK_SHADER_STAGE_FRAGMENT_BIT but does not contains VK_SHADER_STAGE_VERTEX_BIT or " + "VK_SHADER_STAGE_MESH_BIT_EXT.", + string_VkShaderStageFlags(shader_stages).c_str()); + } + } + + const bool has_stage_graphics = shader_stages & kShaderStageAllGraphics; + const bool has_stage_compute = shader_stages & VK_SHADER_STAGE_COMPUTE_BIT; + const bool has_stage_ray_tracing = shader_stages & kShaderStageAllRayTracing; + const bool has_stage_mesh = shader_stages & VK_SHADER_STAGE_MESH_BIT_EXT; // also VK_SHADER_STAGE_MESH_BIT_NV + { + // Mesh can/will have a fragment stage in it + const VkShaderStageFlags all_mesh_stages = + VK_SHADER_STAGE_TASK_BIT_EXT | VK_SHADER_STAGE_MESH_BIT_EXT | VK_SHADER_STAGE_FRAGMENT_BIT; + + bool valid_stages = true; + if (has_stage_graphics && ((shader_stages | kShaderStageAllGraphics) != kShaderStageAllGraphics)) { + valid_stages = false; + } else if (has_stage_compute && ((shader_stages | VK_SHADER_STAGE_COMPUTE_BIT) != VK_SHADER_STAGE_COMPUTE_BIT)) { + valid_stages = false; + } else if (has_stage_ray_tracing && ((shader_stages | kShaderStageAllRayTracing) != kShaderStageAllRayTracing)) { + valid_stages = false; + } else if (has_stage_mesh && ((shader_stages | all_mesh_stages) != all_mesh_stages)) { + valid_stages = false; + } + if (!valid_stages) { + skip |= LogError("VUID-VkIndirectCommandsLayoutCreateInfoEXT-shaderStages-11112", device, + create_info_loc.dot(Field::shaderStages), + "is %s but you can't mix graphics/compute/mesh/rayTracing stages with each other.", + string_VkShaderStageFlags(shader_stages).c_str()); + } + } + + ASSERT_AND_RETURN_SKIP(pCreateInfo->pTokens); + + uint32_t current_token_offset = 0; + + for (uint32_t i = 0; i < pCreateInfo->tokenCount; ++i) { + const Location token_loc = create_info_loc.dot(Field::pTokens, i); + const auto& token = pCreateInfo->pTokens[i]; + skip |= ValidateIndirectCommandsLayoutToken(token, token_loc); + skip |= ValidateIndirectCommandsLayoutStage(token, token_loc, shader_stages, has_stage_graphics, has_stage_compute, + has_stage_ray_tracing, has_stage_mesh); + + if (token.type != VK_INDIRECT_COMMANDS_TOKEN_TYPE_SEQUENCE_INDEX_EXT) { + if (token.offset < current_token_offset) { + skip |= LogError("VUID-VkIndirectCommandsLayoutCreateInfoEXT-pTokens-11103", device, token_loc.dot(Field::offset), + "(%" PRIu32 ") is less than pTokens[%" PRIu32 "].offset (%" PRIu32 ")", token.offset, i - 1, + pCreateInfo->pTokens[i - 1].offset); + } + // is a monotonic increasing value so can give previous value + current_token_offset = token.offset; + } + } + + return skip; +} + +bool StatelessValidation::ValidateGeneratedCommandsInfo(VkCommandBuffer command_buffer, + const VkGeneratedCommandsInfoEXT& generated_commands_info, + const Location& info_loc) const { + bool skip = false; + + if (generated_commands_info.sequenceCountAddress != 0) { + if (SafeModulo(generated_commands_info.sequenceCountAddress, 4) != 0) { + skip |= LogError("VUID-VkGeneratedCommandsInfoEXT-sequenceCountAddress-11073", command_buffer, + info_loc.dot(Field::sequenceCountAddress), "(%" PRIuLEAST64 ") is not aligned to 4.", + generated_commands_info.sequenceCountAddress); + } + } + + if (generated_commands_info.indirectAddress == 0) { + skip |= LogError("VUID-VkGeneratedCommandsInfoEXT-indirectAddress-11076", command_buffer, + info_loc.dot(Field::indirectAddress), "is NULL."); + } else if (SafeModulo(generated_commands_info.indirectAddress, 4) != 0) { + skip |= + LogError("VUID-VkGeneratedCommandsInfoEXT-indirectAddress-11074", command_buffer, info_loc.dot(Field::indirectAddress), + "(%" PRIuLEAST64 ") is not aligned to 4.", generated_commands_info.indirectAddress); + } + + if (generated_commands_info.indirectAddressSize == 0) { + skip |= LogError("VUID-VkGeneratedCommandsInfoEXT-indirectAddressSize-11077", command_buffer, + info_loc.dot(Field::indirectAddressSize), "is zero."); + } + + return skip; +} + +bool StatelessValidation::manual_PreCallValidateCmdPreprocessGeneratedCommandsEXT( + VkCommandBuffer commandBuffer, const VkGeneratedCommandsInfoEXT* pGeneratedCommandsInfo, VkCommandBuffer stateCommandBuffer, + const ErrorObject& error_obj) const { + bool skip = false; + if (!enabled_features.deviceGeneratedCommands) { + skip |= LogError("VUID-vkCmdPreprocessGeneratedCommandsEXT-deviceGeneratedCommands-11087", device, error_obj.location, + "deviceGeneratedCommands feature was not enabled."); + } + + const Location info_loc = error_obj.location.dot(Field::pGeneratedCommandsInfo); + if (pGeneratedCommandsInfo->shaderStages & + ~phys_dev_ext_props.device_generated_commands_props.supportedIndirectCommandsShaderStages) { + skip |= LogError( + "VUID-vkCmdPreprocessGeneratedCommandsEXT-supportedIndirectCommandsShaderStages-11088", commandBuffer, + info_loc.dot(Field::shaderStages), "(%s) contains stages not found in supportedIndirectCommandsShaderStages (%s).", + string_VkShaderStageFlags(pGeneratedCommandsInfo->shaderStages).c_str(), + string_VkShaderStageFlags(phys_dev_ext_props.device_generated_commands_props.supportedIndirectCommandsShaderStages) + .c_str()); + } + + skip |= ValidateGeneratedCommandsInfo(commandBuffer, *pGeneratedCommandsInfo, info_loc); + + return skip; +} + +bool StatelessValidation::manual_PreCallValidateCmdExecuteGeneratedCommandsEXT( + VkCommandBuffer commandBuffer, VkBool32 isPreprocessed, const VkGeneratedCommandsInfoEXT* pGeneratedCommandsInfo, + const ErrorObject& error_obj) const { + bool skip = false; + + if (!enabled_features.deviceGeneratedCommands) { + skip |= LogError("VUID-vkCmdExecuteGeneratedCommandsEXT-deviceGeneratedCommands-11059", device, error_obj.location, + "deviceGeneratedCommands feature was not enabled."); + } + + const Location info_loc = error_obj.location.dot(Field::pGeneratedCommandsInfo); + if (pGeneratedCommandsInfo->shaderStages & + ~phys_dev_ext_props.device_generated_commands_props.supportedIndirectCommandsShaderStages) { + skip |= LogError( + "VUID-vkCmdExecuteGeneratedCommandsEXT-supportedIndirectCommandsShaderStages-11061", commandBuffer, + info_loc.dot(Field::shaderStages), "(%s) contains stages not found in supportedIndirectCommandsShaderStages (%s).", + string_VkShaderStageFlags(pGeneratedCommandsInfo->shaderStages).c_str(), + string_VkShaderStageFlags(phys_dev_ext_props.device_generated_commands_props.supportedIndirectCommandsShaderStages) + .c_str()); + } + + skip |= ValidateGeneratedCommandsInfo(commandBuffer, *pGeneratedCommandsInfo, info_loc); + + return skip; +} \ No newline at end of file diff --git a/layers/stateless/sl_instance_device.cpp b/layers/stateless/sl_instance_device.cpp index 64e837cc647..7b6dc17cb39 100644 --- a/layers/stateless/sl_instance_device.cpp +++ b/layers/stateless/sl_instance_device.cpp @@ -412,6 +412,13 @@ void StatelessValidation::PostCallRecordCreateDevice(VkPhysicalDevice physicalDe phys_dev_ext_props.external_memory_host_props = external_memory_host_props; } + if (IsExtEnabled(device_extensions.vk_ext_device_generated_commands)) { + VkPhysicalDeviceDeviceGeneratedCommandsPropertiesEXT device_generated_commands_props = vku::InitStructHelper(); + VkPhysicalDeviceProperties2 prop2 = vku::InitStructHelper(&device_generated_commands_props); + DispatchGetPhysicalDeviceProperties2Helper(physicalDevice, &prop2); + phys_dev_ext_props.device_generated_commands_props = device_generated_commands_props; + } + if (IsExtEnabled(device_extensions.vk_arm_render_pass_striped)) { VkPhysicalDeviceRenderPassStripedPropertiesARM renderpass_striped_props = vku::InitStructHelper(); VkPhysicalDeviceProperties2 prop2 = vku::InitStructHelper(&renderpass_striped_props); diff --git a/layers/stateless/sl_pipeline.cpp b/layers/stateless/sl_pipeline.cpp index 7feee10ee79..ba667a0cc18 100644 --- a/layers/stateless/sl_pipeline.cpp +++ b/layers/stateless/sl_pipeline.cpp @@ -252,6 +252,15 @@ bool StatelessValidation::ValidateCreateGraphicsPipelinesFlags(const VkPipelineC } } + if ((flags & VK_PIPELINE_CREATE_2_INDIRECT_BINDABLE_BIT_EXT) != 0) { + if (!enabled_features.deviceGeneratedCommands) { + skip |= LogError("VUID-VkGraphicsPipelineCreateInfo-flags-11000", device, flags_loc, + "(%s) contains VK_PIPELINE_CREATE_2_INDIRECT_BINDABLE_BIT_EXT, but " + "VkPhysicalDeviceDeviceGeneratedCommandsFeaturesEXT::deviceGeneratedCommands is not enabled.", + string_VkPipelineCreateFlags2KHR(flags).c_str()); + } + } + if ((flags & VK_PIPELINE_CREATE_LIBRARY_BIT_KHR) != 0) { if (!enabled_features.graphicsPipelineLibrary) { skip |= LogError("VUID-VkGraphicsPipelineCreateInfo-graphicsPipelineLibrary-06606", device, flags_loc, @@ -1247,6 +1256,14 @@ bool StatelessValidation::ValidateCreateComputePipelinesFlags(const VkPipelineCr "(%s) must not include VK_PIPELINE_CREATE_RAY_TRACING_DISPLACEMENT_MICROMAP_BIT_NV.", string_VkPipelineCreateFlags2KHR(flags).c_str()); } + if ((flags & VK_PIPELINE_CREATE_2_INDIRECT_BINDABLE_BIT_EXT) != 0) { + if (!enabled_features.deviceGeneratedCommands) { + skip |= LogError("VUID-VkComputePipelineCreateInfo-flags-11007", device, flags_loc, + "(%s) contains VK_PIPELINE_CREATE_2_INDIRECT_BINDABLE_BIT_EXT, but " + "VkPhysicalDeviceDeviceGeneratedCommandsFeaturesEXT::deviceGeneratedCommands is not enabled.", + string_VkPipelineCreateFlags2KHR(flags).c_str()); + } + } return skip; } diff --git a/layers/stateless/sl_ray_tracing.cpp b/layers/stateless/sl_ray_tracing.cpp index 009ffa22fad..6ba14566a4d 100644 --- a/layers/stateless/sl_ray_tracing.cpp +++ b/layers/stateless/sl_ray_tracing.cpp @@ -304,6 +304,10 @@ bool StatelessValidation::ValidateCreateRayTracingPipelinesFlagsNV(const VkPipel skip |= LogError("VUID-VkRayTracingPipelineCreateInfoNV-flags-02904", device, flags_loc, "is %s.", string_VkPipelineCreateFlags2KHR(flags).c_str()); } + if (flags & VK_PIPELINE_CREATE_2_INDIRECT_BINDABLE_BIT_EXT) { + skip |= LogError("VUID-VkRayTracingPipelineCreateInfoNV-flags-11008", device, flags_loc, "is %s.", + string_VkPipelineCreateFlags2KHR(flags).c_str()); + } if ((flags & VK_PIPELINE_CREATE_DEFER_COMPILE_BIT_NV) && (flags & VK_PIPELINE_CREATE_FAIL_ON_PIPELINE_COMPILE_REQUIRED_BIT_EXT)) { skip |= LogError("VUID-VkRayTracingPipelineCreateInfoNV-flags-02957", device, flags_loc, "is %s.", diff --git a/layers/stateless/stateless_validation.h b/layers/stateless/stateless_validation.h index 10b2bff4036..18a45c03130 100644 --- a/layers/stateless/stateless_validation.h +++ b/layers/stateless/stateless_validation.h @@ -62,6 +62,7 @@ class StatelessValidation : public ValidationObject { VkPhysicalDeviceFragmentShadingRatePropertiesKHR fragment_shading_rate_props; VkPhysicalDeviceDepthStencilResolveProperties depth_stencil_resolve_props; VkPhysicalDeviceExternalMemoryHostPropertiesEXT external_memory_host_props; + VkPhysicalDeviceDeviceGeneratedCommandsPropertiesEXT device_generated_commands_props; VkPhysicalDeviceRenderPassStripedPropertiesARM renderpass_striped_props; }; DeviceExtensionProperties phys_dev_ext_props = {}; @@ -1168,6 +1169,43 @@ class StatelessValidation : public ValidationObject { bool manual_PreCallValidateQueueBindSparse(VkQueue queue, uint32_t bindInfoCount, const VkBindSparseInfo *pBindInfo, VkFence fence, const ErrorObject &error_obj) const; + bool ValidateIndirectExecutionSetPipelineInfo(const VkIndirectExecutionSetPipelineInfoEXT &pipeline_info, + const Location &pipeline_info_loc) const; + bool ValidateIndirectExecutionSetShaderInfo(const VkIndirectExecutionSetShaderInfoEXT &shader_info, + const Location &shader_info_loc) const; + bool manual_PreCallValidateCreateIndirectExecutionSetEXT(VkDevice device, + const VkIndirectExecutionSetCreateInfoEXT *pCreateInfo, + const VkAllocationCallbacks *pAllocator, + VkIndirectExecutionSetEXT *pIndirectExecutionSet, + const ErrorObject &error_obj) const; + bool ValidateIndirectCommandsPushConstantToken(const VkIndirectCommandsPushConstantTokenEXT &push_constant_token, + VkIndirectCommandsTokenTypeEXT token_type, + const Location &push_constant_token_loc) const; + bool ValidateIndirectCommandsIndexBufferToken(const VkIndirectCommandsIndexBufferTokenEXT &index_buffer_token, + const Location &index_buffer_token_loc) const; + bool ValidateIndirectCommandsExecutionSetToken(const VkIndirectCommandsExecutionSetTokenEXT &exe_set_token, + const Location &exe_set_token_loc) const; + bool ValidateIndirectCommandsLayoutToken(const VkIndirectCommandsLayoutTokenEXT &token, const Location &token_loc) const; + bool ValidateIndirectCommandsLayoutStage(const VkIndirectCommandsLayoutTokenEXT &token, const Location &token_loc, + VkShaderStageFlags shader_stages, bool has_stage_graphics, bool has_stage_compute, + bool has_stage_ray_tracing, bool has_stage_mesh) const; + bool manual_PreCallValidateCreateIndirectCommandsLayoutEXT(VkDevice device, + const VkIndirectCommandsLayoutCreateInfoEXT *pCreateInfo, + const VkAllocationCallbacks *pAllocator, + VkIndirectCommandsLayoutEXT *pIndirectCommandsLayout, + const ErrorObject &error_obj) const; + bool ValidateGeneratedCommandsInfo(VkCommandBuffer command_buffer, const VkGeneratedCommandsInfoEXT &generated_commands_info, + const Location &info_loc) const; + + bool manual_PreCallValidateCmdPreprocessGeneratedCommandsEXT(VkCommandBuffer commandBuffer, + const VkGeneratedCommandsInfoEXT *pGeneratedCommandsInfo, + VkCommandBuffer stateCommandBuffer, + const ErrorObject &error_obj) const; + + bool manual_PreCallValidateCmdExecuteGeneratedCommandsEXT(VkCommandBuffer commandBuffer, VkBool32 isPreprocessed, + const VkGeneratedCommandsInfoEXT *pGeneratedCommandsInfo, + const ErrorObject &error_obj) const; + #ifdef VK_USE_PLATFORM_METAL_EXT bool manual_PreCallValidateExportMetalObjectsEXT(VkDevice device, VkExportMetalObjectsInfoEXT *pMetalObjectsInfo, const ErrorObject &error_obj) const; diff --git a/layers/vulkan/generated/stateless_validation_helper.cpp b/layers/vulkan/generated/stateless_validation_helper.cpp index 4b10d68575b..92b52842062 100644 --- a/layers/vulkan/generated/stateless_validation_helper.cpp +++ b/layers/vulkan/generated/stateless_validation_helper.cpp @@ -26304,6 +26304,9 @@ bool StatelessValidation::PreCallValidateCmdPreprocessGeneratedCommandsEXT(VkCom pGeneratedCommandsInfo->indirectCommandsLayout); } skip |= ValidateRequiredHandle(loc.dot(Field::stateCommandBuffer), stateCommandBuffer); + if (!skip) + skip |= manual_PreCallValidateCmdPreprocessGeneratedCommandsEXT(commandBuffer, pGeneratedCommandsInfo, stateCommandBuffer, + error_obj); return skip; } @@ -26328,6 +26331,9 @@ bool StatelessValidation::PreCallValidateCmdExecuteGeneratedCommandsEXT(VkComman skip |= ValidateRequiredHandle(pGeneratedCommandsInfo_loc.dot(Field::indirectCommandsLayout), pGeneratedCommandsInfo->indirectCommandsLayout); } + if (!skip) + skip |= + manual_PreCallValidateCmdExecuteGeneratedCommandsEXT(commandBuffer, isPreprocessed, pGeneratedCommandsInfo, error_obj); return skip; } @@ -26384,6 +26390,9 @@ bool StatelessValidation::PreCallValidateCreateIndirectCommandsLayoutEXT(VkDevic } skip |= ValidateRequiredPointer(loc.dot(Field::pIndirectCommandsLayout), pIndirectCommandsLayout, "VUID-vkCreateIndirectCommandsLayoutEXT-pIndirectCommandsLayout-parameter"); + if (!skip) + skip |= manual_PreCallValidateCreateIndirectCommandsLayoutEXT(device, pCreateInfo, pAllocator, pIndirectCommandsLayout, + error_obj); return skip; } @@ -26425,6 +26434,9 @@ bool StatelessValidation::PreCallValidateCreateIndirectExecutionSetEXT(VkDevice } skip |= ValidateRequiredPointer(loc.dot(Field::pIndirectExecutionSet), pIndirectExecutionSet, "VUID-vkCreateIndirectExecutionSetEXT-pIndirectExecutionSet-parameter"); + if (!skip) + skip |= + manual_PreCallValidateCreateIndirectExecutionSetEXT(device, pCreateInfo, pAllocator, pIndirectExecutionSet, error_obj); return skip; } diff --git a/scripts/generators/stateless_validation_helper_generator.py b/scripts/generators/stateless_validation_helper_generator.py index 68c45fb3e24..d1ff66f53da 100644 --- a/scripts/generators/stateless_validation_helper_generator.py +++ b/scripts/generators/stateless_validation_helper_generator.py @@ -98,6 +98,10 @@ def __init__(self, 'vkCmdBindDescriptorBufferEmbeddedSamplers2EXT', 'vkCmdPushDescriptorSetWithTemplate2KHR', 'vkCmdBindDescriptorSets2KHR', + 'vkCreateIndirectExecutionSetEXT', + 'vkCreateIndirectCommandsLayoutEXT', + 'vkCmdPreprocessGeneratedCommandsEXT', + 'vkCmdExecuteGeneratedCommandsEXT', 'vkCmdSetExclusiveScissorNV', 'vkCmdSetViewportShadingRatePaletteNV', 'vkCmdSetCoarseSampleOrderNV', diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index bc7414ada60..9f7e4d32169 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -85,6 +85,8 @@ target_sources(vk_layer_validation_tests PRIVATE unit/descriptor_indexing_positive.cpp unit/descriptors.cpp unit/descriptors_positive.cpp + unit/device_generated_commands.cpp + unit/device_generated_commands_positive.cpp unit/device_queue.cpp unit/dynamic_rendering.cpp unit/dynamic_rendering_positive.cpp diff --git a/tests/device_profiles/max_profile.json b/tests/device_profiles/max_profile.json index 6226cff0804..617d4e71136 100644 --- a/tests/device_profiles/max_profile.json +++ b/tests/device_profiles/max_profile.json @@ -83,6 +83,10 @@ "VkPhysicalDeviceDeviceGeneratedCommandsFeaturesNV": { "deviceGeneratedCommands": true }, + "VkPhysicalDeviceDeviceGeneratedCommandsFeaturesEXT": { + "deviceGeneratedCommandsEXT": true, + "dynamicGeneratedPipelineLayout": true + }, "VkPhysicalDeviceDeviceGeneratedCommandsComputeFeaturesNV": { "deviceGeneratedCompute": true, "deviceGeneratedComputePipelines": true, @@ -1136,6 +1140,64 @@ "maxIndirectCommandsTokenOffset": 4294967000, "maxIndirectCommandsStreamStride": 4294967000 }, + "VkPhysicalDeviceDeviceGeneratedCommandsPropertiesEXT": { + "maxIndirectPipelineCount": 4096, + "maxIndirectShaderObjectCount": 4096, + "maxIndirectSequenceCount": 4096, + "maxIndirectCommandsTokenCount": 16, + "maxIndirectCommandsTokenOffset": 2048, + "maxIndirectCommandsIndirectStride": 2048, + "supportedIndirectCommandsShaderStages": [ + "VK_SHADER_STAGE_VERTEX_BIT", + "VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT", + "VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT", + "VK_SHADER_STAGE_GEOMETRY_BIT", + "VK_SHADER_STAGE_FRAGMENT_BIT", + "VK_SHADER_STAGE_COMPUTE_BIT", + "VK_SHADER_STAGE_RAYGEN_BIT_KHR", + "VK_SHADER_STAGE_ANY_HIT_BIT_KHR", + "VK_SHADER_STAGE_CLOSEST_HIT_BIT_KHR", + "VK_SHADER_STAGE_MISS_BIT_KHR", + "VK_SHADER_STAGE_INTERSECTION_BIT_KHR", + "VK_SHADER_STAGE_CALLABLE_BIT_KHR", + "VK_SHADER_STAGE_TASK_BIT_EXT", + "VK_SHADER_STAGE_MESH_BIT_EXT" + ], + "supportedIndirectCommandsShaderStagesPipelineBinding": [ + "VK_SHADER_STAGE_VERTEX_BIT", + "VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT", + "VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT", + "VK_SHADER_STAGE_GEOMETRY_BIT", + "VK_SHADER_STAGE_FRAGMENT_BIT", + "VK_SHADER_STAGE_COMPUTE_BIT", + "VK_SHADER_STAGE_RAYGEN_BIT_KHR", + "VK_SHADER_STAGE_ANY_HIT_BIT_KHR", + "VK_SHADER_STAGE_CLOSEST_HIT_BIT_KHR", + "VK_SHADER_STAGE_MISS_BIT_KHR", + "VK_SHADER_STAGE_INTERSECTION_BIT_KHR", + "VK_SHADER_STAGE_CALLABLE_BIT_KHR", + "VK_SHADER_STAGE_TASK_BIT_EXT", + "VK_SHADER_STAGE_MESH_BIT_EXT" + ], + "supportedIndirectCommandsShaderStagesShaderBinding": [ + "VK_SHADER_STAGE_VERTEX_BIT", + "VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT", + "VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT", + "VK_SHADER_STAGE_GEOMETRY_BIT", + "VK_SHADER_STAGE_FRAGMENT_BIT", + "VK_SHADER_STAGE_COMPUTE_BIT", + "VK_SHADER_STAGE_RAYGEN_BIT_KHR", + "VK_SHADER_STAGE_ANY_HIT_BIT_KHR", + "VK_SHADER_STAGE_CLOSEST_HIT_BIT_KHR", + "VK_SHADER_STAGE_MISS_BIT_KHR", + "VK_SHADER_STAGE_INTERSECTION_BIT_KHR", + "VK_SHADER_STAGE_CALLABLE_BIT_KHR", + "VK_SHADER_STAGE_TASK_BIT_EXT", + "VK_SHADER_STAGE_MESH_BIT_EXT" + ], + "deviceGeneratedCommandsTransformFeedback": true, + "deviceGeneratedCommandsMultiDrawIndirectCount": true + }, "VkPhysicalDeviceMultiDrawPropertiesEXT": { "maxMultiDrawCount": 1024 }, @@ -1891,6 +1953,7 @@ "VK_EXT_descriptor_indexing": 1, "VK_EXT_device_address_binding_report": 1, "VK_EXT_device_fault": 1, + "VK_EXT_device_generated_commands": 1, "VK_EXT_device_memory_report": 1, "VK_EXT_direct_mode_display": 1, "VK_EXT_directfb_surface": 1, diff --git a/tests/framework/binding.cpp b/tests/framework/binding.cpp index ce2dfe4c880..99664fd3f8e 100644 --- a/tests/framework/binding.cpp +++ b/tests/framework/binding.cpp @@ -1903,4 +1903,31 @@ void SamplerYcbcrConversion::destroy() noexcept { SamplerYcbcrConversion::~SamplerYcbcrConversion() noexcept { destroy(); } +NON_DISPATCHABLE_HANDLE_DTOR(IndirectCommandsLayout, vk::DestroyIndirectCommandsLayoutEXT) +void IndirectCommandsLayout::Init(const Device &dev, const VkIndirectCommandsLayoutCreateInfoEXT &info) { + NON_DISPATCHABLE_HANDLE_INIT(vk::CreateIndirectCommandsLayoutEXT, dev, &info); +} + +NON_DISPATCHABLE_HANDLE_DTOR(IndirectExecutionSet, vk::DestroyIndirectExecutionSetEXT) +void IndirectExecutionSet::Init(const Device &dev, const VkIndirectExecutionSetCreateInfoEXT &info) { + NON_DISPATCHABLE_HANDLE_INIT(vk::CreateIndirectExecutionSetEXT, dev, &info); +} + +IndirectExecutionSet::IndirectExecutionSet(const Device &dev, VkPipeline init_pipeline, uint32_t max_pipelines) { + VkIndirectExecutionSetPipelineInfoEXT exe_set_pipeline_info = vku::InitStructHelper(); + exe_set_pipeline_info.initialPipeline = init_pipeline; + exe_set_pipeline_info.maxPipelineCount = max_pipelines; + + VkIndirectExecutionSetCreateInfoEXT exe_set_ci = vku::InitStructHelper(); + exe_set_ci.type = VK_INDIRECT_EXECUTION_SET_INFO_TYPE_PIPELINES_EXT; + exe_set_ci.info.pPipelineInfo = &exe_set_pipeline_info; + Init(dev, exe_set_ci); +} +IndirectExecutionSet::IndirectExecutionSet(const Device &dev, const VkIndirectExecutionSetShaderInfoEXT &shader_info) { + VkIndirectExecutionSetCreateInfoEXT exe_set_ci = vku::InitStructHelper(); + exe_set_ci.type = VK_INDIRECT_EXECUTION_SET_INFO_TYPE_SHADER_OBJECTS_EXT; + exe_set_ci.info.pShaderInfo = &shader_info; + Init(dev, exe_set_ci); +} + } // namespace vkt diff --git a/tests/framework/binding.h b/tests/framework/binding.h index 9485e6633f8..788816fece2 100644 --- a/tests/framework/binding.h +++ b/tests/framework/binding.h @@ -80,6 +80,8 @@ class DescriptorSetPool; class DescriptorSet; class CommandBuffer; class CommandPool; +class IndirectCommandsLayout; +class IndirectExecutionSet; std::vector GetGlobalLayers(); std::vector GetGlobalExtensions(); @@ -1414,4 +1416,26 @@ inline VkCopyDescriptorSet Device::copy_descriptor_set(const DescriptorSet &src_ return copy; } +class IndirectCommandsLayout : public internal::NonDispHandle { + public: + ~IndirectCommandsLayout() noexcept; + void destroy() noexcept; + + explicit IndirectCommandsLayout() : NonDispHandle() {} + explicit IndirectCommandsLayout(const Device &dev, const VkIndirectCommandsLayoutCreateInfoEXT &info) { Init(dev, info); } + void Init(const Device &dev, const VkIndirectCommandsLayoutCreateInfoEXT &info); +}; + +class IndirectExecutionSet : public internal::NonDispHandle { + public: + ~IndirectExecutionSet() noexcept; + void destroy() noexcept; + + explicit IndirectExecutionSet() : NonDispHandle() {} + explicit IndirectExecutionSet(const Device &dev, const VkIndirectExecutionSetCreateInfoEXT &info) { Init(dev, info); } + explicit IndirectExecutionSet(const Device &dev, VkPipeline init_pipeline, uint32_t max_pipelines); + explicit IndirectExecutionSet(const Device &dev, const VkIndirectExecutionSetShaderInfoEXT &shader_info); + void Init(const Device &dev, const VkIndirectExecutionSetCreateInfoEXT &info); +}; + } // namespace vkt diff --git a/tests/framework/layer_validation_tests.h b/tests/framework/layer_validation_tests.h index 5a28705f75d..34962ce799c 100644 --- a/tests/framework/layer_validation_tests.h +++ b/tests/framework/layer_validation_tests.h @@ -300,6 +300,11 @@ class ExternalMemorySyncTest : public VkLayerTest { #endif }; +class DeviceGeneratedCommandsTest : public VkLayerTest { + public: + void InitBasicDeviceGeneratedCommands(); +}; + class GraphicsLibraryTest : public VkLayerTest { public: void InitBasicGraphicsLibrary(); diff --git a/tests/icd/test_icd.cpp b/tests/icd/test_icd.cpp index 9db4cf7a22d..61bb2cab9d7 100644 --- a/tests/icd/test_icd.cpp +++ b/tests/icd/test_icd.cpp @@ -1594,6 +1594,12 @@ static VKAPI_ATTR void VKAPI_CALL GetPhysicalDeviceFeatures2(VkPhysicalDevice ph if (video_maintenance1_features) { video_maintenance1_features->videoMaintenance1 = VK_TRUE; } + auto device_generated_commands_features = + vku::FindStructInPNextChain(pFeatures->pNext); + if (device_generated_commands_features) { + device_generated_commands_features->deviceGeneratedCommands = VK_TRUE; + device_generated_commands_features->dynamicGeneratedPipelineLayout = VK_TRUE; + } const auto* desc_idx_features = vku::FindStructInPNextChain(pFeatures->pNext); if (desc_idx_features) { const auto bool_size = sizeof(VkPhysicalDeviceDescriptorIndexingFeaturesEXT) - @@ -1711,6 +1717,27 @@ static VKAPI_ATTR void VKAPI_CALL GetPhysicalDeviceProperties2(VkPhysicalDevice fragment_density_map2_props->maxDescriptorSetSubsampledSamplers = 1; } + auto* device_generated_commands_props = + vku::FindStructInPNextChain(pProperties->pNext); + if (device_generated_commands_props) { + device_generated_commands_props->maxIndirectPipelineCount = 4096; + device_generated_commands_props->maxIndirectShaderObjectCount = 4096; + device_generated_commands_props->maxIndirectSequenceCount = 4096; + device_generated_commands_props->maxIndirectCommandsTokenCount = 16; + device_generated_commands_props->maxIndirectCommandsTokenOffset = 2048; + device_generated_commands_props->maxIndirectCommandsIndirectStride = 2048; + device_generated_commands_props->supportedIndirectCommandsInputModes = + VK_INDIRECT_COMMANDS_INPUT_MODE_VULKAN_INDEX_BUFFER_EXT; + device_generated_commands_props->supportedIndirectCommandsShaderStages = + VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT | VK_SHADER_STAGE_COMPUTE_BIT; + device_generated_commands_props->supportedIndirectCommandsShaderStagesPipelineBinding = + VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT | VK_SHADER_STAGE_COMPUTE_BIT; + device_generated_commands_props->supportedIndirectCommandsShaderStagesShaderBinding = + VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT | VK_SHADER_STAGE_COMPUTE_BIT; + device_generated_commands_props->deviceGeneratedCommandsTransformFeedback = VK_TRUE; + device_generated_commands_props->deviceGeneratedCommandsMultiDrawIndirectCount = VK_TRUE; + } + const uint32_t num_copy_layouts = 5; const VkImageLayout HostCopyLayouts[]{ VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, VK_IMAGE_LAYOUT_GENERAL, @@ -2125,7 +2152,7 @@ static VKAPI_ATTR VkResult VKAPI_CALL CreatePipelineBinariesKHR(VkDevice device, const VkAllocationCallbacks* pAllocator, VkPipelineBinaryHandlesInfoKHR* pBinaries) { unique_lock_t lock(global_lock); - + pBinaries->pipelineBinaryCount = 1; if (pBinaries->pPipelineBinaries != nullptr) { diff --git a/tests/unit/debug_printf.cpp b/tests/unit/debug_printf.cpp index 8d4a12dfe5f..400acff7155 100644 --- a/tests/unit/debug_printf.cpp +++ b/tests/unit/debug_printf.cpp @@ -3658,3 +3658,329 @@ TEST_F(NegativeDebugPrintf, DrawIndexedIndirectCount) { m_default_queue->Wait(); m_errorMonitor->VerifyFound(); } + +TEST_F(NegativeDebugPrintf, DeviceGeneratedCommandsCompute) { + SetTargetApiVersion(VK_API_VERSION_1_1); + AddRequiredExtensions(VK_EXT_DEVICE_GENERATED_COMMANDS_EXTENSION_NAME); + AddRequiredExtensions(VK_KHR_BUFFER_DEVICE_ADDRESS_EXTENSION_NAME); + AddRequiredFeature(vkt::Feature::deviceGeneratedCommands); + AddRequiredFeature(vkt::Feature::bufferDeviceAddress); + RETURN_IF_SKIP(InitDebugPrintfFramework()); + RETURN_IF_SKIP(InitState()); + + VkPhysicalDeviceDeviceGeneratedCommandsPropertiesEXT dgc_props = vku::InitStructHelper(); + GetPhysicalDeviceProperties2(dgc_props); + if ((dgc_props.supportedIndirectCommandsShaderStagesPipelineBinding & VK_SHADER_STAGE_COMPUTE_BIT) == 0) { + GTEST_SKIP() << "VK_SHADER_STAGE_COMPUTE_BIT is not supported."; + } + + VkIndirectCommandsLayoutTokenEXT token; + token = vku::InitStructHelper(); + token.type = VK_INDIRECT_COMMANDS_TOKEN_TYPE_DISPATCH_EXT; + token.offset = 0; + + VkIndirectCommandsLayoutCreateInfoEXT command_layout_ci = vku::InitStructHelper(); + command_layout_ci.shaderStages = VK_SHADER_STAGE_COMPUTE_BIT; + command_layout_ci.pipelineLayout = VK_NULL_HANDLE; + command_layout_ci.tokenCount = 1; + command_layout_ci.pTokens = &token; + vkt::IndirectCommandsLayout command_layout(*m_device, command_layout_ci); + + char const *shader_source = R"glsl( + #version 450 + #extension GL_EXT_debug_printf : enable + void main() { + debugPrintfEXT("gl_NumWorkGroups %v3u\n", gl_NumWorkGroups); + } + )glsl"; + + VkPipelineCreateFlags2CreateInfoKHR pipe_flags2 = vku::InitStructHelper(); + pipe_flags2.flags = VK_PIPELINE_CREATE_2_INDIRECT_BINDABLE_BIT_EXT; + CreateComputePipelineHelper pipe(*this, &pipe_flags2); + pipe.cs_ = std::make_unique(this, shader_source, VK_SHADER_STAGE_COMPUTE_BIT, SPV_ENV_VULKAN_1_1); + pipe.CreateComputePipeline(); + + VkGeneratedCommandsPipelineInfoEXT pipeline_info = vku::InitStructHelper(); + pipeline_info.pipeline = pipe.Handle(); + + VkMemoryAllocateFlagsInfo allocate_flag_info = vku::InitStructHelper(); + allocate_flag_info.flags = VK_MEMORY_ALLOCATE_DEVICE_ADDRESS_BIT; + vkt::Buffer block_buffer(*m_device, 64, VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT, + VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, &allocate_flag_info); + + VkDeviceSize pre_process_size = 0; + { + VkGeneratedCommandsMemoryRequirementsInfoEXT dgc_mem_reqs = vku::InitStructHelper(&pipeline_info); + dgc_mem_reqs.indirectCommandsLayout = command_layout.handle(); + dgc_mem_reqs.indirectExecutionSet = VK_NULL_HANDLE; + dgc_mem_reqs.maxSequenceCount = 1; + VkMemoryRequirements2 mem_reqs2 = vku::InitStructHelper(); + vk::GetGeneratedCommandsMemoryRequirementsEXT(device(), &dgc_mem_reqs, &mem_reqs2); + pre_process_size = mem_reqs2.memoryRequirements.size; + } + + VkBufferUsageFlags2CreateInfoKHR buffer_usage_flags = vku::InitStructHelper(); + buffer_usage_flags.usage = VK_BUFFER_USAGE_2_PREPROCESS_BUFFER_BIT_EXT | VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT; + VkBufferCreateInfo buffer_ci = vku::InitStructHelper(&buffer_usage_flags); + buffer_ci.size = pre_process_size; + vkt::Buffer pre_process_buffer(*m_device, buffer_ci, 0, &allocate_flag_info); + + VkDispatchIndirectCommand *block_buffer_ptr = (VkDispatchIndirectCommand *)block_buffer.memory().map(); + block_buffer_ptr->x = 2; + block_buffer_ptr->y = 1; + block_buffer_ptr->z = 1; + block_buffer.memory().unmap(); + + VkGeneratedCommandsInfoEXT generated_commands_info = vku::InitStructHelper(&pipeline_info); + generated_commands_info.shaderStages = VK_SHADER_STAGE_COMPUTE_BIT; + generated_commands_info.indirectExecutionSet = VK_NULL_HANDLE; + generated_commands_info.indirectCommandsLayout = command_layout.handle(); + generated_commands_info.indirectAddressSize = sizeof(VkDispatchIndirectCommand); + generated_commands_info.indirectAddress = block_buffer.address(); + generated_commands_info.preprocessAddress = pre_process_buffer.address(); + generated_commands_info.preprocessSize = pre_process_size; + generated_commands_info.sequenceCountAddress = 0; + generated_commands_info.maxSequenceCount = 1; + + m_command_buffer.begin(); + vk::CmdBindPipeline(m_command_buffer.handle(), VK_PIPELINE_BIND_POINT_COMPUTE, pipe.Handle()); + vk::CmdExecuteGeneratedCommandsEXT(m_command_buffer.handle(), false, &generated_commands_info); + m_command_buffer.end(); + + m_errorMonitor->SetDesiredFailureMsg(kInformationBit, "gl_NumWorkGroups 2, 1, 1"); + m_errorMonitor->SetDesiredFailureMsg(kInformationBit, "gl_NumWorkGroups 2, 1, 1"); + m_default_queue->Submit(m_command_buffer); + m_default_queue->Wait(); + m_errorMonitor->VerifyFound(); +} + +TEST_F(NegativeDebugPrintf, DeviceGeneratedCommandsGraphics) { + SetTargetApiVersion(VK_API_VERSION_1_1); + AddRequiredExtensions(VK_EXT_DEVICE_GENERATED_COMMANDS_EXTENSION_NAME); + AddRequiredExtensions(VK_KHR_BUFFER_DEVICE_ADDRESS_EXTENSION_NAME); + AddRequiredFeature(vkt::Feature::deviceGeneratedCommands); + AddRequiredFeature(vkt::Feature::bufferDeviceAddress); + RETURN_IF_SKIP(InitDebugPrintfFramework()); + RETURN_IF_SKIP(InitState()); + InitRenderTarget(); + + VkPhysicalDeviceDeviceGeneratedCommandsPropertiesEXT dgc_props = vku::InitStructHelper(); + GetPhysicalDeviceProperties2(dgc_props); + if ((dgc_props.supportedIndirectCommandsShaderStagesPipelineBinding & VK_SHADER_STAGE_VERTEX_BIT) == 0) { + GTEST_SKIP() << "VK_SHADER_STAGE_VERTEX_BIT is not supported."; + } + + VkIndirectCommandsLayoutTokenEXT token; + token = vku::InitStructHelper(); + token.type = VK_INDIRECT_COMMANDS_TOKEN_TYPE_DRAW_EXT; + token.offset = 0; + + VkIndirectCommandsLayoutCreateInfoEXT command_layout_ci = vku::InitStructHelper(); + command_layout_ci.shaderStages = VK_SHADER_STAGE_VERTEX_BIT; + command_layout_ci.pipelineLayout = VK_NULL_HANDLE; + command_layout_ci.tokenCount = 1; + command_layout_ci.pTokens = &token; + vkt::IndirectCommandsLayout command_layout(*m_device, command_layout_ci); + + char const *vs_source = R"glsl( + #version 450 + #extension GL_EXT_debug_printf : enable + void main() { + debugPrintfEXT("gl_VertexIndex %u\n", gl_VertexIndex); + } + )glsl"; + VkShaderObj vs(this, vs_source, VK_SHADER_STAGE_VERTEX_BIT); + + VkPipelineCreateFlags2CreateInfoKHR pipe_flags2 = vku::InitStructHelper(); + pipe_flags2.flags = VK_PIPELINE_CREATE_2_INDIRECT_BINDABLE_BIT_EXT; + CreatePipelineHelper pipe(*this, &pipe_flags2); + pipe.shader_stages_ = {vs.GetStageCreateInfo()}; + pipe.rs_state_ci_.rasterizerDiscardEnable = VK_TRUE; + pipe.CreateGraphicsPipeline(); + + VkGeneratedCommandsPipelineInfoEXT pipeline_info = vku::InitStructHelper(); + pipeline_info.pipeline = pipe.Handle(); + + VkMemoryAllocateFlagsInfo allocate_flag_info = vku::InitStructHelper(); + allocate_flag_info.flags = VK_MEMORY_ALLOCATE_DEVICE_ADDRESS_BIT; + vkt::Buffer block_buffer(*m_device, 64, VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT, + VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, &allocate_flag_info); + + VkDeviceSize pre_process_size = 0; + { + VkGeneratedCommandsMemoryRequirementsInfoEXT dgc_mem_reqs = vku::InitStructHelper(&pipeline_info); + dgc_mem_reqs.indirectCommandsLayout = command_layout.handle(); + dgc_mem_reqs.indirectExecutionSet = VK_NULL_HANDLE; + dgc_mem_reqs.maxSequenceCount = 1; + VkMemoryRequirements2 mem_reqs2 = vku::InitStructHelper(); + vk::GetGeneratedCommandsMemoryRequirementsEXT(device(), &dgc_mem_reqs, &mem_reqs2); + pre_process_size = mem_reqs2.memoryRequirements.size; + } + + VkBufferUsageFlags2CreateInfoKHR buffer_usage_flags = vku::InitStructHelper(); + buffer_usage_flags.usage = VK_BUFFER_USAGE_2_PREPROCESS_BUFFER_BIT_EXT | VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT; + VkBufferCreateInfo buffer_ci = vku::InitStructHelper(&buffer_usage_flags); + buffer_ci.size = pre_process_size; + vkt::Buffer pre_process_buffer(*m_device, buffer_ci, 0, &allocate_flag_info); + + VkDrawIndirectCommand *block_buffer_ptr = (VkDrawIndirectCommand *)block_buffer.memory().map(); + block_buffer_ptr->vertexCount = 3; + block_buffer_ptr->instanceCount = 1; + block_buffer_ptr->firstVertex = 0; + block_buffer_ptr->firstInstance = 0; + block_buffer.memory().unmap(); + + VkGeneratedCommandsInfoEXT generated_commands_info = vku::InitStructHelper(&pipeline_info); + generated_commands_info.shaderStages = VK_SHADER_STAGE_VERTEX_BIT; + generated_commands_info.indirectExecutionSet = VK_NULL_HANDLE; + generated_commands_info.indirectCommandsLayout = command_layout.handle(); + generated_commands_info.indirectAddressSize = sizeof(VkDrawIndirectCommand); + generated_commands_info.indirectAddress = block_buffer.address(); + generated_commands_info.preprocessAddress = pre_process_buffer.address(); + generated_commands_info.preprocessSize = pre_process_size; + generated_commands_info.sequenceCountAddress = 0; + generated_commands_info.maxSequenceCount = 1; + + m_command_buffer.begin(); + vk::CmdBindPipeline(m_command_buffer.handle(), VK_PIPELINE_BIND_POINT_GRAPHICS, pipe.Handle()); + vk::CmdExecuteGeneratedCommandsEXT(m_command_buffer.handle(), false, &generated_commands_info); + m_command_buffer.end(); + + m_errorMonitor->SetDesiredFailureMsg(kInformationBit, "gl_VertexIndex 0"); + m_errorMonitor->SetDesiredFailureMsg(kInformationBit, "gl_VertexIndex 1"); + m_errorMonitor->SetDesiredFailureMsg(kInformationBit, "gl_VertexIndex 2"); + m_default_queue->Submit(m_command_buffer); + m_default_queue->Wait(); + m_errorMonitor->VerifyFound(); +} + +TEST_F(NegativeDebugPrintf, DeviceGeneratedCommandsIES) { + SetTargetApiVersion(VK_API_VERSION_1_1); + AddRequiredExtensions(VK_EXT_DEVICE_GENERATED_COMMANDS_EXTENSION_NAME); + AddRequiredExtensions(VK_KHR_BUFFER_DEVICE_ADDRESS_EXTENSION_NAME); + AddRequiredFeature(vkt::Feature::deviceGeneratedCommands); + AddRequiredFeature(vkt::Feature::bufferDeviceAddress); + RETURN_IF_SKIP(InitDebugPrintfFramework()); + RETURN_IF_SKIP(InitState()); + + VkPhysicalDeviceDeviceGeneratedCommandsPropertiesEXT dgc_props = vku::InitStructHelper(); + GetPhysicalDeviceProperties2(dgc_props); + if ((dgc_props.supportedIndirectCommandsShaderStagesPipelineBinding & VK_SHADER_STAGE_COMPUTE_BIT) == 0) { + GTEST_SKIP() << "VK_SHADER_STAGE_COMPUTE_BIT is not supported."; + } + + VkIndirectCommandsExecutionSetTokenEXT exe_set_token = {VK_INDIRECT_EXECUTION_SET_INFO_TYPE_PIPELINES_EXT, + VK_SHADER_STAGE_COMPUTE_BIT}; + VkIndirectCommandsLayoutTokenEXT tokens[2]; + tokens[0] = vku::InitStructHelper(); + tokens[0].type = VK_INDIRECT_COMMANDS_TOKEN_TYPE_EXECUTION_SET_EXT; + tokens[0].data.pExecutionSet = &exe_set_token; + tokens[0].offset = 0; + tokens[1] = vku::InitStructHelper(); + tokens[1].type = VK_INDIRECT_COMMANDS_TOKEN_TYPE_DISPATCH_EXT; + tokens[1].offset = sizeof(uint32_t); + + VkIndirectCommandsLayoutCreateInfoEXT command_layout_ci = vku::InitStructHelper(); + command_layout_ci.shaderStages = VK_SHADER_STAGE_COMPUTE_BIT; + command_layout_ci.pipelineLayout = VK_NULL_HANDLE; + command_layout_ci.tokenCount = 2; + command_layout_ci.pTokens = tokens; + vkt::IndirectCommandsLayout command_layout(*m_device, command_layout_ci); + + char const *shader_source_1 = R"glsl( + #version 450 + #extension GL_EXT_debug_printf : enable + void main() { + debugPrintfEXT("Init Pipeline\n"); + } + )glsl"; + char const *shader_source_2 = R"glsl( + #version 450 + #extension GL_EXT_debug_printf : enable + void main() { + debugPrintfEXT("IndirectExecutionSet Pipeline 1\n"); + } + )glsl"; + char const *shader_source_3 = R"glsl( + #version 450 + #extension GL_EXT_debug_printf : enable + void main() { + debugPrintfEXT("IndirectExecutionSet Pipeline 2\n"); + } + )glsl"; + + VkPipelineCreateFlags2CreateInfoKHR pipe_flags2 = vku::InitStructHelper(); + pipe_flags2.flags = VK_PIPELINE_CREATE_2_INDIRECT_BINDABLE_BIT_EXT; + CreateComputePipelineHelper init_pipe(*this, &pipe_flags2); + init_pipe.cs_ = std::make_unique(this, shader_source_1, VK_SHADER_STAGE_COMPUTE_BIT, SPV_ENV_VULKAN_1_1); + init_pipe.CreateComputePipeline(); + + CreateComputePipelineHelper pipe_1(*this, &pipe_flags2); + pipe_1.cs_ = std::make_unique(this, shader_source_2, VK_SHADER_STAGE_COMPUTE_BIT, SPV_ENV_VULKAN_1_1); + pipe_1.CreateComputePipeline(); + + CreateComputePipelineHelper pipe_2(*this, &pipe_flags2); + pipe_2.cs_ = std::make_unique(this, shader_source_3, VK_SHADER_STAGE_COMPUTE_BIT, SPV_ENV_VULKAN_1_1); + pipe_2.CreateComputePipeline(); + + vkt::IndirectExecutionSet exe_set(*m_device, init_pipe.Handle(), 3); + VkWriteIndirectExecutionSetPipelineEXT write_exe_sets[2]; + write_exe_sets[0] = vku::InitStructHelper(); + write_exe_sets[0].index = 1; + write_exe_sets[0].pipeline = pipe_1.Handle(); + write_exe_sets[1] = vku::InitStructHelper(); + write_exe_sets[1].index = 2; + write_exe_sets[1].pipeline = pipe_2.Handle(); + vk::UpdateIndirectExecutionSetPipelineEXT(device(), exe_set.handle(), 2, write_exe_sets); + + VkMemoryAllocateFlagsInfo allocate_flag_info = vku::InitStructHelper(); + allocate_flag_info.flags = VK_MEMORY_ALLOCATE_DEVICE_ADDRESS_BIT; + vkt::Buffer block_buffer(*m_device, 64, VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT, + VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, &allocate_flag_info); + + VkDeviceSize pre_process_size = 0; + { + VkGeneratedCommandsMemoryRequirementsInfoEXT dgc_mem_reqs = vku::InitStructHelper(); + dgc_mem_reqs.indirectCommandsLayout = command_layout.handle(); + dgc_mem_reqs.indirectExecutionSet = exe_set.handle(); + dgc_mem_reqs.maxSequenceCount = 1; + VkMemoryRequirements2 mem_reqs2 = vku::InitStructHelper(); + vk::GetGeneratedCommandsMemoryRequirementsEXT(device(), &dgc_mem_reqs, &mem_reqs2); + pre_process_size = mem_reqs2.memoryRequirements.size; + } + + VkBufferUsageFlags2CreateInfoKHR buffer_usage_flags = vku::InitStructHelper(); + buffer_usage_flags.usage = VK_BUFFER_USAGE_2_PREPROCESS_BUFFER_BIT_EXT | VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT; + VkBufferCreateInfo buffer_ci = vku::InitStructHelper(&buffer_usage_flags); + buffer_ci.size = pre_process_size; + vkt::Buffer pre_process_buffer(*m_device, buffer_ci, 0, &allocate_flag_info); + + uint32_t *block_buffer_ptr = (uint32_t *)block_buffer.memory().map(); + block_buffer_ptr[0] = 2; // pick pipeline 2 + VkDispatchIndirectCommand *indirect_command_ptr = (VkDispatchIndirectCommand *)(block_buffer_ptr + 1); + indirect_command_ptr->x = 1; + indirect_command_ptr->y = 1; + indirect_command_ptr->z = 1; + block_buffer.memory().unmap(); + + VkGeneratedCommandsInfoEXT generated_commands_info = vku::InitStructHelper(); + generated_commands_info.shaderStages = VK_SHADER_STAGE_COMPUTE_BIT; + generated_commands_info.indirectExecutionSet = exe_set.handle(); + generated_commands_info.indirectCommandsLayout = command_layout.handle(); + generated_commands_info.indirectAddressSize = sizeof(uint32_t) + sizeof(VkDispatchIndirectCommand); + generated_commands_info.indirectAddress = block_buffer.address(); + generated_commands_info.preprocessAddress = pre_process_buffer.address(); + generated_commands_info.preprocessSize = pre_process_size; + generated_commands_info.sequenceCountAddress = 0; + generated_commands_info.maxSequenceCount = 1; + + m_command_buffer.begin(); + vk::CmdBindPipeline(m_command_buffer.handle(), VK_PIPELINE_BIND_POINT_COMPUTE, init_pipe.Handle()); + vk::CmdExecuteGeneratedCommandsEXT(m_command_buffer.handle(), false, &generated_commands_info); + m_command_buffer.end(); + + m_errorMonitor->SetDesiredFailureMsg(kInformationBit, "IndirectExecutionSet Pipeline 2"); + m_default_queue->Submit(m_command_buffer); + m_default_queue->Wait(); + m_errorMonitor->VerifyFound(); +} \ No newline at end of file diff --git a/tests/unit/device_generated_commands.cpp b/tests/unit/device_generated_commands.cpp new file mode 100644 index 00000000000..054b2a0c02e --- /dev/null +++ b/tests/unit/device_generated_commands.cpp @@ -0,0 +1,2222 @@ +/* + * Copyright (c) 2023-2024 Valve Corporation + * Copyright (c) 2023-2024 LunarG, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + */ + +#include +#include "../framework/layer_validation_tests.h" +#include "../framework/descriptor_helper.h" +#include "../framework/pipeline_helper.h" +#include "../framework/shader_object_helper.h" + +class NegativeDeviceGeneratedCommands : public DeviceGeneratedCommandsTest {}; + +TEST_F(NegativeDeviceGeneratedCommands, MissingFeature) { + SetTargetApiVersion(VK_API_VERSION_1_1); + AddRequiredExtensions(VK_EXT_DEVICE_GENERATED_COMMANDS_EXTENSION_NAME); + RETURN_IF_SKIP(Init()); + InitRenderTarget(); + + CreatePipelineHelper pipe(*this); + pipe.CreateGraphicsPipeline(); + + vkt::PipelineLayout pipeline_layout(*m_device, {}); + VkIndirectExecutionSetPipelineInfoEXT exe_set_pipeline_info = vku::InitStructHelper(); + exe_set_pipeline_info.initialPipeline = pipe.Handle(); + exe_set_pipeline_info.maxPipelineCount = 1; + + VkIndirectExecutionSetCreateInfoEXT exe_set_ci = vku::InitStructHelper(); + exe_set_ci.type = VK_INDIRECT_EXECUTION_SET_INFO_TYPE_PIPELINES_EXT; + exe_set_ci.info.pPipelineInfo = &exe_set_pipeline_info; + + VkIndirectExecutionSetEXT exe_set; + m_errorMonitor->SetDesiredError("VUID-vkCreateIndirectExecutionSetEXT-deviceGeneratedCommands-11013"); + vk::CreateIndirectExecutionSetEXT(device(), &exe_set_ci, nullptr, &exe_set); + m_errorMonitor->VerifyFound(); + + VkIndirectCommandsLayoutTokenEXT token = vku::InitStructHelper(); + token.type = VK_INDIRECT_COMMANDS_TOKEN_TYPE_DRAW_EXT; + token.offset = 0; + + VkIndirectCommandsLayoutCreateInfoEXT command_layout_ci = vku::InitStructHelper(); + command_layout_ci.shaderStages = VK_SHADER_STAGE_VERTEX_BIT; + command_layout_ci.pipelineLayout = VK_NULL_HANDLE; + command_layout_ci.tokenCount = 1; + command_layout_ci.pTokens = &token; + + VkIndirectCommandsLayoutEXT command_layout; + m_errorMonitor->SetDesiredError("VUID-vkCreateIndirectCommandsLayoutEXT-deviceGeneratedCommands-11089"); + vk::CreateIndirectCommandsLayoutEXT(device(), &command_layout_ci, nullptr, &command_layout); + m_errorMonitor->VerifyFound(); +} + +TEST_F(NegativeDeviceGeneratedCommands, NullPipelineInfo) { + RETURN_IF_SKIP(InitBasicDeviceGeneratedCommands()); + + VkIndirectExecutionSetCreateInfoEXT exe_set_ci = vku::InitStructHelper(); + exe_set_ci.type = VK_INDIRECT_EXECUTION_SET_INFO_TYPE_PIPELINES_EXT; + exe_set_ci.info.pPipelineInfo = nullptr; + + VkIndirectExecutionSetEXT exe_set; + m_errorMonitor->SetDesiredError("VUID-VkIndirectExecutionSetCreateInfoEXT-pPipelineInfo-parameter"); + vk::CreateIndirectExecutionSetEXT(device(), &exe_set_ci, nullptr, &exe_set); + m_errorMonitor->VerifyFound(); +} + +TEST_F(NegativeDeviceGeneratedCommands, IndirectCommandShaderStageBinding) { + SetTargetApiVersion(VK_API_VERSION_1_1); + AddRequiredExtensions(VK_EXT_DEVICE_GENERATED_COMMANDS_EXTENSION_NAME); + AddRequiredExtensions(VK_KHR_BUFFER_DEVICE_ADDRESS_EXTENSION_NAME); + AddRequiredFeature(vkt::Feature::deviceGeneratedCommands); + AddRequiredFeature(vkt::Feature::bufferDeviceAddress); + RETURN_IF_SKIP(Init()); + InitRenderTarget(); + + VkPhysicalDeviceDeviceGeneratedCommandsPropertiesEXT dgc_props = vku::InitStructHelper(); + GetPhysicalDeviceProperties2(dgc_props); + if (dgc_props.supportedIndirectCommandsShaderStagesPipelineBinding & VK_SHADER_STAGE_COMPUTE_BIT) { + GTEST_SKIP() << "VK_SHADER_STAGE_COMPUTE_BIT is supported."; + } + + VkPipelineCreateFlags2CreateInfoKHR pipe_flags2 = vku::InitStructHelper(); + pipe_flags2.flags = VK_PIPELINE_CREATE_2_INDIRECT_BINDABLE_BIT_EXT; + CreatePipelineHelper pipe(*this, &pipe_flags2); + pipe.CreateGraphicsPipeline(); + + vkt::PipelineLayout pipeline_layout(*m_device, {}); + VkIndirectExecutionSetPipelineInfoEXT exe_set_pipeline_info = vku::InitStructHelper(); + exe_set_pipeline_info.initialPipeline = pipe.Handle(); + exe_set_pipeline_info.maxPipelineCount = 1; + + VkIndirectExecutionSetCreateInfoEXT exe_set_ci = vku::InitStructHelper(); + exe_set_ci.type = VK_INDIRECT_EXECUTION_SET_INFO_TYPE_PIPELINES_EXT; + exe_set_ci.info.pPipelineInfo = &exe_set_pipeline_info; + + VkIndirectExecutionSetEXT exe_set; + m_errorMonitor->SetDesiredError( + "VUID-VkIndirectExecutionSetPipelineInfoEXT-supportedIndirectCommandsShaderStagesPipelineBinding-11015"); + vk::CreateIndirectExecutionSetEXT(device(), &exe_set_ci, nullptr, &exe_set); + m_errorMonitor->VerifyFound(); +} + +TEST_F(NegativeDeviceGeneratedCommands, IndirectCommandMaxPipelineCount) { + RETURN_IF_SKIP(InitBasicDeviceGeneratedCommands()); + InitRenderTarget(); + + VkPipelineCreateFlags2CreateInfoKHR pipe_flags2 = vku::InitStructHelper(); + pipe_flags2.flags = VK_PIPELINE_CREATE_2_INDIRECT_BINDABLE_BIT_EXT; + CreatePipelineHelper pipe(*this, &pipe_flags2); + pipe.CreateGraphicsPipeline(); + + vkt::PipelineLayout pipeline_layout(*m_device, {}); + VkIndirectExecutionSetPipelineInfoEXT exe_set_pipeline_info = vku::InitStructHelper(); + exe_set_pipeline_info.initialPipeline = pipe.Handle(); + exe_set_pipeline_info.maxPipelineCount = 0; + + VkIndirectExecutionSetCreateInfoEXT exe_set_ci = vku::InitStructHelper(); + exe_set_ci.type = VK_INDIRECT_EXECUTION_SET_INFO_TYPE_PIPELINES_EXT; + exe_set_ci.info.pPipelineInfo = &exe_set_pipeline_info; + + VkIndirectExecutionSetEXT exe_set; + m_errorMonitor->SetDesiredError("VUID-VkIndirectExecutionSetPipelineInfoEXT-maxPipelineCount-11018"); + vk::CreateIndirectExecutionSetEXT(device(), &exe_set_ci, nullptr, &exe_set); + m_errorMonitor->VerifyFound(); + + VkPhysicalDeviceDeviceGeneratedCommandsPropertiesEXT dgc_props = vku::InitStructHelper(); + GetPhysicalDeviceProperties2(dgc_props); + exe_set_pipeline_info.maxPipelineCount = dgc_props.maxIndirectPipelineCount + 1; + m_errorMonitor->SetDesiredError("VUID-VkIndirectExecutionSetPipelineInfoEXT-maxPipelineCount-11018"); + vk::CreateIndirectExecutionSetEXT(device(), &exe_set_ci, nullptr, &exe_set); + m_errorMonitor->VerifyFound(); +} + +TEST_F(NegativeDeviceGeneratedCommands, IndirectCommandDescriptorType) { + RETURN_IF_SKIP(InitBasicDeviceGeneratedCommands()); + InitRenderTarget(); + + OneOffDescriptorSet descriptor_set_0(m_device, + { + {0, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1, VK_SHADER_STAGE_ALL, nullptr}, + }); + OneOffDescriptorSet descriptor_set_1(m_device, + { + {0, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1, VK_SHADER_STAGE_ALL, nullptr}, + {2, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC, 1, VK_SHADER_STAGE_ALL, nullptr}, + }); + vkt::PipelineLayout pipeline_layout(*m_device, {&descriptor_set_0.layout_, &descriptor_set_1.layout_}); + + VkPipelineCreateFlags2CreateInfoKHR pipe_flags2 = vku::InitStructHelper(); + pipe_flags2.flags = VK_PIPELINE_CREATE_2_INDIRECT_BINDABLE_BIT_EXT; + CreatePipelineHelper pipe(*this, &pipe_flags2); + pipe.gp_ci_.layout = pipeline_layout.handle(); + pipe.CreateGraphicsPipeline(); + + VkIndirectExecutionSetPipelineInfoEXT exe_set_pipeline_info = vku::InitStructHelper(); + exe_set_pipeline_info.initialPipeline = pipe.Handle(); + exe_set_pipeline_info.maxPipelineCount = 1; + + VkIndirectExecutionSetCreateInfoEXT exe_set_ci = vku::InitStructHelper(); + exe_set_ci.type = VK_INDIRECT_EXECUTION_SET_INFO_TYPE_PIPELINES_EXT; + exe_set_ci.info.pPipelineInfo = &exe_set_pipeline_info; + + VkIndirectExecutionSetEXT exe_set; + m_errorMonitor->SetDesiredError("VUID-VkIndirectExecutionSetPipelineInfoEXT-initialPipeline-11019"); + vk::CreateIndirectExecutionSetEXT(device(), &exe_set_ci, nullptr, &exe_set); + m_errorMonitor->VerifyFound(); +} + +TEST_F(NegativeDeviceGeneratedCommands, IndirectCommandsShaderStages) { + RETURN_IF_SKIP(InitBasicDeviceGeneratedCommands()); + + VkPhysicalDeviceDeviceGeneratedCommandsPropertiesEXT dgc_props = vku::InitStructHelper(); + GetPhysicalDeviceProperties2(dgc_props); + if (dgc_props.supportedIndirectCommandsShaderStages & VK_SHADER_STAGE_GEOMETRY_BIT) { + GTEST_SKIP() << "VK_SHADER_STAGE_GEOMETRY_BIT is supported."; + } + + VkIndirectCommandsLayoutTokenEXT token = vku::InitStructHelper(); + token.type = VK_INDIRECT_COMMANDS_TOKEN_TYPE_DRAW_EXT; + token.offset = 0; + + VkIndirectCommandsLayoutCreateInfoEXT command_layout_ci = vku::InitStructHelper(); + command_layout_ci.shaderStages = VK_SHADER_STAGE_GEOMETRY_BIT; + command_layout_ci.pipelineLayout = VK_NULL_HANDLE; + command_layout_ci.tokenCount = 1; + command_layout_ci.pTokens = &token; + + VkIndirectCommandsLayoutEXT command_layout; + m_errorMonitor->SetDesiredError("VUID-VkIndirectCommandsLayoutCreateInfoEXT-shaderStages-11091"); + vk::CreateIndirectCommandsLayoutEXT(device(), &command_layout_ci, nullptr, &command_layout); + m_errorMonitor->VerifyFound(); + + command_layout_ci.shaderStages = VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_GEOMETRY_BIT; + m_errorMonitor->SetDesiredError("VUID-VkIndirectCommandsLayoutCreateInfoEXT-shaderStages-11091"); + vk::CreateIndirectCommandsLayoutEXT(device(), &command_layout_ci, nullptr, &command_layout); + m_errorMonitor->VerifyFound(); +} + +TEST_F(NegativeDeviceGeneratedCommands, IndirectCommandsNonGraphics) { + RETURN_IF_SKIP(InitBasicDeviceGeneratedCommands()); + + VkIndirectCommandsVertexBufferTokenEXT vertex_buffer_token = {0}; + VkIndirectCommandsLayoutTokenEXT tokens[2]; + tokens[0] = vku::InitStructHelper(); + tokens[0].type = VK_INDIRECT_COMMANDS_TOKEN_TYPE_VERTEX_BUFFER_EXT; + tokens[0].data.pVertexBuffer = &vertex_buffer_token; + tokens[0].offset = 0; + + tokens[1] = vku::InitStructHelper(); + tokens[1].type = VK_INDIRECT_COMMANDS_TOKEN_TYPE_DRAW_EXT; + tokens[1].offset = 8; + + VkIndirectCommandsLayoutCreateInfoEXT command_layout_ci = vku::InitStructHelper(); + command_layout_ci.shaderStages = VK_SHADER_STAGE_COMPUTE_BIT; + command_layout_ci.pipelineLayout = VK_NULL_HANDLE; + command_layout_ci.tokenCount = 2; + command_layout_ci.pTokens = tokens; + // One for each token + m_errorMonitor->SetDesiredError("VUID-VkIndirectCommandsLayoutCreateInfoEXT-shaderStages-11110", 2); + m_errorMonitor->SetDesiredError("VUID-VkIndirectCommandsLayoutCreateInfoEXT-pTokens-11104", 2); + vkt::IndirectCommandsLayout command_layout(*m_device, command_layout_ci); + m_errorMonitor->VerifyFound(); +} + +TEST_F(NegativeDeviceGeneratedCommands, IndirectCommandsNullUnionPointer) { + RETURN_IF_SKIP(InitBasicDeviceGeneratedCommands()); + + VkIndirectCommandsLayoutTokenEXT tokens[2]; + tokens[0] = vku::InitStructHelper(); + tokens[0].type = VK_INDIRECT_COMMANDS_TOKEN_TYPE_VERTEX_BUFFER_EXT; + tokens[0].data.pVertexBuffer = nullptr; // is null for all types + tokens[0].offset = 0; + + tokens[1] = vku::InitStructHelper(); + tokens[1].type = VK_INDIRECT_COMMANDS_TOKEN_TYPE_DRAW_EXT; + tokens[1].offset = 8; + + VkIndirectCommandsLayoutCreateInfoEXT command_layout_ci = vku::InitStructHelper(); + command_layout_ci.shaderStages = VK_SHADER_STAGE_VERTEX_BIT; + command_layout_ci.pipelineLayout = VK_NULL_HANDLE; + command_layout_ci.tokenCount = 2; + command_layout_ci.pTokens = tokens; + + { + m_errorMonitor->SetDesiredError("VUID-VkIndirectCommandsLayoutTokenEXT-pVertexBuffer-parameter"); + vkt::IndirectCommandsLayout command_layout(*m_device, command_layout_ci); + m_errorMonitor->VerifyFound(); + } + + { + tokens[0].type = VK_INDIRECT_COMMANDS_TOKEN_TYPE_INDEX_BUFFER_EXT; + m_errorMonitor->SetDesiredError("VUID-VkIndirectCommandsLayoutTokenEXT-pIndexBuffer-parameter"); + vkt::IndirectCommandsLayout command_layout(*m_device, command_layout_ci); + m_errorMonitor->VerifyFound(); + } + + { + tokens[0].type = VK_INDIRECT_COMMANDS_TOKEN_TYPE_EXECUTION_SET_EXT; + m_errorMonitor->SetDesiredError("VUID-VkIndirectCommandsLayoutTokenEXT-pExecutionSet-parameter"); + vkt::IndirectCommandsLayout command_layout(*m_device, command_layout_ci); + m_errorMonitor->VerifyFound(); + } + + { + tokens[0].type = VK_INDIRECT_COMMANDS_TOKEN_TYPE_PUSH_CONSTANT_EXT; + m_errorMonitor->SetDesiredError("VUID-VkIndirectCommandsLayoutTokenEXT-pPushConstant-parameter"); + vkt::IndirectCommandsLayout command_layout(*m_device, command_layout_ci); + m_errorMonitor->VerifyFound(); + } + + { + tokens[0].type = VK_INDIRECT_COMMANDS_TOKEN_TYPE_SEQUENCE_INDEX_EXT; + m_errorMonitor->SetDesiredError("VUID-VkIndirectCommandsLayoutTokenEXT-pPushConstant-parameter"); + vkt::IndirectCommandsLayout command_layout(*m_device, command_layout_ci); + m_errorMonitor->VerifyFound(); + } +} + +TEST_F(NegativeDeviceGeneratedCommands, MaxIndirectCommandsTokenCount) { + RETURN_IF_SKIP(InitBasicDeviceGeneratedCommands()); + + VkPhysicalDeviceDeviceGeneratedCommandsPropertiesEXT dgc_props = vku::InitStructHelper(); + GetPhysicalDeviceProperties2(dgc_props); + const uint32_t over_max = dgc_props.maxIndirectCommandsTokenCount + 1; + + std::vector tokens(over_max); + for (uint32_t i = 0; i < over_max; i++) { + tokens[i] = vku::InitStructHelper(); + tokens[i].type = VK_INDIRECT_COMMANDS_TOKEN_TYPE_DRAW_EXT; + tokens[i].offset = 0; + } + + VkIndirectCommandsLayoutCreateInfoEXT command_layout_ci = vku::InitStructHelper(); + command_layout_ci.shaderStages = VK_SHADER_STAGE_VERTEX_BIT; + command_layout_ci.pipelineLayout = VK_NULL_HANDLE; + command_layout_ci.tokenCount = 0; + command_layout_ci.pTokens = tokens.data(); + + VkIndirectCommandsLayoutEXT command_layout; + m_errorMonitor->SetDesiredError("VUID-VkIndirectCommandsLayoutCreateInfoEXT-tokenCount-arraylength"); + vk::CreateIndirectCommandsLayoutEXT(device(), &command_layout_ci, nullptr, &command_layout); + m_errorMonitor->VerifyFound(); + + command_layout_ci.tokenCount = over_max; + m_errorMonitor->SetDesiredError("VUID-VkIndirectCommandsLayoutCreateInfoEXT-tokenCount-11092"); + vk::CreateIndirectCommandsLayoutEXT(device(), &command_layout_ci, nullptr, &command_layout); + m_errorMonitor->VerifyFound(); +} + +TEST_F(NegativeDeviceGeneratedCommands, NonActionTokens) { + RETURN_IF_SKIP(InitBasicDeviceGeneratedCommands()); + + VkIndirectCommandsExecutionSetTokenEXT exe_set_token = {VK_INDIRECT_EXECUTION_SET_INFO_TYPE_PIPELINES_EXT, + VK_SHADER_STAGE_COMPUTE_BIT}; + + VkIndirectCommandsLayoutTokenEXT token = vku::InitStructHelper(); + token.type = VK_INDIRECT_COMMANDS_TOKEN_TYPE_EXECUTION_SET_EXT; + token.data.pExecutionSet = &exe_set_token; + token.offset = 0; + + VkIndirectCommandsLayoutCreateInfoEXT command_layout_ci = vku::InitStructHelper(); + command_layout_ci.shaderStages = VK_SHADER_STAGE_COMPUTE_BIT; + command_layout_ci.pipelineLayout = VK_NULL_HANDLE; + command_layout_ci.tokenCount = 1; + command_layout_ci.pTokens = &token; + + VkIndirectCommandsLayoutEXT command_layout; + m_errorMonitor->SetDesiredError("VUID-VkIndirectCommandsLayoutCreateInfoEXT-pTokens-11100"); + vk::CreateIndirectCommandsLayoutEXT(device(), &command_layout_ci, nullptr, &command_layout); + m_errorMonitor->VerifyFound(); +} + +TEST_F(NegativeDeviceGeneratedCommands, EndWithExecutionSetToken) { + RETURN_IF_SKIP(InitBasicDeviceGeneratedCommands()); + + VkIndirectCommandsExecutionSetTokenEXT exe_set_token = {VK_INDIRECT_EXECUTION_SET_INFO_TYPE_PIPELINES_EXT, + VK_SHADER_STAGE_COMPUTE_BIT}; + + VkIndirectCommandsLayoutTokenEXT tokens[4]; + tokens[0] = vku::InitStructHelper(); + tokens[0].type = VK_INDIRECT_COMMANDS_TOKEN_TYPE_EXECUTION_SET_EXT; + tokens[0].data.pExecutionSet = &exe_set_token; + tokens[0].offset = 0; + tokens[1] = tokens[0]; + tokens[2] = tokens[0]; + tokens[3] = tokens[0]; + + tokens[1].type = VK_INDIRECT_COMMANDS_TOKEN_TYPE_DISPATCH_EXT; + tokens[3].type = VK_INDIRECT_COMMANDS_TOKEN_TYPE_DISPATCH_EXT; + + VkIndirectCommandsLayoutCreateInfoEXT command_layout_ci = vku::InitStructHelper(); + command_layout_ci.shaderStages = VK_SHADER_STAGE_COMPUTE_BIT; + command_layout_ci.pipelineLayout = VK_NULL_HANDLE; + command_layout_ci.tokenCount = 4; + command_layout_ci.pTokens = tokens; + + VkIndirectCommandsLayoutEXT command_layout; + m_errorMonitor->SetDesiredError("VUID-VkIndirectCommandsLayoutCreateInfoEXT-pTokens-11093"); + vk::CreateIndirectCommandsLayoutEXT(device(), &command_layout_ci, nullptr, &command_layout); + m_errorMonitor->VerifyFound(); +} + +TEST_F(NegativeDeviceGeneratedCommands, NullPipelineLayout) { + AddRequiredFeature(vkt::Feature::dynamicGeneratedPipelineLayout); + RETURN_IF_SKIP(InitBasicDeviceGeneratedCommands()); + + VkIndirectCommandsPushConstantTokenEXT pc_token; + pc_token.updateRange = {VK_SHADER_STAGE_VERTEX_BIT, 4, 0}; + + VkIndirectCommandsLayoutTokenEXT token = vku::InitStructHelper(); + token.type = VK_INDIRECT_COMMANDS_TOKEN_TYPE_PUSH_CONSTANT_EXT; + token.data.pPushConstant = &pc_token; + token.offset = 0; + + VkIndirectCommandsLayoutCreateInfoEXT command_layout_ci = vku::InitStructHelper(); + command_layout_ci.shaderStages = VK_SHADER_STAGE_VERTEX_BIT; + command_layout_ci.pipelineLayout = VK_NULL_HANDLE; + command_layout_ci.tokenCount = 1; + command_layout_ci.pTokens = &token; + + VkIndirectCommandsLayoutEXT command_layout; + m_errorMonitor->SetDesiredError("VUID-VkIndirectCommandsLayoutCreateInfoEXT-pTokens-11102"); + m_errorMonitor->SetDesiredError("VUID-VkIndirectCommandsLayoutCreateInfoEXT-pTokens-11100"); + vk::CreateIndirectCommandsLayoutEXT(device(), &command_layout_ci, nullptr, &command_layout); + m_errorMonitor->VerifyFound(); +} + +TEST_F(NegativeDeviceGeneratedCommands, MissingVertex) { + RETURN_IF_SKIP(InitBasicDeviceGeneratedCommands()); + + VkIndirectCommandsLayoutTokenEXT token = vku::InitStructHelper(); + token.type = VK_INDIRECT_COMMANDS_TOKEN_TYPE_DRAW_EXT; + token.offset = 0; + + VkIndirectCommandsLayoutCreateInfoEXT command_layout_ci = vku::InitStructHelper(); + command_layout_ci.shaderStages = VK_SHADER_STAGE_FRAGMENT_BIT; + command_layout_ci.pipelineLayout = VK_NULL_HANDLE; + command_layout_ci.tokenCount = 1; + command_layout_ci.pTokens = &token; + + VkIndirectCommandsLayoutEXT command_layout; + m_errorMonitor->SetDesiredError("VUID-VkIndirectCommandsLayoutCreateInfoEXT-shaderStages-11113"); + vk::CreateIndirectCommandsLayoutEXT(device(), &command_layout_ci, nullptr, &command_layout); + m_errorMonitor->VerifyFound(); +} + +TEST_F(NegativeDeviceGeneratedCommands, TokenOffsetDecrease) { + RETURN_IF_SKIP(InitBasicDeviceGeneratedCommands()); + + VkIndirectCommandsLayoutTokenEXT tokens[2]; + tokens[0] = vku::InitStructHelper(); + tokens[0].type = VK_INDIRECT_COMMANDS_TOKEN_TYPE_DRAW_EXT; + tokens[0].offset = 4; + + tokens[1] = tokens[0]; + tokens[1].offset = 0; + + VkIndirectCommandsLayoutCreateInfoEXT command_layout_ci = vku::InitStructHelper(); + command_layout_ci.shaderStages = VK_SHADER_STAGE_VERTEX_BIT; + command_layout_ci.pipelineLayout = VK_NULL_HANDLE; + command_layout_ci.tokenCount = 2; + command_layout_ci.pTokens = tokens; + + VkIndirectCommandsLayoutEXT command_layout; + m_errorMonitor->SetDesiredError("VUID-VkIndirectCommandsLayoutCreateInfoEXT-pTokens-11103"); + vk::CreateIndirectCommandsLayoutEXT(device(), &command_layout_ci, nullptr, &command_layout); + m_errorMonitor->VerifyFound(); +} + +TEST_F(NegativeDeviceGeneratedCommands, TokenOffsetLimit) { + RETURN_IF_SKIP(InitBasicDeviceGeneratedCommands()); + + VkPhysicalDeviceDeviceGeneratedCommandsPropertiesEXT dgc_props = vku::InitStructHelper(); + GetPhysicalDeviceProperties2(dgc_props); + + VkIndirectCommandsLayoutTokenEXT token = vku::InitStructHelper(); + token.type = VK_INDIRECT_COMMANDS_TOKEN_TYPE_DRAW_EXT; + token.offset = dgc_props.maxIndirectCommandsTokenOffset + 1; + + VkIndirectCommandsLayoutCreateInfoEXT command_layout_ci = vku::InitStructHelper(); + command_layout_ci.shaderStages = VK_SHADER_STAGE_VERTEX_BIT; + command_layout_ci.pipelineLayout = VK_NULL_HANDLE; + command_layout_ci.tokenCount = 1; + command_layout_ci.pTokens = &token; + + VkIndirectCommandsLayoutEXT command_layout; + m_errorMonitor->SetDesiredError("VUID-VkIndirectCommandsLayoutTokenEXT-offset-11124"); + m_errorMonitor->SetUnexpectedError("VUID-VkIndirectCommandsLayoutTokenEXT-offset-11125"); + vk::CreateIndirectCommandsLayoutEXT(device(), &command_layout_ci, nullptr, &command_layout); + m_errorMonitor->VerifyFound(); +} + +TEST_F(NegativeDeviceGeneratedCommands, PushConstantNoStage) { + RETURN_IF_SKIP(InitBasicDeviceGeneratedCommands()); + + VkIndirectCommandsPushConstantTokenEXT pc_token; + pc_token.updateRange = {VK_SHADER_STAGE_FRAGMENT_BIT, 4, 8}; + + VkIndirectCommandsLayoutTokenEXT tokens[2]; + tokens[0] = vku::InitStructHelper(); + tokens[0].type = VK_INDIRECT_COMMANDS_TOKEN_TYPE_PUSH_CONSTANT_EXT; + tokens[0].data.pPushConstant = &pc_token; + tokens[0].offset = 0; + + tokens[1] = vku::InitStructHelper(); + tokens[1].type = VK_INDIRECT_COMMANDS_TOKEN_TYPE_DRAW_EXT; + tokens[1].offset = 8; + + const std::vector pc_range = {{VK_SHADER_STAGE_VERTEX_BIT, 16, 64}}; + vkt::PipelineLayout pipeline_layout(*m_device, {}, pc_range); + VkIndirectCommandsLayoutCreateInfoEXT command_layout_ci = vku::InitStructHelper(); + command_layout_ci.shaderStages = VK_SHADER_STAGE_VERTEX_BIT; + command_layout_ci.pipelineLayout = pipeline_layout.handle(); + command_layout_ci.tokenCount = 2; + command_layout_ci.pTokens = tokens; + + VkIndirectCommandsLayoutEXT command_layout; + m_errorMonitor->SetDesiredError("VUID-VkIndirectCommandsPushConstantTokenEXT-updateRange-11132"); + vk::CreateIndirectCommandsLayoutEXT(device(), &command_layout_ci, nullptr, &command_layout); + m_errorMonitor->VerifyFound(); +} + +TEST_F(NegativeDeviceGeneratedCommands, PushConstantOutOfRange) { + RETURN_IF_SKIP(InitBasicDeviceGeneratedCommands()); + + VkIndirectCommandsPushConstantTokenEXT pc_token; + pc_token.updateRange = {VK_SHADER_STAGE_VERTEX_BIT, 4, 8}; + + VkIndirectCommandsLayoutTokenEXT tokens[2]; + tokens[0] = vku::InitStructHelper(); + tokens[0].type = VK_INDIRECT_COMMANDS_TOKEN_TYPE_PUSH_CONSTANT_EXT; + tokens[0].data.pPushConstant = &pc_token; + tokens[0].offset = 0; + + tokens[1] = vku::InitStructHelper(); + tokens[1].type = VK_INDIRECT_COMMANDS_TOKEN_TYPE_DRAW_EXT; + tokens[1].offset = 8; + + const std::vector pc_range = {{VK_SHADER_STAGE_VERTEX_BIT, 16, 64}}; + vkt::PipelineLayout pipeline_layout(*m_device, {}, pc_range); + VkIndirectCommandsLayoutCreateInfoEXT command_layout_ci = vku::InitStructHelper(); + command_layout_ci.shaderStages = VK_SHADER_STAGE_VERTEX_BIT; + command_layout_ci.pipelineLayout = pipeline_layout.handle(); + command_layout_ci.tokenCount = 2; + command_layout_ci.pTokens = tokens; + + VkIndirectCommandsLayoutEXT command_layout; + m_errorMonitor->SetDesiredError("VUID-VkIndirectCommandsPushConstantTokenEXT-updateRange-11132"); + vk::CreateIndirectCommandsLayoutEXT(device(), &command_layout_ci, nullptr, &command_layout); + m_errorMonitor->VerifyFound(); +} + +TEST_F(NegativeDeviceGeneratedCommands, PushConstantOutOfRangeDynamic) { + AddRequiredFeature(vkt::Feature::dynamicGeneratedPipelineLayout); + RETURN_IF_SKIP(InitBasicDeviceGeneratedCommands()); + + VkIndirectCommandsPushConstantTokenEXT pc_token; + pc_token.updateRange = {VK_SHADER_STAGE_VERTEX_BIT, 4, 8}; + + VkIndirectCommandsLayoutTokenEXT tokens[2]; + tokens[0] = vku::InitStructHelper(); + tokens[0].type = VK_INDIRECT_COMMANDS_TOKEN_TYPE_PUSH_CONSTANT_EXT; + tokens[0].data.pPushConstant = &pc_token; + tokens[0].offset = 0; + + tokens[1] = vku::InitStructHelper(); + tokens[1].type = VK_INDIRECT_COMMANDS_TOKEN_TYPE_DRAW_EXT; + tokens[1].offset = 8; + + VkPushConstantRange pc_range = {VK_SHADER_STAGE_VERTEX_BIT, 16, 64}; + VkPipelineLayoutCreateInfo layout_ci = vku::InitStructHelper(); + layout_ci.pushConstantRangeCount = 1; + layout_ci.pPushConstantRanges = &pc_range; + + VkIndirectCommandsLayoutCreateInfoEXT command_layout_ci = vku::InitStructHelper(&layout_ci); + command_layout_ci.shaderStages = VK_SHADER_STAGE_VERTEX_BIT; + command_layout_ci.pipelineLayout = VK_NULL_HANDLE; + command_layout_ci.tokenCount = 2; + command_layout_ci.pTokens = tokens; + + VkIndirectCommandsLayoutEXT command_layout; + m_errorMonitor->SetDesiredError("VUID-VkIndirectCommandsPushConstantTokenEXT-updateRange-11132"); + vk::CreateIndirectCommandsLayoutEXT(device(), &command_layout_ci, nullptr, &command_layout); + m_errorMonitor->VerifyFound(); +} + +TEST_F(NegativeDeviceGeneratedCommands, PushConstantMultipleTokens) { + RETURN_IF_SKIP(InitBasicDeviceGeneratedCommands()); + + VkIndirectCommandsPushConstantTokenEXT pc_token_0; + pc_token_0.updateRange = {VK_SHADER_STAGE_VERTEX_BIT, 4, 16}; + + VkIndirectCommandsPushConstantTokenEXT pc_token_1; + pc_token_1.updateRange = {VK_SHADER_STAGE_VERTEX_BIT, 0, 8}; + + VkIndirectCommandsLayoutTokenEXT tokens[3]; + tokens[0] = vku::InitStructHelper(); + tokens[0].type = VK_INDIRECT_COMMANDS_TOKEN_TYPE_PUSH_CONSTANT_EXT; + tokens[0].data.pPushConstant = &pc_token_0; + tokens[0].offset = 0; + + tokens[1] = vku::InitStructHelper(); + tokens[1].type = VK_INDIRECT_COMMANDS_TOKEN_TYPE_PUSH_CONSTANT_EXT; + tokens[1].data.pPushConstant = &pc_token_1; + tokens[1].offset = 8; + + tokens[2] = vku::InitStructHelper(); + tokens[2].type = VK_INDIRECT_COMMANDS_TOKEN_TYPE_DRAW_EXT; + tokens[2].offset = 16; + + const std::vector pc_range = {{VK_SHADER_STAGE_VERTEX_BIT, 0, 64}}; + vkt::PipelineLayout pipeline_layout(*m_device, {}, pc_range); + VkIndirectCommandsLayoutCreateInfoEXT command_layout_ci = vku::InitStructHelper(); + command_layout_ci.shaderStages = VK_SHADER_STAGE_VERTEX_BIT; + command_layout_ci.pipelineLayout = pipeline_layout.handle(); + command_layout_ci.tokenCount = 3; + command_layout_ci.pTokens = tokens; + + VkIndirectCommandsLayoutEXT command_layout; + m_errorMonitor->SetDesiredError("VUID-VkIndirectCommandsLayoutCreateInfoEXT-pTokens-11099"); + vk::CreateIndirectCommandsLayoutEXT(device(), &command_layout_ci, nullptr, &command_layout); + m_errorMonitor->VerifyFound(); +} + +TEST_F(NegativeDeviceGeneratedCommands, PushConstantSequenceIndex) { + RETURN_IF_SKIP(InitBasicDeviceGeneratedCommands()); + + VkIndirectCommandsPushConstantTokenEXT pc_token_0; + pc_token_0.updateRange = {VK_SHADER_STAGE_VERTEX_BIT, 0, 16}; + + VkIndirectCommandsPushConstantTokenEXT pc_token_1; + pc_token_1.updateRange = {VK_SHADER_STAGE_VERTEX_BIT, 0, 4}; + + VkIndirectCommandsLayoutTokenEXT tokens[3]; + tokens[0] = vku::InitStructHelper(); + tokens[0].type = VK_INDIRECT_COMMANDS_TOKEN_TYPE_PUSH_CONSTANT_EXT; + tokens[0].data.pPushConstant = &pc_token_0; + tokens[0].offset = 0; + + tokens[1] = vku::InitStructHelper(); + tokens[1].type = VK_INDIRECT_COMMANDS_TOKEN_TYPE_SEQUENCE_INDEX_EXT; + tokens[1].data.pPushConstant = &pc_token_1; + tokens[1].offset = 8; + + tokens[2] = vku::InitStructHelper(); + tokens[2].type = VK_INDIRECT_COMMANDS_TOKEN_TYPE_DRAW_EXT; + tokens[2].offset = 16; + + const std::vector pc_range = {{VK_SHADER_STAGE_VERTEX_BIT, 0, 64}}; + vkt::PipelineLayout pipeline_layout(*m_device, {}, pc_range); + VkIndirectCommandsLayoutCreateInfoEXT command_layout_ci = vku::InitStructHelper(); + command_layout_ci.shaderStages = VK_SHADER_STAGE_VERTEX_BIT; + command_layout_ci.pipelineLayout = pipeline_layout.handle(); + command_layout_ci.tokenCount = 3; + command_layout_ci.pTokens = tokens; + + VkIndirectCommandsLayoutEXT command_layout; + m_errorMonitor->SetDesiredError("VUID-VkIndirectCommandsLayoutCreateInfoEXT-pTokens-11099"); + vk::CreateIndirectCommandsLayoutEXT(device(), &command_layout_ci, nullptr, &command_layout); + m_errorMonitor->VerifyFound(); +} + +TEST_F(NegativeDeviceGeneratedCommands, CmdExecuteGeneratedCommandsSecondary) { + RETURN_IF_SKIP(InitBasicDeviceGeneratedCommands()); + InitRenderTarget(); + + VkPipelineCreateFlags2CreateInfoKHR pipe_flags2 = vku::InitStructHelper(); + pipe_flags2.flags = VK_PIPELINE_CREATE_2_INDIRECT_BINDABLE_BIT_EXT; + CreatePipelineHelper pipe(*this, &pipe_flags2); + pipe.CreateGraphicsPipeline(); + + vkt::IndirectExecutionSet exe_set(*m_device, pipe.Handle(), 1); + + VkIndirectCommandsLayoutTokenEXT token = vku::InitStructHelper(); + token.type = VK_INDIRECT_COMMANDS_TOKEN_TYPE_DRAW_EXT; + token.offset = 0; + + vkt::PipelineLayout pipeline_layout(*m_device, {}); + VkIndirectCommandsLayoutCreateInfoEXT command_layout_ci = vku::InitStructHelper(); + command_layout_ci.shaderStages = VK_SHADER_STAGE_VERTEX_BIT; + command_layout_ci.pipelineLayout = pipeline_layout.handle(); + command_layout_ci.tokenCount = 1; + command_layout_ci.pTokens = &token; + vkt::IndirectCommandsLayout command_layout(*m_device, command_layout_ci); + + VkMemoryAllocateFlagsInfo allocate_flag_info = vku::InitStructHelper(); + allocate_flag_info.flags = VK_MEMORY_ALLOCATE_DEVICE_ADDRESS_BIT; + vkt::Buffer block_buffer(*m_device, 64, VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT_KHR, + VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, &allocate_flag_info); + + VkGeneratedCommandsInfoEXT generated_commands_info = vku::InitStructHelper(); + generated_commands_info.shaderStages = VK_SHADER_STAGE_VERTEX_BIT; + generated_commands_info.indirectExecutionSet = exe_set.handle(); + generated_commands_info.indirectCommandsLayout = command_layout.handle(); + generated_commands_info.indirectAddressSize = 64; + generated_commands_info.indirectAddress = block_buffer.address(); + generated_commands_info.preprocessAddress = 0; + generated_commands_info.sequenceCountAddress = 0; + generated_commands_info.maxDrawCount = 1; + + vkt::CommandBuffer secondary(*m_device, m_command_pool, VK_COMMAND_BUFFER_LEVEL_SECONDARY); + + secondary.begin(); + m_errorMonitor->SetDesiredError("VUID-vkCmdExecuteGeneratedCommandsEXT-bufferlevel"); + vk::CmdExecuteGeneratedCommandsEXT(secondary.handle(), false, &generated_commands_info); + m_errorMonitor->VerifyFound(); + secondary.end(); +} + +TEST_F(NegativeDeviceGeneratedCommands, UpdateIESPipelineFlags) { + RETURN_IF_SKIP(InitBasicDeviceGeneratedCommands()); + InitRenderTarget(); + + CreatePipelineHelper pipe(*this); + pipe.CreateGraphicsPipeline(); + + m_errorMonitor->SetDesiredError("VUID-VkIndirectExecutionSetPipelineInfoEXT-initialPipeline-11153"); + vkt::IndirectExecutionSet exe_set(*m_device, pipe.Handle(), 2); + m_errorMonitor->VerifyFound(); +} + +TEST_F(NegativeDeviceGeneratedCommands, UpdateIESPipelineWriteCount) { + RETURN_IF_SKIP(InitBasicDeviceGeneratedCommands()); + InitRenderTarget(); + + VkPipelineCreateFlags2CreateInfoKHR pipe_flags2 = vku::InitStructHelper(); + pipe_flags2.flags = VK_PIPELINE_CREATE_2_INDIRECT_BINDABLE_BIT_EXT; + CreatePipelineHelper pipe(*this, &pipe_flags2); + pipe.CreateGraphicsPipeline(); + + vkt::IndirectExecutionSet exe_set(*m_device, pipe.Handle(), 1); + + m_errorMonitor->SetDesiredError("VUID-vkUpdateIndirectExecutionSetPipelineEXT-executionSetWriteCount-arraylength"); + vk::UpdateIndirectExecutionSetPipelineEXT(device(), exe_set.handle(), 0, nullptr); + m_errorMonitor->VerifyFound(); + + VkWriteIndirectExecutionSetPipelineEXT write_exe_sets[2]; + write_exe_sets[0] = vku::InitStructHelper(); + write_exe_sets[0].index = 0; + write_exe_sets[0].pipeline = pipe.Handle(); + write_exe_sets[1] = vku::InitStructHelper(); + write_exe_sets[1].index = 0; + write_exe_sets[1].pipeline = pipe.Handle(); + m_errorMonitor->SetDesiredError("VUID-vkUpdateIndirectExecutionSetPipelineEXT-pExecutionSetWrites-11042"); + m_errorMonitor->SetDesiredError("VUID-vkUpdateIndirectExecutionSetPipelineEXT-executionSetWriteCount-11037"); + vk::UpdateIndirectExecutionSetPipelineEXT(device(), exe_set.handle(), 2, write_exe_sets); + m_errorMonitor->VerifyFound(); +} + +TEST_F(NegativeDeviceGeneratedCommands, UpdateIESPipelineIndex) { + RETURN_IF_SKIP(InitBasicDeviceGeneratedCommands()); + InitRenderTarget(); + + VkPipelineCreateFlags2CreateInfoKHR pipe_flags2 = vku::InitStructHelper(); + pipe_flags2.flags = VK_PIPELINE_CREATE_2_INDIRECT_BINDABLE_BIT_EXT; + CreatePipelineHelper pipe(*this, &pipe_flags2); + pipe.CreateGraphicsPipeline(); + + vkt::IndirectExecutionSet exe_set(*m_device, pipe.Handle(), 2); + + VkWriteIndirectExecutionSetPipelineEXT write_exe_set = vku::InitStructHelper(); + write_exe_set.index = 2; + write_exe_set.pipeline = pipe.Handle(); + m_errorMonitor->SetDesiredError("VUID-VkWriteIndirectExecutionSetPipelineEXT-index-11026"); + vk::UpdateIndirectExecutionSetPipelineEXT(device(), exe_set.handle(), 1, &write_exe_set); + m_errorMonitor->VerifyFound(); +} + +TEST_F(NegativeDeviceGeneratedCommands, UpdateIESPipelineDynamicState) { + RETURN_IF_SKIP(InitBasicDeviceGeneratedCommands()); + InitRenderTarget(); + + VkPipelineCreateFlags2CreateInfoKHR pipe_flags2 = vku::InitStructHelper(); + pipe_flags2.flags = VK_PIPELINE_CREATE_2_INDIRECT_BINDABLE_BIT_EXT; + CreatePipelineHelper init_pipe(*this, &pipe_flags2); + init_pipe.AddDynamicState(VK_DYNAMIC_STATE_VIEWPORT); + init_pipe.CreateGraphicsPipeline(); + + CreatePipelineHelper pipe(*this, &pipe_flags2); + pipe.AddDynamicState(VK_DYNAMIC_STATE_SCISSOR); + pipe.CreateGraphicsPipeline(); + + vkt::IndirectExecutionSet exe_set(*m_device, init_pipe.Handle(), 2); + + VkWriteIndirectExecutionSetPipelineEXT write_exe_set = vku::InitStructHelper(); + write_exe_set.index = 1; + write_exe_set.pipeline = pipe.Handle(); + m_errorMonitor->SetDesiredError("VUID-vkUpdateIndirectExecutionSetPipelineEXT-None-11040"); + vk::UpdateIndirectExecutionSetPipelineEXT(device(), exe_set.handle(), 1, &write_exe_set); + m_errorMonitor->VerifyFound(); +} + +TEST_F(NegativeDeviceGeneratedCommands, UpdateIESPipelineFragmentOutput) { + RETURN_IF_SKIP(InitBasicDeviceGeneratedCommands()); + InitRenderTarget(); + + VkPipelineCreateFlags2CreateInfoKHR pipe_flags2 = vku::InitStructHelper(); + pipe_flags2.flags = VK_PIPELINE_CREATE_2_INDIRECT_BINDABLE_BIT_EXT; + CreatePipelineHelper init_pipe(*this, &pipe_flags2); + init_pipe.CreateGraphicsPipeline(); + + char const *fs_source = R"glsl( + #version 460 + layout(location = 1) out vec4 uFragColor; + void main(){ + uFragColor = vec4(0,1,0,1); + } + )glsl"; + VkShaderObj fs(this, fs_source, VK_SHADER_STAGE_FRAGMENT_BIT); + + CreatePipelineHelper pipe(*this, &pipe_flags2); + pipe.shader_stages_ = {pipe.vs_->GetStageCreateInfo(), fs.GetStageCreateInfo()}; + pipe.CreateGraphicsPipeline(); + + vkt::IndirectExecutionSet exe_set(*m_device, init_pipe.Handle(), 2); + + VkWriteIndirectExecutionSetPipelineEXT write_exe_set = vku::InitStructHelper(); + write_exe_set.index = 1; + write_exe_set.pipeline = pipe.Handle(); + m_errorMonitor->SetDesiredError("VUID-vkUpdateIndirectExecutionSetPipelineEXT-initialPipeline-11147"); + vk::UpdateIndirectExecutionSetPipelineEXT(device(), exe_set.handle(), 1, &write_exe_set); + m_errorMonitor->VerifyFound(); +} + +TEST_F(NegativeDeviceGeneratedCommands, UpdateIESPipelineFragDepth) { + RETURN_IF_SKIP(InitBasicDeviceGeneratedCommands()); + InitRenderTarget(); + + // has no FragDepth + VkPipelineCreateFlags2CreateInfoKHR pipe_flags2 = vku::InitStructHelper(); + pipe_flags2.flags = VK_PIPELINE_CREATE_2_INDIRECT_BINDABLE_BIT_EXT; + CreatePipelineHelper init_pipe(*this, &pipe_flags2); + init_pipe.CreateGraphicsPipeline(); + vkt::IndirectExecutionSet exe_set(*m_device, init_pipe.Handle(), 1); + + char const *fs_source = R"glsl( + #version 460 + layout(location = 0) out vec4 uFragColor; + void main(){ + uFragColor = vec4(0,1,0,1); + gl_FragDepth = 1.0f; + } + )glsl"; + VkShaderObj fs(this, fs_source, VK_SHADER_STAGE_FRAGMENT_BIT); + + CreatePipelineHelper pipe(*this, &pipe_flags2); + pipe.shader_stages_ = {pipe.vs_->GetStageCreateInfo(), fs.GetStageCreateInfo()}; + pipe.CreateGraphicsPipeline(); + + VkWriteIndirectExecutionSetPipelineEXT write_exe_sets = vku::InitStructHelper(); + write_exe_sets.index = 0; + write_exe_sets.pipeline = pipe.Handle(); + m_errorMonitor->SetDesiredError("VUID-vkUpdateIndirectExecutionSetPipelineEXT-initialPipeline-11098"); + vk::UpdateIndirectExecutionSetPipelineEXT(device(), exe_set.handle(), 1, &write_exe_sets); + m_errorMonitor->VerifyFound(); +} + +TEST_F(NegativeDeviceGeneratedCommands, UpdateIESPipelineSampleMask) { + RETURN_IF_SKIP(InitBasicDeviceGeneratedCommands()); + InitRenderTarget(); + + // has no SampleMask + VkPipelineCreateFlags2CreateInfoKHR pipe_flags2 = vku::InitStructHelper(); + pipe_flags2.flags = VK_PIPELINE_CREATE_2_INDIRECT_BINDABLE_BIT_EXT; + CreatePipelineHelper init_pipe(*this, &pipe_flags2); + init_pipe.CreateGraphicsPipeline(); + vkt::IndirectExecutionSet exe_set(*m_device, init_pipe.Handle(), 1); + + char const *fs_source = R"glsl( + #version 460 + layout(location = 0) out vec4 uFragColor; + void main(){ + uFragColor = vec4(0,1,0,1); + gl_SampleMask[0] = 1; + } + )glsl"; + VkShaderObj fs(this, fs_source, VK_SHADER_STAGE_FRAGMENT_BIT); + + CreatePipelineHelper pipe(*this, &pipe_flags2); + pipe.shader_stages_ = {pipe.vs_->GetStageCreateInfo(), fs.GetStageCreateInfo()}; + pipe.CreateGraphicsPipeline(); + + VkWriteIndirectExecutionSetPipelineEXT write_exe_sets = vku::InitStructHelper(); + write_exe_sets.index = 0; + write_exe_sets.pipeline = pipe.Handle(); + m_errorMonitor->SetDesiredError("VUID-vkUpdateIndirectExecutionSetPipelineEXT-initialPipeline-11086"); + vk::UpdateIndirectExecutionSetPipelineEXT(device(), exe_set.handle(), 1, &write_exe_sets); + m_errorMonitor->VerifyFound(); +} + +TEST_F(NegativeDeviceGeneratedCommands, UpdateIESPipelineStencilExportEXT) { + AddRequiredExtensions(VK_EXT_SHADER_STENCIL_EXPORT_EXTENSION_NAME); + RETURN_IF_SKIP(InitBasicDeviceGeneratedCommands()); + InitRenderTarget(); + + // has no StencilExportEXT + VkPipelineCreateFlags2CreateInfoKHR pipe_flags2 = vku::InitStructHelper(); + pipe_flags2.flags = VK_PIPELINE_CREATE_2_INDIRECT_BINDABLE_BIT_EXT; + CreatePipelineHelper init_pipe(*this, &pipe_flags2); + init_pipe.CreateGraphicsPipeline(); + vkt::IndirectExecutionSet exe_set(*m_device, init_pipe.Handle(), 1); + + char const *fs_source = R"glsl( + #version 460 + #extension GL_ARB_shader_stencil_export: enable + layout(location = 0) out vec4 uFragColor; + out int gl_FragStencilRefARB; + void main(){ + uFragColor = vec4(0,1,0,1); + gl_FragStencilRefARB = 1; + } + )glsl"; + VkShaderObj fs(this, fs_source, VK_SHADER_STAGE_FRAGMENT_BIT); + + CreatePipelineHelper pipe(*this, &pipe_flags2); + pipe.shader_stages_ = {pipe.vs_->GetStageCreateInfo(), fs.GetStageCreateInfo()}; + pipe.CreateGraphicsPipeline(); + + VkWriteIndirectExecutionSetPipelineEXT write_exe_sets = vku::InitStructHelper(); + write_exe_sets.index = 0; + write_exe_sets.pipeline = pipe.Handle(); + m_errorMonitor->SetDesiredError("VUID-vkUpdateIndirectExecutionSetPipelineEXT-initialPipeline-11085"); + vk::UpdateIndirectExecutionSetPipelineEXT(device(), exe_set.handle(), 1, &write_exe_sets); + m_errorMonitor->VerifyFound(); +} + +TEST_F(NegativeDeviceGeneratedCommands, UpdateIESPipelineShaderStages) { + AddRequiredFeature(vkt::Feature::geometryShader); + AddRequiredFeature(vkt::Feature::shaderTessellationAndGeometryPointSize); + RETURN_IF_SKIP(InitBasicDeviceGeneratedCommands()); + InitRenderTarget(); + + VkPhysicalDeviceDeviceGeneratedCommandsPropertiesEXT dgc_props = vku::InitStructHelper(); + GetPhysicalDeviceProperties2(dgc_props); + if ((dgc_props.supportedIndirectCommandsShaderStagesPipelineBinding & VK_SHADER_STAGE_GEOMETRY_BIT) == 0) { + GTEST_SKIP() << "VK_SHADER_STAGE_GEOMETRY_BIT is not supported."; + } + + // has no StencilExportEXT + VkPipelineCreateFlags2CreateInfoKHR pipe_flags2 = vku::InitStructHelper(); + pipe_flags2.flags = VK_PIPELINE_CREATE_2_INDIRECT_BINDABLE_BIT_EXT; + CreatePipelineHelper init_pipe(*this, &pipe_flags2); + init_pipe.CreateGraphicsPipeline(); + vkt::IndirectExecutionSet exe_set(*m_device, init_pipe.Handle(), 1); + + VkShaderObj gs(this, kGeometryMinimalGlsl, VK_SHADER_STAGE_GEOMETRY_BIT); + + CreatePipelineHelper pipe(*this, &pipe_flags2); + pipe.shader_stages_ = {pipe.vs_->GetStageCreateInfo(), gs.GetStageCreateInfo(), pipe.fs_->GetStageCreateInfo()}; + pipe.CreateGraphicsPipeline(); + + VkWriteIndirectExecutionSetPipelineEXT write_exe_sets = vku::InitStructHelper(); + write_exe_sets.index = 0; + write_exe_sets.pipeline = pipe.Handle(); + m_errorMonitor->SetDesiredError("VUID-vkUpdateIndirectExecutionSetPipelineEXT-initialPipeline-11152"); + vk::UpdateIndirectExecutionSetPipelineEXT(device(), exe_set.handle(), 1, &write_exe_sets); + m_errorMonitor->VerifyFound(); +} + +TEST_F(NegativeDeviceGeneratedCommands, UpdateIESMixShaderObjectPipeline) { + AddRequiredExtensions(VK_EXT_SHADER_OBJECT_EXTENSION_NAME); + AddRequiredFeature(vkt::Feature::shaderObject); + RETURN_IF_SKIP(InitBasicDeviceGeneratedCommands()); + InitRenderTarget(); + + VkPipelineCreateFlags2CreateInfoKHR pipe_flags2 = vku::InitStructHelper(); + pipe_flags2.flags = VK_PIPELINE_CREATE_2_INDIRECT_BINDABLE_BIT_EXT; + CreatePipelineHelper pipe(*this, &pipe_flags2); + pipe.CreateGraphicsPipeline(); + vkt::IndirectExecutionSet exe_set_pipeline(*m_device, pipe.Handle(), 1); + + const auto vert_spv = GLSLToSPV(VK_SHADER_STAGE_VERTEX_BIT, kVertexMinimalGlsl); + VkShaderCreateInfoEXT vert_create_info = + ShaderCreateInfoFlag(vert_spv, VK_SHADER_STAGE_VERTEX_BIT, VK_SHADER_CREATE_INDIRECT_BINDABLE_BIT_EXT); + const vkt::Shader vertShader(*m_device, vert_create_info); + const VkShaderEXT shaders[] = {vertShader.handle()}; + OneOffDescriptorSet descriptor_set(m_device, {{0, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1, VK_SHADER_STAGE_ALL, nullptr}}); + VkIndirectExecutionSetShaderLayoutInfoEXT exe_set_layouts = vku::InitStructHelper(); + exe_set_layouts.setLayoutCount = 1; + exe_set_layouts.pSetLayouts = &descriptor_set.layout_.handle(); + + VkIndirectExecutionSetShaderInfoEXT exe_set_shader_info = vku::InitStructHelper(); + exe_set_shader_info.shaderCount = 1; + exe_set_shader_info.pInitialShaders = shaders; + exe_set_shader_info.pSetLayoutInfos = &exe_set_layouts; + exe_set_shader_info.maxShaderCount = 1; + exe_set_shader_info.pushConstantRangeCount = 0; + vkt::IndirectExecutionSet exe_set_shader(*m_device, exe_set_shader_info); + + VkWriteIndirectExecutionSetShaderEXT write_exe_set_shader = vku::InitStructHelper(); + write_exe_set_shader.index = 0; + write_exe_set_shader.shader = vertShader.handle(); + m_errorMonitor->SetDesiredError("VUID-vkUpdateIndirectExecutionSetShaderEXT-indirectExecutionSet-11041"); + vk::UpdateIndirectExecutionSetShaderEXT(device(), exe_set_pipeline.handle(), 1, &write_exe_set_shader); + m_errorMonitor->VerifyFound(); + + VkWriteIndirectExecutionSetPipelineEXT write_exe_set_pipeline = vku::InitStructHelper(); + write_exe_set_pipeline.index = 0; + write_exe_set_pipeline.pipeline = pipe.Handle(); + m_errorMonitor->SetDesiredError("VUID-vkUpdateIndirectExecutionSetPipelineEXT-indirectExecutionSet-11035"); + vk::UpdateIndirectExecutionSetPipelineEXT(device(), exe_set_shader.handle(), 1, &write_exe_set_pipeline); + m_errorMonitor->VerifyFound(); +} + +TEST_F(NegativeDeviceGeneratedCommands, UpdateIESShaderObjectFlags) { + AddRequiredExtensions(VK_EXT_SHADER_OBJECT_EXTENSION_NAME); + AddRequiredFeature(vkt::Feature::shaderObject); + RETURN_IF_SKIP(InitBasicDeviceGeneratedCommands()); + InitRenderTarget(); + + const vkt::Shader vertShader(*m_device, VK_SHADER_STAGE_VERTEX_BIT, GLSLToSPV(VK_SHADER_STAGE_VERTEX_BIT, kVertexMinimalGlsl)); + const VkShaderEXT shaders[] = {vertShader.handle()}; + OneOffDescriptorSet descriptor_set(m_device, {{0, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1, VK_SHADER_STAGE_ALL, nullptr}}); + VkIndirectExecutionSetShaderLayoutInfoEXT exe_set_layouts = vku::InitStructHelper(); + exe_set_layouts.setLayoutCount = 1; + exe_set_layouts.pSetLayouts = &descriptor_set.layout_.handle(); + + VkIndirectExecutionSetShaderInfoEXT exe_set_shader_info = vku::InitStructHelper(); + exe_set_shader_info.shaderCount = 1; + exe_set_shader_info.pInitialShaders = shaders; + exe_set_shader_info.pSetLayoutInfos = &exe_set_layouts; + exe_set_shader_info.maxShaderCount = 1; + exe_set_shader_info.pushConstantRangeCount = 0; + m_errorMonitor->SetDesiredError("VUID-VkIndirectExecutionSetShaderInfoEXT-pInitialShaders-11154"); + vkt::IndirectExecutionSet exe_set(*m_device, exe_set_shader_info); + m_errorMonitor->VerifyFound(); +} + +TEST_F(NegativeDeviceGeneratedCommands, UpdateIESShaderObjectWriteCount) { + AddRequiredExtensions(VK_EXT_SHADER_OBJECT_EXTENSION_NAME); + AddRequiredFeature(vkt::Feature::shaderObject); + RETURN_IF_SKIP(InitBasicDeviceGeneratedCommands()); + InitRenderTarget(); + + const auto vert_spv = GLSLToSPV(VK_SHADER_STAGE_VERTEX_BIT, kVertexMinimalGlsl); + VkShaderCreateInfoEXT vert_create_info = + ShaderCreateInfoFlag(vert_spv, VK_SHADER_STAGE_VERTEX_BIT, VK_SHADER_CREATE_INDIRECT_BINDABLE_BIT_EXT); + const vkt::Shader vertShader(*m_device, vert_create_info); + const VkShaderEXT shaders[] = {vertShader.handle()}; + OneOffDescriptorSet descriptor_set(m_device, {{0, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1, VK_SHADER_STAGE_ALL, nullptr}}); + VkIndirectExecutionSetShaderLayoutInfoEXT exe_set_layouts = vku::InitStructHelper(); + exe_set_layouts.setLayoutCount = 1; + exe_set_layouts.pSetLayouts = &descriptor_set.layout_.handle(); + + VkIndirectExecutionSetShaderInfoEXT exe_set_shader_info = vku::InitStructHelper(); + exe_set_shader_info.shaderCount = 1; + exe_set_shader_info.pInitialShaders = shaders; + exe_set_shader_info.pSetLayoutInfos = &exe_set_layouts; + exe_set_shader_info.maxShaderCount = 1; + exe_set_shader_info.pushConstantRangeCount = 0; + vkt::IndirectExecutionSet exe_set(*m_device, exe_set_shader_info); + + m_errorMonitor->SetDesiredError("VUID-vkUpdateIndirectExecutionSetShaderEXT-executionSetWriteCount-arraylength"); + vk::UpdateIndirectExecutionSetShaderEXT(device(), exe_set.handle(), 0, nullptr); + m_errorMonitor->VerifyFound(); + + VkWriteIndirectExecutionSetShaderEXT write_exe_set = vku::InitStructHelper(); + write_exe_set.index = 1; + write_exe_set.shader = vertShader.handle(); + m_errorMonitor->SetDesiredError("VUID-VkWriteIndirectExecutionSetShaderEXT-index-11031"); + vk::UpdateIndirectExecutionSetShaderEXT(device(), exe_set.handle(), 1, &write_exe_set); + m_errorMonitor->VerifyFound(); +} + +TEST_F(NegativeDeviceGeneratedCommands, UpdateIESShaderObjectDuplicateIndex) { + AddRequiredExtensions(VK_EXT_SHADER_OBJECT_EXTENSION_NAME); + AddRequiredFeature(vkt::Feature::shaderObject); + RETURN_IF_SKIP(InitBasicDeviceGeneratedCommands()); + InitRenderTarget(); + + const auto vert_spv = GLSLToSPV(VK_SHADER_STAGE_VERTEX_BIT, kVertexMinimalGlsl); + VkShaderCreateInfoEXT vert_create_info = + ShaderCreateInfoFlag(vert_spv, VK_SHADER_STAGE_VERTEX_BIT, VK_SHADER_CREATE_INDIRECT_BINDABLE_BIT_EXT); + const vkt::Shader vertShader(*m_device, vert_create_info); + const VkShaderEXT shaders[] = {vertShader.handle()}; + OneOffDescriptorSet descriptor_set(m_device, {{0, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1, VK_SHADER_STAGE_ALL, nullptr}}); + VkIndirectExecutionSetShaderLayoutInfoEXT exe_set_layouts = vku::InitStructHelper(); + exe_set_layouts.setLayoutCount = 1; + exe_set_layouts.pSetLayouts = &descriptor_set.layout_.handle(); + + VkIndirectExecutionSetShaderInfoEXT exe_set_shader_info = vku::InitStructHelper(); + exe_set_shader_info.shaderCount = 1; + exe_set_shader_info.pInitialShaders = shaders; + exe_set_shader_info.pSetLayoutInfos = &exe_set_layouts; + exe_set_shader_info.maxShaderCount = 1; + exe_set_shader_info.pushConstantRangeCount = 0; + vkt::IndirectExecutionSet exe_set(*m_device, exe_set_shader_info); + + VkWriteIndirectExecutionSetShaderEXT write_exe_sets[2]; + write_exe_sets[0] = vku::InitStructHelper(); + write_exe_sets[0].index = 0; + write_exe_sets[0].shader = vertShader.handle(); + write_exe_sets[1] = vku::InitStructHelper(); + write_exe_sets[1].index = 0; + write_exe_sets[1].shader = vertShader.handle(); + m_errorMonitor->SetDesiredError("VUID-vkUpdateIndirectExecutionSetShaderEXT-pExecutionSetWrites-11043"); + vk::UpdateIndirectExecutionSetShaderEXT(device(), exe_set.handle(), 2, write_exe_sets); + m_errorMonitor->VerifyFound(); +} + +TEST_F(NegativeDeviceGeneratedCommands, UpdateIESShaderObjectInitialShaders) { + AddRequiredExtensions(VK_EXT_SHADER_OBJECT_EXTENSION_NAME); + AddRequiredFeature(vkt::Feature::shaderObject); + RETURN_IF_SKIP(InitBasicDeviceGeneratedCommands()); + InitRenderTarget(); + + const auto frag_spv = GLSLToSPV(VK_SHADER_STAGE_FRAGMENT_BIT, kFragmentMinimalGlsl); + VkShaderCreateInfoEXT frag_create_info = + ShaderCreateInfoFlag(frag_spv, VK_SHADER_STAGE_FRAGMENT_BIT, VK_SHADER_CREATE_INDIRECT_BINDABLE_BIT_EXT); + const vkt::Shader fragShader(*m_device, frag_create_info); + + const auto vert_spv = GLSLToSPV(VK_SHADER_STAGE_VERTEX_BIT, kVertexMinimalGlsl); + VkShaderCreateInfoEXT vert_create_info = + ShaderCreateInfoFlag(vert_spv, VK_SHADER_STAGE_VERTEX_BIT, VK_SHADER_CREATE_INDIRECT_BINDABLE_BIT_EXT); + const vkt::Shader vertShader(*m_device, vert_create_info); + + const VkShaderEXT shaders[] = {vertShader.handle()}; + OneOffDescriptorSet descriptor_set(m_device, {{0, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1, VK_SHADER_STAGE_ALL, nullptr}}); + VkIndirectExecutionSetShaderLayoutInfoEXT exe_set_layouts = vku::InitStructHelper(); + exe_set_layouts.setLayoutCount = 1; + exe_set_layouts.pSetLayouts = &descriptor_set.layout_.handle(); + + VkIndirectExecutionSetShaderInfoEXT exe_set_shader_info = vku::InitStructHelper(); + exe_set_shader_info.shaderCount = 1; + exe_set_shader_info.pInitialShaders = shaders; + exe_set_shader_info.pSetLayoutInfos = &exe_set_layouts; + exe_set_shader_info.maxShaderCount = 1; + exe_set_shader_info.pushConstantRangeCount = 0; + vkt::IndirectExecutionSet exe_set(*m_device, exe_set_shader_info); + + VkWriteIndirectExecutionSetShaderEXT write_exe_set = vku::InitStructHelper(); + write_exe_set.index = 0; + write_exe_set.shader = fragShader.handle(); + m_errorMonitor->SetDesiredError("VUID-VkWriteIndirectExecutionSetShaderEXT-pInitialShaders-11033"); + vk::UpdateIndirectExecutionSetShaderEXT(device(), exe_set.handle(), 1, &write_exe_set); + m_errorMonitor->VerifyFound(); +} + +TEST_F(NegativeDeviceGeneratedCommands, UpdateIESShaderObjectFragmentOutput) { + AddRequiredExtensions(VK_EXT_SHADER_OBJECT_EXTENSION_NAME); + AddRequiredFeature(vkt::Feature::shaderObject); + AddRequiredExtensions(VK_EXT_SHADER_STENCIL_EXPORT_EXTENSION_NAME); + RETURN_IF_SKIP(InitBasicDeviceGeneratedCommands()); + InitRenderTarget(); + + const auto frag_spv = GLSLToSPV(VK_SHADER_STAGE_FRAGMENT_BIT, kFragmentMinimalGlsl); // uses location 0 + VkShaderCreateInfoEXT frag_create_info = + ShaderCreateInfoFlag(frag_spv, VK_SHADER_STAGE_FRAGMENT_BIT, VK_SHADER_CREATE_INDIRECT_BINDABLE_BIT_EXT); + const vkt::Shader fragShader(*m_device, frag_create_info); + + const auto vert_spv = GLSLToSPV(VK_SHADER_STAGE_VERTEX_BIT, kVertexMinimalGlsl); + VkShaderCreateInfoEXT vert_create_info = + ShaderCreateInfoFlag(vert_spv, VK_SHADER_STAGE_VERTEX_BIT, VK_SHADER_CREATE_INDIRECT_BINDABLE_BIT_EXT); + const vkt::Shader vertShader(*m_device, vert_create_info); + const VkShaderEXT shaders[] = {vertShader.handle(), fragShader.handle()}; + VkIndirectExecutionSetShaderLayoutInfoEXT exe_set_layouts[2]; + exe_set_layouts[0] = vku::InitStructHelper(); + exe_set_layouts[0].setLayoutCount = 0; + exe_set_layouts[1] = vku::InitStructHelper(); + exe_set_layouts[1].setLayoutCount = 0; + + VkIndirectExecutionSetShaderInfoEXT exe_set_shader_info = vku::InitStructHelper(); + exe_set_shader_info.shaderCount = 2; + exe_set_shader_info.pInitialShaders = shaders; + exe_set_shader_info.pSetLayoutInfos = exe_set_layouts; + exe_set_shader_info.maxShaderCount = 2; + exe_set_shader_info.pushConstantRangeCount = 0; + vkt::IndirectExecutionSet exe_set(*m_device, exe_set_shader_info); + + VkWriteIndirectExecutionSetShaderEXT write_exe_set = vku::InitStructHelper(); + write_exe_set.index = 1; + + { + char const *fs_source_location = R"glsl( + #version 460 + layout(location = 1) out vec4 uFragColor; + void main(){ + uFragColor = vec4(0,1,0,1); + } + )glsl"; + + const auto frag_spv_location = GLSLToSPV(VK_SHADER_STAGE_FRAGMENT_BIT, fs_source_location); + VkShaderCreateInfoEXT frag_create_info_location = + ShaderCreateInfoFlag(frag_spv_location, VK_SHADER_STAGE_FRAGMENT_BIT, VK_SHADER_CREATE_INDIRECT_BINDABLE_BIT_EXT); + const vkt::Shader frag_shader_location(*m_device, frag_create_info_location); + write_exe_set.shader = frag_shader_location.handle(); + m_errorMonitor->SetDesiredError("VUID-vkUpdateIndirectExecutionSetShaderEXT-None-11148"); + vk::UpdateIndirectExecutionSetShaderEXT(device(), exe_set.handle(), 1, &write_exe_set); + m_errorMonitor->VerifyFound(); + } + + { + char const *fs_source_depth = R"glsl( + #version 460 + layout(location = 0) out vec4 uFragColor; + void main(){ + uFragColor = vec4(0,1,0,1); + gl_FragDepth = 1.0f; + } + )glsl"; + + const auto frag_spv_depth = GLSLToSPV(VK_SHADER_STAGE_FRAGMENT_BIT, fs_source_depth); + VkShaderCreateInfoEXT frag_create_info_depth = + ShaderCreateInfoFlag(frag_spv_depth, VK_SHADER_STAGE_FRAGMENT_BIT, VK_SHADER_CREATE_INDIRECT_BINDABLE_BIT_EXT); + const vkt::Shader frag_shader_depth(*m_device, frag_create_info_depth); + + write_exe_set.shader = frag_shader_depth.handle(); + m_errorMonitor->SetDesiredError("VUID-vkUpdateIndirectExecutionSetShaderEXT-FragDepth-11054"); + vk::UpdateIndirectExecutionSetShaderEXT(device(), exe_set.handle(), 1, &write_exe_set); + m_errorMonitor->VerifyFound(); + } + + { + char const *fs_source_mask = R"glsl( + #version 460 + layout(location = 0) out vec4 uFragColor; + void main(){ + uFragColor = vec4(0,1,0,1); + gl_SampleMask[0] = 1; + } + )glsl"; + + const auto frag_spv_mask = GLSLToSPV(VK_SHADER_STAGE_FRAGMENT_BIT, fs_source_mask); + VkShaderCreateInfoEXT frag_create_info_mask = + ShaderCreateInfoFlag(frag_spv_mask, VK_SHADER_STAGE_FRAGMENT_BIT, VK_SHADER_CREATE_INDIRECT_BINDABLE_BIT_EXT); + const vkt::Shader frag_shader_mask(*m_device, frag_create_info_mask); + + write_exe_set.shader = frag_shader_mask.handle(); + m_errorMonitor->SetDesiredError("VUID-vkUpdateIndirectExecutionSetShaderEXT-SampleMask-11050"); + vk::UpdateIndirectExecutionSetShaderEXT(device(), exe_set.handle(), 1, &write_exe_set); + m_errorMonitor->VerifyFound(); + } + + { + char const *fs_source_stencil = R"glsl( + #version 460 + #extension GL_ARB_shader_stencil_export: enable + layout(location = 0) out vec4 uFragColor; + out int gl_FragStencilRefARB; + void main(){ + uFragColor = vec4(0,1,0,1); + gl_FragStencilRefARB = 1; + } + )glsl"; + + const auto frag_spv_stencil = GLSLToSPV(VK_SHADER_STAGE_FRAGMENT_BIT, fs_source_stencil); + VkShaderCreateInfoEXT frag_create_info_stencil = + ShaderCreateInfoFlag(frag_spv_stencil, VK_SHADER_STAGE_FRAGMENT_BIT, VK_SHADER_CREATE_INDIRECT_BINDABLE_BIT_EXT); + const vkt::Shader frag_shader_stencil(*m_device, frag_create_info_stencil); + + write_exe_set.shader = frag_shader_stencil.handle(); + m_errorMonitor->SetDesiredError("VUID-vkUpdateIndirectExecutionSetShaderEXT-StencilExportEXT-11003"); + vk::UpdateIndirectExecutionSetShaderEXT(device(), exe_set.handle(), 1, &write_exe_set); + m_errorMonitor->VerifyFound(); + } +} + +TEST_F(NegativeDeviceGeneratedCommands, IESShaderObjectUniqueShaders) { + AddRequiredExtensions(VK_EXT_SHADER_OBJECT_EXTENSION_NAME); + AddRequiredFeature(vkt::Feature::shaderObject); + RETURN_IF_SKIP(InitBasicDeviceGeneratedCommands()); + InitRenderTarget(); + + const auto vert_spv = GLSLToSPV(VK_SHADER_STAGE_VERTEX_BIT, kVertexMinimalGlsl); + VkShaderCreateInfoEXT vert_create_info = + ShaderCreateInfoFlag(vert_spv, VK_SHADER_STAGE_VERTEX_BIT, VK_SHADER_CREATE_INDIRECT_BINDABLE_BIT_EXT); + const vkt::Shader vertShader(*m_device, vert_create_info); + const VkShaderEXT shaders[2] = {vertShader.handle(), vertShader.handle()}; + OneOffDescriptorSet descriptor_set(m_device, {{0, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1, VK_SHADER_STAGE_ALL, nullptr}}); + VkIndirectExecutionSetShaderLayoutInfoEXT exe_set_layouts[2]; + exe_set_layouts[0] = vku::InitStructHelper(); + exe_set_layouts[0].setLayoutCount = 1; + exe_set_layouts[0].pSetLayouts = &descriptor_set.layout_.handle(); + exe_set_layouts[1] = exe_set_layouts[0]; + + VkIndirectExecutionSetShaderInfoEXT exe_set_shader_info = vku::InitStructHelper(); + exe_set_shader_info.shaderCount = 2; + exe_set_shader_info.pInitialShaders = shaders; + exe_set_shader_info.pSetLayoutInfos = exe_set_layouts; + exe_set_shader_info.maxShaderCount = 2; + exe_set_shader_info.pushConstantRangeCount = 0; + m_errorMonitor->SetDesiredError("VUID-VkIndirectExecutionSetShaderInfoEXT-stage-11023"); + vkt::IndirectExecutionSet exe_set(*m_device, exe_set_shader_info); + m_errorMonitor->VerifyFound(); +} + +TEST_F(NegativeDeviceGeneratedCommands, IESShaderObjectMaxShaderCount) { + AddRequiredExtensions(VK_EXT_SHADER_OBJECT_EXTENSION_NAME); + AddRequiredFeature(vkt::Feature::shaderObject); + RETURN_IF_SKIP(InitBasicDeviceGeneratedCommands()); + InitRenderTarget(); + + const vkt::Shader vertShader(*m_device, VK_SHADER_STAGE_VERTEX_BIT, GLSLToSPV(VK_SHADER_STAGE_VERTEX_BIT, kVertexMinimalGlsl)); + const VkShaderEXT shaders[] = {vertShader.handle()}; + OneOffDescriptorSet descriptor_set(m_device, {{0, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1, VK_SHADER_STAGE_ALL, nullptr}}); + VkIndirectExecutionSetShaderLayoutInfoEXT exe_set_layouts = vku::InitStructHelper(); + exe_set_layouts.setLayoutCount = 1; + exe_set_layouts.pSetLayouts = &descriptor_set.layout_.handle(); + + VkIndirectExecutionSetShaderInfoEXT exe_set_shader_info = vku::InitStructHelper(); + exe_set_shader_info.shaderCount = 1; + exe_set_shader_info.pInitialShaders = shaders; + exe_set_shader_info.pSetLayoutInfos = &exe_set_layouts; + exe_set_shader_info.maxShaderCount = 0; + exe_set_shader_info.pushConstantRangeCount = 0; + + { + m_errorMonitor->SetDesiredError("VUID-VkIndirectExecutionSetShaderInfoEXT-maxShaderCount-11021"); + vkt::IndirectExecutionSet exe_set(*m_device, exe_set_shader_info); + m_errorMonitor->VerifyFound(); + } + + { + VkPhysicalDeviceDeviceGeneratedCommandsPropertiesEXT dgc_props = vku::InitStructHelper(); + GetPhysicalDeviceProperties2(dgc_props); + exe_set_shader_info.maxShaderCount = dgc_props.maxIndirectShaderObjectCount + 1; + m_errorMonitor->SetDesiredError("VUID-VkIndirectExecutionSetShaderInfoEXT-maxShaderCount-11022"); + vkt::IndirectExecutionSet exe_set(*m_device, exe_set_shader_info); + m_errorMonitor->VerifyFound(); + } +} + +TEST_F(NegativeDeviceGeneratedCommands, IESShaderObjectMaxShaderCount2) { + AddRequiredExtensions(VK_EXT_SHADER_OBJECT_EXTENSION_NAME); + AddRequiredFeature(vkt::Feature::shaderObject); + RETURN_IF_SKIP(InitBasicDeviceGeneratedCommands()); + InitRenderTarget(); + + const vkt::Shader fragShader(*m_device, VK_SHADER_STAGE_FRAGMENT_BIT, + GLSLToSPV(VK_SHADER_STAGE_FRAGMENT_BIT, kFragmentMinimalGlsl)); + const vkt::Shader vertShader(*m_device, VK_SHADER_STAGE_VERTEX_BIT, GLSLToSPV(VK_SHADER_STAGE_VERTEX_BIT, kVertexMinimalGlsl)); + const VkShaderEXT shaders[2] = {vertShader.handle(), fragShader.handle()}; + OneOffDescriptorSet descriptor_set(m_device, {{0, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1, VK_SHADER_STAGE_ALL, nullptr}}); + VkIndirectExecutionSetShaderLayoutInfoEXT exe_set_layouts[2]; + exe_set_layouts[0] = vku::InitStructHelper(); + exe_set_layouts[0].setLayoutCount = 1; + exe_set_layouts[0].pSetLayouts = &descriptor_set.layout_.handle(); + exe_set_layouts[1] = exe_set_layouts[0]; + + VkIndirectExecutionSetShaderInfoEXT exe_set_shader_info = vku::InitStructHelper(); + exe_set_shader_info.shaderCount = 2; + exe_set_shader_info.pInitialShaders = shaders; + exe_set_shader_info.pSetLayoutInfos = exe_set_layouts; + exe_set_shader_info.maxShaderCount = 1; + exe_set_shader_info.pushConstantRangeCount = 0; + + m_errorMonitor->SetDesiredError("VUID-VkIndirectExecutionSetShaderInfoEXT-maxShaderCount-11036"); + vkt::IndirectExecutionSet exe_set(*m_device, exe_set_shader_info); + m_errorMonitor->VerifyFound(); +} + +TEST_F(NegativeDeviceGeneratedCommands, GetRequirementsExecutionSetTokenStage) { + RETURN_IF_SKIP(InitBasicDeviceGeneratedCommands()); + InitRenderTarget(); + + VkIndirectCommandsExecutionSetTokenEXT exe_set_token = {VK_INDIRECT_EXECUTION_SET_INFO_TYPE_PIPELINES_EXT, + VK_SHADER_STAGE_VERTEX_BIT}; + + VkIndirectCommandsLayoutTokenEXT tokens[2]; + tokens[0] = vku::InitStructHelper(); + tokens[0].type = VK_INDIRECT_COMMANDS_TOKEN_TYPE_EXECUTION_SET_EXT; + tokens[0].data.pExecutionSet = &exe_set_token; + tokens[0].offset = 0; + + tokens[1] = vku::InitStructHelper(); + tokens[1].type = VK_INDIRECT_COMMANDS_TOKEN_TYPE_DRAW_EXT; + tokens[1].offset = 8; + + VkIndirectCommandsLayoutCreateInfoEXT command_layout_ci = vku::InitStructHelper(); + command_layout_ci.shaderStages = VK_SHADER_STAGE_VERTEX_BIT; + command_layout_ci.pipelineLayout = VK_NULL_HANDLE; + command_layout_ci.tokenCount = 2; + command_layout_ci.pTokens = tokens; + vkt::IndirectCommandsLayout command_layout(*m_device, command_layout_ci); + + VkPipelineCreateFlags2CreateInfoKHR pipe_flags2 = vku::InitStructHelper(); + pipe_flags2.flags = VK_PIPELINE_CREATE_2_INDIRECT_BINDABLE_BIT_EXT; + CreatePipelineHelper init_pipe(*this, &pipe_flags2); + init_pipe.CreateGraphicsPipeline(); // vert and frag + vkt::IndirectExecutionSet exe_set(*m_device, init_pipe.Handle(), 1); + + VkGeneratedCommandsMemoryRequirementsInfoEXT req_info = vku::InitStructHelper(); + req_info.maxSequenceCount = 1; + req_info.indirectExecutionSet = exe_set.handle(); + req_info.indirectCommandsLayout = command_layout.handle(); + + VkMemoryRequirements2 mem_req2 = vku::InitStructHelper(); + m_errorMonitor->SetDesiredError("VUID-VkGeneratedCommandsMemoryRequirementsInfoEXT-indirectCommandsLayout-11151"); + vk::GetGeneratedCommandsMemoryRequirementsEXT(device(), &req_info, &mem_req2); + m_errorMonitor->VerifyFound(); +} + +TEST_F(NegativeDeviceGeneratedCommands, GetRequirementsMaxIndirectSequenceCount) { + RETURN_IF_SKIP(InitBasicDeviceGeneratedCommands()); + InitRenderTarget(); + + VkPhysicalDeviceDeviceGeneratedCommandsPropertiesEXT dgc_props = vku::InitStructHelper(); + GetPhysicalDeviceProperties2(dgc_props); + + VkIndirectCommandsLayoutTokenEXT token = vku::InitStructHelper(); + token.type = VK_INDIRECT_COMMANDS_TOKEN_TYPE_DRAW_EXT; + token.offset = 0; + + VkIndirectCommandsLayoutCreateInfoEXT command_layout_ci = vku::InitStructHelper(); + command_layout_ci.shaderStages = VK_SHADER_STAGE_VERTEX_BIT; + command_layout_ci.pipelineLayout = VK_NULL_HANDLE; + command_layout_ci.tokenCount = 1; + command_layout_ci.pTokens = &token; + vkt::IndirectCommandsLayout command_layout(*m_device, command_layout_ci); + + VkPipelineCreateFlags2CreateInfoKHR pipe_flags2 = vku::InitStructHelper(); + pipe_flags2.flags = VK_PIPELINE_CREATE_2_INDIRECT_BINDABLE_BIT_EXT; + CreatePipelineHelper pipe(*this, &pipe_flags2); + pipe.CreateGraphicsPipeline(); + VkGeneratedCommandsPipelineInfoEXT pipeline_info = vku::InitStructHelper(); + pipeline_info.pipeline = pipe.Handle(); + + VkGeneratedCommandsMemoryRequirementsInfoEXT req_info = vku::InitStructHelper(&pipeline_info); + req_info.maxSequenceCount = dgc_props.maxIndirectSequenceCount + 1; + req_info.indirectExecutionSet = VK_NULL_HANDLE; + req_info.indirectCommandsLayout = command_layout.handle(); + + VkMemoryRequirements2 mem_req2 = vku::InitStructHelper(); + m_errorMonitor->SetDesiredError("VUID-VkGeneratedCommandsMemoryRequirementsInfoEXT-maxSequencesCount-11009"); + vk::GetGeneratedCommandsMemoryRequirementsEXT(device(), &req_info, &mem_req2); + m_errorMonitor->VerifyFound(); +} + +TEST_F(NegativeDeviceGeneratedCommands, GetRequirementsExecutionSetToken) { + RETURN_IF_SKIP(InitBasicDeviceGeneratedCommands()); + + VkIndirectCommandsExecutionSetTokenEXT exe_set_token = {VK_INDIRECT_EXECUTION_SET_INFO_TYPE_PIPELINES_EXT, + VK_SHADER_STAGE_COMPUTE_BIT}; + VkIndirectCommandsLayoutTokenEXT tokens[2]; + tokens[0] = vku::InitStructHelper(); + tokens[0].type = VK_INDIRECT_COMMANDS_TOKEN_TYPE_EXECUTION_SET_EXT; + tokens[0].data.pExecutionSet = &exe_set_token; + tokens[0].offset = 0; + + tokens[1] = vku::InitStructHelper(); + tokens[1].type = VK_INDIRECT_COMMANDS_TOKEN_TYPE_DISPATCH_EXT; + tokens[1].offset = 8; + + VkIndirectCommandsLayoutCreateInfoEXT command_layout_ci = vku::InitStructHelper(); + command_layout_ci.shaderStages = VK_SHADER_STAGE_COMPUTE_BIT; + command_layout_ci.pipelineLayout = VK_NULL_HANDLE; + command_layout_ci.tokenCount = 2; + command_layout_ci.pTokens = tokens; + + vkt::IndirectCommandsLayout command_layout(*m_device, command_layout_ci); + + VkPipelineCreateFlags2CreateInfoKHR pipe_flags2 = vku::InitStructHelper(); + pipe_flags2.flags = VK_PIPELINE_CREATE_2_INDIRECT_BINDABLE_BIT_EXT; + CreateComputePipelineHelper pipe(*this, &pipe_flags2); + pipe.CreateComputePipeline(); + VkGeneratedCommandsPipelineInfoEXT pipeline_info = vku::InitStructHelper(); + pipeline_info.pipeline = pipe.Handle(); + + VkGeneratedCommandsMemoryRequirementsInfoEXT req_info = vku::InitStructHelper(&pipeline_info); + req_info.maxSequenceCount = 1; + req_info.indirectExecutionSet = VK_NULL_HANDLE; + req_info.indirectCommandsLayout = command_layout.handle(); + + VkMemoryRequirements2 mem_req2 = vku::InitStructHelper(); + m_errorMonitor->SetDesiredError("VUID-VkGeneratedCommandsMemoryRequirementsInfoEXT-indirectCommandsLayout-11010"); + vk::GetGeneratedCommandsMemoryRequirementsEXT(device(), &req_info, &mem_req2); + m_errorMonitor->VerifyFound(); +} + +TEST_F(NegativeDeviceGeneratedCommands, GetRequirementsNullIES) { + RETURN_IF_SKIP(InitBasicDeviceGeneratedCommands()); + + VkIndirectCommandsLayoutTokenEXT token = vku::InitStructHelper(); + token.type = VK_INDIRECT_COMMANDS_TOKEN_TYPE_DRAW_EXT; + token.offset = 0; + + VkIndirectCommandsLayoutCreateInfoEXT command_layout_ci = vku::InitStructHelper(); + command_layout_ci.shaderStages = VK_SHADER_STAGE_VERTEX_BIT; + command_layout_ci.pipelineLayout = VK_NULL_HANDLE; + command_layout_ci.tokenCount = 1; + command_layout_ci.pTokens = &token; + vkt::IndirectCommandsLayout command_layout(*m_device, command_layout_ci); + + VkGeneratedCommandsMemoryRequirementsInfoEXT req_info = vku::InitStructHelper(); + req_info.maxSequenceCount = 1; + req_info.indirectExecutionSet = VK_NULL_HANDLE; + req_info.indirectCommandsLayout = command_layout.handle(); + + VkMemoryRequirements2 mem_req2 = vku::InitStructHelper(); + m_errorMonitor->SetDesiredError("VUID-VkGeneratedCommandsMemoryRequirementsInfoEXT-indirectExecutionSet-11012"); + vk::GetGeneratedCommandsMemoryRequirementsEXT(device(), &req_info, &mem_req2); + m_errorMonitor->VerifyFound(); +} + +TEST_F(NegativeDeviceGeneratedCommands, GetRequirementsNoExecutionTokenNullIES) { + RETURN_IF_SKIP(InitBasicDeviceGeneratedCommands()); + InitRenderTarget(); + + VkIndirectCommandsLayoutTokenEXT token = vku::InitStructHelper(); + token.type = VK_INDIRECT_COMMANDS_TOKEN_TYPE_DRAW_EXT; + token.offset = 0; + + VkIndirectCommandsLayoutCreateInfoEXT command_layout_ci = vku::InitStructHelper(); + command_layout_ci.shaderStages = VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT; + command_layout_ci.pipelineLayout = VK_NULL_HANDLE; + command_layout_ci.tokenCount = 1; + command_layout_ci.pTokens = &token; + vkt::IndirectCommandsLayout command_layout(*m_device, command_layout_ci); + + VkPipelineCreateFlags2CreateInfoKHR pipe_flags2 = vku::InitStructHelper(); + pipe_flags2.flags = VK_PIPELINE_CREATE_2_INDIRECT_BINDABLE_BIT_EXT; + CreatePipelineHelper pipe(*this, &pipe_flags2); + pipe.CreateGraphicsPipeline(); + vkt::IndirectExecutionSet exe_set(*m_device, pipe.Handle(), 1); + + VkGeneratedCommandsMemoryRequirementsInfoEXT req_info = vku::InitStructHelper(); + req_info.maxSequenceCount = 1; + req_info.indirectExecutionSet = exe_set.handle(); + req_info.indirectCommandsLayout = command_layout.handle(); + + VkMemoryRequirements2 mem_req2 = vku::InitStructHelper(); + m_errorMonitor->SetDesiredError("VUID-VkGeneratedCommandsMemoryRequirementsInfoEXT-indirectCommandsLayout-11011"); + vk::GetGeneratedCommandsMemoryRequirementsEXT(device(), &req_info, &mem_req2); + m_errorMonitor->VerifyFound(); +} + +TEST_F(NegativeDeviceGeneratedCommands, ExecuteNoBoundPipeline) { + RETURN_IF_SKIP(InitBasicDeviceGeneratedCommands()); + + VkIndirectCommandsExecutionSetTokenEXT exe_set_token = {VK_INDIRECT_EXECUTION_SET_INFO_TYPE_PIPELINES_EXT, + VK_SHADER_STAGE_COMPUTE_BIT}; + + VkIndirectCommandsLayoutTokenEXT tokens[2]; + tokens[0] = vku::InitStructHelper(); + tokens[0].type = VK_INDIRECT_COMMANDS_TOKEN_TYPE_EXECUTION_SET_EXT; + tokens[0].data.pExecutionSet = &exe_set_token; + tokens[0].offset = 0; + + tokens[1] = vku::InitStructHelper(); + tokens[1].type = VK_INDIRECT_COMMANDS_TOKEN_TYPE_DISPATCH_EXT; + tokens[1].offset = 8; + + VkIndirectCommandsLayoutCreateInfoEXT command_layout_ci = vku::InitStructHelper(); + command_layout_ci.shaderStages = VK_SHADER_STAGE_COMPUTE_BIT; + command_layout_ci.pipelineLayout = VK_NULL_HANDLE; + command_layout_ci.tokenCount = 2; + command_layout_ci.pTokens = tokens; + vkt::IndirectCommandsLayout command_layout(*m_device, command_layout_ci); + + VkPipelineCreateFlags2CreateInfoKHR pipe_flags2 = vku::InitStructHelper(); + pipe_flags2.flags = VK_PIPELINE_CREATE_2_INDIRECT_BINDABLE_BIT_EXT; + CreateComputePipelineHelper pipe(*this, &pipe_flags2); + pipe.CreateComputePipeline(); + vkt::IndirectExecutionSet exe_set(*m_device, pipe.Handle(), 1); + + VkMemoryAllocateFlagsInfo allocate_flag_info = vku::InitStructHelper(); + allocate_flag_info.flags = VK_MEMORY_ALLOCATE_DEVICE_ADDRESS_BIT; + vkt::Buffer block_buffer(*m_device, 64, VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT_KHR, + VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, &allocate_flag_info); + + VkGeneratedCommandsInfoEXT generated_commands_info = vku::InitStructHelper(); + generated_commands_info.shaderStages = VK_SHADER_STAGE_COMPUTE_BIT; + generated_commands_info.indirectExecutionSet = exe_set.handle(); + generated_commands_info.indirectCommandsLayout = command_layout.handle(); + generated_commands_info.indirectAddressSize = 64; + generated_commands_info.indirectAddress = block_buffer.address(); + generated_commands_info.preprocessAddress = 0; + generated_commands_info.sequenceCountAddress = 0; + generated_commands_info.maxDrawCount = 1; + + m_command_buffer.begin(); + m_errorMonitor->SetDesiredError("VUID-vkCmdExecuteGeneratedCommandsEXT-indirectCommandsLayout-11053"); + vk::CmdExecuteGeneratedCommandsEXT(m_command_buffer.handle(), false, &generated_commands_info); + m_errorMonitor->VerifyFound(); + m_command_buffer.end(); +} + +TEST_F(NegativeDeviceGeneratedCommands, ExecuteNoBoundShaderObject) { + AddRequiredExtensions(VK_EXT_SHADER_OBJECT_EXTENSION_NAME); + AddRequiredFeature(vkt::Feature::shaderObject); + RETURN_IF_SKIP(InitBasicDeviceGeneratedCommands()); + InitRenderTarget(); + + VkIndirectCommandsExecutionSetTokenEXT exe_set_token = {VK_INDIRECT_EXECUTION_SET_INFO_TYPE_SHADER_OBJECTS_EXT, + VK_SHADER_STAGE_VERTEX_BIT}; + + VkIndirectCommandsLayoutTokenEXT tokens[2]; + tokens[0] = vku::InitStructHelper(); + tokens[0].type = VK_INDIRECT_COMMANDS_TOKEN_TYPE_EXECUTION_SET_EXT; + tokens[0].data.pExecutionSet = &exe_set_token; + tokens[0].offset = 0; + + tokens[1] = vku::InitStructHelper(); + tokens[1].type = VK_INDIRECT_COMMANDS_TOKEN_TYPE_DRAW_EXT; + tokens[1].offset = 8; + + VkIndirectCommandsLayoutCreateInfoEXT command_layout_ci = vku::InitStructHelper(); + command_layout_ci.shaderStages = VK_INDIRECT_EXECUTION_SET_INFO_TYPE_SHADER_OBJECTS_EXT; + command_layout_ci.pipelineLayout = VK_NULL_HANDLE; + command_layout_ci.tokenCount = 2; + command_layout_ci.pTokens = tokens; + vkt::IndirectCommandsLayout command_layout(*m_device, command_layout_ci); + + const auto vert_spv = GLSLToSPV(VK_SHADER_STAGE_VERTEX_BIT, kVertexMinimalGlsl); + VkShaderCreateInfoEXT vert_create_info = + ShaderCreateInfoFlag(vert_spv, VK_SHADER_STAGE_VERTEX_BIT, VK_SHADER_CREATE_INDIRECT_BINDABLE_BIT_EXT); + const vkt::Shader vertShader(*m_device, vert_create_info); + const VkShaderEXT shaders[] = {vertShader.handle()}; + OneOffDescriptorSet descriptor_set(m_device, {{0, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1, VK_SHADER_STAGE_ALL, nullptr}}); + VkIndirectExecutionSetShaderLayoutInfoEXT exe_set_layouts = vku::InitStructHelper(); + exe_set_layouts.setLayoutCount = 1; + exe_set_layouts.pSetLayouts = &descriptor_set.layout_.handle(); + + VkIndirectExecutionSetShaderInfoEXT exe_set_shader_info = vku::InitStructHelper(); + exe_set_shader_info.shaderCount = 1; + exe_set_shader_info.pInitialShaders = shaders; + exe_set_shader_info.pSetLayoutInfos = &exe_set_layouts; + exe_set_shader_info.maxShaderCount = 1; + exe_set_shader_info.pushConstantRangeCount = 0; + vkt::IndirectExecutionSet exe_set(*m_device, exe_set_shader_info); + + VkMemoryAllocateFlagsInfo allocate_flag_info = vku::InitStructHelper(); + allocate_flag_info.flags = VK_MEMORY_ALLOCATE_DEVICE_ADDRESS_BIT; + vkt::Buffer block_buffer(*m_device, 64, VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT_KHR, + VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, &allocate_flag_info); + + VkGeneratedCommandsInfoEXT generated_commands_info = vku::InitStructHelper(); + generated_commands_info.shaderStages = VK_SHADER_STAGE_VERTEX_BIT; + generated_commands_info.indirectExecutionSet = exe_set.handle(); + generated_commands_info.indirectCommandsLayout = command_layout.handle(); + generated_commands_info.indirectAddressSize = 64; + generated_commands_info.indirectAddress = block_buffer.address(); + generated_commands_info.preprocessAddress = 0; + generated_commands_info.sequenceCountAddress = 0; + generated_commands_info.maxDrawCount = 1; + + m_command_buffer.begin(); + m_errorMonitor->SetDesiredError("VUID-vkCmdExecuteGeneratedCommandsEXT-indirectCommandsLayout-11053"); + vk::CmdExecuteGeneratedCommandsEXT(m_command_buffer.handle(), false, &generated_commands_info); + m_errorMonitor->VerifyFound(); + m_command_buffer.end(); +} + +TEST_F(NegativeDeviceGeneratedCommands, ExecuteIsPreprocessed) { + RETURN_IF_SKIP(InitBasicDeviceGeneratedCommands()); + + VkIndirectCommandsLayoutTokenEXT token; + token = vku::InitStructHelper(); + token.type = VK_INDIRECT_COMMANDS_TOKEN_TYPE_DISPATCH_EXT; + token.offset = 0; + + VkIndirectCommandsLayoutCreateInfoEXT command_layout_ci = vku::InitStructHelper(); + command_layout_ci.flags = VK_INDIRECT_COMMANDS_LAYOUT_USAGE_EXPLICIT_PREPROCESS_BIT_EXT; + command_layout_ci.shaderStages = VK_SHADER_STAGE_COMPUTE_BIT; + command_layout_ci.pipelineLayout = VK_NULL_HANDLE; + command_layout_ci.tokenCount = 1; + command_layout_ci.pTokens = &token; + vkt::IndirectCommandsLayout command_layout(*m_device, command_layout_ci); + + VkPipelineCreateFlags2CreateInfoKHR pipe_flags2 = vku::InitStructHelper(); + pipe_flags2.flags = VK_PIPELINE_CREATE_2_INDIRECT_BINDABLE_BIT_EXT; + CreateComputePipelineHelper pipe(*this, &pipe_flags2); + pipe.CreateComputePipeline(); + vkt::IndirectExecutionSet exe_set(*m_device, pipe.Handle(), 1); + + VkMemoryAllocateFlagsInfo allocate_flag_info = vku::InitStructHelper(); + allocate_flag_info.flags = VK_MEMORY_ALLOCATE_DEVICE_ADDRESS_BIT; + vkt::Buffer block_buffer(*m_device, 64, VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT_KHR, + VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, &allocate_flag_info); + + VkGeneratedCommandsInfoEXT generated_commands_info = vku::InitStructHelper(); + generated_commands_info.shaderStages = VK_SHADER_STAGE_COMPUTE_BIT; + generated_commands_info.indirectExecutionSet = exe_set.handle(); + generated_commands_info.indirectCommandsLayout = command_layout.handle(); + generated_commands_info.indirectAddressSize = 64; + generated_commands_info.indirectAddress = block_buffer.address(); + generated_commands_info.preprocessAddress = 0; + generated_commands_info.sequenceCountAddress = 0; + generated_commands_info.maxDrawCount = 1; + + m_command_buffer.begin(); + m_errorMonitor->SetDesiredError("VUID-vkCmdExecuteGeneratedCommandsEXT-indirectCommandsLayout-11141"); + vk::CmdExecuteGeneratedCommandsEXT(m_command_buffer.handle(), false, &generated_commands_info); + m_errorMonitor->VerifyFound(); + m_command_buffer.end(); +} + +TEST_F(NegativeDeviceGeneratedCommands, PreprocessNoBoundPipeline) { + RETURN_IF_SKIP(InitBasicDeviceGeneratedCommands()); + + VkIndirectCommandsExecutionSetTokenEXT exe_set_token = {VK_INDIRECT_EXECUTION_SET_INFO_TYPE_PIPELINES_EXT, + VK_SHADER_STAGE_COMPUTE_BIT}; + + VkIndirectCommandsLayoutTokenEXT tokens[2]; + tokens[0] = vku::InitStructHelper(); + tokens[0].type = VK_INDIRECT_COMMANDS_TOKEN_TYPE_EXECUTION_SET_EXT; + tokens[0].data.pExecutionSet = &exe_set_token; + tokens[0].offset = 0; + + tokens[1] = vku::InitStructHelper(); + tokens[1].type = VK_INDIRECT_COMMANDS_TOKEN_TYPE_DISPATCH_EXT; + tokens[1].offset = 8; + + VkIndirectCommandsLayoutCreateInfoEXT command_layout_ci = vku::InitStructHelper(); + command_layout_ci.flags = VK_INDIRECT_COMMANDS_LAYOUT_USAGE_EXPLICIT_PREPROCESS_BIT_EXT; + command_layout_ci.shaderStages = VK_SHADER_STAGE_COMPUTE_BIT; + command_layout_ci.pipelineLayout = VK_NULL_HANDLE; + command_layout_ci.tokenCount = 2; + command_layout_ci.pTokens = tokens; + vkt::IndirectCommandsLayout command_layout(*m_device, command_layout_ci); + + VkPipelineCreateFlags2CreateInfoKHR pipe_flags2 = vku::InitStructHelper(); + pipe_flags2.flags = VK_PIPELINE_CREATE_2_INDIRECT_BINDABLE_BIT_EXT; + CreateComputePipelineHelper pipe(*this, &pipe_flags2); + pipe.CreateComputePipeline(); + vkt::IndirectExecutionSet exe_set(*m_device, pipe.Handle(), 1); + + VkMemoryAllocateFlagsInfo allocate_flag_info = vku::InitStructHelper(); + allocate_flag_info.flags = VK_MEMORY_ALLOCATE_DEVICE_ADDRESS_BIT; + vkt::Buffer block_buffer(*m_device, 64, VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT_KHR, + VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, &allocate_flag_info); + + VkGeneratedCommandsInfoEXT generated_commands_info = vku::InitStructHelper(); + generated_commands_info.shaderStages = VK_SHADER_STAGE_COMPUTE_BIT; + generated_commands_info.indirectExecutionSet = exe_set.handle(); + generated_commands_info.indirectCommandsLayout = command_layout.handle(); + generated_commands_info.indirectAddressSize = 64; + generated_commands_info.indirectAddress = block_buffer.address(); + generated_commands_info.preprocessAddress = 0; + generated_commands_info.sequenceCountAddress = 0; + generated_commands_info.maxDrawCount = 1; + + vkt::CommandBuffer state_cb(*m_device, m_command_pool); + state_cb.begin(); + + m_command_buffer.begin(); + m_errorMonitor->SetDesiredError("VUID-vkCmdPreprocessGeneratedCommandsEXT-indirectCommandsLayout-11084"); + vk::CmdPreprocessGeneratedCommandsEXT(m_command_buffer.handle(), &generated_commands_info, state_cb.handle()); + m_errorMonitor->VerifyFound(); + m_command_buffer.end(); + + state_cb.end(); +} + +TEST_F(NegativeDeviceGeneratedCommands, PreprocessRecordingState) { + RETURN_IF_SKIP(InitBasicDeviceGeneratedCommands()); + + VkIndirectCommandsLayoutTokenEXT token; + token = vku::InitStructHelper(); + token.type = VK_INDIRECT_COMMANDS_TOKEN_TYPE_DISPATCH_EXT; + token.offset = 0; + + VkIndirectCommandsLayoutCreateInfoEXT command_layout_ci = vku::InitStructHelper(); + command_layout_ci.flags = VK_INDIRECT_COMMANDS_LAYOUT_USAGE_EXPLICIT_PREPROCESS_BIT_EXT; + command_layout_ci.shaderStages = VK_SHADER_STAGE_COMPUTE_BIT; + command_layout_ci.pipelineLayout = VK_NULL_HANDLE; + command_layout_ci.tokenCount = 1; + command_layout_ci.pTokens = &token; + vkt::IndirectCommandsLayout command_layout(*m_device, command_layout_ci); + + VkPipelineCreateFlags2CreateInfoKHR pipe_flags2 = vku::InitStructHelper(); + pipe_flags2.flags = VK_PIPELINE_CREATE_2_INDIRECT_BINDABLE_BIT_EXT; + CreateComputePipelineHelper pipe(*this, &pipe_flags2); + pipe.CreateComputePipeline(); + vkt::IndirectExecutionSet exe_set(*m_device, pipe.Handle(), 1); + + VkMemoryAllocateFlagsInfo allocate_flag_info = vku::InitStructHelper(); + allocate_flag_info.flags = VK_MEMORY_ALLOCATE_DEVICE_ADDRESS_BIT; + vkt::Buffer block_buffer(*m_device, 64, VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT_KHR, + VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, &allocate_flag_info); + + VkGeneratedCommandsInfoEXT generated_commands_info = vku::InitStructHelper(); + generated_commands_info.shaderStages = VK_SHADER_STAGE_COMPUTE_BIT; + generated_commands_info.indirectExecutionSet = exe_set.handle(); + generated_commands_info.indirectCommandsLayout = command_layout.handle(); + generated_commands_info.indirectAddressSize = 64; + generated_commands_info.indirectAddress = block_buffer.address(); + generated_commands_info.preprocessAddress = 0; + generated_commands_info.sequenceCountAddress = 0; + generated_commands_info.maxDrawCount = 1; + + vkt::CommandBuffer state_cb(*m_device, m_command_pool); + state_cb.begin(); + state_cb.end(); + + m_command_buffer.begin(); + m_errorMonitor->SetDesiredError("VUID-vkCmdPreprocessGeneratedCommandsEXT-stateCommandBuffer-11138"); + vk::CmdPreprocessGeneratedCommandsEXT(m_command_buffer.handle(), &generated_commands_info, state_cb.handle()); + m_errorMonitor->VerifyFound(); + + state_cb.begin(); + state_cb.end(); + m_errorMonitor->SetDesiredError("VUID-vkCmdPreprocessGeneratedCommandsEXT-stateCommandBuffer-11138"); + vk::CmdPreprocessGeneratedCommandsEXT(m_command_buffer.handle(), &generated_commands_info, state_cb.handle()); + m_errorMonitor->VerifyFound(); + m_command_buffer.end(); +} + +TEST_F(NegativeDeviceGeneratedCommands, PreprocessCommandLayoutFlag) { + RETURN_IF_SKIP(InitBasicDeviceGeneratedCommands()); + + VkIndirectCommandsLayoutTokenEXT token; + token = vku::InitStructHelper(); + token.type = VK_INDIRECT_COMMANDS_TOKEN_TYPE_DISPATCH_EXT; + token.offset = 0; + + VkIndirectCommandsLayoutCreateInfoEXT command_layout_ci = vku::InitStructHelper(); + command_layout_ci.shaderStages = VK_SHADER_STAGE_COMPUTE_BIT; + command_layout_ci.pipelineLayout = VK_NULL_HANDLE; + command_layout_ci.tokenCount = 1; + command_layout_ci.pTokens = &token; + vkt::IndirectCommandsLayout command_layout(*m_device, command_layout_ci); + + VkPipelineCreateFlags2CreateInfoKHR pipe_flags2 = vku::InitStructHelper(); + pipe_flags2.flags = VK_PIPELINE_CREATE_2_INDIRECT_BINDABLE_BIT_EXT; + CreateComputePipelineHelper pipe(*this, &pipe_flags2); + pipe.CreateComputePipeline(); + vkt::IndirectExecutionSet exe_set(*m_device, pipe.Handle(), 1); + + VkMemoryAllocateFlagsInfo allocate_flag_info = vku::InitStructHelper(); + allocate_flag_info.flags = VK_MEMORY_ALLOCATE_DEVICE_ADDRESS_BIT; + vkt::Buffer block_buffer(*m_device, 64, VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT_KHR, + VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, &allocate_flag_info); + + VkGeneratedCommandsInfoEXT generated_commands_info = vku::InitStructHelper(); + generated_commands_info.shaderStages = VK_SHADER_STAGE_COMPUTE_BIT; + generated_commands_info.indirectExecutionSet = exe_set.handle(); + generated_commands_info.indirectCommandsLayout = command_layout.handle(); + generated_commands_info.indirectAddressSize = 64; + generated_commands_info.indirectAddress = block_buffer.address(); + generated_commands_info.preprocessAddress = 0; + generated_commands_info.sequenceCountAddress = 0; + generated_commands_info.maxDrawCount = 1; + + vkt::CommandBuffer state_cb(*m_device, m_command_pool); + state_cb.begin(); + + m_command_buffer.begin(); + m_errorMonitor->SetDesiredError("VUID-vkCmdPreprocessGeneratedCommandsEXT-pGeneratedCommandsInfo-11082"); + vk::CmdPreprocessGeneratedCommandsEXT(m_command_buffer.handle(), &generated_commands_info, state_cb.handle()); + m_errorMonitor->VerifyFound(); + + m_command_buffer.end(); + state_cb.end(); +} + +TEST_F(NegativeDeviceGeneratedCommands, GeneratedCommandsInfoDynamicVertex) { + RETURN_IF_SKIP(InitBasicDeviceGeneratedCommands()); + InitRenderTarget(); + + VkIndirectCommandsVertexBufferTokenEXT vertex_buffer_token = {0}; + VkIndirectCommandsLayoutTokenEXT tokens[2]; + tokens[0] = vku::InitStructHelper(); + tokens[0].type = VK_INDIRECT_COMMANDS_TOKEN_TYPE_VERTEX_BUFFER_EXT; + tokens[0].data.pVertexBuffer = &vertex_buffer_token; + tokens[0].offset = 0; + + tokens[1] = vku::InitStructHelper(); + tokens[1].type = VK_INDIRECT_COMMANDS_TOKEN_TYPE_DRAW_EXT; + tokens[1].offset = 8; + + VkIndirectCommandsLayoutCreateInfoEXT command_layout_ci = vku::InitStructHelper(); + command_layout_ci.shaderStages = VK_SHADER_STAGE_VERTEX_BIT; + command_layout_ci.pipelineLayout = VK_NULL_HANDLE; + command_layout_ci.tokenCount = 2; + command_layout_ci.pTokens = tokens; + vkt::IndirectCommandsLayout command_layout(*m_device, command_layout_ci); + + VkPipelineCreateFlags2CreateInfoKHR pipe_flags2 = vku::InitStructHelper(); + pipe_flags2.flags = VK_PIPELINE_CREATE_2_INDIRECT_BINDABLE_BIT_EXT; + CreatePipelineHelper pipe(*this, &pipe_flags2); // Missing VK_DYNAMIC_STATE_VERTEX_INPUT_BINDING_STRIDE + pipe.CreateGraphicsPipeline(); + vkt::IndirectExecutionSet exe_set(*m_device, pipe.Handle(), 1); + + VkMemoryAllocateFlagsInfo allocate_flag_info = vku::InitStructHelper(); + allocate_flag_info.flags = VK_MEMORY_ALLOCATE_DEVICE_ADDRESS_BIT; + vkt::Buffer block_buffer(*m_device, 64, VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT_KHR, + VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, &allocate_flag_info); + + VkGeneratedCommandsInfoEXT generated_commands_info = vku::InitStructHelper(); + generated_commands_info.shaderStages = VK_SHADER_STAGE_COMPUTE_BIT; + generated_commands_info.indirectExecutionSet = exe_set.handle(); + generated_commands_info.indirectCommandsLayout = command_layout.handle(); + generated_commands_info.indirectAddressSize = 64; + generated_commands_info.indirectAddress = block_buffer.address(); + generated_commands_info.preprocessAddress = 0; + generated_commands_info.sequenceCountAddress = 0; + generated_commands_info.maxDrawCount = 1; + + m_command_buffer.begin(); + vk::CmdBindPipeline(m_command_buffer.handle(), VK_PIPELINE_BIND_POINT_GRAPHICS, pipe.Handle()); + m_errorMonitor->SetDesiredError("VUID-VkGeneratedCommandsInfoEXT-indirectCommandsLayout-11079"); + vk::CmdExecuteGeneratedCommandsEXT(m_command_buffer.handle(), false, &generated_commands_info); + m_errorMonitor->VerifyFound(); + m_command_buffer.end(); +} + +TEST_F(NegativeDeviceGeneratedCommands, GeneratedCommandsInfoAddresses) { + RETURN_IF_SKIP(InitBasicDeviceGeneratedCommands()); + + VkIndirectCommandsLayoutTokenEXT token; + token = vku::InitStructHelper(); + token.type = VK_INDIRECT_COMMANDS_TOKEN_TYPE_DISPATCH_EXT; + token.offset = 0; + + VkIndirectCommandsLayoutCreateInfoEXT command_layout_ci = vku::InitStructHelper(); + command_layout_ci.shaderStages = VK_SHADER_STAGE_COMPUTE_BIT; + command_layout_ci.pipelineLayout = VK_NULL_HANDLE; + command_layout_ci.tokenCount = 1; + command_layout_ci.pTokens = &token; + vkt::IndirectCommandsLayout command_layout(*m_device, command_layout_ci); + + VkPipelineCreateFlags2CreateInfoKHR pipe_flags2 = vku::InitStructHelper(); + pipe_flags2.flags = VK_PIPELINE_CREATE_2_INDIRECT_BINDABLE_BIT_EXT; + CreateComputePipelineHelper pipe(*this, &pipe_flags2); + pipe.CreateComputePipeline(); + vkt::IndirectExecutionSet exe_set(*m_device, pipe.Handle(), 1); + + VkMemoryAllocateFlagsInfo allocate_flag_info = vku::InitStructHelper(); + allocate_flag_info.flags = VK_MEMORY_ALLOCATE_DEVICE_ADDRESS_BIT; + vkt::Buffer block_buffer(*m_device, 64, VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT_KHR, + VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, &allocate_flag_info); + + VkGeneratedCommandsInfoEXT generated_commands_info = vku::InitStructHelper(); + generated_commands_info.shaderStages = VK_SHADER_STAGE_COMPUTE_BIT; + generated_commands_info.indirectExecutionSet = exe_set.handle(); + generated_commands_info.indirectCommandsLayout = command_layout.handle(); + generated_commands_info.indirectAddressSize = 0; + generated_commands_info.indirectAddress = 0; + generated_commands_info.preprocessAddress = 0; + generated_commands_info.sequenceCountAddress = 3; + generated_commands_info.maxDrawCount = 1; + + m_command_buffer.begin(); + vk::CmdBindPipeline(m_command_buffer.handle(), VK_PIPELINE_BIND_POINT_COMPUTE, pipe.Handle()); + m_errorMonitor->SetDesiredError("VUID-VkGeneratedCommandsInfoEXT-sequenceCountAddress-11073"); + m_errorMonitor->SetDesiredError("VUID-VkGeneratedCommandsInfoEXT-indirectAddress-11076"); + m_errorMonitor->SetDesiredError("VUID-VkGeneratedCommandsInfoEXT-indirectAddressSize-11077"); + vk::CmdExecuteGeneratedCommandsEXT(m_command_buffer.handle(), false, &generated_commands_info); + m_errorMonitor->VerifyFound(); + m_command_buffer.end(); +} + +TEST_F(NegativeDeviceGeneratedCommands, GeneratedCommandsInfoMultiDrawLimit) { + RETURN_IF_SKIP(InitBasicDeviceGeneratedCommands()); + InitRenderTarget(); + + VkIndirectCommandsLayoutTokenEXT token; + token = vku::InitStructHelper(); + token.type = VK_INDIRECT_COMMANDS_TOKEN_TYPE_DRAW_COUNT_EXT; + token.offset = 0; + + VkIndirectCommandsLayoutCreateInfoEXT command_layout_ci = vku::InitStructHelper(); + command_layout_ci.shaderStages = VK_SHADER_STAGE_VERTEX_BIT; + command_layout_ci.pipelineLayout = VK_NULL_HANDLE; + command_layout_ci.tokenCount = 1; + command_layout_ci.pTokens = &token; + vkt::IndirectCommandsLayout command_layout(*m_device, command_layout_ci); + + VkPipelineCreateFlags2CreateInfoKHR pipe_flags2 = vku::InitStructHelper(); + pipe_flags2.flags = VK_PIPELINE_CREATE_2_INDIRECT_BINDABLE_BIT_EXT; + CreatePipelineHelper pipe(*this, &pipe_flags2); + pipe.CreateGraphicsPipeline(); + vkt::IndirectExecutionSet exe_set(*m_device, pipe.Handle(), 1); + + VkMemoryAllocateFlagsInfo allocate_flag_info = vku::InitStructHelper(); + allocate_flag_info.flags = VK_MEMORY_ALLOCATE_DEVICE_ADDRESS_BIT; + vkt::Buffer block_buffer(*m_device, 64, VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT_KHR, + VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, &allocate_flag_info); + + VkGeneratedCommandsInfoEXT generated_commands_info = vku::InitStructHelper(); + generated_commands_info.shaderStages = VK_SHADER_STAGE_VERTEX_BIT; + generated_commands_info.indirectExecutionSet = exe_set.handle(); + generated_commands_info.indirectCommandsLayout = command_layout.handle(); + generated_commands_info.indirectAddressSize = 64; + generated_commands_info.indirectAddress = block_buffer.address(); + generated_commands_info.preprocessAddress = 0; + generated_commands_info.sequenceCountAddress = 0; + generated_commands_info.maxDrawCount = 1 << 21; + generated_commands_info.maxSequenceCount = 1 << 4; + + m_command_buffer.begin(); + vk::CmdBindPipeline(m_command_buffer.handle(), VK_PIPELINE_BIND_POINT_GRAPHICS, pipe.Handle()); + m_errorMonitor->SetDesiredError("VUID-VkGeneratedCommandsInfoEXT-maxDrawCount-11078"); + m_errorMonitor->SetAllowedFailureMsg("VUID-VkGeneratedCommandsInfoEXT-preprocessAddress-11063"); + m_errorMonitor->SetAllowedFailureMsg("VUID-VkGeneratedCommandsInfoEXT-preprocessSize-11071"); + vk::CmdExecuteGeneratedCommandsEXT(m_command_buffer.handle(), false, &generated_commands_info); + m_errorMonitor->VerifyFound(); + m_command_buffer.end(); +} + +TEST_F(NegativeDeviceGeneratedCommands, ExecuteStageMismatch) { + RETURN_IF_SKIP(InitBasicDeviceGeneratedCommands()); + InitRenderTarget(); + + VkIndirectCommandsExecutionSetTokenEXT exe_set_token = {VK_INDIRECT_EXECUTION_SET_INFO_TYPE_PIPELINES_EXT, + VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT}; + VkIndirectCommandsLayoutTokenEXT tokens[2]; + tokens[0] = vku::InitStructHelper(); + tokens[0].type = VK_INDIRECT_COMMANDS_TOKEN_TYPE_EXECUTION_SET_EXT; + tokens[0].data.pExecutionSet = &exe_set_token; + tokens[0].offset = 0; + + tokens[1] = vku::InitStructHelper(); + tokens[1].type = VK_INDIRECT_COMMANDS_TOKEN_TYPE_DRAW_EXT; + tokens[1].offset = 8; + + VkIndirectCommandsLayoutCreateInfoEXT command_layout_ci = vku::InitStructHelper(); + command_layout_ci.shaderStages = VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT; + command_layout_ci.pipelineLayout = VK_NULL_HANDLE; + command_layout_ci.tokenCount = 2; + command_layout_ci.pTokens = tokens; + vkt::IndirectCommandsLayout command_layout(*m_device, command_layout_ci); + + // Pipeline (and IES) have no fragment stage + VkPipelineCreateFlags2CreateInfoKHR pipe_flags2 = vku::InitStructHelper(); + pipe_flags2.flags = VK_PIPELINE_CREATE_2_INDIRECT_BINDABLE_BIT_EXT; + CreatePipelineHelper pipe(*this, &pipe_flags2); + pipe.shader_stages_ = {pipe.vs_->GetStageCreateInfo()}; + pipe.rs_state_ci_.rasterizerDiscardEnable = VK_TRUE; + pipe.CreateGraphicsPipeline(); + vkt::IndirectExecutionSet exe_set(*m_device, pipe.Handle(), 1); + + VkMemoryAllocateFlagsInfo allocate_flag_info = vku::InitStructHelper(); + allocate_flag_info.flags = VK_MEMORY_ALLOCATE_DEVICE_ADDRESS_BIT; + vkt::Buffer block_buffer(*m_device, 64, VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT_KHR, + VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, &allocate_flag_info); + + VkGeneratedCommandsInfoEXT generated_commands_info = vku::InitStructHelper(); + generated_commands_info.shaderStages = VK_SHADER_STAGE_VERTEX_BIT; + generated_commands_info.indirectExecutionSet = exe_set.handle(); + generated_commands_info.indirectCommandsLayout = command_layout.handle(); + generated_commands_info.indirectAddressSize = 64; + generated_commands_info.indirectAddress = block_buffer.address(); + generated_commands_info.preprocessAddress = 0; + generated_commands_info.sequenceCountAddress = 0; + generated_commands_info.maxDrawCount = 1; + + m_command_buffer.begin(); + vk::CmdBindPipeline(m_command_buffer.handle(), VK_PIPELINE_BIND_POINT_GRAPHICS, pipe.Handle()); + m_errorMonitor->SetDesiredError("VUID-VkGeneratedCommandsInfoEXT-indirectCommandsLayout-11002"); + vk::CmdExecuteGeneratedCommandsEXT(m_command_buffer.handle(), false, &generated_commands_info); + m_errorMonitor->VerifyFound(); + m_command_buffer.end(); +} + +TEST_F(NegativeDeviceGeneratedCommands, ExecutePreprocessBufferUsage) { + RETURN_IF_SKIP(InitBasicDeviceGeneratedCommands()); + + VkIndirectCommandsExecutionSetTokenEXT exe_set_token = {VK_INDIRECT_EXECUTION_SET_INFO_TYPE_PIPELINES_EXT, + VK_SHADER_STAGE_COMPUTE_BIT}; + VkIndirectCommandsLayoutTokenEXT tokens[2]; + tokens[0] = vku::InitStructHelper(); + tokens[0].type = VK_INDIRECT_COMMANDS_TOKEN_TYPE_EXECUTION_SET_EXT; + tokens[0].data.pExecutionSet = &exe_set_token; + tokens[0].offset = 0; + + tokens[1] = vku::InitStructHelper(); + tokens[1].type = VK_INDIRECT_COMMANDS_TOKEN_TYPE_DISPATCH_EXT; + tokens[1].offset = 8; + + VkIndirectCommandsLayoutCreateInfoEXT command_layout_ci = vku::InitStructHelper(); + command_layout_ci.shaderStages = VK_SHADER_STAGE_COMPUTE_BIT; + command_layout_ci.pipelineLayout = VK_NULL_HANDLE; + command_layout_ci.tokenCount = 2; + command_layout_ci.pTokens = tokens; + vkt::IndirectCommandsLayout command_layout(*m_device, command_layout_ci); + + VkPipelineCreateFlags2CreateInfoKHR pipe_flags2 = vku::InitStructHelper(); + pipe_flags2.flags = VK_PIPELINE_CREATE_2_INDIRECT_BINDABLE_BIT_EXT; + CreateComputePipelineHelper pipe(*this, &pipe_flags2); + pipe.CreateComputePipeline(); + vkt::IndirectExecutionSet exe_set(*m_device, pipe.Handle(), 1); + + VkMemoryAllocateFlagsInfo allocate_flag_info = vku::InitStructHelper(); + allocate_flag_info.flags = VK_MEMORY_ALLOCATE_DEVICE_ADDRESS_BIT; + vkt::Buffer block_buffer(*m_device, 64, VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT_KHR, + VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, &allocate_flag_info); + + VkGeneratedCommandsInfoEXT generated_commands_info = vku::InitStructHelper(); + generated_commands_info.shaderStages = VK_SHADER_STAGE_COMPUTE_BIT; + generated_commands_info.indirectExecutionSet = exe_set.handle(); + generated_commands_info.indirectCommandsLayout = command_layout.handle(); + generated_commands_info.indirectAddressSize = 64; + generated_commands_info.indirectAddress = block_buffer.address(); + generated_commands_info.sequenceCountAddress = 0; + generated_commands_info.maxDrawCount = 1; + + m_command_buffer.begin(); + vk::CmdBindPipeline(m_command_buffer.handle(), VK_PIPELINE_BIND_POINT_COMPUTE, pipe.Handle()); + + { + generated_commands_info.preprocessAddress = block_buffer.address(); // missing usage + m_errorMonitor->SetDesiredError("VUID-VkGeneratedCommandsInfoEXT-preprocessAddress-11069"); + vk::CmdExecuteGeneratedCommandsEXT(m_command_buffer.handle(), false, &generated_commands_info); + m_errorMonitor->VerifyFound(); + } + + { + VkBufferUsageFlags2CreateInfoKHR buffer_usage_flags = vku::InitStructHelper(); + buffer_usage_flags.usage = VK_BUFFER_USAGE_2_PREPROCESS_BUFFER_BIT_EXT | VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT | + VK_BUFFER_USAGE_STORAGE_BUFFER_BIT; + VkBufferCreateInfo buffer_ci = vku::InitStructHelper(&buffer_usage_flags); + buffer_ci.size = 1024; + vkt::Buffer bad_buffer(*m_device, buffer_ci, 0, &allocate_flag_info); + + generated_commands_info.preprocessAddress = bad_buffer.address(); + bad_buffer.memory().destroy(); + m_errorMonitor->SetDesiredError("VUID-VkGeneratedCommandsInfoEXT-preprocessAddress-11070"); + vk::CmdExecuteGeneratedCommandsEXT(m_command_buffer.handle(), false, &generated_commands_info); + m_errorMonitor->VerifyFound(); + } + m_command_buffer.end(); +} + +TEST_F(NegativeDeviceGeneratedCommands, ExecuteSequenceCountBufferUsage) { + RETURN_IF_SKIP(InitBasicDeviceGeneratedCommands()); + + VkIndirectCommandsExecutionSetTokenEXT exe_set_token = {VK_INDIRECT_EXECUTION_SET_INFO_TYPE_PIPELINES_EXT, + VK_SHADER_STAGE_COMPUTE_BIT}; + VkIndirectCommandsLayoutTokenEXT tokens[2]; + tokens[0] = vku::InitStructHelper(); + tokens[0].type = VK_INDIRECT_COMMANDS_TOKEN_TYPE_EXECUTION_SET_EXT; + tokens[0].data.pExecutionSet = &exe_set_token; + tokens[0].offset = 0; + + tokens[1] = vku::InitStructHelper(); + tokens[1].type = VK_INDIRECT_COMMANDS_TOKEN_TYPE_DISPATCH_EXT; + tokens[1].offset = 8; + + VkIndirectCommandsLayoutCreateInfoEXT command_layout_ci = vku::InitStructHelper(); + command_layout_ci.shaderStages = VK_SHADER_STAGE_COMPUTE_BIT; + command_layout_ci.pipelineLayout = VK_NULL_HANDLE; + command_layout_ci.tokenCount = 2; + command_layout_ci.pTokens = tokens; + vkt::IndirectCommandsLayout command_layout(*m_device, command_layout_ci); + + VkPipelineCreateFlags2CreateInfoKHR pipe_flags2 = vku::InitStructHelper(); + pipe_flags2.flags = VK_PIPELINE_CREATE_2_INDIRECT_BINDABLE_BIT_EXT; + CreateComputePipelineHelper pipe(*this, &pipe_flags2); + pipe.CreateComputePipeline(); + vkt::IndirectExecutionSet exe_set(*m_device, pipe.Handle(), 1); + + VkMemoryAllocateFlagsInfo allocate_flag_info = vku::InitStructHelper(); + allocate_flag_info.flags = VK_MEMORY_ALLOCATE_DEVICE_ADDRESS_BIT; + vkt::Buffer block_buffer(*m_device, 64, VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT_KHR, + VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, &allocate_flag_info); + + VkGeneratedCommandsInfoEXT generated_commands_info = vku::InitStructHelper(); + generated_commands_info.shaderStages = VK_SHADER_STAGE_COMPUTE_BIT; + generated_commands_info.indirectExecutionSet = exe_set.handle(); + generated_commands_info.indirectCommandsLayout = command_layout.handle(); + generated_commands_info.indirectAddressSize = 64; + generated_commands_info.indirectAddress = block_buffer.address(); + generated_commands_info.preprocessAddress = 0; + generated_commands_info.maxDrawCount = 1; + + m_command_buffer.begin(); + vk::CmdBindPipeline(m_command_buffer.handle(), VK_PIPELINE_BIND_POINT_COMPUTE, pipe.Handle()); + + { + generated_commands_info.sequenceCountAddress = block_buffer.address(); // missing usage + m_errorMonitor->SetDesiredError("VUID-VkGeneratedCommandsInfoEXT-sequenceCountAddress-11072"); + vk::CmdExecuteGeneratedCommandsEXT(m_command_buffer.handle(), false, &generated_commands_info); + m_errorMonitor->VerifyFound(); + } + + { + VkBufferUsageFlags2CreateInfoKHR buffer_usage_flags = vku::InitStructHelper(); + buffer_usage_flags.usage = VK_BUFFER_USAGE_2_INDIRECT_BUFFER_BIT_KHR | VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT | + VK_BUFFER_USAGE_STORAGE_BUFFER_BIT; + VkBufferCreateInfo buffer_ci = vku::InitStructHelper(&buffer_usage_flags); + buffer_ci.size = 1024; + vkt::Buffer bad_buffer(*m_device, buffer_ci, 0, &allocate_flag_info); + + generated_commands_info.sequenceCountAddress = bad_buffer.address(); + bad_buffer.memory().destroy(); + m_errorMonitor->SetDesiredError("VUID-VkGeneratedCommandsInfoEXT-sequenceCountAddress-11075"); + vk::CmdExecuteGeneratedCommandsEXT(m_command_buffer.handle(), false, &generated_commands_info); + m_errorMonitor->VerifyFound(); + } + m_command_buffer.end(); +} + +TEST_F(NegativeDeviceGeneratedCommands, ExecuteShaderObjectStages) { + AddRequiredExtensions(VK_KHR_DYNAMIC_RENDERING_EXTENSION_NAME); + AddRequiredExtensions(VK_EXT_SHADER_OBJECT_EXTENSION_NAME); + AddRequiredFeature(vkt::Feature::shaderObject); + AddRequiredFeature(vkt::Feature::dynamicRendering); + RETURN_IF_SKIP(InitBasicDeviceGeneratedCommands()); + InitRenderTarget(); + + VkIndirectCommandsExecutionSetTokenEXT exe_set_token = {VK_INDIRECT_EXECUTION_SET_INFO_TYPE_SHADER_OBJECTS_EXT, + VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT}; + + VkIndirectCommandsLayoutTokenEXT tokens[2]; + tokens[0] = vku::InitStructHelper(); + tokens[0].type = VK_INDIRECT_COMMANDS_TOKEN_TYPE_EXECUTION_SET_EXT; + tokens[0].data.pExecutionSet = &exe_set_token; + tokens[0].offset = 0; + + tokens[1] = vku::InitStructHelper(); + tokens[1].type = VK_INDIRECT_COMMANDS_TOKEN_TYPE_DRAW_EXT; + tokens[1].offset = 8; + + VkIndirectCommandsLayoutCreateInfoEXT command_layout_ci = vku::InitStructHelper(); + command_layout_ci.shaderStages = VK_INDIRECT_EXECUTION_SET_INFO_TYPE_SHADER_OBJECTS_EXT; + command_layout_ci.pipelineLayout = VK_NULL_HANDLE; + command_layout_ci.tokenCount = 2; + command_layout_ci.pTokens = tokens; + vkt::IndirectCommandsLayout command_layout(*m_device, command_layout_ci); + + const auto vert_spv = GLSLToSPV(VK_SHADER_STAGE_VERTEX_BIT, kVertexMinimalGlsl); + VkShaderCreateInfoEXT vert_create_info = + ShaderCreateInfoFlag(vert_spv, VK_SHADER_STAGE_VERTEX_BIT, VK_SHADER_CREATE_INDIRECT_BINDABLE_BIT_EXT); + const vkt::Shader vertShader(*m_device, vert_create_info); + const VkShaderEXT shaders[] = {vertShader.handle()}; + OneOffDescriptorSet descriptor_set(m_device, {{0, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1, VK_SHADER_STAGE_ALL, nullptr}}); + VkIndirectExecutionSetShaderLayoutInfoEXT exe_set_layouts = vku::InitStructHelper(); + exe_set_layouts.setLayoutCount = 1; + exe_set_layouts.pSetLayouts = &descriptor_set.layout_.handle(); + + VkIndirectExecutionSetShaderInfoEXT exe_set_shader_info = vku::InitStructHelper(); + exe_set_shader_info.shaderCount = 1; + exe_set_shader_info.pInitialShaders = shaders; + exe_set_shader_info.pSetLayoutInfos = &exe_set_layouts; + exe_set_shader_info.maxShaderCount = 1; + exe_set_shader_info.pushConstantRangeCount = 0; + vkt::IndirectExecutionSet exe_set(*m_device, exe_set_shader_info); + + VkMemoryAllocateFlagsInfo allocate_flag_info = vku::InitStructHelper(); + allocate_flag_info.flags = VK_MEMORY_ALLOCATE_DEVICE_ADDRESS_BIT; + vkt::Buffer block_buffer(*m_device, 64, VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT_KHR, + VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, &allocate_flag_info); + + VkGeneratedCommandsInfoEXT generated_commands_info = vku::InitStructHelper(); + generated_commands_info.shaderStages = VK_SHADER_STAGE_VERTEX_BIT; + generated_commands_info.indirectExecutionSet = exe_set.handle(); + generated_commands_info.indirectCommandsLayout = command_layout.handle(); + generated_commands_info.indirectAddressSize = 64; + generated_commands_info.indirectAddress = block_buffer.address(); + generated_commands_info.preprocessAddress = 0; + generated_commands_info.sequenceCountAddress = 0; + generated_commands_info.maxDrawCount = 1; + + m_command_buffer.begin(); + m_command_buffer.BeginRenderingColor(GetDynamicRenderTarget(), GetRenderTargetArea()); + const VkShaderStageFlagBits stages[] = {VK_SHADER_STAGE_VERTEX_BIT}; + vk::CmdBindShadersEXT(m_command_buffer.handle(), 1u, stages, shaders); + SetDefaultDynamicStatesAll(m_command_buffer.handle()); + m_errorMonitor->SetDesiredError("VUID-VkGeneratedCommandsInfoEXT-indirectCommandsLayout-11002"); + vk::CmdExecuteGeneratedCommandsEXT(m_command_buffer.handle(), false, &generated_commands_info); + m_errorMonitor->VerifyFound(); + m_command_buffer.EndRendering(); + m_command_buffer.end(); +} \ No newline at end of file diff --git a/tests/unit/device_generated_commands_positive.cpp b/tests/unit/device_generated_commands_positive.cpp new file mode 100644 index 00000000000..75f9f1b623c --- /dev/null +++ b/tests/unit/device_generated_commands_positive.cpp @@ -0,0 +1,366 @@ +/* + * Copyright (c) 2023-2024 Valve Corporation + * Copyright (c) 2023-2024 LunarG, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + */ + +#include +#include "../framework/layer_validation_tests.h" +#include "../framework/pipeline_helper.h" +#include "../framework/shader_object_helper.h" +#include "generated/vk_function_pointers.h" + +void DeviceGeneratedCommandsTest::InitBasicDeviceGeneratedCommands() { + SetTargetApiVersion(VK_API_VERSION_1_1); + AddRequiredExtensions(VK_EXT_DEVICE_GENERATED_COMMANDS_EXTENSION_NAME); + AddRequiredExtensions(VK_KHR_BUFFER_DEVICE_ADDRESS_EXTENSION_NAME); + AddRequiredFeature(vkt::Feature::deviceGeneratedCommands); + AddRequiredFeature(vkt::Feature::bufferDeviceAddress); + RETURN_IF_SKIP(Init()); + + VkPhysicalDeviceDeviceGeneratedCommandsPropertiesEXT dgc_props = vku::InitStructHelper(); + GetPhysicalDeviceProperties2(dgc_props); + if ((dgc_props.supportedIndirectCommandsShaderStagesPipelineBinding & VK_SHADER_STAGE_COMPUTE_BIT) == 0) { + GTEST_SKIP() << "VK_SHADER_STAGE_COMPUTE_BIT is not supported."; + } else if ((dgc_props.supportedIndirectCommandsShaderStagesPipelineBinding & VK_SHADER_STAGE_VERTEX_BIT) == 0) { + GTEST_SKIP() << "VK_SHADER_STAGE_VERTEX_BIT is not supported."; + } +} + +class PositiveDeviceGeneratedCommands : public DeviceGeneratedCommandsTest {}; + +TEST_F(PositiveDeviceGeneratedCommands, CreateIndirectExecutionSetPipeline) { + RETURN_IF_SKIP(InitBasicDeviceGeneratedCommands()); + InitRenderTarget(); + + VkPipelineCreateFlags2CreateInfoKHR pipe_flags2 = vku::InitStructHelper(); + pipe_flags2.flags = VK_PIPELINE_CREATE_2_INDIRECT_BINDABLE_BIT_EXT; + CreatePipelineHelper pipe(*this, &pipe_flags2); + pipe.CreateGraphicsPipeline(); + + vkt::IndirectExecutionSet exe_set(*m_device, pipe.Handle(), 1); +} + +TEST_F(PositiveDeviceGeneratedCommands, CreateIndirectExecutionSetShaderObject) { + AddRequiredExtensions(VK_EXT_SHADER_OBJECT_EXTENSION_NAME); + AddRequiredFeature(vkt::Feature::shaderObject); + RETURN_IF_SKIP(InitBasicDeviceGeneratedCommands()); + InitRenderTarget(); + + const auto vert_spv = GLSLToSPV(VK_SHADER_STAGE_VERTEX_BIT, kVertexMinimalGlsl); + VkShaderCreateInfoEXT vert_create_info = + ShaderCreateInfoFlag(vert_spv, VK_SHADER_STAGE_VERTEX_BIT, VK_SHADER_CREATE_INDIRECT_BINDABLE_BIT_EXT); + const vkt::Shader vertShader(*m_device, vert_create_info); + const VkShaderEXT shaders[] = {vertShader.handle()}; + OneOffDescriptorSet descriptor_set(m_device, {{0, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1, VK_SHADER_STAGE_ALL, nullptr}}); + VkIndirectExecutionSetShaderLayoutInfoEXT exe_set_layouts = vku::InitStructHelper(); + exe_set_layouts.setLayoutCount = 1; + exe_set_layouts.pSetLayouts = &descriptor_set.layout_.handle(); + + VkIndirectExecutionSetShaderInfoEXT exe_set_shader_info = vku::InitStructHelper(); + exe_set_shader_info.shaderCount = 1; + exe_set_shader_info.pInitialShaders = shaders; + exe_set_shader_info.pSetLayoutInfos = &exe_set_layouts; + exe_set_shader_info.maxShaderCount = 1; + exe_set_shader_info.pushConstantRangeCount = 0; + vkt::IndirectExecutionSet exe_set(*m_device, exe_set_shader_info); +} + +TEST_F(PositiveDeviceGeneratedCommands, CreateIndirectCommandsLayout) { + RETURN_IF_SKIP(InitBasicDeviceGeneratedCommands()); + + VkIndirectCommandsLayoutTokenEXT token = vku::InitStructHelper(); + token.type = VK_INDIRECT_COMMANDS_TOKEN_TYPE_DRAW_EXT; + token.offset = 0; + + VkIndirectCommandsLayoutCreateInfoEXT command_layout_ci = vku::InitStructHelper(); + command_layout_ci.shaderStages = VK_SHADER_STAGE_VERTEX_BIT; + command_layout_ci.pipelineLayout = VK_NULL_HANDLE; + command_layout_ci.tokenCount = 1; + command_layout_ci.pTokens = &token; + + VkIndirectCommandsLayoutEXT command_layout; + vk::CreateIndirectCommandsLayoutEXT(device(), &command_layout_ci, nullptr, &command_layout); + vk::DestroyIndirectCommandsLayoutEXT(device(), command_layout, nullptr); +} + +TEST_F(PositiveDeviceGeneratedCommands, GetGeneratedCommandsMemoryRequirements) { + RETURN_IF_SKIP(InitBasicDeviceGeneratedCommands()); + InitRenderTarget(); + + VkIndirectCommandsLayoutTokenEXT token = vku::InitStructHelper(); + token.type = VK_INDIRECT_COMMANDS_TOKEN_TYPE_DRAW_EXT; + token.offset = 0; + + VkIndirectCommandsLayoutCreateInfoEXT command_layout_ci = vku::InitStructHelper(); + command_layout_ci.shaderStages = VK_SHADER_STAGE_VERTEX_BIT; + command_layout_ci.pipelineLayout = VK_NULL_HANDLE; + command_layout_ci.tokenCount = 1; + command_layout_ci.pTokens = &token; + + vkt::IndirectCommandsLayout command_layout(*m_device, command_layout_ci); + + VkPipelineCreateFlags2CreateInfoKHR pipe_flags2 = vku::InitStructHelper(); + pipe_flags2.flags = VK_PIPELINE_CREATE_2_INDIRECT_BINDABLE_BIT_EXT; + CreatePipelineHelper pipe(*this, &pipe_flags2); + pipe.CreateGraphicsPipeline(); + VkGeneratedCommandsPipelineInfoEXT pipeline_info = vku::InitStructHelper(); + pipeline_info.pipeline = pipe.Handle(); + + VkGeneratedCommandsMemoryRequirementsInfoEXT req_info = vku::InitStructHelper(&pipeline_info); + req_info.maxSequenceCount = 1; + req_info.indirectExecutionSet = VK_NULL_HANDLE; + req_info.indirectCommandsLayout = command_layout.handle(); + + VkMemoryRequirements2 mem_req2 = vku::InitStructHelper(); + vk::GetGeneratedCommandsMemoryRequirementsEXT(device(), &req_info, &mem_req2); +} + +TEST_F(PositiveDeviceGeneratedCommands, PushConstant) { + RETURN_IF_SKIP(InitBasicDeviceGeneratedCommands()); + + VkIndirectCommandsPushConstantTokenEXT pc_token; + pc_token.updateRange = {VK_SHADER_STAGE_VERTEX_BIT, 8, 8}; + + VkIndirectCommandsLayoutTokenEXT tokens[2]; + tokens[0] = vku::InitStructHelper(); + tokens[0].type = VK_INDIRECT_COMMANDS_TOKEN_TYPE_PUSH_CONSTANT_EXT; + tokens[0].data.pPushConstant = &pc_token; + tokens[0].offset = 0; + + tokens[1] = vku::InitStructHelper(); + tokens[1].type = VK_INDIRECT_COMMANDS_TOKEN_TYPE_DRAW_EXT; + tokens[1].offset = 8; + + const std::vector pc_range = {{VK_SHADER_STAGE_VERTEX_BIT, 4, 12}}; + vkt::PipelineLayout pipeline_layout(*m_device, {}, pc_range); + VkIndirectCommandsLayoutCreateInfoEXT command_layout_ci = vku::InitStructHelper(); + command_layout_ci.shaderStages = VK_SHADER_STAGE_VERTEX_BIT; + command_layout_ci.pipelineLayout = pipeline_layout.handle(); + command_layout_ci.tokenCount = 2; + command_layout_ci.pTokens = tokens; + + vkt::IndirectCommandsLayout command_layout(*m_device, command_layout_ci); +} + +TEST_F(PositiveDeviceGeneratedCommands, CmdExecuteGeneratedCommands) { + RETURN_IF_SKIP(InitBasicDeviceGeneratedCommands()); + InitRenderTarget(); + + VkPipelineCreateFlags2CreateInfoKHR pipe_flags2 = vku::InitStructHelper(); + pipe_flags2.flags = VK_PIPELINE_CREATE_2_INDIRECT_BINDABLE_BIT_EXT; + CreatePipelineHelper pipe(*this, &pipe_flags2); + pipe.CreateGraphicsPipeline(); + + vkt::IndirectExecutionSet exe_set(*m_device, pipe.Handle(), 1); + + VkIndirectCommandsLayoutTokenEXT token = vku::InitStructHelper(); + token.type = VK_INDIRECT_COMMANDS_TOKEN_TYPE_DRAW_EXT; + token.offset = 0; + + VkIndirectCommandsLayoutCreateInfoEXT command_layout_ci = vku::InitStructHelper(); + command_layout_ci.shaderStages = VK_SHADER_STAGE_VERTEX_BIT; + command_layout_ci.pipelineLayout = VK_NULL_HANDLE; + command_layout_ci.tokenCount = 1; + command_layout_ci.pTokens = &token; + + vkt::IndirectCommandsLayout command_layout(*m_device, command_layout_ci); + + VkMemoryAllocateFlagsInfo allocate_flag_info = vku::InitStructHelper(); + allocate_flag_info.flags = VK_MEMORY_ALLOCATE_DEVICE_ADDRESS_BIT; + vkt::Buffer block_buffer(*m_device, 64, VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT, + VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, &allocate_flag_info); + + VkGeneratedCommandsInfoEXT generated_commands_info = vku::InitStructHelper(); + generated_commands_info.shaderStages = VK_SHADER_STAGE_VERTEX_BIT; + generated_commands_info.indirectExecutionSet = exe_set.handle(); + generated_commands_info.indirectCommandsLayout = command_layout.handle(); + generated_commands_info.indirectAddressSize = 64; + generated_commands_info.indirectAddress = block_buffer.address(); + generated_commands_info.preprocessAddress = 0; + generated_commands_info.sequenceCountAddress = 0; + generated_commands_info.maxDrawCount = 1; + m_command_buffer.begin(); + vk::CmdExecuteGeneratedCommandsEXT(m_command_buffer.handle(), false, &generated_commands_info); + m_command_buffer.end(); +} + +TEST_F(PositiveDeviceGeneratedCommands, UpdateIndirectExecutionSetPipeline) { + RETURN_IF_SKIP(InitBasicDeviceGeneratedCommands()); + InitRenderTarget(); + + VkPipelineCreateFlags2CreateInfoKHR pipe_flags2 = vku::InitStructHelper(); + pipe_flags2.flags = VK_PIPELINE_CREATE_2_INDIRECT_BINDABLE_BIT_EXT; + CreatePipelineHelper pipe(*this, &pipe_flags2); + pipe.CreateGraphicsPipeline(); + + vkt::IndirectExecutionSet exe_set(*m_device, pipe.Handle(), 4); + + VkWriteIndirectExecutionSetPipelineEXT write_exe_sets[3]; + write_exe_sets[0] = vku::InitStructHelper(); + write_exe_sets[0].index = 1; + write_exe_sets[0].pipeline = pipe.Handle(); + write_exe_sets[1] = vku::InitStructHelper(); + write_exe_sets[1].index = 2; + write_exe_sets[1].pipeline = pipe.Handle(); + write_exe_sets[2] = vku::InitStructHelper(); + write_exe_sets[2].index = 3; + write_exe_sets[2].pipeline = pipe.Handle(); + vk::UpdateIndirectExecutionSetPipelineEXT(device(), exe_set.handle(), 3, write_exe_sets); +} + +TEST_F(PositiveDeviceGeneratedCommands, UpdateIndirectExecutionSetShader) { + AddRequiredExtensions(VK_EXT_SHADER_OBJECT_EXTENSION_NAME); + AddRequiredFeature(vkt::Feature::shaderObject); + RETURN_IF_SKIP(InitBasicDeviceGeneratedCommands()); + InitRenderTarget(); + + const auto vert_spv = GLSLToSPV(VK_SHADER_STAGE_VERTEX_BIT, kVertexMinimalGlsl); + VkShaderCreateInfoEXT vert_create_info = + ShaderCreateInfoFlag(vert_spv, VK_SHADER_STAGE_VERTEX_BIT, VK_SHADER_CREATE_INDIRECT_BINDABLE_BIT_EXT); + const vkt::Shader vertShader(*m_device, vert_create_info); + const VkShaderEXT shaders[] = {vertShader.handle()}; + OneOffDescriptorSet descriptor_set(m_device, {{0, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1, VK_SHADER_STAGE_ALL, nullptr}}); + VkIndirectExecutionSetShaderLayoutInfoEXT exe_set_layouts = vku::InitStructHelper(); + exe_set_layouts.setLayoutCount = 1; + exe_set_layouts.pSetLayouts = &descriptor_set.layout_.handle(); + + VkIndirectExecutionSetShaderInfoEXT exe_set_shader_info = vku::InitStructHelper(); + exe_set_shader_info.shaderCount = 1; + exe_set_shader_info.pInitialShaders = shaders; + exe_set_shader_info.pSetLayoutInfos = &exe_set_layouts; + exe_set_shader_info.maxShaderCount = 1; + exe_set_shader_info.pushConstantRangeCount = 0; + vkt::IndirectExecutionSet exe_set(*m_device, exe_set_shader_info); + + VkWriteIndirectExecutionSetShaderEXT write_exe_set = vku::InitStructHelper(); + write_exe_set.index = 0; + write_exe_set.shader = vertShader.handle(); + vk::UpdateIndirectExecutionSetShaderEXT(device(), exe_set.handle(), 1, &write_exe_set); +} + +TEST_F(PositiveDeviceGeneratedCommands, CmdExecuteGeneratedCommandsEXT) { + RETURN_IF_SKIP(InitBasicDeviceGeneratedCommands()); + + VkIndirectCommandsExecutionSetTokenEXT exe_set_token = {VK_INDIRECT_EXECUTION_SET_INFO_TYPE_PIPELINES_EXT, + VK_SHADER_STAGE_COMPUTE_BIT}; + VkIndirectCommandsLayoutTokenEXT tokens[2]; + tokens[0] = vku::InitStructHelper(); + tokens[0].type = VK_INDIRECT_COMMANDS_TOKEN_TYPE_EXECUTION_SET_EXT; + tokens[0].data.pExecutionSet = &exe_set_token; + tokens[0].offset = 0; + + tokens[1] = vku::InitStructHelper(); + tokens[1].type = VK_INDIRECT_COMMANDS_TOKEN_TYPE_DISPATCH_EXT; + tokens[1].offset = 8; + + VkIndirectCommandsLayoutCreateInfoEXT command_layout_ci = vku::InitStructHelper(); + command_layout_ci.shaderStages = VK_SHADER_STAGE_COMPUTE_BIT; + command_layout_ci.pipelineLayout = VK_NULL_HANDLE; + command_layout_ci.tokenCount = 2; + command_layout_ci.pTokens = tokens; + vkt::IndirectCommandsLayout command_layout(*m_device, command_layout_ci); + + VkPipelineCreateFlags2CreateInfoKHR pipe_flags2 = vku::InitStructHelper(); + pipe_flags2.flags = VK_PIPELINE_CREATE_2_INDIRECT_BINDABLE_BIT_EXT; + CreateComputePipelineHelper pipe(*this, &pipe_flags2); + pipe.CreateComputePipeline(); + vkt::IndirectExecutionSet exe_set(*m_device, pipe.Handle(), 1); + + VkMemoryAllocateFlagsInfo allocate_flag_info = vku::InitStructHelper(); + allocate_flag_info.flags = VK_MEMORY_ALLOCATE_DEVICE_ADDRESS_BIT; + vkt::Buffer block_buffer(*m_device, 64, VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT, + VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, &allocate_flag_info); + + VkGeneratedCommandsInfoEXT generated_commands_info = vku::InitStructHelper(); + generated_commands_info.shaderStages = VK_SHADER_STAGE_COMPUTE_BIT; + generated_commands_info.indirectExecutionSet = exe_set.handle(); + generated_commands_info.indirectCommandsLayout = command_layout.handle(); + generated_commands_info.indirectAddressSize = 64; + generated_commands_info.indirectAddress = block_buffer.address(); + generated_commands_info.preprocessAddress = 0; + generated_commands_info.sequenceCountAddress = 0; + generated_commands_info.maxDrawCount = 1; + + m_command_buffer.begin(); + vk::CmdBindPipeline(m_command_buffer.handle(), VK_PIPELINE_BIND_POINT_COMPUTE, pipe.Handle()); + vk::CmdExecuteGeneratedCommandsEXT(m_command_buffer.handle(), false, &generated_commands_info); + m_command_buffer.end(); +} + +TEST_F(PositiveDeviceGeneratedCommands, ExecuteShaderObjectVertex) { + AddRequiredExtensions(VK_KHR_DYNAMIC_RENDERING_EXTENSION_NAME); + AddRequiredExtensions(VK_EXT_SHADER_OBJECT_EXTENSION_NAME); + AddRequiredFeature(vkt::Feature::shaderObject); + AddRequiredFeature(vkt::Feature::dynamicRendering); + RETURN_IF_SKIP(InitBasicDeviceGeneratedCommands()); + InitRenderTarget(); + + VkIndirectCommandsExecutionSetTokenEXT exe_set_token = {VK_INDIRECT_EXECUTION_SET_INFO_TYPE_SHADER_OBJECTS_EXT, + VK_SHADER_STAGE_VERTEX_BIT}; + + VkIndirectCommandsLayoutTokenEXT tokens[2]; + tokens[0] = vku::InitStructHelper(); + tokens[0].type = VK_INDIRECT_COMMANDS_TOKEN_TYPE_EXECUTION_SET_EXT; + tokens[0].data.pExecutionSet = &exe_set_token; + tokens[0].offset = 0; + + tokens[1] = vku::InitStructHelper(); + tokens[1].type = VK_INDIRECT_COMMANDS_TOKEN_TYPE_DRAW_EXT; + tokens[1].offset = 8; + + VkIndirectCommandsLayoutCreateInfoEXT command_layout_ci = vku::InitStructHelper(); + command_layout_ci.shaderStages = VK_INDIRECT_EXECUTION_SET_INFO_TYPE_SHADER_OBJECTS_EXT; + command_layout_ci.pipelineLayout = VK_NULL_HANDLE; + command_layout_ci.tokenCount = 2; + command_layout_ci.pTokens = tokens; + vkt::IndirectCommandsLayout command_layout(*m_device, command_layout_ci); + + const auto vert_spv = GLSLToSPV(VK_SHADER_STAGE_VERTEX_BIT, kVertexMinimalGlsl); + VkShaderCreateInfoEXT vert_create_info = + ShaderCreateInfoFlag(vert_spv, VK_SHADER_STAGE_VERTEX_BIT, VK_SHADER_CREATE_INDIRECT_BINDABLE_BIT_EXT); + const vkt::Shader vertShader(*m_device, vert_create_info); + const VkShaderEXT shaders[] = {vertShader.handle()}; + OneOffDescriptorSet descriptor_set(m_device, {{0, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1, VK_SHADER_STAGE_ALL, nullptr}}); + VkIndirectExecutionSetShaderLayoutInfoEXT exe_set_layouts = vku::InitStructHelper(); + exe_set_layouts.setLayoutCount = 1; + exe_set_layouts.pSetLayouts = &descriptor_set.layout_.handle(); + + VkIndirectExecutionSetShaderInfoEXT exe_set_shader_info = vku::InitStructHelper(); + exe_set_shader_info.shaderCount = 1; + exe_set_shader_info.pInitialShaders = shaders; + exe_set_shader_info.pSetLayoutInfos = &exe_set_layouts; + exe_set_shader_info.maxShaderCount = 1; + exe_set_shader_info.pushConstantRangeCount = 0; + vkt::IndirectExecutionSet exe_set(*m_device, exe_set_shader_info); + + VkMemoryAllocateFlagsInfo allocate_flag_info = vku::InitStructHelper(); + allocate_flag_info.flags = VK_MEMORY_ALLOCATE_DEVICE_ADDRESS_BIT; + vkt::Buffer block_buffer(*m_device, 64, VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT, + VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, &allocate_flag_info); + + VkGeneratedCommandsInfoEXT generated_commands_info = vku::InitStructHelper(); + generated_commands_info.shaderStages = VK_SHADER_STAGE_VERTEX_BIT; + generated_commands_info.indirectExecutionSet = exe_set.handle(); + generated_commands_info.indirectCommandsLayout = command_layout.handle(); + generated_commands_info.indirectAddressSize = 64; + generated_commands_info.indirectAddress = block_buffer.address(); + generated_commands_info.preprocessAddress = 0; + generated_commands_info.sequenceCountAddress = 0; + generated_commands_info.maxDrawCount = 1; + + m_command_buffer.begin(); + m_command_buffer.BeginRenderingColor(GetDynamicRenderTarget(), GetRenderTargetArea()); + const VkShaderStageFlagBits stages[] = {VK_SHADER_STAGE_VERTEX_BIT}; + vk::CmdBindShadersEXT(m_command_buffer.handle(), 1u, stages, shaders); + SetDefaultDynamicStatesAll(m_command_buffer.handle()); + vk::CmdExecuteGeneratedCommandsEXT(m_command_buffer.handle(), false, &generated_commands_info); + m_command_buffer.EndRendering(); + m_command_buffer.end(); +} diff --git a/tests/unit/gpu_av_oob.cpp b/tests/unit/gpu_av_oob.cpp index c327230f7f5..e426071b5a2 100644 --- a/tests/unit/gpu_av_oob.cpp +++ b/tests/unit/gpu_av_oob.cpp @@ -1818,3 +1818,108 @@ TEST_F(NegativeGpuAVOOB, DISABLED_ConstantArrayOOBTexture) { m_default_queue->Wait(); m_errorMonitor->VerifyFound(); } + +TEST_F(NegativeGpuAVOOB, DeviceGeneratedCommandsCompute) { + SetTargetApiVersion(VK_API_VERSION_1_2); + AddRequiredExtensions(VK_EXT_DEVICE_GENERATED_COMMANDS_EXTENSION_NAME); + AddRequiredExtensions(VK_KHR_BUFFER_DEVICE_ADDRESS_EXTENSION_NAME); + AddRequiredFeature(vkt::Feature::deviceGeneratedCommands); + AddRequiredFeature(vkt::Feature::bufferDeviceAddress); + AddDisabledFeature(vkt::Feature::robustBufferAccess); + RETURN_IF_SKIP(InitGpuAvFramework()); + RETURN_IF_SKIP(InitState()); + + VkPhysicalDeviceDeviceGeneratedCommandsPropertiesEXT dgc_props = vku::InitStructHelper(); + GetPhysicalDeviceProperties2(dgc_props); + if ((dgc_props.supportedIndirectCommandsShaderStagesPipelineBinding & VK_SHADER_STAGE_COMPUTE_BIT) == 0) { + GTEST_SKIP() << "VK_SHADER_STAGE_COMPUTE_BIT is not supported."; + } + + VkIndirectCommandsLayoutTokenEXT token; + token = vku::InitStructHelper(); + token.type = VK_INDIRECT_COMMANDS_TOKEN_TYPE_DISPATCH_EXT; + token.offset = 0; + + VkIndirectCommandsLayoutCreateInfoEXT command_layout_ci = vku::InitStructHelper(); + command_layout_ci.shaderStages = VK_SHADER_STAGE_COMPUTE_BIT; + command_layout_ci.pipelineLayout = VK_NULL_HANDLE; + command_layout_ci.tokenCount = 1; + command_layout_ci.pTokens = &token; + vkt::IndirectCommandsLayout command_layout(*m_device, command_layout_ci); + + char const *shader_source = R"glsl( + #version 450 + layout(set = 0, binding = 0) buffer ssbo { + uint x[]; + }; + void main() { + x[48] = 0; + } + )glsl"; + + VkPipelineCreateFlags2CreateInfoKHR pipe_flags2 = vku::InitStructHelper(); + pipe_flags2.flags = VK_PIPELINE_CREATE_2_INDIRECT_BINDABLE_BIT_EXT; + CreateComputePipelineHelper pipe(*this, &pipe_flags2); + pipe.dsl_bindings_ = {{0, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1, VK_SHADER_STAGE_ALL, nullptr}}; + pipe.cs_ = std::make_unique(this, shader_source, VK_SHADER_STAGE_COMPUTE_BIT, SPV_ENV_VULKAN_1_1); + pipe.CreateComputePipeline(); + + vkt::Buffer ssbo_buffer(*m_device, 8, VK_BUFFER_USAGE_STORAGE_BUFFER_BIT, + VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT); + pipe.descriptor_set_->WriteDescriptorBufferInfo(0, ssbo_buffer.handle(), 0, VK_WHOLE_SIZE, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER); + pipe.descriptor_set_->UpdateDescriptorSets(); + + VkGeneratedCommandsPipelineInfoEXT pipeline_info = vku::InitStructHelper(); + pipeline_info.pipeline = pipe.Handle(); + + VkMemoryAllocateFlagsInfo allocate_flag_info = vku::InitStructHelper(); + allocate_flag_info.flags = VK_MEMORY_ALLOCATE_DEVICE_ADDRESS_BIT; + vkt::Buffer block_buffer(*m_device, 64, VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT, + VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, &allocate_flag_info); + + VkDeviceSize pre_process_size = 0; + { + VkGeneratedCommandsMemoryRequirementsInfoEXT dgc_mem_reqs = vku::InitStructHelper(&pipeline_info); + dgc_mem_reqs.indirectCommandsLayout = command_layout.handle(); + dgc_mem_reqs.indirectExecutionSet = VK_NULL_HANDLE; + dgc_mem_reqs.maxSequenceCount = 1; + VkMemoryRequirements2 mem_reqs2 = vku::InitStructHelper(); + vk::GetGeneratedCommandsMemoryRequirementsEXT(device(), &dgc_mem_reqs, &mem_reqs2); + pre_process_size = mem_reqs2.memoryRequirements.size; + } + + VkBufferUsageFlags2CreateInfoKHR buffer_usage_flags = vku::InitStructHelper(); + buffer_usage_flags.usage = VK_BUFFER_USAGE_2_PREPROCESS_BUFFER_BIT_EXT | VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT; + VkBufferCreateInfo buffer_ci = vku::InitStructHelper(&buffer_usage_flags); + buffer_ci.size = pre_process_size; + vkt::Buffer pre_process_buffer(*m_device, buffer_ci, 0, &allocate_flag_info); + + VkDispatchIndirectCommand *block_buffer_ptr = (VkDispatchIndirectCommand *)block_buffer.memory().map(); + block_buffer_ptr->x = 1; + block_buffer_ptr->y = 1; + block_buffer_ptr->z = 1; + block_buffer.memory().unmap(); + + VkGeneratedCommandsInfoEXT generated_commands_info = vku::InitStructHelper(&pipeline_info); + generated_commands_info.shaderStages = VK_SHADER_STAGE_COMPUTE_BIT; + generated_commands_info.indirectExecutionSet = VK_NULL_HANDLE; + generated_commands_info.indirectCommandsLayout = command_layout.handle(); + generated_commands_info.indirectAddressSize = sizeof(VkDispatchIndirectCommand); + generated_commands_info.indirectAddress = block_buffer.address(); + generated_commands_info.preprocessAddress = pre_process_buffer.address(); + generated_commands_info.preprocessSize = pre_process_size; + generated_commands_info.sequenceCountAddress = 0; + generated_commands_info.maxSequenceCount = 1; + + m_command_buffer.begin(); + vk::CmdBindPipeline(m_command_buffer.handle(), VK_PIPELINE_BIND_POINT_COMPUTE, pipe.Handle()); + vk::CmdBindDescriptorSets(m_commandBuffer->handle(), VK_PIPELINE_BIND_POINT_COMPUTE, pipe.pipeline_layout_.handle(), 0, 1, + &pipe.descriptor_set_->set_, 0, nullptr); + vk::CmdExecuteGeneratedCommandsEXT(m_command_buffer.handle(), false, &generated_commands_info); + m_command_buffer.end(); + + m_errorMonitor->SetDesiredError("VUID-vkCmdExecuteGeneratedCommandsEXT-storageBuffers-06936"); + m_default_queue->Submit(m_command_buffer); + m_default_queue->Wait(); + m_errorMonitor->VerifyFound(); +} diff --git a/tests/unit/gpu_av_positive.cpp b/tests/unit/gpu_av_positive.cpp index 390bd682453..5e3fdc4be7e 100644 --- a/tests/unit/gpu_av_positive.cpp +++ b/tests/unit/gpu_av_positive.cpp @@ -1870,3 +1870,150 @@ TEST_F(PositiveGpuAV, DestroyedPipelineLayout2) { m_commandBuffer->EndRenderPass(); m_commandBuffer->end(); } + +TEST_F(PositiveGpuAV, DeviceGeneratedCommandsIES) { + SetTargetApiVersion(VK_API_VERSION_1_1); + AddRequiredExtensions(VK_EXT_DEVICE_GENERATED_COMMANDS_EXTENSION_NAME); + AddRequiredExtensions(VK_KHR_BUFFER_DEVICE_ADDRESS_EXTENSION_NAME); + AddRequiredFeature(vkt::Feature::deviceGeneratedCommands); + AddRequiredFeature(vkt::Feature::bufferDeviceAddress); + AddDisabledFeature(vkt::Feature::robustBufferAccess); + RETURN_IF_SKIP(InitGpuAvFramework()); + RETURN_IF_SKIP(InitState()); + + VkPhysicalDeviceDeviceGeneratedCommandsPropertiesEXT dgc_props = vku::InitStructHelper(); + GetPhysicalDeviceProperties2(dgc_props); + if ((dgc_props.supportedIndirectCommandsShaderStagesPipelineBinding & VK_SHADER_STAGE_COMPUTE_BIT) == 0) { + GTEST_SKIP() << "VK_SHADER_STAGE_COMPUTE_BIT is not supported."; + } + + VkIndirectCommandsExecutionSetTokenEXT exe_set_token = {VK_INDIRECT_EXECUTION_SET_INFO_TYPE_PIPELINES_EXT, + VK_SHADER_STAGE_COMPUTE_BIT}; + VkIndirectCommandsLayoutTokenEXT tokens[2]; + tokens[0] = vku::InitStructHelper(); + tokens[0].type = VK_INDIRECT_COMMANDS_TOKEN_TYPE_EXECUTION_SET_EXT; + tokens[0].data.pExecutionSet = &exe_set_token; + tokens[0].offset = 0; + tokens[1] = vku::InitStructHelper(); + tokens[1].type = VK_INDIRECT_COMMANDS_TOKEN_TYPE_DISPATCH_EXT; + tokens[1].offset = sizeof(uint32_t); + + VkIndirectCommandsLayoutCreateInfoEXT command_layout_ci = vku::InitStructHelper(); + command_layout_ci.shaderStages = VK_SHADER_STAGE_COMPUTE_BIT; + command_layout_ci.pipelineLayout = VK_NULL_HANDLE; + command_layout_ci.tokenCount = 2; + command_layout_ci.pTokens = tokens; + vkt::IndirectCommandsLayout command_layout(*m_device, command_layout_ci); + + char const *shader_source_1 = R"glsl( + #version 450 + layout(set = 0, binding = 0) buffer ssbo { + uint x[]; + }; + void main() { + x[48] = 0; // invalid! + } + )glsl"; + char const *shader_source_2 = R"glsl( + #version 450 + layout(set = 0, binding = 0) buffer ssbo { + uint x[]; + }; + void main() { + x[24] = 0; // invalid! + } + )glsl"; + char const *shader_source_3 = R"glsl( + #version 450 + layout(set = 0, binding = 0) buffer ssbo { + uint x[]; + }; + void main() { + x[0] = 0; // valid + } + )glsl"; + + VkPipelineCreateFlags2CreateInfoKHR pipe_flags2 = vku::InitStructHelper(); + pipe_flags2.flags = VK_PIPELINE_CREATE_2_INDIRECT_BINDABLE_BIT_EXT; + CreateComputePipelineHelper init_pipe(*this, &pipe_flags2); + init_pipe.dsl_bindings_ = {{0, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1, VK_SHADER_STAGE_ALL, nullptr}}; + init_pipe.cs_ = std::make_unique(this, shader_source_1, VK_SHADER_STAGE_COMPUTE_BIT, SPV_ENV_VULKAN_1_1); + init_pipe.CreateComputePipeline(); + + CreateComputePipelineHelper pipe_1(*this, &pipe_flags2); + pipe_1.dsl_bindings_ = {{0, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1, VK_SHADER_STAGE_ALL, nullptr}}; + pipe_1.cs_ = std::make_unique(this, shader_source_2, VK_SHADER_STAGE_COMPUTE_BIT, SPV_ENV_VULKAN_1_1); + pipe_1.CreateComputePipeline(); + + CreateComputePipelineHelper pipe_2(*this, &pipe_flags2); + pipe_2.dsl_bindings_ = {{0, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1, VK_SHADER_STAGE_ALL, nullptr}}; + pipe_2.cs_ = std::make_unique(this, shader_source_3, VK_SHADER_STAGE_COMPUTE_BIT, SPV_ENV_VULKAN_1_1); + pipe_2.CreateComputePipeline(); + + vkt::IndirectExecutionSet exe_set(*m_device, init_pipe.Handle(), 3); + VkWriteIndirectExecutionSetPipelineEXT write_exe_sets[2]; + write_exe_sets[0] = vku::InitStructHelper(); + write_exe_sets[0].index = 1; + write_exe_sets[0].pipeline = pipe_1.Handle(); + write_exe_sets[1] = vku::InitStructHelper(); + write_exe_sets[1].index = 2; + write_exe_sets[1].pipeline = pipe_2.Handle(); + vk::UpdateIndirectExecutionSetPipelineEXT(device(), exe_set.handle(), 2, write_exe_sets); + + vkt::Buffer ssbo_buffer(*m_device, 8, VK_BUFFER_USAGE_STORAGE_BUFFER_BIT, + VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT); + init_pipe.descriptor_set_->WriteDescriptorBufferInfo(0, ssbo_buffer.handle(), 0, VK_WHOLE_SIZE, + VK_DESCRIPTOR_TYPE_STORAGE_BUFFER); + init_pipe.descriptor_set_->UpdateDescriptorSets(); + + VkMemoryAllocateFlagsInfo allocate_flag_info = vku::InitStructHelper(); + allocate_flag_info.flags = VK_MEMORY_ALLOCATE_DEVICE_ADDRESS_BIT; + vkt::Buffer block_buffer(*m_device, 64, VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT, + VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, &allocate_flag_info); + + VkDeviceSize pre_process_size = 0; + { + VkGeneratedCommandsMemoryRequirementsInfoEXT dgc_mem_reqs = vku::InitStructHelper(); + dgc_mem_reqs.indirectCommandsLayout = command_layout.handle(); + dgc_mem_reqs.indirectExecutionSet = exe_set.handle(); + dgc_mem_reqs.maxSequenceCount = 1; + VkMemoryRequirements2 mem_reqs2 = vku::InitStructHelper(); + vk::GetGeneratedCommandsMemoryRequirementsEXT(device(), &dgc_mem_reqs, &mem_reqs2); + pre_process_size = mem_reqs2.memoryRequirements.size; + } + + VkBufferUsageFlags2CreateInfoKHR buffer_usage_flags = vku::InitStructHelper(); + buffer_usage_flags.usage = VK_BUFFER_USAGE_2_PREPROCESS_BUFFER_BIT_EXT | VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT; + VkBufferCreateInfo buffer_ci = vku::InitStructHelper(&buffer_usage_flags); + buffer_ci.size = pre_process_size; + vkt::Buffer pre_process_buffer(*m_device, buffer_ci, 0, &allocate_flag_info); + + uint32_t *block_buffer_ptr = (uint32_t *)block_buffer.memory().map(); + block_buffer_ptr[0] = 2; // pick pipeline 2 + VkDispatchIndirectCommand *indirect_command_ptr = (VkDispatchIndirectCommand *)(block_buffer_ptr + 1); + indirect_command_ptr->x = 1; + indirect_command_ptr->y = 1; + indirect_command_ptr->z = 1; + block_buffer.memory().unmap(); + + VkGeneratedCommandsInfoEXT generated_commands_info = vku::InitStructHelper(); + generated_commands_info.shaderStages = VK_SHADER_STAGE_COMPUTE_BIT; + generated_commands_info.indirectExecutionSet = exe_set.handle(); + generated_commands_info.indirectCommandsLayout = command_layout.handle(); + generated_commands_info.indirectAddressSize = sizeof(uint32_t) + sizeof(VkDispatchIndirectCommand); + generated_commands_info.indirectAddress = block_buffer.address(); + generated_commands_info.preprocessAddress = pre_process_buffer.address(); + generated_commands_info.preprocessSize = pre_process_size; + generated_commands_info.sequenceCountAddress = 0; + generated_commands_info.maxSequenceCount = 1; + + m_command_buffer.begin(); + vk::CmdBindPipeline(m_command_buffer.handle(), VK_PIPELINE_BIND_POINT_COMPUTE, init_pipe.Handle()); + vk::CmdBindDescriptorSets(m_commandBuffer->handle(), VK_PIPELINE_BIND_POINT_COMPUTE, init_pipe.pipeline_layout_.handle(), 0, 1, + &init_pipe.descriptor_set_->set_, 0, nullptr); + vk::CmdExecuteGeneratedCommandsEXT(m_command_buffer.handle(), false, &generated_commands_info); + m_command_buffer.end(); + + m_default_queue->Submit(m_command_buffer); + m_default_queue->Wait(); +} diff --git a/tests/unit/pipeline_positive.cpp b/tests/unit/pipeline_positive.cpp index 33504712be0..9c9d9001065 100644 --- a/tests/unit/pipeline_positive.cpp +++ b/tests/unit/pipeline_positive.cpp @@ -11,6 +11,7 @@ * http://www.apache.org/licenses/LICENSE-2.0 */ +#include #include "../framework/layer_validation_tests.h" #include "../framework/pipeline_helper.h" #include "../framework/descriptor_helper.h" @@ -1545,7 +1546,6 @@ TEST_F(PositivePipeline, RasterStateWithDepthBiasRepresentationInfo) { TEST_F(PositivePipeline, DeviceGeneratedCommands) { TEST_DESCRIPTION("Test creating pipeline with device generated commands"); - SetTargetApiVersion(VK_API_VERSION_1_1); AddRequiredExtensions(VK_NV_DEVICE_GENERATED_COMMANDS_EXTENSION_NAME); RETURN_IF_SKIP(InitFramework());