| /* Copyright (c) 2015-2023 The Khronos Group Inc. |
| * Copyright (c) 2015-2023 Valve Corporation |
| * Copyright (c) 2015-2023 LunarG, Inc. |
| * Copyright (C) 2015-2023 Google Inc. |
| * Modifications Copyright (C) 2020 Advanced Micro Devices, Inc. All rights reserved. |
| * Modifications Copyright (C) 2022 RasterGrid Kft. |
| * |
| * 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/cmd_buffer_state.h" |
| |
| COMMAND_POOL_STATE::COMMAND_POOL_STATE(ValidationStateTracker *dev, VkCommandPool cp, const VkCommandPoolCreateInfo *pCreateInfo, |
| VkQueueFlags flags) |
| : BASE_NODE(cp, kVulkanObjectTypeCommandPool), |
| dev_data(dev), |
| createFlags(pCreateInfo->flags), |
| queueFamilyIndex(pCreateInfo->queueFamilyIndex), |
| queue_flags(flags), |
| unprotected((pCreateInfo->flags & VK_COMMAND_POOL_CREATE_PROTECTED_BIT) == 0) {} |
| |
| void COMMAND_POOL_STATE::Allocate(const VkCommandBufferAllocateInfo *create_info, const VkCommandBuffer *command_buffers) { |
| for (uint32_t i = 0; i < create_info->commandBufferCount; i++) { |
| auto new_cb = dev_data->CreateCmdBufferState(command_buffers[i], create_info, this); |
| commandBuffers.emplace(command_buffers[i], new_cb.get()); |
| dev_data->Add(std::move(new_cb)); |
| } |
| } |
| |
| void COMMAND_POOL_STATE::Free(uint32_t count, const VkCommandBuffer *command_buffers) { |
| for (uint32_t i = 0; i < count; i++) { |
| auto iter = commandBuffers.find(command_buffers[i]); |
| if (iter != commandBuffers.end()) { |
| dev_data->Destroy<CMD_BUFFER_STATE>(iter->first); |
| commandBuffers.erase(iter); |
| } |
| } |
| } |
| |
| void COMMAND_POOL_STATE::Reset() { |
| for (auto &entry : commandBuffers) { |
| auto guard = entry.second->WriteLock(); |
| entry.second->Reset(); |
| } |
| } |
| |
| void COMMAND_POOL_STATE::Destroy() { |
| for (auto &entry : commandBuffers) { |
| dev_data->Destroy<CMD_BUFFER_STATE>(entry.first); |
| } |
| commandBuffers.clear(); |
| BASE_NODE::Destroy(); |
| } |
| |
| void CMD_BUFFER_STATE::SetActiveSubpass(uint32_t subpass) { |
| active_subpass_ = subpass; |
| // Always reset stored rasterization samples count |
| active_subpass_sample_count_ = std::nullopt; |
| } |
| |
| CMD_BUFFER_STATE::CMD_BUFFER_STATE(ValidationStateTracker *dev, VkCommandBuffer cb, const VkCommandBufferAllocateInfo *pCreateInfo, |
| const COMMAND_POOL_STATE *pool) |
| : REFCOUNTED_NODE(cb, kVulkanObjectTypeCommandBuffer), |
| createInfo(*pCreateInfo), |
| command_pool(pool), |
| dev_data(dev), |
| unprotected(pool->unprotected), |
| lastBound({*this, *this, *this}) { |
| ResetCBState(); |
| } |
| |
| // Get the image viewstate for a given framebuffer attachment |
| IMAGE_VIEW_STATE *CMD_BUFFER_STATE::GetActiveAttachmentImageViewState(uint32_t index) { |
| assert(active_attachments && index != VK_ATTACHMENT_UNUSED && (index < active_attachments->size())); |
| return active_attachments->at(index); |
| } |
| |
| // Get the image viewstate for a given framebuffer attachment |
| const IMAGE_VIEW_STATE *CMD_BUFFER_STATE::GetActiveAttachmentImageViewState(uint32_t index) const { |
| if (!active_attachments || index == VK_ATTACHMENT_UNUSED || (index >= active_attachments->size())) { |
| return nullptr; |
| } |
| return active_attachments->at(index); |
| } |
| |
| void CMD_BUFFER_STATE::AddChild(std::shared_ptr<BASE_NODE> &child_node) { |
| assert(child_node); |
| if (child_node->AddParent(this)) { |
| object_bindings.insert(child_node); |
| } |
| } |
| |
| void CMD_BUFFER_STATE::RemoveChild(std::shared_ptr<BASE_NODE> &child_node) { |
| assert(child_node); |
| child_node->RemoveParent(this); |
| object_bindings.erase(child_node); |
| } |
| |
| // Reset the command buffer state |
| // Maintain the createInfo and set state to CB_NEW, but clear all other state |
| void CMD_BUFFER_STATE::ResetCBState() { |
| // Remove object bindings |
| for (const auto &obj : object_bindings) { |
| obj->RemoveParent(this); |
| } |
| object_bindings.clear(); |
| broken_bindings.clear(); |
| |
| // Reset CB state (note that createInfo is not cleared) |
| memset(&beginInfo, 0, sizeof(VkCommandBufferBeginInfo)); |
| memset(&inheritanceInfo, 0, sizeof(VkCommandBufferInheritanceInfo)); |
| has_draw_cmd = false; |
| has_dispatch_cmd = false; |
| has_trace_rays_cmd = false; |
| has_build_as_cmd = false; |
| hasRenderPassInstance = false; |
| suspendsRenderPassInstance = false; |
| resumesRenderPassInstance = false; |
| state = CbState::New; |
| command_count = 0; |
| submitCount = 0; |
| image_layout_change_count = 1; // Start at 1. 0 is insert value for validation cache versions, s.t. new == dirty |
| dynamic_state_status.cb.reset(); |
| dynamic_state_status.pipeline.reset(); |
| dynamic_state_value.reset(); |
| inheritedViewportDepths.clear(); |
| usedViewportScissorCount = 0; |
| pipelineStaticViewportCount = 0; |
| pipelineStaticScissorCount = 0; |
| viewportMask = 0; |
| viewportWithCountMask = 0; |
| scissorMask = 0; |
| scissorWithCountMask = 0; |
| trashedViewportMask = 0; |
| trashedScissorMask = 0; |
| trashedViewportCount = false; |
| trashedScissorCount = false; |
| usedDynamicViewportCount = false; |
| usedDynamicScissorCount = false; |
| |
| active_render_pass_begin_info = safe_VkRenderPassBeginInfo(); |
| activeRenderPass = nullptr; |
| active_attachments = nullptr; |
| active_subpasses = nullptr; |
| active_color_attachments_index.clear(); |
| attachments_view_states.clear(); |
| activeSubpassContents = VK_SUBPASS_CONTENTS_INLINE; |
| SetActiveSubpass(0); |
| waitedEvents.clear(); |
| events.clear(); |
| writeEventsBeforeWait.clear(); |
| activeQueries.clear(); |
| startedQueries.clear(); |
| image_layout_map.clear(); |
| aliased_image_layout_map.clear(); |
| descriptorset_cache.clear(); |
| current_vertex_buffer_binding_info.vertex_buffer_bindings.clear(); |
| vertex_buffer_used = false; |
| primaryCommandBuffer = VK_NULL_HANDLE; |
| linkedCommandBuffers.clear(); |
| queue_submit_functions.clear(); |
| queue_submit_functions_after_render_pass.clear(); |
| cmd_execute_commands_functions.clear(); |
| eventUpdates.clear(); |
| queryUpdates.clear(); |
| |
| for (auto &item : lastBound) { |
| item.Reset(); |
| } |
| activeFramebuffer = VK_NULL_HANDLE; |
| index_buffer_binding.reset(); |
| |
| qfo_transfer_image_barriers.Reset(); |
| qfo_transfer_buffer_barriers.Reset(); |
| |
| // Clean up video specific states |
| bound_video_session = nullptr; |
| bound_video_session_parameters = nullptr; |
| bound_video_picture_resources.clear(); |
| video_session_updates.clear(); |
| |
| // Clean up the label data |
| debug_label.Reset(); |
| validate_descriptorsets_in_queuesubmit.clear(); |
| |
| // Best practices info |
| small_indexed_draw_call_count = 0; |
| |
| transform_feedback_active = false; |
| |
| // Clean up the label data |
| ResetCmdDebugUtilsLabel(dev_data->report_data, commandBuffer()); |
| } |
| |
| void CMD_BUFFER_STATE::Reset() { |
| ResetCBState(); |
| // Remove reverse command buffer links. |
| Invalidate(true); |
| } |
| |
| // Track which resources are in-flight by atomically incrementing their "in_use" count |
| void CMD_BUFFER_STATE::IncrementResources() { |
| submitCount++; |
| |
| // TODO : We should be able to remove the NULL look-up checks from the code below as long as |
| // all the corresponding cases are verified to cause CB_INVALID state and the CB_INVALID state |
| // should then be flagged prior to calling this function |
| for (auto event : writeEventsBeforeWait) { |
| auto event_state = dev_data->Get<EVENT_STATE>(event); |
| if (event_state) event_state->write_in_use++; |
| } |
| } |
| |
| // Discussed in details in https://github.com/KhronosGroup/Vulkan-Docs/issues/1081 |
| // Internal discussion and CTS were written to prove that this is not called after an incompatible vkCmdBindPipeline |
| // "Binding a pipeline with a layout that is not compatible with the push constant layout does not disturb the push constant values" |
| // |
| // vkCmdBindDescriptorSet has nothing to do with push constants and don't need to call this after neither |
| // |
| // Part of this assumes apps at draw/dispath/traceRays/etc time will have it properly compatabile or else other VU will be triggered |
| void CMD_BUFFER_STATE::ResetPushConstantDataIfIncompatible(const PIPELINE_LAYOUT_STATE *pipeline_layout_state) { |
| if (pipeline_layout_state == nullptr) { |
| return; |
| } |
| if (push_constant_data_ranges == pipeline_layout_state->push_constant_ranges) { |
| return; |
| } |
| |
| push_constant_data_ranges = pipeline_layout_state->push_constant_ranges; |
| push_constant_data.clear(); |
| uint32_t size_needed = 0; |
| for (const auto &push_constant_range : *push_constant_data_ranges) { |
| auto size = push_constant_range.offset + push_constant_range.size; |
| size_needed = std::max(size_needed, size); |
| } |
| push_constant_data.resize(size_needed, 0); |
| } |
| |
| void CMD_BUFFER_STATE::Destroy() { |
| // Remove the cb debug labels |
| EraseCmdDebugUtilsLabel(dev_data->report_data, commandBuffer()); |
| { |
| auto guard = WriteLock(); |
| ResetCBState(); |
| } |
| BASE_NODE::Destroy(); |
| } |
| |
| void CMD_BUFFER_STATE::NotifyInvalidate(const BASE_NODE::NodeList &invalid_nodes, bool unlink) { |
| { |
| auto guard = WriteLock(); |
| assert(!invalid_nodes.empty()); |
| // Save all of the vulkan handles between the command buffer and the now invalid node |
| LogObjectList log_list; |
| for (auto &obj : invalid_nodes) { |
| log_list.add(obj->Handle()); |
| } |
| |
| bool found_invalid = false; |
| for (auto &obj : invalid_nodes) { |
| // Only record a broken binding if one of the nodes in the invalid chain is still |
| // being tracked by the command buffer. This is to try to avoid race conditions |
| // caused by separate CMD_BUFFER_STATE and BASE_NODE::parent_nodes locking. |
| if (object_bindings.erase(obj)) { |
| obj->RemoveParent(this); |
| found_invalid = true; |
| } |
| switch (obj->Type()) { |
| case kVulkanObjectTypeCommandBuffer: |
| if (unlink) { |
| linkedCommandBuffers.erase(static_cast<CMD_BUFFER_STATE *>(obj.get())); |
| } |
| break; |
| case kVulkanObjectTypeImage: |
| if (unlink) { |
| image_layout_map.erase(static_cast<IMAGE_STATE *>(obj.get())); |
| } |
| break; |
| default: |
| break; |
| } |
| } |
| if (found_invalid) { |
| if (state == CbState::Recording) { |
| state = CbState::InvalidIncomplete; |
| } else if (state == CbState::Recorded) { |
| state = CbState::InvalidComplete; |
| } |
| broken_bindings.emplace(invalid_nodes[0]->Handle(), log_list); |
| } |
| } |
| BASE_NODE::NotifyInvalidate(invalid_nodes, unlink); |
| } |
| |
| const CommandBufferImageLayoutMap &CMD_BUFFER_STATE::GetImageSubresourceLayoutMap() const { return image_layout_map; } |
| |
| // The const variant only need the image as it is the key for the map |
| const ImageSubresourceLayoutMap *CMD_BUFFER_STATE::GetImageSubresourceLayoutMap(const IMAGE_STATE &image_state) const { |
| auto it = image_layout_map.find(&image_state); |
| if (it == image_layout_map.cend()) { |
| return nullptr; |
| } |
| return it->second.get(); |
| } |
| |
| // The non-const variant only needs the image state, as the factory requires it to construct a new entry |
| ImageSubresourceLayoutMap *CMD_BUFFER_STATE::GetImageSubresourceLayoutMap(const IMAGE_STATE &image_state) { |
| auto &layout_map = image_layout_map[&image_state]; |
| if (!layout_map) { |
| // Make sure we don't create a nullptr keyed entry for a zombie Image |
| if (image_state.Destroyed() || !image_state.layout_range_map) { |
| return nullptr; |
| } |
| // Was an empty slot... fill it in. |
| if (image_state.CanAlias()) { |
| // Aliasing images need to share the same local layout map. |
| // Since they use the same global layout state, use it as a key |
| // for the local state. We don't need a lock on the global range |
| // map to do a lookup based on its pointer. |
| const auto *global_layout_map = image_state.layout_range_map.get(); |
| auto iter = aliased_image_layout_map.find(global_layout_map); |
| if (iter != aliased_image_layout_map.end()) { |
| layout_map = iter->second; |
| } else { |
| layout_map = std::make_shared<ImageSubresourceLayoutMap>(image_state); |
| // Save the local layout map for the next aliased image. |
| // The global layout map pointer is only used as a key into the local lookup |
| // table so it doesn't need to be locked. |
| aliased_image_layout_map.emplace(global_layout_map, layout_map); |
| } |
| |
| } else { |
| layout_map = std::make_shared<ImageSubresourceLayoutMap>(image_state); |
| } |
| } |
| return layout_map.get(); |
| } |
| |
| static bool SetQueryState(const QueryObject &object, QueryState value, QueryMap *localQueryToStateMap) { |
| (*localQueryToStateMap)[object] = value; |
| return false; |
| } |
| |
| void CMD_BUFFER_STATE::BeginQuery(const QueryObject &query_obj) { |
| activeQueries.insert(query_obj); |
| startedQueries.insert(query_obj); |
| queryUpdates.emplace_back([query_obj](CMD_BUFFER_STATE &cb_state_arg, bool do_validate, VkQueryPool &firstPerfQueryPool, |
| uint32_t perfQueryPass, QueryMap *localQueryToStateMap) { |
| SetQueryState(QueryObject(query_obj, perfQueryPass), QUERYSTATE_RUNNING, localQueryToStateMap); |
| return false; |
| }); |
| updatedQueries.insert(query_obj); |
| } |
| |
| void CMD_BUFFER_STATE::EndQuery(const QueryObject &query_obj) { |
| activeQueries.erase(query_obj); |
| queryUpdates.emplace_back([query_obj](CMD_BUFFER_STATE &cb_state_arg, bool do_validate, VkQueryPool &firstPerfQueryPool, |
| uint32_t perfQueryPass, QueryMap *localQueryToStateMap) { |
| return SetQueryState(QueryObject(query_obj, perfQueryPass), QUERYSTATE_ENDED, localQueryToStateMap); |
| }); |
| updatedQueries.insert(query_obj); |
| } |
| |
| bool CMD_BUFFER_STATE::UpdatesQuery(const QueryObject &query_obj) const { |
| // Clear out the perf_pass from the caller because it isn't known when the command buffer is recorded. |
| auto key = query_obj; |
| key.perf_pass = 0; |
| for (auto *sub_cb : linkedCommandBuffers) { |
| auto guard = sub_cb->ReadLock(); |
| if (sub_cb->updatedQueries.find(key) != sub_cb->updatedQueries.end()) { |
| return true; |
| } |
| } |
| return updatedQueries.find(key) != updatedQueries.end(); |
| } |
| |
| static bool SetQueryStateMulti(VkQueryPool queryPool, uint32_t firstQuery, uint32_t queryCount, uint32_t perfPass, QueryState value, |
| QueryMap *localQueryToStateMap) { |
| for (uint32_t i = 0; i < queryCount; i++) { |
| QueryObject query_obj = {queryPool, firstQuery + i, perfPass}; |
| (*localQueryToStateMap)[query_obj] = value; |
| } |
| return false; |
| } |
| |
| void CMD_BUFFER_STATE::EndQueries(VkQueryPool queryPool, uint32_t firstQuery, uint32_t queryCount) { |
| for (uint32_t slot = firstQuery; slot < (firstQuery + queryCount); slot++) { |
| QueryObject query_obj = {queryPool, slot}; |
| activeQueries.erase(query_obj); |
| updatedQueries.insert(query_obj); |
| } |
| queryUpdates.emplace_back([queryPool, firstQuery, queryCount](CMD_BUFFER_STATE &cb_state_arg, bool do_validate, |
| VkQueryPool &firstPerfQueryPool, uint32_t perfQueryPass, |
| QueryMap *localQueryToStateMap) { |
| return SetQueryStateMulti(queryPool, firstQuery, queryCount, perfQueryPass, QUERYSTATE_ENDED, localQueryToStateMap); |
| }); |
| } |
| |
| void CMD_BUFFER_STATE::ResetQueryPool(VkQueryPool queryPool, uint32_t firstQuery, uint32_t queryCount) { |
| for (uint32_t slot = firstQuery; slot < (firstQuery + queryCount); slot++) { |
| QueryObject query_obj = {queryPool, slot}; |
| resetQueries.insert(query_obj); |
| updatedQueries.insert(query_obj); |
| } |
| |
| queryUpdates.emplace_back([queryPool, firstQuery, queryCount](CMD_BUFFER_STATE &cb_state_arg, bool do_validate, |
| VkQueryPool &firstPerfQueryPool, uint32_t perfQueryPass, |
| QueryMap *localQueryToStateMap) { |
| return SetQueryStateMulti(queryPool, firstQuery, queryCount, perfQueryPass, QUERYSTATE_RESET, localQueryToStateMap); |
| }); |
| } |
| |
| void CMD_BUFFER_STATE::UpdateSubpassAttachments(const safe_VkSubpassDescription2 &subpass, std::vector<SUBPASS_INFO> &subpasses) { |
| for (uint32_t index = 0; index < subpass.inputAttachmentCount; ++index) { |
| const uint32_t attachment_index = subpass.pInputAttachments[index].attachment; |
| if (attachment_index != VK_ATTACHMENT_UNUSED) { |
| subpasses[attachment_index].used = true; |
| subpasses[attachment_index].usage = VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT; |
| subpasses[attachment_index].layout = subpass.pInputAttachments[index].layout; |
| } |
| } |
| |
| for (uint32_t index = 0; index < subpass.colorAttachmentCount; ++index) { |
| const uint32_t attachment_index = subpass.pColorAttachments[index].attachment; |
| if (attachment_index != VK_ATTACHMENT_UNUSED) { |
| subpasses[attachment_index].used = true; |
| subpasses[attachment_index].usage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; |
| subpasses[attachment_index].layout = subpass.pColorAttachments[index].layout; |
| active_color_attachments_index.insert(index); |
| } |
| if (subpass.pResolveAttachments) { |
| const uint32_t attachment_index2 = subpass.pResolveAttachments[index].attachment; |
| if (attachment_index2 != VK_ATTACHMENT_UNUSED) { |
| subpasses[attachment_index2].used = true; |
| subpasses[attachment_index2].usage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; |
| subpasses[attachment_index2].layout = subpass.pResolveAttachments[index].layout; |
| } |
| } |
| } |
| |
| if (subpass.pDepthStencilAttachment) { |
| const uint32_t attachment_index = subpass.pDepthStencilAttachment->attachment; |
| if (attachment_index != VK_ATTACHMENT_UNUSED) { |
| subpasses[attachment_index].used = true; |
| subpasses[attachment_index].usage = VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT; |
| subpasses[attachment_index].layout = subpass.pDepthStencilAttachment->layout; |
| } |
| } |
| } |
| |
| void CMD_BUFFER_STATE::UpdateAttachmentsView(const VkRenderPassBeginInfo *pRenderPassBegin) { |
| auto &attachments = *(active_attachments.get()); |
| const bool imageless = (activeFramebuffer->createInfo.flags & VK_FRAMEBUFFER_CREATE_IMAGELESS_BIT) != 0; |
| const VkRenderPassAttachmentBeginInfo *attachment_info_struct = nullptr; |
| if (pRenderPassBegin) attachment_info_struct = vku::FindStructInPNextChain<VkRenderPassAttachmentBeginInfo>(pRenderPassBegin->pNext); |
| |
| for (uint32_t i = 0; i < attachments.size(); ++i) { |
| if (imageless) { |
| if (attachment_info_struct && i < attachment_info_struct->attachmentCount) { |
| auto res = attachments_view_states.insert(dev_data->Get<IMAGE_VIEW_STATE>(attachment_info_struct->pAttachments[i])); |
| attachments[i] = res.first->get(); |
| } |
| } else { |
| auto res = attachments_view_states.insert(activeFramebuffer->attachments_view_state[i]); |
| attachments[i] = res.first->get(); |
| } |
| } |
| } |
| |
| void CMD_BUFFER_STATE::BeginRenderPass(Func command, const VkRenderPassBeginInfo *pRenderPassBegin, |
| const VkSubpassContents contents) { |
| RecordCmd(command); |
| activeFramebuffer = dev_data->Get<FRAMEBUFFER_STATE>(pRenderPassBegin->framebuffer); |
| activeRenderPass = dev_data->Get<RENDER_PASS_STATE>(pRenderPassBegin->renderPass); |
| active_render_pass_begin_info = safe_VkRenderPassBeginInfo(pRenderPassBegin); |
| SetActiveSubpass(0); |
| activeSubpassContents = contents; |
| |
| // Connect this RP to cmdBuffer |
| if (!dev_data->disabled[command_buffer_state]) { |
| AddChild(activeRenderPass); |
| } |
| |
| // Spec states that after BeginRenderPass all resources should be rebound |
| if (activeRenderPass->has_multiview_enabled) { |
| UnbindResources(); |
| } |
| |
| auto chained_device_group_struct = vku::FindStructInPNextChain<VkDeviceGroupRenderPassBeginInfo>(pRenderPassBegin->pNext); |
| if (chained_device_group_struct) { |
| active_render_pass_device_mask = chained_device_group_struct->deviceMask; |
| } else { |
| active_render_pass_device_mask = initial_device_mask; |
| } |
| |
| active_subpasses = nullptr; |
| active_attachments = nullptr; |
| |
| if (activeFramebuffer) { |
| // Set cb_state->active_subpasses |
| active_subpasses = std::make_shared<std::vector<SUBPASS_INFO>>(activeFramebuffer->createInfo.attachmentCount); |
| const auto &subpass = activeRenderPass->createInfo.pSubpasses[GetActiveSubpass()]; |
| UpdateSubpassAttachments(subpass, *active_subpasses); |
| |
| // Set cb_state->active_attachments & cb_state->attachments_view_states |
| active_attachments = std::make_shared<std::vector<IMAGE_VIEW_STATE *>>(activeFramebuffer->createInfo.attachmentCount); |
| UpdateAttachmentsView(pRenderPassBegin); |
| |
| // Connect this framebuffer and its children to this cmdBuffer |
| AddChild(activeFramebuffer); |
| } |
| } |
| |
| void CMD_BUFFER_STATE::NextSubpass(Func command, VkSubpassContents contents) { |
| RecordCmd(command); |
| SetActiveSubpass(GetActiveSubpass() + 1); |
| activeSubpassContents = contents; |
| |
| // Update cb_state->active_subpasses |
| if (activeFramebuffer) { |
| active_subpasses = nullptr; |
| active_subpasses = std::make_shared<std::vector<SUBPASS_INFO>>(activeFramebuffer->createInfo.attachmentCount); |
| |
| if (GetActiveSubpass() < activeRenderPass->createInfo.subpassCount) { |
| const auto &subpass = activeRenderPass->createInfo.pSubpasses[GetActiveSubpass()]; |
| UpdateSubpassAttachments(subpass, *active_subpasses); |
| } |
| } |
| |
| // Spec states that after NextSubpass all resources should be rebound |
| if (activeRenderPass->has_multiview_enabled) { |
| UnbindResources(); |
| } |
| } |
| |
| void CMD_BUFFER_STATE::EndRenderPass(Func command) { |
| RecordCmd(command); |
| activeRenderPass = nullptr; |
| active_attachments = nullptr; |
| active_subpasses = nullptr; |
| active_color_attachments_index.clear(); |
| SetActiveSubpass(0); |
| activeFramebuffer = VK_NULL_HANDLE; |
| } |
| |
| void CMD_BUFFER_STATE::BeginRendering(Func command, const VkRenderingInfo *pRenderingInfo) { |
| RecordCmd(command); |
| activeRenderPass = std::make_shared<RENDER_PASS_STATE>(pRenderingInfo, true); |
| |
| auto chained_device_group_struct = vku::FindStructInPNextChain<VkDeviceGroupRenderPassBeginInfo>(pRenderingInfo->pNext); |
| if (chained_device_group_struct) { |
| active_render_pass_device_mask = chained_device_group_struct->deviceMask; |
| } else { |
| active_render_pass_device_mask = initial_device_mask; |
| } |
| |
| activeSubpassContents = ((pRenderingInfo->flags & VK_RENDERING_CONTENTS_SECONDARY_COMMAND_BUFFERS_BIT_KHR) |
| ? VK_SUBPASS_CONTENTS_SECONDARY_COMMAND_BUFFERS |
| : VK_SUBPASS_CONTENTS_INLINE); |
| |
| // Handle flags for dynamic rendering |
| if (!hasRenderPassInstance && pRenderingInfo->flags & VK_RENDERING_RESUMING_BIT) { |
| resumesRenderPassInstance = true; |
| } |
| suspendsRenderPassInstance = (pRenderingInfo->flags & VK_RENDERING_SUSPENDING_BIT) > 0; |
| hasRenderPassInstance = true; |
| |
| active_attachments = nullptr; |
| uint32_t attachment_count = (pRenderingInfo->colorAttachmentCount + 2) * 2; |
| |
| // Set cb_state->active_attachments & cb_state->attachments_view_states |
| active_attachments = std::make_shared<std::vector<IMAGE_VIEW_STATE *>>(attachment_count); |
| auto &attachments = *(active_attachments.get()); |
| |
| for (uint32_t i = 0; i < pRenderingInfo->colorAttachmentCount; ++i) { |
| active_color_attachments_index.insert(i); |
| auto &colorAttachment = attachments[GetDynamicColorAttachmentImageIndex(i)]; |
| auto &colorResolveAttachment = attachments[GetDynamicColorResolveAttachmentImageIndex(i)]; |
| colorAttachment = nullptr; |
| colorResolveAttachment = nullptr; |
| |
| if (pRenderingInfo->pColorAttachments[i].imageView != VK_NULL_HANDLE) { |
| auto res = |
| attachments_view_states.insert(dev_data->Get<IMAGE_VIEW_STATE>(pRenderingInfo->pColorAttachments[i].imageView)); |
| colorAttachment = res.first->get(); |
| if (pRenderingInfo->pColorAttachments[i].resolveMode != VK_RESOLVE_MODE_NONE && |
| pRenderingInfo->pColorAttachments[i].resolveImageView != VK_NULL_HANDLE) { |
| colorResolveAttachment = res.first->get(); |
| } |
| } |
| } |
| |
| if (pRenderingInfo->pDepthAttachment && pRenderingInfo->pDepthAttachment->imageView != VK_NULL_HANDLE) { |
| auto &depthAttachment = attachments[GetDynamicDepthAttachmentImageIndex()]; |
| auto &depthResolveAttachment = attachments[GetDynamicDepthResolveAttachmentImageIndex()]; |
| depthAttachment = nullptr; |
| depthResolveAttachment = nullptr; |
| |
| auto res = attachments_view_states.insert(dev_data->Get<IMAGE_VIEW_STATE>(pRenderingInfo->pDepthAttachment->imageView)); |
| depthAttachment = res.first->get(); |
| if (pRenderingInfo->pDepthAttachment->resolveMode != VK_RESOLVE_MODE_NONE && |
| pRenderingInfo->pDepthAttachment->resolveImageView != VK_NULL_HANDLE) { |
| depthResolveAttachment = res.first->get(); |
| } |
| } |
| |
| if (pRenderingInfo->pStencilAttachment && pRenderingInfo->pStencilAttachment->imageView != VK_NULL_HANDLE) { |
| auto &stencilAttachment = attachments[GetDynamicStencilAttachmentImageIndex()]; |
| auto &stencilResolveAttachment = attachments[GetDynamicStencilResolveAttachmentImageIndex()]; |
| stencilAttachment = nullptr; |
| stencilResolveAttachment = nullptr; |
| |
| auto res = attachments_view_states.insert(dev_data->Get<IMAGE_VIEW_STATE>(pRenderingInfo->pStencilAttachment->imageView)); |
| stencilAttachment = res.first->get(); |
| if (pRenderingInfo->pStencilAttachment->resolveMode != VK_RESOLVE_MODE_NONE && |
| pRenderingInfo->pStencilAttachment->resolveImageView != VK_NULL_HANDLE) { |
| stencilResolveAttachment = res.first->get(); |
| } |
| } |
| } |
| |
| void CMD_BUFFER_STATE::EndRendering(Func command) { |
| RecordCmd(command); |
| activeRenderPass = nullptr; |
| active_color_attachments_index.clear(); |
| } |
| |
| void CMD_BUFFER_STATE::BeginVideoCoding(const VkVideoBeginCodingInfoKHR *pBeginInfo) { |
| RecordCmd(Func::vkCmdBeginVideoCodingKHR); |
| bound_video_session = dev_data->Get<VIDEO_SESSION_STATE>(pBeginInfo->videoSession); |
| bound_video_session_parameters = dev_data->Get<VIDEO_SESSION_PARAMETERS_STATE>(pBeginInfo->videoSessionParameters); |
| |
| if (bound_video_session) { |
| // Connect this video session to cmdBuffer |
| if (!dev_data->disabled[command_buffer_state]) { |
| AddChild(bound_video_session); |
| } |
| } |
| |
| if (bound_video_session_parameters) { |
| // Connect this video session parameters object to cmdBuffer |
| if (!dev_data->disabled[command_buffer_state]) { |
| AddChild(bound_video_session_parameters); |
| } |
| } |
| |
| if (pBeginInfo && pBeginInfo->pReferenceSlots) { |
| std::vector<VideoReferenceSlot> expected_slots{}; |
| expected_slots.reserve(pBeginInfo->referenceSlotCount); |
| |
| for (uint32_t i = 0; i < pBeginInfo->referenceSlotCount; ++i) { |
| // Initialize the set of bound video picture resources |
| if (pBeginInfo->pReferenceSlots[i].pPictureResource != nullptr) { |
| int32_t slot_index = pBeginInfo->pReferenceSlots[i].slotIndex; |
| VideoPictureResource res(dev_data, *pBeginInfo->pReferenceSlots[i].pPictureResource); |
| bound_video_picture_resources.emplace(std::make_pair(res, slot_index)); |
| } |
| |
| if (pBeginInfo->pReferenceSlots[i].slotIndex >= 0) { |
| expected_slots.emplace_back(dev_data, *bound_video_session->profile, pBeginInfo->pReferenceSlots[i], false); |
| } |
| } |
| |
| // Enqueue submission time validation |
| video_session_updates[bound_video_session->videoSession()].emplace_back( |
| [expected_slots](const ValidationStateTracker *dev_data, const VIDEO_SESSION_STATE *vs_state, |
| VideoSessionDeviceState &dev_state, bool do_validate) { |
| bool skip = false; |
| |
| if (do_validate) { |
| for (const auto &slot : expected_slots) { |
| if (!dev_state.IsSlotActive(slot.index)) { |
| skip |= dev_data->LogError(vs_state->Handle(), "VUID-vkCmdBeginVideoCodingKHR-slotIndex-07239", |
| "DPB slot index %d is not active in %s", slot.index, |
| dev_data->FormatHandle(*vs_state).c_str()); |
| } else if (slot.resource && !dev_state.IsSlotPicture(slot.index, slot.resource)) { |
| skip |= dev_data->LogError(vs_state->Handle(), "VUID-vkCmdBeginVideoCodingKHR-pPictureResource-07265", |
| "DPB slot index %d of %s is not currently associated with the specified " |
| "video picture resource: %s, layer %u, offset (%u,%u), extent (%u,%u)", |
| slot.index, dev_data->FormatHandle(*vs_state).c_str(), |
| dev_data->FormatHandle(slot.resource.image_state->Handle()).c_str(), |
| slot.resource.range.baseArrayLayer, slot.resource.coded_offset.x, |
| slot.resource.coded_offset.y, slot.resource.coded_extent.width, |
| slot.resource.coded_extent.height); |
| } |
| } |
| } |
| |
| for (const auto &slot : expected_slots) { |
| if (!slot.resource) { |
| dev_state.Deactivate(slot.index); |
| } |
| } |
| |
| return skip; |
| }); |
| } |
| } |
| |
| void CMD_BUFFER_STATE::EndVideoCoding(const VkVideoEndCodingInfoKHR *pEndCodingInfo) { |
| RecordCmd(Func::vkCmdEndVideoCodingKHR); |
| bound_video_session = nullptr; |
| bound_video_session_parameters = nullptr; |
| bound_video_picture_resources.clear(); |
| } |
| |
| void CMD_BUFFER_STATE::ControlVideoCoding(const VkVideoCodingControlInfoKHR *pControlInfo) { |
| RecordCmd(Func::vkCmdControlVideoCodingKHR); |
| |
| if (pControlInfo && bound_video_session) { |
| auto control_flags = pControlInfo->flags; |
| |
| if (control_flags & VK_VIDEO_CODING_CONTROL_RESET_BIT_KHR) { |
| // Remove DPB slot index association for bound video picture resources |
| for (auto &binding : bound_video_picture_resources) { |
| binding.second = -1; |
| } |
| } |
| |
| // Enqueue submission time validation and device state changes |
| video_session_updates[bound_video_session->videoSession()].emplace_back( |
| [control_flags](const ValidationStateTracker *dev_data, const VIDEO_SESSION_STATE *vs_state, |
| VideoSessionDeviceState &dev_state, bool do_validate) { |
| bool skip = false; |
| bool reset_session = control_flags & VK_VIDEO_CODING_CONTROL_RESET_BIT_KHR; |
| if (do_validate) { |
| if (!reset_session && !dev_state.IsInitialized()) { |
| skip |= dev_data->LogError(vs_state->Handle(), "VUID-vkCmdControlVideoCodingKHR-flags-07017", |
| "Bound video session %s is uninitialized", |
| dev_data->FormatHandle(*vs_state).c_str()); |
| } |
| } |
| |
| // Reset video session at submission time, if requested |
| if (reset_session) { |
| dev_state.Reset(); |
| } |
| |
| return skip; |
| }); |
| } |
| } |
| |
| void CMD_BUFFER_STATE::DecodeVideo(const VkVideoDecodeInfoKHR *pDecodeInfo) { |
| RecordCmd(Func::vkCmdDecodeVideoKHR); |
| |
| if (bound_video_session && pDecodeInfo) { |
| VideoReferenceSlot setup_slot{}; |
| if (pDecodeInfo->pSetupReferenceSlot && pDecodeInfo->pSetupReferenceSlot->pPictureResource) { |
| setup_slot = VideoReferenceSlot(dev_data, *bound_video_session->profile, *pDecodeInfo->pSetupReferenceSlot); |
| // Update bound video picture resource DPB slot index association |
| bound_video_picture_resources[setup_slot.resource] = setup_slot.index; |
| } |
| |
| // Need to also validate the picture kind (frame, top field, bottom field) for H.264 |
| bool need_reference_slot_validation = (bound_video_session->GetCodecOp() == VK_VIDEO_CODEC_OPERATION_DECODE_H264_BIT_KHR); |
| |
| std::vector<VideoReferenceSlot> reference_slots{}; |
| if (need_reference_slot_validation) { |
| reference_slots.reserve(pDecodeInfo->referenceSlotCount); |
| |
| for (uint32_t i = 0; i < pDecodeInfo->referenceSlotCount; ++i) { |
| reference_slots.emplace_back(dev_data, *bound_video_session->profile, pDecodeInfo->pReferenceSlots[i]); |
| } |
| } |
| |
| // Enqueue submission time validation and device state changes |
| video_session_updates[bound_video_session->videoSession()].emplace_back( |
| [setup_slot, reference_slots](const ValidationStateTracker *dev_data, const VIDEO_SESSION_STATE *vs_state, |
| VideoSessionDeviceState &dev_state, bool do_validate) { |
| bool skip = false; |
| if (do_validate) { |
| if (!dev_state.IsInitialized()) { |
| skip |= dev_data->LogError(vs_state->Handle(), "VUID-vkCmdDecodeVideoKHR-None-07011", "%s is uninitialized", |
| dev_data->FormatHandle(*vs_state).c_str()); |
| } |
| |
| const auto log_picture_kind_error = [&](const VideoReferenceSlot &slot, const char *vuid, |
| const char *picture_kind) -> bool { |
| return dev_data->LogError(vs_state->Handle(), vuid, |
| "DPB slot index %d of %s does not currently contain a %s with the specified " |
| "video picture resource: %s, layer %u, offset (%u,%u), extent (%u,%u)", |
| slot.index, dev_data->FormatHandle(*vs_state).c_str(), picture_kind, |
| dev_data->FormatHandle(slot.resource.image_state->Handle()).c_str(), |
| slot.resource.range.baseArrayLayer, slot.resource.coded_offset.x, |
| slot.resource.coded_offset.y, slot.resource.coded_extent.width, |
| slot.resource.coded_extent.height); |
| }; |
| |
| for (const auto &slot : reference_slots) { |
| if (slot.picture_id.IsFrame() && |
| !dev_state.IsSlotPicture(slot.index, VideoPictureID::Frame(), slot.resource)) { |
| skip |= log_picture_kind_error(slot, "VUID-vkCmdDecodeVideoKHR-pDecodeInfo-07266", "frame"); |
| } |
| if (slot.picture_id.ContainsTopField() && |
| !dev_state.IsSlotPicture(slot.index, VideoPictureID::TopField(), slot.resource)) { |
| skip |= log_picture_kind_error(slot, "VUID-vkCmdDecodeVideoKHR-pDecodeInfo-07267", "top field"); |
| } |
| if (slot.picture_id.ContainsBottomField() && |
| !dev_state.IsSlotPicture(slot.index, VideoPictureID::BottomField(), slot.resource)) { |
| skip |= log_picture_kind_error(slot, "VUID-vkCmdDecodeVideoKHR-pDecodeInfo-07268", "bottom field"); |
| } |
| } |
| } |
| |
| // Set up reference slot at submission time, if requested |
| if (setup_slot) { |
| dev_state.Activate(setup_slot.index, setup_slot.picture_id, setup_slot.resource); |
| } |
| |
| return skip; |
| }); |
| |
| // Update active query indices |
| for (auto &query : activeQueries) { |
| uint32_t op_count = bound_video_session->GetVideoDecodeOperationCount(pDecodeInfo); |
| query.active_query_index += op_count; |
| } |
| } |
| } |
| |
| void CMD_BUFFER_STATE::Begin(const VkCommandBufferBeginInfo *pBeginInfo) { |
| if (CbState::Recorded == state || CbState::InvalidComplete == state) { |
| Reset(); |
| } |
| |
| descriptorset_cache.clear(); |
| |
| // Set updated state here in case implicit reset occurs above |
| state = CbState::Recording; |
| beginInfo = *pBeginInfo; |
| if (beginInfo.pInheritanceInfo && (createInfo.level == VK_COMMAND_BUFFER_LEVEL_SECONDARY)) { |
| inheritanceInfo = *(beginInfo.pInheritanceInfo); |
| beginInfo.pInheritanceInfo = &inheritanceInfo; |
| // If we are a secondary command-buffer and inheriting. Update the items we should inherit. |
| if ((createInfo.level != VK_COMMAND_BUFFER_LEVEL_PRIMARY) && |
| (beginInfo.flags & VK_COMMAND_BUFFER_USAGE_RENDER_PASS_CONTINUE_BIT)) { |
| if (beginInfo.pInheritanceInfo->renderPass) { |
| activeRenderPass = dev_data->Get<RENDER_PASS_STATE>(beginInfo.pInheritanceInfo->renderPass); |
| SetActiveSubpass(beginInfo.pInheritanceInfo->subpass); |
| |
| if (beginInfo.pInheritanceInfo->framebuffer) { |
| activeFramebuffer = dev_data->Get<FRAMEBUFFER_STATE>(beginInfo.pInheritanceInfo->framebuffer); |
| active_subpasses = nullptr; |
| active_attachments = nullptr; |
| |
| if (activeFramebuffer) { |
| // Set active_subpasses |
| active_subpasses = |
| std::make_shared<std::vector<SUBPASS_INFO>>(activeFramebuffer->createInfo.attachmentCount); |
| const auto &subpass = activeRenderPass->createInfo.pSubpasses[GetActiveSubpass()]; |
| UpdateSubpassAttachments(subpass, *active_subpasses); |
| |
| // Set active_attachments & attachments_view_states |
| active_attachments = |
| std::make_shared<std::vector<IMAGE_VIEW_STATE *>>(activeFramebuffer->createInfo.attachmentCount); |
| UpdateAttachmentsView(nullptr); |
| |
| // Connect this framebuffer and its children to this cmdBuffer |
| if (!dev_data->disabled[command_buffer_state]) { |
| AddChild(activeFramebuffer); |
| } |
| } |
| } |
| } else { |
| auto inheritance_rendering_info = |
| vku::FindStructInPNextChain<VkCommandBufferInheritanceRenderingInfo>(beginInfo.pInheritanceInfo->pNext); |
| if (inheritance_rendering_info) { |
| activeRenderPass = std::make_shared<RENDER_PASS_STATE>(inheritance_rendering_info); |
| } |
| } |
| |
| // Check for VkCommandBufferInheritanceViewportScissorInfoNV (VK_NV_inherited_viewport_scissor) |
| auto p_inherited_viewport_scissor_info = |
| vku::FindStructInPNextChain<VkCommandBufferInheritanceViewportScissorInfoNV>(beginInfo.pInheritanceInfo->pNext); |
| if (p_inherited_viewport_scissor_info != nullptr && p_inherited_viewport_scissor_info->viewportScissor2D) { |
| auto pViewportDepths = p_inherited_viewport_scissor_info->pViewportDepths; |
| inheritedViewportDepths.assign(pViewportDepths, |
| pViewportDepths + p_inherited_viewport_scissor_info->viewportDepthCount); |
| } |
| } |
| } |
| |
| auto chained_device_group_struct = vku::FindStructInPNextChain<VkDeviceGroupCommandBufferBeginInfo>(pBeginInfo->pNext); |
| if (chained_device_group_struct) { |
| initial_device_mask = chained_device_group_struct->deviceMask; |
| } else { |
| initial_device_mask = (1 << dev_data->physical_device_count) - 1; |
| } |
| performance_lock_acquired = dev_data->performance_lock_acquired; |
| updatedQueries.clear(); |
| } |
| |
| void CMD_BUFFER_STATE::End(VkResult result) { |
| // Cached validation is specific to a specific recording of a specific command buffer. |
| descriptorset_cache.clear(); |
| if (VK_SUCCESS == result) { |
| state = CbState::Recorded; |
| } |
| } |
| |
| void CMD_BUFFER_STATE::ExecuteCommands(vvl::span<const VkCommandBuffer> secondary_command_buffers) { |
| RecordCmd(Func::vkCmdExecuteCommands); |
| for (const VkCommandBuffer sub_command_buffer : secondary_command_buffers) { |
| auto sub_cb_state = dev_data->GetWrite<CMD_BUFFER_STATE>(sub_command_buffer); |
| assert(sub_cb_state); |
| if (!(sub_cb_state->beginInfo.flags & VK_COMMAND_BUFFER_USAGE_SIMULTANEOUS_USE_BIT)) { |
| if (beginInfo.flags & VK_COMMAND_BUFFER_USAGE_SIMULTANEOUS_USE_BIT) { |
| // TODO: Because this is a state change, clearing the VK_COMMAND_BUFFER_USAGE_SIMULTANEOUS_USE_BIT needs to be moved |
| // from the validation step to the recording step |
| beginInfo.flags &= ~VK_COMMAND_BUFFER_USAGE_SIMULTANEOUS_USE_BIT; |
| } |
| } |
| |
| // Propagate inital layout and current layout state to the primary cmd buffer |
| // NOTE: The update/population of the image_layout_map is done in CoreChecks, but for other classes derived from |
| // ValidationStateTracker these maps will be empty, so leaving the propagation in the the state tracker should be a no-op |
| // for those other classes. |
| for (const auto &sub_layout_map_entry : sub_cb_state->image_layout_map) { |
| const auto *image_state = sub_layout_map_entry.first; |
| |
| auto *cb_subres_map = GetImageSubresourceLayoutMap(*image_state); |
| if (cb_subres_map) { |
| const auto &sub_cb_subres_map = sub_layout_map_entry.second; |
| cb_subres_map->UpdateFrom(*sub_cb_subres_map); |
| } |
| } |
| |
| sub_cb_state->primaryCommandBuffer = commandBuffer(); |
| linkedCommandBuffers.insert(sub_cb_state.get()); |
| AddChild(sub_cb_state); |
| // Add a query update that runs all the query updates that happen in the sub command buffer. |
| // This avoids locking ambiguity because primary command buffers are locked when these |
| // callbacks run, but secondary command buffers are not. |
| queryUpdates.emplace_back([sub_command_buffer](CMD_BUFFER_STATE &cb_state_arg, bool do_validate, |
| VkQueryPool &firstPerfQueryPool, uint32_t perfQueryPass, |
| QueryMap *localQueryToStateMap) { |
| bool skip = false; |
| auto sub_cb_state_arg = cb_state_arg.dev_data->GetWrite<CMD_BUFFER_STATE>(sub_command_buffer); |
| for (auto &function : sub_cb_state_arg->queryUpdates) { |
| skip |= function(*sub_cb_state_arg, do_validate, firstPerfQueryPool, perfQueryPass, localQueryToStateMap); |
| } |
| return skip; |
| }); |
| for (auto &function : sub_cb_state->eventUpdates) { |
| eventUpdates.push_back(function); |
| } |
| for (auto &event : sub_cb_state->events) { |
| events.push_back(event); |
| } |
| for (auto &function : sub_cb_state->queue_submit_functions) { |
| queue_submit_functions.push_back(function); |
| } |
| |
| // State is trashed after executing secondary command buffers. |
| // Importantly, this function runs after CoreChecks::PreCallValidateCmdExecuteCommands. |
| trashedViewportMask = vvl::MaxTypeValue(trashedViewportMask); |
| trashedScissorMask = vvl::MaxTypeValue(trashedScissorMask); |
| trashedViewportCount = true; |
| trashedScissorCount = true; |
| |
| // Pass along if any commands are used in the secondary command buffer |
| has_draw_cmd |= sub_cb_state->has_draw_cmd; |
| has_dispatch_cmd |= sub_cb_state->has_dispatch_cmd; |
| has_trace_rays_cmd |= sub_cb_state->has_trace_rays_cmd; |
| has_build_as_cmd |= sub_cb_state->has_build_as_cmd; |
| |
| // Handle secondary command buffer updates for dynamic rendering |
| if (!hasRenderPassInstance) { |
| resumesRenderPassInstance = sub_cb_state->resumesRenderPassInstance; |
| } |
| if (!sub_cb_state->activeRenderPass) { |
| suspendsRenderPassInstance = sub_cb_state->suspendsRenderPassInstance; |
| hasRenderPassInstance |= sub_cb_state->hasRenderPassInstance; |
| } |
| } |
| } |
| |
| void CMD_BUFFER_STATE::PushDescriptorSetState(VkPipelineBindPoint pipelineBindPoint, const PIPELINE_LAYOUT_STATE &pipeline_layout, |
| uint32_t set, uint32_t descriptorWriteCount, |
| const VkWriteDescriptorSet *pDescriptorWrites) { |
| // Short circuit invalid updates |
| if ((set >= pipeline_layout.set_layouts.size()) || !pipeline_layout.set_layouts[set] || |
| !pipeline_layout.set_layouts[set]->IsPushDescriptor()) { |
| return; |
| } |
| |
| // We need a descriptor set to update the bindings with, compatible with the passed layout |
| const auto &dsl = pipeline_layout.set_layouts[set]; |
| const auto lv_bind_point = ConvertToLvlBindPoint(pipelineBindPoint); |
| auto &last_bound = lastBound[lv_bind_point]; |
| auto &push_descriptor_set = last_bound.push_descriptor_set; |
| // If we are disturbing the current push_desriptor_set clear it |
| if (!push_descriptor_set || !IsBoundSetCompat(set, last_bound, pipeline_layout)) { |
| last_bound.UnbindAndResetPushDescriptorSet(dev_data->CreateDescriptorSet(VK_NULL_HANDLE, nullptr, dsl, 0)); |
| } |
| |
| UpdateLastBoundDescriptorSets(pipelineBindPoint, pipeline_layout, set, 1, nullptr, push_descriptor_set, 0, nullptr); |
| last_bound.pipeline_layout = pipeline_layout.layout(); |
| |
| // Now that we have either the new or extant push_descriptor set ... do the write updates against it |
| push_descriptor_set->PerformPushDescriptorsUpdate(descriptorWriteCount, pDescriptorWrites); |
| } |
| |
| // Generic function to handle state update for all CmdDraw* type functions |
| void CMD_BUFFER_STATE::UpdateDrawCmd(Func command) { |
| has_draw_cmd = true; |
| UpdatePipelineState(command, VK_PIPELINE_BIND_POINT_GRAPHICS); |
| } |
| |
| // Generic function to handle state update for all CmdDispatch* type functions |
| void CMD_BUFFER_STATE::UpdateDispatchCmd(Func command) { |
| has_dispatch_cmd = true; |
| UpdatePipelineState(command, VK_PIPELINE_BIND_POINT_COMPUTE); |
| } |
| |
| // Generic function to handle state update for all CmdTraceRay* type functions |
| void CMD_BUFFER_STATE::UpdateTraceRayCmd(Func command) { |
| has_trace_rays_cmd = true; |
| UpdatePipelineState(command, VK_PIPELINE_BIND_POINT_RAY_TRACING_KHR); |
| } |
| |
| // Generic function to handle state update for all Provoking functions calls (draw/dispatch/traceray/etc) |
| void CMD_BUFFER_STATE::UpdatePipelineState(Func command, const VkPipelineBindPoint bind_point) { |
| RecordCmd(command); |
| |
| const auto lv_bind_point = ConvertToLvlBindPoint(bind_point); |
| auto &last_bound = lastBound[lv_bind_point]; |
| PIPELINE_STATE *pipe = last_bound.pipeline_state; |
| if (!pipe) { |
| return; |
| } |
| if (pipe->vertex_input_state && !pipe->vertex_input_state->binding_descriptions.empty()) { |
| vertex_buffer_used = true; |
| } |
| |
| // Update the consumed viewport/scissor count. |
| { |
| usedViewportScissorCount = std::max({usedViewportScissorCount, pipelineStaticViewportCount, pipelineStaticScissorCount}); |
| usedDynamicViewportCount |= pipe->IsDynamic(VK_DYNAMIC_STATE_VIEWPORT_WITH_COUNT); |
| usedDynamicScissorCount |= pipe->IsDynamic(VK_DYNAMIC_STATE_SCISSOR_WITH_COUNT); |
| } |
| |
| if (last_bound.pipeline_layout != VK_NULL_HANDLE) { |
| for (const auto &set_binding_pair : pipe->active_slots) { |
| uint32_t set_index = set_binding_pair.first; |
| if (set_index >= last_bound.per_set.size()) { |
| continue; |
| } |
| auto &set_info = last_bound.per_set[set_index]; |
| // Pull the set node |
| auto &descriptor_set = set_info.bound_descriptor_set; |
| if (!descriptor_set) { |
| continue; |
| } |
| |
| // For the "bindless" style resource usage with many descriptors, need to optimize command <-> descriptor binding |
| |
| // TODO: If recreating the reduced_map here shows up in profilinging, need to find a way of sharing with the |
| // Validate pass. Though in the case of "many" descriptors, typically the descriptor count >> binding count |
| cvdescriptorset::PrefilterBindRequestMap reduced_map(*descriptor_set, set_binding_pair.second); |
| const auto &binding_req_map = reduced_map.FilteredMap(*this, pipe); |
| |
| if (reduced_map.IsManyDescriptors()) { |
| // Only update validate binding tags if we meet the "many" criteria in the Prefilter class |
| descriptor_set->UpdateValidationCache(*this, *pipe, binding_req_map); |
| } |
| |
| // We can skip updating the state if "nothing" has changed since the last validation. |
| // See CoreChecks::ValidateActionState for more details. |
| const bool descriptor_set_changed = !reduced_map.IsManyDescriptors() || |
| // Update if descriptor set (or contents) has changed |
| set_info.validated_set != descriptor_set.get() || |
| set_info.validated_set_change_count != descriptor_set->GetChangeCount() || |
| (!dev_data->disabled[image_layout_validation] && |
| set_info.validated_set_image_layout_change_count != image_layout_change_count); |
| const bool need_update = |
| descriptor_set_changed || |
| // Update if previous bindingReqMap doesn't include new bindingReqMap |
| !std::includes(set_info.validated_set_binding_req_map.begin(), set_info.validated_set_binding_req_map.end(), |
| binding_req_map.begin(), binding_req_map.end()); |
| |
| if (need_update) { |
| if (!dev_data->disabled[command_buffer_state] && !descriptor_set->IsPushDescriptor()) { |
| AddChild(descriptor_set); |
| } |
| |
| // Bind this set and its active descriptor resources to the command buffer |
| if (!descriptor_set_changed && reduced_map.IsManyDescriptors()) { |
| // Only record the bindings that haven't already been recorded |
| BindingVariableMap delta_reqs; |
| std::set_difference(binding_req_map.begin(), binding_req_map.end(), |
| set_info.validated_set_binding_req_map.begin(), |
| set_info.validated_set_binding_req_map.end(), |
| vvl::insert_iterator<BindingVariableMap>(delta_reqs, delta_reqs.begin())); |
| descriptor_set->UpdateDrawState(dev_data, this, command, pipe, delta_reqs); |
| } else { |
| descriptor_set->UpdateDrawState(dev_data, this, command, pipe, binding_req_map); |
| } |
| |
| set_info.validated_set = descriptor_set.get(); |
| set_info.validated_set_change_count = descriptor_set->GetChangeCount(); |
| set_info.validated_set_image_layout_change_count = image_layout_change_count; |
| if (reduced_map.IsManyDescriptors()) { |
| // Check whether old == new before assigning, the equality check is much cheaper than |
| // freeing and reallocating the map. |
| if (set_info.validated_set_binding_req_map != set_binding_pair.second) { |
| set_info.validated_set_binding_req_map = set_binding_pair.second; |
| } |
| } else { |
| set_info.validated_set_binding_req_map = BindingVariableMap(); |
| } |
| } |
| } |
| } |
| } |
| |
| // Helper for descriptor set (and buffer) updates. |
| static bool PushDescriptorCleanup(LAST_BOUND_STATE &last_bound, uint32_t set_idx) { |
| // All uses are from loops over per_set, but just in case.. |
| assert(set_idx < last_bound.per_set.size()); |
| |
| auto ds = last_bound.per_set[set_idx].bound_descriptor_set.get(); |
| if (ds && ds->IsPushDescriptor()) { |
| assert(ds == last_bound.push_descriptor_set.get()); |
| last_bound.push_descriptor_set = nullptr; |
| return true; |
| } |
| return true; |
| } |
| |
| // Update pipeline_layout bind points applying the "Pipeline Layout Compatibility" rules. |
| // One of pDescriptorSets or push_descriptor_set should be nullptr, indicating whether this |
| // is called for CmdBindDescriptorSets or CmdPushDescriptorSet. |
| void CMD_BUFFER_STATE::UpdateLastBoundDescriptorSets(VkPipelineBindPoint pipeline_bind_point, |
| const PIPELINE_LAYOUT_STATE &pipeline_layout, uint32_t first_set, |
| uint32_t set_count, const VkDescriptorSet *pDescriptorSets, |
| std::shared_ptr<cvdescriptorset::DescriptorSet> &push_descriptor_set, |
| uint32_t dynamic_offset_count, const uint32_t *p_dynamic_offsets) { |
| assert((pDescriptorSets == nullptr) ^ (push_descriptor_set == nullptr)); |
| |
| uint32_t required_size = first_set + set_count; |
| const uint32_t last_binding_index = required_size - 1; |
| assert(last_binding_index < pipeline_layout.set_compat_ids.size()); |
| |
| // Some useful shorthand |
| const auto lv_bind_point = ConvertToLvlBindPoint(pipeline_bind_point); |
| auto &last_bound = lastBound[lv_bind_point]; |
| last_bound.pipeline_layout = pipeline_layout.layout(); |
| auto &pipe_compat_ids = pipeline_layout.set_compat_ids; |
| // Resize binding arrays |
| if (last_binding_index >= last_bound.per_set.size()) { |
| last_bound.per_set.resize(required_size); |
| } |
| const uint32_t current_size = static_cast<uint32_t>(last_bound.per_set.size()); |
| |
| // Clean up the "disturbed" before and after the range to be set |
| if (required_size < current_size) { |
| if (last_bound.per_set[last_binding_index].compat_id_for_set != pipe_compat_ids[last_binding_index]) { |
| // We're disturbing those after last, we'll shrink below, but first need to check for and cleanup the push_descriptor |
| for (auto set_idx = required_size; set_idx < current_size; ++set_idx) { |
| if (PushDescriptorCleanup(last_bound, set_idx)) { |
| break; |
| } |
| } |
| } else { |
| // We're not disturbing past last, so leave the upper binding data alone. |
| required_size = current_size; |
| } |
| } |
| |
| // We resize if we need more set entries or if those past "last" are disturbed |
| if (required_size != current_size) { |
| last_bound.per_set.resize(required_size); |
| } |
| |
| // For any previously bound sets, need to set them to "invalid" if they were disturbed by this update |
| for (uint32_t set_idx = 0; set_idx < first_set; ++set_idx) { |
| auto &set_info = last_bound.per_set[set_idx]; |
| if (set_info.compat_id_for_set != pipe_compat_ids[set_idx]) { |
| PushDescriptorCleanup(last_bound, set_idx); |
| set_info.Reset(); |
| set_info.compat_id_for_set = pipe_compat_ids[set_idx]; |
| } |
| } |
| |
| // Now update the bound sets with the input sets |
| const uint32_t *input_dynamic_offsets = p_dynamic_offsets; // "read" pointer for dynamic offset data |
| for (uint32_t input_idx = 0; input_idx < set_count; input_idx++) { |
| auto set_idx = input_idx + first_set; // set_idx is index within layout, input_idx is index within input descriptor sets |
| auto &set_info = last_bound.per_set[set_idx]; |
| auto descriptor_set = |
| push_descriptor_set ? push_descriptor_set : dev_data->Get<cvdescriptorset::DescriptorSet>(pDescriptorSets[input_idx]); |
| |
| set_info.Reset(); |
| // Record binding (or push) |
| if (descriptor_set != last_bound.push_descriptor_set) { |
| // Only cleanup the push descriptors if they aren't the currently used set. |
| PushDescriptorCleanup(last_bound, set_idx); |
| } |
| set_info.bound_descriptor_set = descriptor_set; |
| set_info.compat_id_for_set = pipe_compat_ids[set_idx]; // compat ids are canonical *per* set index |
| |
| if (descriptor_set) { |
| auto set_dynamic_descriptor_count = descriptor_set->GetDynamicDescriptorCount(); |
| // TODO: Add logic for tracking push_descriptor offsets (here or in caller) |
| if (set_dynamic_descriptor_count && input_dynamic_offsets) { |
| const uint32_t *end_offset = input_dynamic_offsets + set_dynamic_descriptor_count; |
| set_info.dynamicOffsets = std::vector<uint32_t>(input_dynamic_offsets, end_offset); |
| input_dynamic_offsets = end_offset; |
| assert(input_dynamic_offsets <= (p_dynamic_offsets + dynamic_offset_count)); |
| } else { |
| set_info.dynamicOffsets.clear(); |
| } |
| } |
| } |
| } |
| |
| void CMD_BUFFER_STATE::UpdateLastBoundDescriptorBuffers(VkPipelineBindPoint pipeline_bind_point, |
| const PIPELINE_LAYOUT_STATE &pipeline_layout, uint32_t first_set, |
| uint32_t set_count, const uint32_t *buffer_indicies, |
| const VkDeviceSize *buffer_offsets) { |
| uint32_t required_size = first_set + set_count; |
| const uint32_t last_binding_index = required_size - 1; |
| assert(last_binding_index < pipeline_layout.set_compat_ids.size()); |
| |
| // Some useful shorthand |
| const auto lv_bind_point = ConvertToLvlBindPoint(pipeline_bind_point); |
| auto &last_bound = lastBound[lv_bind_point]; |
| last_bound.pipeline_layout = pipeline_layout.layout(); |
| auto &pipe_compat_ids = pipeline_layout.set_compat_ids; |
| // Resize binding arrays |
| if (last_binding_index >= last_bound.per_set.size()) { |
| last_bound.per_set.resize(required_size); |
| } |
| const uint32_t current_size = static_cast<uint32_t>(last_bound.per_set.size()); |
| |
| // Clean up the "disturbed" before and after the range to be set |
| if (required_size < current_size) { |
| if (last_bound.per_set[last_binding_index].compat_id_for_set != pipe_compat_ids[last_binding_index]) { |
| // We're disturbing those after last, we'll shrink below, but first need to check for and cleanup the push_descriptor |
| for (auto set_idx = required_size; set_idx < current_size; ++set_idx) { |
| if (PushDescriptorCleanup(last_bound, set_idx)) { |
| break; |
| } |
| } |
| } else { |
| // We're not disturbing past last, so leave the upper binding data alone. |
| required_size = current_size; |
| } |
| } |
| |
| // We resize if we need more set entries or if those past "last" are disturbed |
| if (required_size != current_size) { |
| last_bound.per_set.resize(required_size); |
| } |
| |
| // For any previously bound sets, need to set them to "invalid" if they were disturbed by this update |
| for (uint32_t set_idx = 0; set_idx < first_set; ++set_idx) { |
| PushDescriptorCleanup(last_bound, set_idx); |
| last_bound.per_set[set_idx].Reset(); |
| } |
| |
| // Now update the bound sets with the input sets |
| for (uint32_t input_idx = 0; input_idx < set_count; input_idx++) { |
| auto set_idx = input_idx + first_set; // set_idx is index within layout, input_idx is index within input descriptor sets |
| auto &set_info = last_bound.per_set[set_idx]; |
| set_info.Reset(); |
| |
| // Record binding |
| set_info.bound_descriptor_buffer = {buffer_indicies[input_idx], buffer_offsets[input_idx]}; |
| set_info.compat_id_for_set = pipe_compat_ids[set_idx]; // compat ids are canonical *per* set index |
| } |
| } |
| |
| // Set image layout for given VkImageSubresourceRange struct |
| void CMD_BUFFER_STATE::SetImageLayout(const IMAGE_STATE &image_state, const VkImageSubresourceRange &image_subresource_range, |
| VkImageLayout layout, VkImageLayout expected_layout) { |
| auto *subresource_map = GetImageSubresourceLayoutMap(image_state); |
| if (subresource_map && subresource_map->SetSubresourceRangeLayout(*this, image_subresource_range, layout, expected_layout)) { |
| image_layout_change_count++; // Change the version of this data to force revalidation |
| } |
| } |
| |
| // Set the initial image layout for all slices of an image view |
| void CMD_BUFFER_STATE::SetImageViewInitialLayout(const IMAGE_VIEW_STATE &view_state, VkImageLayout layout) { |
| if (dev_data->disabled[image_layout_validation]) { |
| return; |
| } |
| IMAGE_STATE *image_state = view_state.image_state.get(); |
| auto *subresource_map = GetImageSubresourceLayoutMap(*image_state); |
| if (subresource_map) { |
| subresource_map->SetSubresourceRangeInitialLayout(*this, layout, view_state); |
| } |
| } |
| |
| // Set the initial image layout for a passed non-normalized subresource range |
| void CMD_BUFFER_STATE::SetImageInitialLayout(const IMAGE_STATE &image_state, const VkImageSubresourceRange &range, |
| VkImageLayout layout) { |
| auto *subresource_map = GetImageSubresourceLayoutMap(image_state); |
| if (subresource_map) { |
| subresource_map->SetSubresourceRangeInitialLayout(*this, image_state.NormalizeSubresourceRange(range), layout); |
| } |
| } |
| |
| void CMD_BUFFER_STATE::SetImageInitialLayout(VkImage image, const VkImageSubresourceRange &range, VkImageLayout layout) { |
| auto image_state = dev_data->Get<IMAGE_STATE>(image); |
| if (!image_state) return; |
| SetImageInitialLayout(*image_state, range, layout); |
| } |
| |
| void CMD_BUFFER_STATE::SetImageInitialLayout(const IMAGE_STATE &image_state, const VkImageSubresourceLayers &layers, |
| VkImageLayout layout) { |
| SetImageInitialLayout(image_state, RangeFromLayers(layers), layout); |
| } |
| |
| // Set image layout for all slices of an image view |
| void CMD_BUFFER_STATE::SetImageViewLayout(const IMAGE_VIEW_STATE &view_state, VkImageLayout layout, VkImageLayout layoutStencil) { |
| const IMAGE_STATE *image_state = view_state.image_state.get(); |
| |
| VkImageSubresourceRange sub_range = view_state.normalized_subresource_range; |
| |
| if (sub_range.aspectMask == (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT) && layoutStencil != kInvalidLayout) { |
| sub_range.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT; |
| SetImageLayout(*image_state, sub_range, layout); |
| sub_range.aspectMask = VK_IMAGE_ASPECT_STENCIL_BIT; |
| SetImageLayout(*image_state, sub_range, layoutStencil); |
| } else { |
| SetImageLayout(*image_state, sub_range, layout); |
| } |
| } |
| |
| void CMD_BUFFER_STATE::RecordCmd(Func command) { command_count++; } |
| |
| void CMD_BUFFER_STATE::RecordStateCmd(Func command, CBDynamicState state) { |
| CBDynamicFlags state_bits; |
| state_bits.set(state); |
| RecordStateCmd(command, state_bits); |
| } |
| |
| void CMD_BUFFER_STATE::RecordStateCmd(Func command, CBDynamicFlags const &state_bits) { |
| RecordCmd(command); |
| dynamic_state_status.cb |= state_bits; |
| dynamic_state_status.pipeline |= state_bits; |
| } |
| |
| void CMD_BUFFER_STATE::RecordTransferCmd(Func command, std::shared_ptr<BINDABLE> &&buf1, std::shared_ptr<BINDABLE> &&buf2) { |
| RecordCmd(command); |
| if (buf1) { |
| AddChild(buf1); |
| } |
| if (buf2) { |
| AddChild(buf2); |
| } |
| } |
| |
| static bool SetEventStageMask(VkEvent event, VkPipelineStageFlags2KHR stageMask, EventToStageMap *localEventToStageMap) { |
| (*localEventToStageMap)[event] = stageMask; |
| return false; |
| } |
| |
| void CMD_BUFFER_STATE::RecordSetEvent(Func command, VkEvent event, VkPipelineStageFlags2KHR stageMask) { |
| RecordCmd(command); |
| if (!dev_data->disabled[command_buffer_state]) { |
| auto event_state = dev_data->Get<EVENT_STATE>(event); |
| if (event_state) { |
| AddChild(event_state); |
| } |
| } |
| events.push_back(event); |
| if (!waitedEvents.count(event)) { |
| writeEventsBeforeWait.push_back(event); |
| } |
| eventUpdates.emplace_back([event, stageMask](CMD_BUFFER_STATE &, bool do_validate, EventToStageMap *localEventToStageMap) { |
| return SetEventStageMask(event, stageMask, localEventToStageMap); |
| }); |
| } |
| |
| void CMD_BUFFER_STATE::RecordResetEvent(Func command, VkEvent event, VkPipelineStageFlags2KHR stageMask) { |
| RecordCmd(command); |
| if (!dev_data->disabled[command_buffer_state]) { |
| auto event_state = dev_data->Get<EVENT_STATE>(event); |
| if (event_state) { |
| AddChild(event_state); |
| } |
| } |
| events.push_back(event); |
| if (!waitedEvents.count(event)) { |
| writeEventsBeforeWait.push_back(event); |
| } |
| |
| eventUpdates.emplace_back([event](CMD_BUFFER_STATE &, bool do_validate, EventToStageMap *localEventToStageMap) { |
| return SetEventStageMask(event, VkPipelineStageFlags2KHR(0), localEventToStageMap); |
| }); |
| } |
| |
| void CMD_BUFFER_STATE::RecordWaitEvents(Func command, uint32_t eventCount, const VkEvent *pEvents, |
| VkPipelineStageFlags2KHR src_stage_mask) { |
| RecordCmd(command); |
| for (uint32_t i = 0; i < eventCount; ++i) { |
| if (!dev_data->disabled[command_buffer_state]) { |
| auto event_state = dev_data->Get<EVENT_STATE>(pEvents[i]); |
| if (event_state) { |
| AddChild(event_state); |
| } |
| } |
| waitedEvents.insert(pEvents[i]); |
| events.push_back(pEvents[i]); |
| } |
| } |
| |
| void CMD_BUFFER_STATE::RecordBarriers(uint32_t memoryBarrierCount, const VkMemoryBarrier *pMemoryBarriers, |
| uint32_t bufferMemoryBarrierCount, const VkBufferMemoryBarrier *pBufferMemoryBarriers, |
| uint32_t imageMemoryBarrierCount, const VkImageMemoryBarrier *pImageMemoryBarriers) { |
| if (dev_data->disabled[command_buffer_state]) return; |
| |
| for (uint32_t i = 0; i < bufferMemoryBarrierCount; i++) { |
| auto buffer_state = dev_data->Get<BUFFER_STATE>(pBufferMemoryBarriers[i].buffer); |
| if (buffer_state) { |
| AddChild(buffer_state); |
| } |
| } |
| for (uint32_t i = 0; i < imageMemoryBarrierCount; i++) { |
| auto image_state = dev_data->Get<IMAGE_STATE>(pImageMemoryBarriers[i].image); |
| if (image_state) { |
| AddChild(image_state); |
| } |
| } |
| } |
| |
| void CMD_BUFFER_STATE::RecordBarriers(const VkDependencyInfoKHR &dep_info) { |
| if (dev_data->disabled[command_buffer_state]) return; |
| |
| for (uint32_t i = 0; i < dep_info.bufferMemoryBarrierCount; i++) { |
| auto buffer_state = dev_data->Get<BUFFER_STATE>(dep_info.pBufferMemoryBarriers[i].buffer); |
| if (buffer_state) { |
| AddChild(buffer_state); |
| } |
| } |
| for (uint32_t i = 0; i < dep_info.imageMemoryBarrierCount; i++) { |
| auto image_state = dev_data->Get<IMAGE_STATE>(dep_info.pImageMemoryBarriers[i].image); |
| if (image_state) { |
| AddChild(image_state); |
| } |
| } |
| } |
| |
| void CMD_BUFFER_STATE::RecordWriteTimestamp(Func command, VkPipelineStageFlags2KHR pipelineStage, VkQueryPool queryPool, |
| uint32_t slot) { |
| RecordCmd(command); |
| if (dev_data->disabled[query_validation]) return; |
| |
| if (!dev_data->disabled[command_buffer_state]) { |
| auto pool_state = dev_data->Get<QUERY_POOL_STATE>(queryPool); |
| AddChild(pool_state); |
| } |
| QueryObject query_obj = {queryPool, slot}; |
| EndQuery(query_obj); |
| } |
| |
| void CMD_BUFFER_STATE::Submit(uint32_t perf_submit_pass) { |
| VkQueryPool first_pool = VK_NULL_HANDLE; |
| EventToStageMap local_event_to_stage_map; |
| QueryMap local_query_to_state_map; |
| for (auto &function : queryUpdates) { |
| function(*this, /*do_validate*/ false, first_pool, perf_submit_pass, &local_query_to_state_map); |
| } |
| |
| for (const auto &query_state_pair : local_query_to_state_map) { |
| auto query_pool_state = dev_data->Get<QUERY_POOL_STATE>(query_state_pair.first.pool); |
| query_pool_state->SetQueryState(query_state_pair.first.slot, query_state_pair.first.perf_pass, query_state_pair.second); |
| } |
| |
| for (const auto &function : eventUpdates) { |
| function(*this, /*do_validate*/ false, &local_event_to_stage_map); |
| } |
| |
| for (const auto &eventStagePair : local_event_to_stage_map) { |
| auto event_state = dev_data->Get<EVENT_STATE>(eventStagePair.first); |
| event_state->stageMask = eventStagePair.second; |
| } |
| |
| for (const auto &it : video_session_updates) { |
| auto video_session_state = dev_data->Get<VIDEO_SESSION_STATE>(it.first); |
| auto device_state = video_session_state->DeviceStateWrite(); |
| for (const auto &function : it.second) { |
| function(nullptr, video_session_state.get(), *device_state, /*do_validate*/ false); |
| } |
| } |
| } |
| |
| void CMD_BUFFER_STATE::Retire(uint32_t perf_submit_pass, const std::function<bool(const QueryObject &)> &is_query_updated_after) { |
| // First perform decrement on general case bound objects |
| for (auto event : writeEventsBeforeWait) { |
| auto event_state = dev_data->Get<EVENT_STATE>(event); |
| if (event_state) { |
| event_state->write_in_use--; |
| } |
| } |
| QueryMap local_query_to_state_map; |
| VkQueryPool first_pool = VK_NULL_HANDLE; |
| for (auto &function : queryUpdates) { |
| function(*this, /*do_validate*/ false, first_pool, perf_submit_pass, &local_query_to_state_map); |
| } |
| |
| for (const auto &query_state_pair : local_query_to_state_map) { |
| if (query_state_pair.second == QUERYSTATE_ENDED && !is_query_updated_after(query_state_pair.first)) { |
| auto query_pool_state = dev_data->Get<QUERY_POOL_STATE>(query_state_pair.first.pool); |
| if (query_pool_state) { |
| query_pool_state->SetQueryState(query_state_pair.first.slot, query_state_pair.first.perf_pass, |
| QUERYSTATE_AVAILABLE); |
| } |
| } |
| } |
| } |
| |
| void CMD_BUFFER_STATE::UnbindResources() { |
| // Vertex and index buffers |
| index_buffer_binding.reset(); |
| vertex_buffer_used = false; |
| current_vertex_buffer_binding_info.vertex_buffer_bindings.clear(); |
| |
| // Push constants |
| push_constant_data.clear(); |
| push_constant_data_ranges.reset(); |
| |
| // Reset status of cb to force rebinding of all resources |
| // Index buffer included |
| dynamic_state_status.cb.reset(); |
| dynamic_state_status.pipeline.reset(); |
| |
| // Pipeline and descriptor sets |
| lastBound[BindPoint_Graphics].Reset(); |
| } |
| |
| LogObjectList CMD_BUFFER_STATE::GetObjectList(VkShaderStageFlagBits stage) const { |
| LogObjectList objlist(handle_); |
| const auto lv_bind_point = ConvertToLvlBindPoint(stage); |
| const auto &last_bound = lastBound[lv_bind_point]; |
| const auto *pipeline_state = last_bound.pipeline_state; |
| |
| if (pipeline_state) { |
| objlist.add(pipeline_state->pipeline()); |
| } else if (VkShaderEXT shader = last_bound.GetShader(ConvertToShaderObjectStage(stage))) { |
| objlist.add(shader); |
| } |
| return objlist; |
| } |
| |
| LogObjectList CMD_BUFFER_STATE::GetObjectList(VkPipelineBindPoint pipeline_bind_point) const { |
| LogObjectList objlist(handle_); |
| const auto lv_bind_point = ConvertToLvlBindPoint(pipeline_bind_point); |
| const auto &last_bound = lastBound[lv_bind_point]; |
| const auto *pipeline_state = last_bound.pipeline_state; |
| |
| if (pipeline_state) { |
| objlist.add(pipeline_state->pipeline()); |
| } else if (pipeline_bind_point == VK_PIPELINE_BIND_POINT_COMPUTE) { |
| if (VkShaderEXT shader = last_bound.GetShader(ShaderObjectStage::COMPUTE)) { |
| objlist.add(shader); |
| } |
| } else if (pipeline_bind_point == VK_PIPELINE_BIND_POINT_GRAPHICS) { |
| // If using non-compute, need to check all graphics stages |
| if (VkShaderEXT shader = last_bound.GetShader(ShaderObjectStage::VERTEX)) { |
| objlist.add(shader); |
| } |
| if (VkShaderEXT shader = last_bound.GetShader(ShaderObjectStage::TESSELLATION_CONTROL)) { |
| objlist.add(shader); |
| } |
| if (VkShaderEXT shader = last_bound.GetShader(ShaderObjectStage::TESSELLATION_EVALUATION)) { |
| objlist.add(shader); |
| } |
| if (VkShaderEXT shader = last_bound.GetShader(ShaderObjectStage::GEOMETRY)) { |
| objlist.add(shader); |
| } |
| if (VkShaderEXT shader = last_bound.GetShader(ShaderObjectStage::FRAGMENT)) { |
| objlist.add(shader); |
| } |
| if (VkShaderEXT shader = last_bound.GetShader(ShaderObjectStage::MESH)) { |
| objlist.add(shader); |
| } |
| if (VkShaderEXT shader = last_bound.GetShader(ShaderObjectStage::TASK)) { |
| objlist.add(shader); |
| } |
| } |
| return objlist; |
| } |
| |
| PIPELINE_STATE *CMD_BUFFER_STATE::GetCurrentPipeline(VkPipelineBindPoint pipelineBindPoint) const { |
| const auto lv_bind_point = ConvertToLvlBindPoint(pipelineBindPoint); |
| return lastBound[lv_bind_point].pipeline_state; |
| } |
| |
| void CMD_BUFFER_STATE::GetCurrentPipelineAndDesriptorSets(VkPipelineBindPoint pipelineBindPoint, const PIPELINE_STATE **rtn_pipe, |
| const std::vector<LAST_BOUND_STATE::PER_SET> **rtn_sets) const { |
| const auto lv_bind_point = ConvertToLvlBindPoint(pipelineBindPoint); |
| const auto &last_bound = lastBound[lv_bind_point]; |
| if (!last_bound.IsUsing()) { |
| return; |
| } |
| *rtn_pipe = last_bound.pipeline_state; |
| *rtn_sets = &(last_bound.per_set); |
| } |