blob: 44cec5479bdfd7997082bdd73961a8a0a9c9caea [file] [log] [blame]
/* Copyright (c) 2015-2021 The Khronos Group Inc.
* Copyright (c) 2015-2021 Valve Corporation
* Copyright (c) 2015-2021 LunarG, Inc.
* Copyright (C) 2015-2021 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.
*
* Author: Courtney Goeltzenleuchter <courtneygo@google.com>
* Author: Tobin Ehlis <tobine@google.com>
* Author: Chris Forbes <chrisf@ijw.co.nz>
* Author: Mark Lobodzinski <mark@lunarg.com>
* Author: Dave Houlton <daveh@lunarg.com>
* Author: John Zulauf <jzulauf@lunarg.com>
* Author: Tobias Hector <tobias.hector@amd.com>
*/
#include "cmd_buffer_state.h"
#include "render_pass_state.h"
#include "state_tracker.h"
#include "image_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) {
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();
}
const char *CommandTypeString(CMD_TYPE type) {
// Autogenerated as part of the command_validation.h codegen
return kGeneratedCommandNameList[type];
}
VkDynamicState ConvertToDynamicState(CBStatusFlagBits flag) {
switch (flag) {
case CBSTATUS_LINE_WIDTH_SET:
return VK_DYNAMIC_STATE_LINE_WIDTH;
case CBSTATUS_DEPTH_BIAS_SET:
return VK_DYNAMIC_STATE_DEPTH_BIAS;
case CBSTATUS_BLEND_CONSTANTS_SET:
return VK_DYNAMIC_STATE_BLEND_CONSTANTS;
case CBSTATUS_DEPTH_BOUNDS_SET:
return VK_DYNAMIC_STATE_DEPTH_BOUNDS;
case CBSTATUS_STENCIL_READ_MASK_SET:
return VK_DYNAMIC_STATE_STENCIL_COMPARE_MASK;
case CBSTATUS_STENCIL_WRITE_MASK_SET:
return VK_DYNAMIC_STATE_STENCIL_WRITE_MASK;
case CBSTATUS_STENCIL_REFERENCE_SET:
return VK_DYNAMIC_STATE_STENCIL_REFERENCE;
case CBSTATUS_VIEWPORT_SET:
return VK_DYNAMIC_STATE_VIEWPORT;
case CBSTATUS_SCISSOR_SET:
return VK_DYNAMIC_STATE_SCISSOR;
case CBSTATUS_EXCLUSIVE_SCISSOR_SET:
return VK_DYNAMIC_STATE_EXCLUSIVE_SCISSOR_NV;
case CBSTATUS_SHADING_RATE_PALETTE_SET:
return VK_DYNAMIC_STATE_VIEWPORT_SHADING_RATE_PALETTE_NV;
case CBSTATUS_LINE_STIPPLE_SET:
return VK_DYNAMIC_STATE_LINE_STIPPLE_EXT;
case CBSTATUS_VIEWPORT_W_SCALING_SET:
return VK_DYNAMIC_STATE_VIEWPORT_W_SCALING_NV;
case CBSTATUS_CULL_MODE_SET:
return VK_DYNAMIC_STATE_CULL_MODE_EXT;
case CBSTATUS_FRONT_FACE_SET:
return VK_DYNAMIC_STATE_FRONT_FACE_EXT;
case CBSTATUS_PRIMITIVE_TOPOLOGY_SET:
return VK_DYNAMIC_STATE_PRIMITIVE_TOPOLOGY_EXT;
case CBSTATUS_VIEWPORT_WITH_COUNT_SET:
return VK_DYNAMIC_STATE_VIEWPORT_WITH_COUNT_EXT;
case CBSTATUS_SCISSOR_WITH_COUNT_SET:
return VK_DYNAMIC_STATE_SCISSOR_WITH_COUNT_EXT;
case CBSTATUS_VERTEX_INPUT_BINDING_STRIDE_SET:
return VK_DYNAMIC_STATE_VERTEX_INPUT_BINDING_STRIDE_EXT;
case CBSTATUS_DEPTH_TEST_ENABLE_SET:
return VK_DYNAMIC_STATE_DEPTH_TEST_ENABLE_EXT;
case CBSTATUS_DEPTH_WRITE_ENABLE_SET:
return VK_DYNAMIC_STATE_DEPTH_WRITE_ENABLE_EXT;
case CBSTATUS_DEPTH_COMPARE_OP_SET:
return VK_DYNAMIC_STATE_DEPTH_COMPARE_OP_EXT;
case CBSTATUS_DEPTH_BOUNDS_TEST_ENABLE_SET:
return VK_DYNAMIC_STATE_DEPTH_BOUNDS_TEST_ENABLE_EXT;
case CBSTATUS_STENCIL_TEST_ENABLE_SET:
return VK_DYNAMIC_STATE_STENCIL_TEST_ENABLE_EXT;
case CBSTATUS_STENCIL_OP_SET:
return VK_DYNAMIC_STATE_STENCIL_OP_EXT;
case CBSTATUS_DISCARD_RECTANGLE_SET:
return VK_DYNAMIC_STATE_DISCARD_RECTANGLE_EXT;
case CBSTATUS_SAMPLE_LOCATIONS_SET:
return VK_DYNAMIC_STATE_SAMPLE_LOCATIONS_EXT;
case CBSTATUS_COARSE_SAMPLE_ORDER_SET:
return VK_DYNAMIC_STATE_VIEWPORT_COARSE_SAMPLE_ORDER_NV;
case CBSTATUS_PATCH_CONTROL_POINTS_SET:
return VK_DYNAMIC_STATE_PATCH_CONTROL_POINTS_EXT;
case CBSTATUS_RASTERIZER_DISCARD_ENABLE_SET:
return VK_DYNAMIC_STATE_RASTERIZER_DISCARD_ENABLE_EXT;
case CBSTATUS_DEPTH_BIAS_ENABLE_SET:
return VK_DYNAMIC_STATE_DEPTH_BIAS_ENABLE_EXT;
case CBSTATUS_LOGIC_OP_SET:
return VK_DYNAMIC_STATE_LOGIC_OP_EXT;
case CBSTATUS_PRIMITIVE_RESTART_ENABLE_SET:
return VK_DYNAMIC_STATE_PRIMITIVE_RESTART_ENABLE_EXT;
case CBSTATUS_VERTEX_INPUT_SET:
return VK_DYNAMIC_STATE_VERTEX_INPUT_EXT;
default:
// CBSTATUS_INDEX_BUFFER_BOUND is not in VkDynamicState
return VK_DYNAMIC_STATE_MAX_ENUM;
}
return VK_DYNAMIC_STATE_MAX_ENUM;
}
CBStatusFlagBits ConvertToCBStatusFlagBits(VkDynamicState state) {
switch (state) {
case VK_DYNAMIC_STATE_VIEWPORT:
return CBSTATUS_VIEWPORT_SET;
case VK_DYNAMIC_STATE_SCISSOR:
return CBSTATUS_SCISSOR_SET;
case VK_DYNAMIC_STATE_LINE_WIDTH:
return CBSTATUS_LINE_WIDTH_SET;
case VK_DYNAMIC_STATE_DEPTH_BIAS:
return CBSTATUS_DEPTH_BIAS_SET;
case VK_DYNAMIC_STATE_BLEND_CONSTANTS:
return CBSTATUS_BLEND_CONSTANTS_SET;
case VK_DYNAMIC_STATE_DEPTH_BOUNDS:
return CBSTATUS_DEPTH_BOUNDS_SET;
case VK_DYNAMIC_STATE_STENCIL_COMPARE_MASK:
return CBSTATUS_STENCIL_READ_MASK_SET;
case VK_DYNAMIC_STATE_STENCIL_WRITE_MASK:
return CBSTATUS_STENCIL_WRITE_MASK_SET;
case VK_DYNAMIC_STATE_STENCIL_REFERENCE:
return CBSTATUS_STENCIL_REFERENCE_SET;
case VK_DYNAMIC_STATE_VIEWPORT_W_SCALING_NV:
return CBSTATUS_VIEWPORT_W_SCALING_SET;
case VK_DYNAMIC_STATE_DISCARD_RECTANGLE_EXT:
return CBSTATUS_DISCARD_RECTANGLE_SET;
case VK_DYNAMIC_STATE_SAMPLE_LOCATIONS_EXT:
return CBSTATUS_SAMPLE_LOCATIONS_SET;
case VK_DYNAMIC_STATE_VIEWPORT_SHADING_RATE_PALETTE_NV:
return CBSTATUS_SHADING_RATE_PALETTE_SET;
case VK_DYNAMIC_STATE_VIEWPORT_COARSE_SAMPLE_ORDER_NV:
return CBSTATUS_COARSE_SAMPLE_ORDER_SET;
case VK_DYNAMIC_STATE_EXCLUSIVE_SCISSOR_NV:
return CBSTATUS_EXCLUSIVE_SCISSOR_SET;
case VK_DYNAMIC_STATE_LINE_STIPPLE_EXT:
return CBSTATUS_LINE_STIPPLE_SET;
case VK_DYNAMIC_STATE_CULL_MODE_EXT:
return CBSTATUS_CULL_MODE_SET;
case VK_DYNAMIC_STATE_FRONT_FACE_EXT:
return CBSTATUS_FRONT_FACE_SET;
case VK_DYNAMIC_STATE_PRIMITIVE_TOPOLOGY_EXT:
return CBSTATUS_PRIMITIVE_TOPOLOGY_SET;
case VK_DYNAMIC_STATE_VIEWPORT_WITH_COUNT_EXT:
return CBSTATUS_VIEWPORT_WITH_COUNT_SET;
case VK_DYNAMIC_STATE_SCISSOR_WITH_COUNT_EXT:
return CBSTATUS_SCISSOR_WITH_COUNT_SET;
case VK_DYNAMIC_STATE_VERTEX_INPUT_BINDING_STRIDE_EXT:
return CBSTATUS_VERTEX_INPUT_BINDING_STRIDE_SET;
case VK_DYNAMIC_STATE_DEPTH_TEST_ENABLE_EXT:
return CBSTATUS_DEPTH_TEST_ENABLE_SET;
case VK_DYNAMIC_STATE_DEPTH_WRITE_ENABLE_EXT:
return CBSTATUS_DEPTH_WRITE_ENABLE_SET;
case VK_DYNAMIC_STATE_DEPTH_COMPARE_OP_EXT:
return CBSTATUS_DEPTH_COMPARE_OP_SET;
case VK_DYNAMIC_STATE_DEPTH_BOUNDS_TEST_ENABLE_EXT:
return CBSTATUS_DEPTH_BOUNDS_TEST_ENABLE_SET;
case VK_DYNAMIC_STATE_STENCIL_TEST_ENABLE_EXT:
return CBSTATUS_STENCIL_TEST_ENABLE_SET;
case VK_DYNAMIC_STATE_STENCIL_OP_EXT:
return CBSTATUS_STENCIL_OP_SET;
case VK_DYNAMIC_STATE_PATCH_CONTROL_POINTS_EXT:
return CBSTATUS_PATCH_CONTROL_POINTS_SET;
case VK_DYNAMIC_STATE_RASTERIZER_DISCARD_ENABLE_EXT:
return CBSTATUS_RASTERIZER_DISCARD_ENABLE_SET;
case VK_DYNAMIC_STATE_DEPTH_BIAS_ENABLE_EXT:
return CBSTATUS_DEPTH_BIAS_ENABLE_SET;
case VK_DYNAMIC_STATE_LOGIC_OP_EXT:
return CBSTATUS_LOGIC_OP_SET;
case VK_DYNAMIC_STATE_PRIMITIVE_RESTART_ENABLE_EXT:
return CBSTATUS_PRIMITIVE_RESTART_ENABLE_SET;
case VK_DYNAMIC_STATE_VERTEX_INPUT_EXT:
return CBSTATUS_VERTEX_INPUT_SET;
default:
return CBSTATUS_NONE;
}
return CBSTATUS_NONE;
}
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) {
Reset();
}
// 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(BASE_NODE *child_node) {
assert(child_node);
if (child_node->AddParent(this)) {
object_bindings.insert(child_node);
}
}
void CMD_BUFFER_STATE::RemoveChild(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::Reset() {
ResetUse();
// Reset CB state (note that createInfo is not cleared)
memset(&beginInfo, 0, sizeof(VkCommandBufferBeginInfo));
memset(&inheritanceInfo, 0, sizeof(VkCommandBufferInheritanceInfo));
hasDrawCmd = false;
hasTraceRaysCmd = false;
hasBuildAccelerationStructureCmd = false;
hasDispatchCmd = false;
state = CB_NEW;
commandCount = 0;
submitCount = 0;
image_layout_change_count = 1; // Start at 1. 0 is insert value for validation cache versions, s.t. new == dirty
status = 0;
static_status = 0;
inheritedViewportDepths.clear();
usedViewportScissorCount = 0;
pipelineStaticViewportCount = 0;
pipelineStaticScissorCount = 0;
viewportMask = 0;
viewportWithCountMask = 0;
viewportWithCountCount = 0;
scissorMask = 0;
scissorWithCountMask = 0;
scissorWithCountCount = 0;
trashedViewportMask = 0;
trashedScissorMask = 0;
trashedViewportCount = false;
trashedScissorCount = false;
usedDynamicViewportCount = false;
usedDynamicScissorCount = false;
primitiveTopology = VK_PRIMITIVE_TOPOLOGY_MAX_ENUM;
activeRenderPassBeginInfo = safe_VkRenderPassBeginInfo();
activeRenderPass = nullptr;
active_attachments = nullptr;
active_subpasses = nullptr;
attachments_view_states.clear();
activeSubpassContents = VK_SUBPASS_CONTENTS_INLINE;
activeSubpass = 0;
broken_bindings.clear();
waitedEvents.clear();
events.clear();
writeEventsBeforeWait.clear();
activeQueries.clear();
startedQueries.clear();
image_layout_map.clear();
current_vertex_buffer_binding_info.vertex_buffer_bindings.clear();
vertex_buffer_used = false;
primaryCommandBuffer = VK_NULL_HANDLE;
linkedCommandBuffers.clear();
// Remove reverse command buffer links.
Invalidate(true);
queue_submit_functions.clear();
queue_submit_functions_after_render_pass.clear();
cmd_execute_commands_functions.clear();
eventUpdates.clear();
queryUpdates.clear();
// Remove object bindings
for (const auto &obj : object_bindings) {
obj->RemoveParent(this);
}
object_bindings.clear();
for (auto &item : lastBound) {
item.Reset();
}
// Remove this cmdBuffer's reference from each FrameBuffer's CB ref list
for (auto &framebuffer : framebuffers) {
framebuffer->RemoveParent(this);
}
framebuffers.clear();
activeFramebuffer = VK_NULL_HANDLE;
index_buffer_binding.reset();
qfo_transfer_image_barriers.Reset();
qfo_transfer_buffer_barriers.Reset();
// 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;
// Remove object bindings
for (auto *base_obj : object_bindings) {
RemoveChild(base_obj);
}
object_bindings.clear();
// Clean up the label data
ResetCmdDebugUtilsLabel(dev_data->report_data, commandBuffer());
if (dev_data->command_buffer_reset_callback) {
(*dev_data->command_buffer_reset_callback)(commandBuffer());
}
}
// 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->GetEventState(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();
push_constant_data_update.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);
auto stage_flags = push_constant_range.stageFlags;
uint32_t bit_shift = 0;
while (stage_flags) {
if (stage_flags & 1) {
VkShaderStageFlagBits flag = static_cast<VkShaderStageFlagBits>(1 << bit_shift);
const auto it = push_constant_data_update.find(flag);
if (it != push_constant_data_update.end()) {
if (it->second.size() < push_constant_range.offset) {
it->second.resize(push_constant_range.offset, PC_Byte_Not_Set);
}
if (it->second.size() < size) {
it->second.resize(size, PC_Byte_Not_Updated);
}
} else {
std::vector<uint8_t> bytes;
bytes.resize(push_constant_range.offset, PC_Byte_Not_Set);
bytes.resize(size, PC_Byte_Not_Updated);
push_constant_data_update[flag] = bytes;
}
}
stage_flags = stage_flags >> 1;
++bit_shift;
}
}
push_constant_data.resize(size_needed, 0);
}
void CMD_BUFFER_STATE::Destroy() {
// Allow any derived class to clean up command buffer state
if (dev_data->command_buffer_reset_callback) {
(*dev_data->command_buffer_reset_callback)(commandBuffer());
}
if (dev_data->command_buffer_free_callback) {
(*dev_data->command_buffer_free_callback)(commandBuffer());
}
// Remove the cb debug labels
EraseCmdDebugUtilsLabel(dev_data->report_data, commandBuffer());
Reset();
BASE_NODE::Destroy();
}
void CMD_BUFFER_STATE::NotifyInvalidate(const BASE_NODE::NodeList &invalid_nodes, bool unlink) {
if (state == CB_RECORDING) {
state = CB_INVALID_INCOMPLETE;
} else if (state == CB_RECORDED) {
state = CB_INVALID_COMPLETE;
}
assert(!invalid_nodes.empty());
LogObjectList log_list;
for (auto *obj : invalid_nodes) {
log_list.object_list.emplace_back(obj->Handle());
}
broken_bindings.emplace(invalid_nodes[0]->Handle(), log_list);
if (unlink) {
for (auto *obj : invalid_nodes) {
object_bindings.erase(obj);
switch (obj->Type()) {
case kVulkanObjectTypeCommandBuffer:
linkedCommandBuffers.erase(static_cast<CMD_BUFFER_STATE *>(obj));
break;
case kVulkanObjectTypeImage:
image_layout_map.erase(static_cast<IMAGE_STATE *>(obj));
break;
default:
break;
}
}
}
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;
}
// 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) {
// Was an empty slot... fill it in.
layout_map.emplace(image_state);
}
return &layout_map;
}
static bool SetQueryState(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](const ValidationStateTracker *device_data, bool do_validate,
VkQueryPool &firstPerfQueryPool, uint32_t perfQueryPass, QueryMap *localQueryToStateMap) {
SetQueryState(QueryObject(query_obj, perfQueryPass), QUERYSTATE_RUNNING, localQueryToStateMap);
return false;
});
}
void CMD_BUFFER_STATE::EndQuery(const QueryObject &query_obj) {
activeQueries.erase(query_obj);
queryUpdates.emplace_back([query_obj](const ValidationStateTracker *device_data, bool do_validate,
VkQueryPool &firstPerfQueryPool, uint32_t perfQueryPass, QueryMap *localQueryToStateMap) {
return SetQueryState(QueryObject(query_obj, perfQueryPass), QUERYSTATE_ENDED, localQueryToStateMap);
});
}
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 object = QueryObject(QueryObject(queryPool, firstQuery + i), perfPass);
(*localQueryToStateMap)[object] = 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 = {queryPool, slot};
activeQueries.erase(query);
}
queryUpdates.emplace_back([queryPool, firstQuery, queryCount](const ValidationStateTracker *device_data, bool do_validate,
VkQueryPool &firstPerfQueryPool, uint32_t perfQueryPass,
QueryMap *localQueryToStateMap) {
return SetQueryStateMulti(queryPool, firstQuery, queryCount, perfQueryPass, QUERYSTATE_RESET, 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 = {queryPool, slot};
resetQueries.insert(query);
}
queryUpdates.emplace_back([queryPool, firstQuery, queryCount](const ValidationStateTracker *device_data, bool do_validate,
VkQueryPool &firstPerfQueryPool, uint32_t perfQueryPass,
QueryMap *localQueryToStateMap) {
return SetQueryStateMulti(queryPool, firstQuery, queryCount, perfQueryPass, QUERYSTATE_RESET, localQueryToStateMap);
});
}
void 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;
}
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) ? true : false;
const VkRenderPassAttachmentBeginInfo *attachment_info_struct = nullptr;
if (pRenderPassBegin) attachment_info_struct = LvlFindInChain<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->GetShared<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(CMD_TYPE cmd_type, const VkRenderPassBeginInfo *pRenderPassBegin,
const VkSubpassContents contents) {
RecordCmd(cmd_type);
activeFramebuffer = dev_data->GetShared<FRAMEBUFFER_STATE>(pRenderPassBegin->framebuffer);
activeRenderPass = dev_data->GetShared<RENDER_PASS_STATE>(pRenderPassBegin->renderPass);
activeRenderPassBeginInfo = safe_VkRenderPassBeginInfo(pRenderPassBegin);
activeSubpass = 0;
activeSubpassContents = contents;
// Connect this RP to cmdBuffer
if (!dev_data->disabled[command_buffer_state] && activeRenderPass) {
AddChild(activeRenderPass.get());
}
auto chained_device_group_struct = LvlFindInChain<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) {
framebuffers.insert(activeFramebuffer);
// Set cb_state->active_subpasses
active_subpasses = std::make_shared<std::vector<SUBPASS_INFO>>(activeFramebuffer->createInfo.attachmentCount);
const auto &subpass = activeRenderPass->createInfo.pSubpasses[activeSubpass];
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.get());
}
}
void CMD_BUFFER_STATE::NextSubpass(CMD_TYPE cmd_type, VkSubpassContents contents) {
RecordCmd(cmd_type);
activeSubpass++;
activeSubpassContents = contents;
// Update cb_state->active_subpasses
if (activeRenderPass && activeFramebuffer) {
active_subpasses = nullptr;
active_subpasses = std::make_shared<std::vector<SUBPASS_INFO>>(activeFramebuffer->createInfo.attachmentCount);
const auto &subpass = activeRenderPass->createInfo.pSubpasses[activeSubpass];
UpdateSubpassAttachments(subpass, *active_subpasses);
}
}
void CMD_BUFFER_STATE::EndRenderPass(CMD_TYPE cmd_type) {
RecordCmd(cmd_type);
activeRenderPass = nullptr;
active_attachments = nullptr;
active_subpasses = nullptr;
activeSubpass = 0;
activeFramebuffer = VK_NULL_HANDLE;
}
void CMD_BUFFER_STATE::BeginRendering(CMD_TYPE cmd_type, const VkRenderingInfoKHR *pRenderingInfo) {
RecordCmd(cmd_type);
activeRenderPass = std::make_shared<RENDER_PASS_STATE>(pRenderingInfo);
auto chained_device_group_struct = LvlFindInChain<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);
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) {
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->GetShared<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->GetShared<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->GetShared<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::Begin(const VkCommandBufferBeginInfo *pBeginInfo) {
if (CB_RECORDED == state || CB_INVALID_COMPLETE == state) {
Reset();
}
// Set updated state here in case implicit reset occurs above
state = CB_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->GetShared<RENDER_PASS_STATE>(beginInfo.pInheritanceInfo->renderPass);
activeSubpass = beginInfo.pInheritanceInfo->subpass;
if (beginInfo.pInheritanceInfo->framebuffer) {
activeFramebuffer = dev_data->GetShared<FRAMEBUFFER_STATE>(beginInfo.pInheritanceInfo->framebuffer);
active_subpasses = nullptr;
active_attachments = nullptr;
if (activeFramebuffer) {
framebuffers.insert(activeFramebuffer);
// Set active_subpasses
active_subpasses = std::make_shared<std::vector<SUBPASS_INFO>>(activeFramebuffer->createInfo.attachmentCount);
const auto& subpass = activeRenderPass->createInfo.pSubpasses[activeSubpass];
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.get());
}
}
}
}
else
{
auto inheritance_rendering_info = lvl_find_in_chain<VkCommandBufferInheritanceRenderingInfoKHR>(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 =
LvlFindInChain<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 = LvlFindInChain<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;
}
void CMD_BUFFER_STATE::End(VkResult result) {
// Cached validation is specific to a specific recording of a specific command buffer.
descriptorset_cache.clear();
validated_descriptor_sets.clear();
if (VK_SUCCESS == result) {
state = CB_RECORDED;
}
}
void CMD_BUFFER_STATE::ExecuteCommands(uint32_t commandBuffersCount, const VkCommandBuffer *pCommandBuffers) {
RecordCmd(CMD_EXECUTECOMMANDS);
CMD_BUFFER_STATE *sub_cb_state = NULL;
for (uint32_t i = 0; i < commandBuffersCount; i++) {
sub_cb_state = dev_data->Get<CMD_BUFFER_STATE>(pCommandBuffers[i]);
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);
const auto *sub_cb_subres_map = &sub_layout_map_entry.second;
assert(cb_subres_map && sub_cb_subres_map); // Non const get and map traversal should never be null
cb_subres_map->UpdateFrom(*sub_cb_subres_map);
}
sub_cb_state->primaryCommandBuffer = commandBuffer();
linkedCommandBuffers.insert(sub_cb_state);
AddChild(sub_cb_state);
for (auto &function : sub_cb_state->queryUpdates) {
queryUpdates.push_back(function);
}
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 = ~uint32_t(0);
trashedScissorMask = ~uint32_t(0);
trashedViewportCount = true;
trashedScissorCount = true;
}
}
void CMD_BUFFER_STATE::PushDescriptorSetState(VkPipelineBindPoint pipelineBindPoint, PIPELINE_LAYOUT_STATE *pipeline_layout,
uint32_t set, uint32_t descriptorWriteCount,
const VkWriteDescriptorSet *pDescriptorWrites) {
// Short circuit invalid updates
if (!pipeline_layout || (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 || !CompatForSet(set, last_bound, pipeline_layout->compat_for_set)) {
last_bound.UnbindAndResetPushDescriptorSet(this, new cvdescriptorset::DescriptorSet(0, nullptr, dsl, 0, dev_data));
}
UpdateLastBoundDescriptorSets(pipelineBindPoint, pipeline_layout, set, 1, nullptr, push_descriptor_set.get(), 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(dev_data, descriptorWriteCount, pDescriptorWrites);
}
// Generic function to handle state update for all CmdDraw* and CmdDispatch* type functions
void CMD_BUFFER_STATE::UpdateStateCmdDrawDispatchType(CMD_TYPE cmd_type, VkPipelineBindPoint bind_point) {
UpdateDrawState(cmd_type, bind_point);
hasDispatchCmd = true;
}
// Generic function to handle state update for all CmdDraw* type functions
void CMD_BUFFER_STATE::UpdateStateCmdDrawType(CMD_TYPE cmd_type, VkPipelineBindPoint bind_point) {
UpdateStateCmdDrawDispatchType(cmd_type, bind_point);
hasDrawCmd = true;
// Update the consumed viewport/scissor count.
uint32_t &used = usedViewportScissorCount;
used = std::max(used, pipelineStaticViewportCount);
used = std::max(used, pipelineStaticScissorCount);
usedDynamicViewportCount |= !!(dynamic_status & CBSTATUS_VIEWPORT_WITH_COUNT_SET); // !! silences MSVC warn
usedDynamicScissorCount |= !!(dynamic_status & CBSTATUS_SCISSOR_WITH_COUNT_SET);
}
void CMD_BUFFER_STATE::UpdateDrawState(CMD_TYPE cmd_type, const VkPipelineBindPoint bind_point) {
RecordCmd(cmd_type);
const auto lv_bind_point = ConvertToLvlBindPoint(bind_point);
auto &state = lastBound[lv_bind_point];
PIPELINE_STATE *pipe = state.pipeline_state;
if (VK_NULL_HANDLE != state.pipeline_layout) {
for (const auto &set_binding_pair : pipe->active_slots) {
uint32_t set_index = set_binding_pair.first;
// Pull the set node
cvdescriptorset::DescriptorSet *descriptor_set = state.per_set[set_index].bound_descriptor_set;
// 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::ValidateCmdBufDrawState for more details.
bool descriptor_set_changed =
!reduced_map.IsManyDescriptors() ||
// Update if descriptor set (or contents) has changed
state.per_set[set_index].validated_set != descriptor_set ||
state.per_set[set_index].validated_set_change_count != descriptor_set->GetChangeCount() ||
(!dev_data->disabled[image_layout_validation] &&
state.per_set[set_index].validated_set_image_layout_change_count != image_layout_change_count);
bool need_update = descriptor_set_changed ||
// Update if previous bindingReqMap doesn't include new bindingReqMap
!std::includes(state.per_set[set_index].validated_set_binding_req_map.begin(),
state.per_set[set_index].validated_set_binding_req_map.end(), binding_req_map.begin(),
binding_req_map.end());
if (need_update) {
// 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
BindingReqMap delta_reqs;
std::set_difference(binding_req_map.begin(), binding_req_map.end(),
state.per_set[set_index].validated_set_binding_req_map.begin(),
state.per_set[set_index].validated_set_binding_req_map.end(),
layer_data::insert_iterator<BindingReqMap>(delta_reqs, delta_reqs.begin()));
descriptor_set->UpdateDrawState(dev_data, this, cmd_type, pipe, delta_reqs);
} else {
descriptor_set->UpdateDrawState(dev_data, this, cmd_type, pipe, binding_req_map);
}
state.per_set[set_index].validated_set = descriptor_set;
state.per_set[set_index].validated_set_change_count = descriptor_set->GetChangeCount();
state.per_set[set_index].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 (state.per_set[set_index].validated_set_binding_req_map != set_binding_pair.second) {
state.per_set[set_index].validated_set_binding_req_map = set_binding_pair.second;
}
} else {
state.per_set[set_index].validated_set_binding_req_map = BindingReqMap();
}
}
}
}
if (pipe && !pipe->vertex_binding_descriptions_.empty()) {
vertex_buffer_used = 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,
cvdescriptorset::DescriptorSet *push_descriptor_set,
uint32_t dynamic_offset_count, const uint32_t *p_dynamic_offsets) {
assert((pDescriptorSets == nullptr) ^ (push_descriptor_set == nullptr));
// Defensive
assert(pipeline_layout);
if (!pipeline_layout) return;
uint32_t required_size = first_set + set_count;
const uint32_t last_binding_index = required_size - 1;
assert(last_binding_index < pipeline_layout->compat_for_set.size());
// Some useful shorthand
const auto lv_bind_point = ConvertToLvlBindPoint(pipeline_bind_point);
auto &last_bound = lastBound[lv_bind_point];
auto &pipe_compat_ids = pipeline_layout->compat_for_set;
const uint32_t current_size = static_cast<uint32_t>(last_bound.per_set.size());
// We need this three times in this function, but nowhere else
auto push_descriptor_cleanup = [&last_bound](const cvdescriptorset::DescriptorSet *ds) -> bool {
if (ds && ds->IsPushDescriptor()) {
assert(ds == last_bound.push_descriptor_set.get());
last_bound.push_descriptor_set = nullptr;
return true;
}
return false;
};
// 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 (push_descriptor_cleanup(last_bound.per_set[set_idx].bound_descriptor_set)) 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) {
if (last_bound.per_set[set_idx].compat_id_for_set != pipe_compat_ids[set_idx]) {
push_descriptor_cleanup(last_bound.per_set[set_idx].bound_descriptor_set);
last_bound.per_set[set_idx].bound_descriptor_set = nullptr;
last_bound.per_set[set_idx].dynamicOffsets.clear();
last_bound.per_set[set_idx].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
cvdescriptorset::DescriptorSet *descriptor_set =
push_descriptor_set ? push_descriptor_set : dev_data->GetSetNode(pDescriptorSets[input_idx]);
// Record binding (or push)
if (descriptor_set != last_bound.push_descriptor_set.get()) {
// Only cleanup the push descriptors if they aren't the currently used set.
push_descriptor_cleanup(last_bound.per_set[set_idx].bound_descriptor_set);
}
last_bound.per_set[set_idx].bound_descriptor_set = descriptor_set;
last_bound.per_set[set_idx].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;
last_bound.per_set[set_idx].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 {
last_bound.per_set[set_idx].dynamicOffsets.clear();
}
if (!descriptor_set->IsPushDescriptor()) {
// Can't cache validation of push_descriptors
validated_descriptor_sets.insert(descriptor_set);
}
}
}
}
// 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);
assert(subresource_map); // the non-const getter must return a valid pointer
if (subresource_map->SetSubresourceRangeLayout(*this, image_subresource_range, layout, expected_layout)) {
image_layout_change_count++; // Change the version of this data to force revalidation
}
for (const auto *alias_state : image_state.aliasing_images) {
assert(alias_state);
// The map state of the aliases should all be in sync, so no need to check the return value
subresource_map = GetImageSubresourceLayoutMap(*alias_state);
assert(subresource_map);
subresource_map->SetSubresourceRangeLayout(*this, image_subresource_range, layout, expected_layout);
}
}
// 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);
subresource_map->SetSubresourceRangeInitialLayout(*this, layout, view_state);
for (const auto *alias_state : image_state->aliasing_images) {
assert(alias_state);
subresource_map = GetImageSubresourceLayoutMap(*alias_state);
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);
assert(subresource_map);
subresource_map->SetSubresourceRangeInitialLayout(*this, image_state.NormalizeSubresourceRange(range), layout);
for (const auto *alias_state : image_state.aliasing_images) {
assert(alias_state);
subresource_map = GetImageSubresourceLayoutMap(*alias_state);
assert(subresource_map);
subresource_map->SetSubresourceRangeInitialLayout(*this, alias_state->NormalizeSubresourceRange(range), layout);
}
}
void CMD_BUFFER_STATE::SetImageInitialLayout(VkImage image, const VkImageSubresourceRange &range, VkImageLayout layout) {
const IMAGE_STATE *image_state = dev_data->GetImageState(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(CMD_TYPE cmd_type) { commandCount++; }
void CMD_BUFFER_STATE::RecordStateCmd(CMD_TYPE cmd_type, CBStatusFlags state_bits) {
RecordCmd(cmd_type);
status |= state_bits;
static_status &= ~state_bits;
}
void CMD_BUFFER_STATE::RecordTransferCmd(CMD_TYPE cmd_type, BINDABLE *buf1, BINDABLE *buf2) {
RecordCmd(cmd_type);
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(CMD_TYPE cmd_type, VkEvent event, VkPipelineStageFlags2KHR stageMask) {
RecordCmd(cmd_type);
if (!dev_data->disabled[command_buffer_state]) {
auto event_state = dev_data->GetEventState(event);
if (event_state) {
AddChild(event_state);
}
}
events.push_back(event);
if (!waitedEvents.count(event)) {
writeEventsBeforeWait.push_back(event);
}
eventUpdates.emplace_back(
[event, stageMask](const ValidationStateTracker *device_data, bool do_validate, EventToStageMap *localEventToStageMap) {
return SetEventStageMask(event, stageMask, localEventToStageMap);
});
}
void CMD_BUFFER_STATE::RecordResetEvent(CMD_TYPE cmd_type, VkEvent event, VkPipelineStageFlags2KHR stageMask) {
RecordCmd(cmd_type);
if (!dev_data->disabled[command_buffer_state]) {
auto event_state = dev_data->GetEventState(event);
if (event_state) {
AddChild(event_state);
}
}
events.push_back(event);
if (!waitedEvents.count(event)) {
writeEventsBeforeWait.push_back(event);
}
eventUpdates.emplace_back([event](const ValidationStateTracker *, bool do_validate, EventToStageMap *localEventToStageMap) {
return SetEventStageMask(event, VkPipelineStageFlags2KHR(0), localEventToStageMap);
});
}
void CMD_BUFFER_STATE::RecordWaitEvents(CMD_TYPE cmd_type, uint32_t eventCount, const VkEvent *pEvents) {
RecordCmd(cmd_type);
for (uint32_t i = 0; i < eventCount; ++i) {
if (!dev_data->disabled[command_buffer_state]) {
auto event_state = dev_data->GetEventState(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->GetBufferState(pBufferMemoryBarriers[i].buffer);
if (buffer_state) {
AddChild(buffer_state);
}
}
for (uint32_t i = 0; i < imageMemoryBarrierCount; i++) {
auto image_state = dev_data->GetImageState(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->GetBufferState(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->GetImageState(dep_info.pImageMemoryBarriers[i].image);
if (image_state) {
AddChild(image_state);
}
}
}
void CMD_BUFFER_STATE::RecordWriteTimestamp(CMD_TYPE cmd_type, VkPipelineStageFlags2KHR pipelineStage, VkQueryPool queryPool,
uint32_t slot) {
RecordCmd(cmd_type);
if (dev_data->disabled[query_validation]) return;
if (!dev_data->disabled[command_buffer_state]) {
auto pool_state = dev_data->GetQueryPoolState(queryPool);
AddChild(pool_state);
}
QueryObject query = {queryPool, slot};
EndQuery(query);
}
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(nullptr, /*do_validate*/ false, first_pool, perf_submit_pass, &local_query_to_state_map);
}
for (const auto &query_state_pair : local_query_to_state_map) {
dev_data->queryToStateMap[query_state_pair.first] = query_state_pair.second;
}
for (const auto &function : eventUpdates) {
function(nullptr, /*do_validate*/ false, &local_event_to_stage_map);
}
for (const auto &eventStagePair : local_event_to_stage_map) {
dev_data->Get<EVENT_STATE>(eventStagePair.first)->stageMask = eventStagePair.second;
}
}
void CMD_BUFFER_STATE::Retire(uint32_t perf_submit_pass) {
// 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(nullptr, /*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) {
dev_data->queryToStateMap[query_state_pair.first] = QUERYSTATE_AVAILABLE;
}
}
}