diff --git a/layers/buffer_validation.cpp b/layers/buffer_validation.cpp index 1d1b9038cb..cdc5ae3945 100644 --- a/layers/buffer_validation.cpp +++ b/layers/buffer_validation.cpp @@ -635,6 +635,219 @@ void TransitionFinalSubpassLayouts(layer_data *device_data, GLOBAL_CB_NODE *pCB, } } +// START: Memory Access Validation Functions +void AddMemoryAccess(CMD_TYPE cmd, std::vector *mem_accesses, MemoryAccess *mem_access, bool write, + uint32_t rw_index) { + mem_access->write = write; + mem_access->src_stage_flags = CommandToFlags[cmd][rw_index].stage_flags; + mem_access->src_access_flags = CommandToFlags[cmd][rw_index].access_flags; + mem_accesses->emplace_back(*mem_access); +} + +void AddReadMemoryAccess(CMD_TYPE cmd, std::vector *mem_accesses, MEM_BINDING const &binding, bool precise) { + MemoryAccess mem_access(binding.mem, binding.offset, binding.size, precise); + AddMemoryAccess(cmd, mem_accesses, &mem_access, false, 0); +} + +void AddWriteMemoryAccess(CMD_TYPE cmd, std::vector *mem_accesses, MEM_BINDING const &binding, bool precise) { + MemoryAccess mem_access(binding.mem, binding.offset, binding.size, precise); + AddMemoryAccess(cmd, mem_accesses, &mem_access, true, 1); +} + +// The first arg is an existing memory access that hasn't been verified by a synch object +// Check if the 2nd access conflicts with the first and return true if there's a conflict +// Pre: Both accesses must occur on the same VkDeviceMemory object +bool MemoryConflict(MemoryAccess const *initial_access, MemoryAccess const *second_access) { + assert(initial_access->location.mem == second_access->location.mem); + // read/read is ok + // TODO: What to do for write/write? Warn? Nothing for now. Just allow RaR & WaW cases. + if (initial_access->write == second_access->write) return false; + // RaW & WaR can present conflicts + // If the second access is outside of the range of initial access, then no conflict + if ((initial_access->location.size != VK_WHOLE_SIZE && second_access->location.size != VK_WHOLE_SIZE) && + ((second_access->location.offset + second_access->location.size) <= initial_access->location.offset) && + (second_access->location.offset >= (initial_access->location.offset + initial_access->location.size))) { + return false; + } + // Without appropriate barrier & appropriate scope overlap we'll have a conflict + if (initial_access->mem_barrier) { + // If the initial dst masks match second access src masks, scopes are good + if ((0 != (initial_access->dst_stage_flags & second_access->src_stage_flags)) && + (0 != (initial_access->dst_access_flags & second_access->src_access_flags))) { + return false; + } + } else if (initial_access->pipe_barrier && second_access->write) { + // Just need an execution dep here so check pipe masks + if (0 != (initial_access->dst_stage_flags & second_access->src_stage_flags)) { + return false; + } + } + // there's some amount of overlap in the accesses so flag conflict + return true; +} + +bool FlagMemoryAccessError(debug_report_data const *report_data, VkCommandBuffer command_buffer, const MemoryAccess *first_access, + const MemoryAccess *second_access, const char *caller) { +#ifndef ENABLE_MEMORY_ACCESS_CALLBACK + // TODO: Temporarily disabling this callback until memory access code more thoroughly tested + // Remove this early return to enable the callback + return false; +#endif + const char *first_type = first_access->write ? "write" : "read"; + const char *second_type = second_access->write ? "write" : "read"; + std::string inter_cb_info = ""; + if (first_access->cmd && second_access->cmd && (first_access->cmd->cb_state != second_access->cmd->cb_state)) { + // This is inter-CB conflict so give a bit more error detail + inter_cb_info = " where the initial access occurred in cmd buffer " + + std::to_string(HandleToUint64(first_access->cmd->cb_state->commandBuffer)); + } + // If either access is imprecise can only warn, otherwise can error + auto level = VK_DEBUG_REPORT_WARNING_BIT_EXT; + // TODO: Un-comment the line below when we're confident in coverage of memory access checks + // so that precise/precise conflicts can be flagged as errors. Keeping everything a warning + // for early release and so that incorrect warnings can be flagged/fixed without causing errors. + // if (earlier_access.precise && mem_access.precise) level = VK_DEBUG_REPORT_ERROR_BIT_EXT; + const auto mem_obj = first_access->location.mem; + return log_msg(report_data, level, VK_DEBUG_REPORT_OBJECT_TYPE_DEVICE_MEMORY_EXT, HandleToUint64(mem_obj), 0, + MEMTRACK_SYNCHRONIZATION_ERROR, "DS", + "%s called on VkCommandBuffer 0x%" PRIxLEAST64 + " causes a %s after %s conflict with memory object 0x%" PRIxLEAST64 + "%s." + " NOTE: This" + " race condition warning is a new feature in validation that still has some holes" + " in tracking Read/Write accesses as well as modeling synchronization objects. Don't" + " spend too much time investigating this warning, and feel free to file GitHub bugs " + " on any warning cases that you known are incorrect.", + caller, HandleToUint64(command_buffer), second_type, first_type, HandleToUint64(mem_obj), inter_cb_info.c_str()); +} + +bool ValidateMemoryAccess(debug_report_data const *report_data, VkCommandBuffer command_buffer, + const std::unordered_map> &prev_mem_access_map, + const MemoryAccess *new_access, const char *prev_type, const char *new_type, const char *caller) { + bool skip = false; + if ((nullptr == new_access) || (prev_mem_access_map.empty())) return skip; + // Check if new_access conflicts with any prev accesses and issue RaW or WaR warning as appropriate + auto mem_obj = new_access->location.mem; + auto mem_access_pair = prev_mem_access_map.find(mem_obj); + if (mem_access_pair != prev_mem_access_map.end()) { + for (auto &earlier_access : mem_access_pair->second) { + if (MemoryConflict(&earlier_access, new_access)) { + skip |= FlagMemoryAccessError(report_data, command_buffer, &earlier_access, new_access, caller); + } + } + } + return skip; +} + +// Helper functions to pass correct "read" "write" strings into validation function above +bool ValidateReadMemoryAccess(debug_report_data const *report_data, VkCommandBuffer command_buffer, + const MemAccessGroup &prev_mem_accesses, const MemoryAccess *read_access, const char *caller) { + return ValidateMemoryAccess(report_data, command_buffer, prev_mem_accesses.access_maps[WRITE_INDEX], read_access, "write", + "read", caller); +} + +bool ValidateWriteMemoryAccess(debug_report_data const *report_data, VkCommandBuffer command_buffer, + const MemAccessGroup &prev_mem_accesses, const MemoryAccess *write_access, const char *caller) { + return ValidateMemoryAccess(report_data, command_buffer, prev_mem_accesses.access_maps[READ_INDEX], write_access, "read", + "write", caller); +} + +// Create read memory access for given binding and add to mem_accesses +bool CreateAndValidateReadMemoryAccess(debug_report_data const *report_data, CMD_TYPE cmd, VkCommandBuffer command_buffer, + const MemAccessGroup &prev_mem_accesses, std::vector *mem_accesses, + MEM_BINDING const &binding, bool precise, const char *caller) { + AddReadMemoryAccess(cmd, mem_accesses, binding, precise); + return ValidateReadMemoryAccess(report_data, command_buffer, prev_mem_accesses, &mem_accesses->back(), caller); +} + +// Create write memory access for given binding, add to mem_accesses vector, and validate against previous accesses +bool CreateAndValidateWriteMemoryAccess(debug_report_data const *report_data, CMD_TYPE cmd, VkCommandBuffer command_buffer, + const MemAccessGroup &prev_mem_accesses, std::vector *mem_accesses, + MEM_BINDING const &binding, bool precise, const char *caller) { + AddWriteMemoryAccess(cmd, mem_accesses, binding, precise); + return ValidateWriteMemoryAccess(report_data, command_buffer, prev_mem_accesses, &mem_accesses->back(), caller); +} + +bool ValidateMemoryAccesses(debug_report_data const *report_data, VkCommandBuffer command_buffer, + std::unordered_map> &prev_mem_access_map, + std::vector *new_accesses, const char *caller, bool pre_check, + MemoryAccess **early_conflict, MemoryAccess **late_conflict) { + bool skip = false; + if (nullptr == new_accesses) return skip; + // For each of the new accesses, check if it conflicts with any prev accesses and issue RaW or WaR warning as appropriate + for (auto &mem_access : *new_accesses) { + auto mem_obj = mem_access.location.mem; + auto mem_access_pair = prev_mem_access_map.find(mem_obj); + if (mem_access_pair != prev_mem_access_map.end()) { + for (auto &earlier_access : mem_access_pair->second) { + if (MemoryConflict(&earlier_access, &mem_access)) { + if (pre_check) { + *early_conflict = &earlier_access; + *late_conflict = &mem_access; + return true; + } + skip |= FlagMemoryAccessError(report_data, command_buffer, &earlier_access, &mem_access, caller); + } + } + } + } + return skip; +} + +// Helper functions to pass correct read or write map into validation function above +bool ValidateReadMemoryAccesses(debug_report_data const *report_data, VkCommandBuffer command_buffer, + MemAccessGroup &prev_mem_accesses, std::vector *read_accesses, const char *caller, + bool pre_check, MemoryAccess **early_conflict, MemoryAccess **late_conflict) { + return ValidateMemoryAccesses(report_data, command_buffer, prev_mem_accesses.access_maps[WRITE_INDEX], read_accesses, caller, + pre_check, early_conflict, late_conflict); +} + +bool ValidateWriteMemoryAccesses(debug_report_data const *report_data, VkCommandBuffer command_buffer, + MemAccessGroup &prev_mem_accesses, std::vector *write_accesses, const char *caller, + bool pre_check, MemoryAccess **early_conflict, MemoryAccess **late_conflict) { + return ValidateMemoryAccesses(report_data, command_buffer, prev_mem_accesses.access_maps[READ_INDEX], write_accesses, caller, + pre_check, early_conflict, late_conflict); +} + +// Check given vector of new_mem_accesses against prev_mem_accesses map, which has live R/W access up to the point. +// The "live" R/W accesses in prev_mem_access_map means the accesses have no barrier has been identified. +// For any new accesses that conflict with prev accesses, flag an error +// If pre_check is true no callback will be issued, instead on any conflict set early & late conflict ptrs and return "true" +bool ValidateRWMemoryAccesses(debug_report_data const *report_data, VkCommandBuffer command_buffer, + MemAccessGroup &prev_mem_accesses, std::vector *read_accesses, + std::vector *write_accesses, const char *caller, bool pre_check, + MemoryAccess **early_conflict, MemoryAccess **late_conflict) { + bool skip = false; + // early out if there's nothing to compare + if ((prev_mem_accesses.access_maps[READ_INDEX].empty() || nullptr == write_accesses || write_accesses->empty()) && + (prev_mem_accesses.access_maps[WRITE_INDEX].empty() || nullptr == read_accesses || read_accesses->empty())) { + return skip; + } + // Avoid warnings on return params w/ default values + (void)early_conflict; + (void)late_conflict; + + // Validate new reads against prev writes and vice-versa + skip |= ValidateReadMemoryAccesses(report_data, command_buffer, prev_mem_accesses, read_accesses, caller, pre_check, + early_conflict, late_conflict); + skip |= ValidateWriteMemoryAccesses(report_data, command_buffer, prev_mem_accesses, write_accesses, caller, pre_check, + early_conflict, late_conflict); + return skip; +} + +// Add the given cmd and its mem_accesses to the command buffer +void AddCommandBufferCommandMemoryAccesses(GLOBAL_CB_NODE *cb_state, CMD_TYPE cmd, std::vector *mem_accesses) { + cb_state->commands.emplace_back(std::unique_ptr(new Command(cmd, cb_state->commands.size(), cb_state))); + Command *cmd_ptr = cb_state->commands.back().get(); + for (auto &mem_access : *mem_accesses) { + mem_access.cmd = cmd_ptr; + cmd_ptr->AddMemoryAccess(mem_access); + auto &access_map = + mem_access.write ? cb_state->mem_accesses.access_maps[WRITE_INDEX] : cb_state->mem_accesses.access_maps[READ_INDEX]; + access_map[mem_access.location.mem].push_back(mem_access); + } +} + bool PreCallValidateCreateImage(layer_data *device_data, const VkImageCreateInfo *pCreateInfo, const VkAllocationCallbacks *pAllocator, VkImage *pImage) { bool skip = false; @@ -997,65 +1210,72 @@ void RecordClearImageLayout(layer_data *device_data, GLOBAL_CB_NODE *cb_node, Vk } bool PreCallValidateCmdClearColorImage(layer_data *dev_data, VkCommandBuffer commandBuffer, VkImage image, - VkImageLayout imageLayout, uint32_t rangeCount, const VkImageSubresourceRange *pRanges) { + VkImageLayout imageLayout, uint32_t rangeCount, const VkImageSubresourceRange *pRanges, + std::vector *mem_accesses) { bool skip = false; // TODO : Verify memory is in VK_IMAGE_STATE_CLEAR state - auto cb_node = GetCBNode(dev_data, commandBuffer); + auto cb_state = GetCBNode(dev_data, commandBuffer); auto image_state = GetImageState(dev_data, image); - if (cb_node && image_state) { + if (cb_state && image_state) { skip |= ValidateMemoryIsBoundToImage(dev_data, image_state, "vkCmdClearColorImage()", VALIDATION_ERROR_18800006); - skip |= ValidateCmdQueueFlags(dev_data, cb_node, "vkCmdClearColorImage()", VK_QUEUE_GRAPHICS_BIT | VK_QUEUE_COMPUTE_BIT, + skip |= ValidateCmdQueueFlags(dev_data, cb_state, "vkCmdClearColorImage()", VK_QUEUE_GRAPHICS_BIT | VK_QUEUE_COMPUTE_BIT, VALIDATION_ERROR_18802415); - skip |= ValidateCmd(dev_data, cb_node, CMD_CLEARCOLORIMAGE, "vkCmdClearColorImage()"); - skip |= insideRenderPass(dev_data, cb_node, "vkCmdClearColorImage()", VALIDATION_ERROR_18800017); + skip |= ValidateCmd(dev_data, cb_state, CMD_CLEARCOLORIMAGE, "vkCmdClearColorImage()"); + skip |= insideRenderPass(dev_data, cb_state, "vkCmdClearColorImage()", VALIDATION_ERROR_18800017); for (uint32_t i = 0; i < rangeCount; ++i) { std::string param_name = "pRanges[" + std::to_string(i) + "]"; skip |= ValidateCmdClearColorSubresourceRange(dev_data, image_state, pRanges[i], param_name.c_str()); skip |= ValidateImageAttributes(dev_data, image_state, pRanges[i]); - skip |= VerifyClearImageLayout(dev_data, cb_node, image_state, pRanges[i], imageLayout, "vkCmdClearColorImage()"); + skip |= VerifyClearImageLayout(dev_data, cb_state, image_state, pRanges[i], imageLayout, "vkCmdClearColorImage()"); } } + // TODO: Currently treating all image accesses as imprecise & covering the whole image area, this should be per-range + skip |= CreateAndValidateWriteMemoryAccess(core_validation::GetReportData(dev_data), CMD_CLEARCOLORIMAGE, commandBuffer, + cb_state->mem_accesses, mem_accesses, image_state->binding, false, + "vkCmdClearColorImage()"); return skip; } // This state recording routine is shared between ClearColorImage and ClearDepthStencilImage void PreCallRecordCmdClearImage(layer_data *dev_data, VkCommandBuffer commandBuffer, VkImage image, VkImageLayout imageLayout, - uint32_t rangeCount, const VkImageSubresourceRange *pRanges) { - auto cb_node = GetCBNode(dev_data, commandBuffer); + uint32_t rangeCount, const VkImageSubresourceRange *pRanges, CMD_TYPE cmd, + std::vector *mem_accesses) { + auto cb_state = GetCBNode(dev_data, commandBuffer); auto image_state = GetImageState(dev_data, image); - if (cb_node && image_state) { - AddCommandBufferBindingImage(dev_data, cb_node, image_state); + if (cb_state && image_state) { + AddCommandBufferBindingImage(dev_data, cb_state, image_state); std::function function = [=]() { SetImageMemoryValid(dev_data, image_state, true); return false; }; - cb_node->queue_submit_functions.push_back(function); + cb_state->queue_submit_functions.push_back(function); for (uint32_t i = 0; i < rangeCount; ++i) { - RecordClearImageLayout(dev_data, cb_node, image, pRanges[i], imageLayout); + RecordClearImageLayout(dev_data, cb_state, image, pRanges[i], imageLayout); } } + AddCommandBufferCommandMemoryAccesses(cb_state, cmd, mem_accesses); } bool PreCallValidateCmdClearDepthStencilImage(layer_data *device_data, VkCommandBuffer commandBuffer, VkImage image, VkImageLayout imageLayout, uint32_t rangeCount, - const VkImageSubresourceRange *pRanges) { + const VkImageSubresourceRange *pRanges, std::vector *mem_accesses) { bool skip = false; const debug_report_data *report_data = core_validation::GetReportData(device_data); // TODO : Verify memory is in VK_IMAGE_STATE_CLEAR state - auto cb_node = GetCBNode(device_data, commandBuffer); + auto cb_state = GetCBNode(device_data, commandBuffer); auto image_state = GetImageState(device_data, image); - if (cb_node && image_state) { + if (cb_state && image_state) { skip |= ValidateMemoryIsBoundToImage(device_data, image_state, "vkCmdClearDepthStencilImage()", VALIDATION_ERROR_18a00014); - skip |= ValidateCmdQueueFlags(device_data, cb_node, "vkCmdClearDepthStencilImage()", VK_QUEUE_GRAPHICS_BIT, + skip |= ValidateCmdQueueFlags(device_data, cb_state, "vkCmdClearDepthStencilImage()", VK_QUEUE_GRAPHICS_BIT, VALIDATION_ERROR_18a02415); - skip |= ValidateCmd(device_data, cb_node, CMD_CLEARDEPTHSTENCILIMAGE, "vkCmdClearDepthStencilImage()"); - skip |= insideRenderPass(device_data, cb_node, "vkCmdClearDepthStencilImage()", VALIDATION_ERROR_18a00017); + skip |= ValidateCmd(device_data, cb_state, CMD_CLEARDEPTHSTENCILIMAGE, "vkCmdClearDepthStencilImage()"); + skip |= insideRenderPass(device_data, cb_state, "vkCmdClearDepthStencilImage()", VALIDATION_ERROR_18a00017); for (uint32_t i = 0; i < rangeCount; ++i) { std::string param_name = "pRanges[" + std::to_string(i) + "]"; skip |= ValidateCmdClearDepthSubresourceRange(device_data, image_state, pRanges[i], param_name.c_str()); - skip |= - VerifyClearImageLayout(device_data, cb_node, image_state, pRanges[i], imageLayout, "vkCmdClearDepthStencilImage()"); + skip |= VerifyClearImageLayout(device_data, cb_state, image_state, pRanges[i], imageLayout, + "vkCmdClearDepthStencilImage()"); // Image aspect must be depth or stencil or both if (((pRanges[i].aspectMask & VK_IMAGE_ASPECT_DEPTH_BIT) != VK_IMAGE_ASPECT_DEPTH_BIT) && ((pRanges[i].aspectMask & VK_IMAGE_ASPECT_STENCIL_BIT) != VK_IMAGE_ASPECT_STENCIL_BIT)) { @@ -1073,6 +1293,9 @@ bool PreCallValidateCmdClearDepthStencilImage(layer_data *device_data, VkCommand validation_error_map[VALIDATION_ERROR_18a0001c]); } } + // TODO: Currently treating all image accesses as imprecise & covering the whole image area, this should be per-range + skip |= CreateAndValidateWriteMemoryAccess(report_data, CMD_CLEARDEPTHSTENCILIMAGE, commandBuffer, cb_state->mem_accesses, + mem_accesses, image_state->binding, false, "vkCmdClearDepthStencilImage()"); return skip; } @@ -1637,14 +1860,15 @@ bool ValidateImageCopyData(const layer_data *device_data, const debug_report_dat return skip; } -bool PreCallValidateCmdCopyImage(layer_data *device_data, GLOBAL_CB_NODE *cb_node, IMAGE_STATE *src_image_state, +bool PreCallValidateCmdCopyImage(layer_data *device_data, GLOBAL_CB_NODE *cb_state, IMAGE_STATE *src_image_state, IMAGE_STATE *dst_image_state, uint32_t region_count, const VkImageCopy *regions, - VkImageLayout src_image_layout, VkImageLayout dst_image_layout) { + VkImageLayout src_image_layout, VkImageLayout dst_image_layout, + std::vector *mem_accesses) { bool skip = false; const debug_report_data *report_data = core_validation::GetReportData(device_data); skip = ValidateImageCopyData(device_data, report_data, region_count, regions, src_image_state, dst_image_state); - VkCommandBuffer command_buffer = cb_node->commandBuffer; + VkCommandBuffer command_buffer = cb_state->commandBuffer; for (uint32_t i = 0; i < region_count; i++) { bool slice_override = false; @@ -1900,6 +2124,12 @@ bool PreCallValidateCmdCopyImage(layer_data *device_data, GLOBAL_CB_NODE *cb_nod } } } + // Add memory access for this image copy + // TODO: Currently treating all image accesses as imprecise & covering the whole image area + skip |= CreateAndValidateReadMemoryAccess(report_data, CMD_COPYIMAGE, cb_state->commandBuffer, cb_state->mem_accesses, + mem_accesses, src_image_state->binding, false, "vkCmdCopyImage()"); + skip |= CreateAndValidateWriteMemoryAccess(report_data, CMD_COPYIMAGE, cb_state->commandBuffer, cb_state->mem_accesses, + mem_accesses, dst_image_state->binding, false, "vkCmdCopyImage()"); } // The formats of src_image and dst_image must be compatible. Formats are considered compatible if their texel size in bytes @@ -1937,41 +2167,43 @@ bool PreCallValidateCmdCopyImage(layer_data *device_data, GLOBAL_CB_NODE *cb_nod "vkCmdCopyImage()", "VK_IMAGE_USAGE_TRANSFER_SRC_BIT"); skip |= ValidateImageUsageFlags(device_data, dst_image_state, VK_IMAGE_USAGE_TRANSFER_DST_BIT, true, VALIDATION_ERROR_19000106, "vkCmdCopyImage()", "VK_IMAGE_USAGE_TRANSFER_DST_BIT"); - skip |= ValidateCmdQueueFlags(device_data, cb_node, "vkCmdCopyImage()", + skip |= ValidateCmdQueueFlags(device_data, cb_state, "vkCmdCopyImage()", VK_QUEUE_TRANSFER_BIT | VK_QUEUE_GRAPHICS_BIT | VK_QUEUE_COMPUTE_BIT, VALIDATION_ERROR_19002415); - skip |= ValidateCmd(device_data, cb_node, CMD_COPYIMAGE, "vkCmdCopyImage()"); - skip |= insideRenderPass(device_data, cb_node, "vkCmdCopyImage()", VALIDATION_ERROR_19000017); + skip |= ValidateCmd(device_data, cb_state, CMD_COPYIMAGE, "vkCmdCopyImage()"); + skip |= insideRenderPass(device_data, cb_state, "vkCmdCopyImage()", VALIDATION_ERROR_19000017); bool hit_error = false; for (uint32_t i = 0; i < region_count; ++i) { - skip |= VerifyImageLayout(device_data, cb_node, src_image_state, regions[i].srcSubresource, src_image_layout, + skip |= VerifyImageLayout(device_data, cb_state, src_image_state, regions[i].srcSubresource, src_image_layout, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, "vkCmdCopyImage()", VALIDATION_ERROR_19000102, &hit_error); - skip |= VerifyImageLayout(device_data, cb_node, dst_image_state, regions[i].dstSubresource, dst_image_layout, + skip |= VerifyImageLayout(device_data, cb_state, dst_image_state, regions[i].dstSubresource, dst_image_layout, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, "vkCmdCopyImage()", VALIDATION_ERROR_1900010c, &hit_error); - skip |= ValidateCopyImageTransferGranularityRequirements(device_data, cb_node, src_image_state, dst_image_state, + skip |= ValidateCopyImageTransferGranularityRequirements(device_data, cb_state, src_image_state, dst_image_state, ®ions[i], i, "vkCmdCopyImage()"); } return skip; } -void PreCallRecordCmdCopyImage(layer_data *device_data, GLOBAL_CB_NODE *cb_node, IMAGE_STATE *src_image_state, +void PreCallRecordCmdCopyImage(layer_data *device_data, GLOBAL_CB_NODE *cb_state, IMAGE_STATE *src_image_state, IMAGE_STATE *dst_image_state, uint32_t region_count, const VkImageCopy *regions, - VkImageLayout src_image_layout, VkImageLayout dst_image_layout) { + VkImageLayout src_image_layout, VkImageLayout dst_image_layout, + std::vector *mem_accesses) { // Make sure that all image slices are updated to correct layout for (uint32_t i = 0; i < region_count; ++i) { - SetImageLayout(device_data, cb_node, src_image_state, regions[i].srcSubresource, src_image_layout); - SetImageLayout(device_data, cb_node, dst_image_state, regions[i].dstSubresource, dst_image_layout); + SetImageLayout(device_data, cb_state, src_image_state, regions[i].srcSubresource, src_image_layout); + SetImageLayout(device_data, cb_state, dst_image_state, regions[i].dstSubresource, dst_image_layout); } // Update bindings between images and cmd buffer - AddCommandBufferBindingImage(device_data, cb_node, src_image_state); - AddCommandBufferBindingImage(device_data, cb_node, dst_image_state); + AddCommandBufferBindingImage(device_data, cb_state, src_image_state); + AddCommandBufferBindingImage(device_data, cb_state, dst_image_state); std::function function = [=]() { return ValidateImageMemoryIsValid(device_data, src_image_state, "vkCmdCopyImage()"); }; - cb_node->queue_submit_functions.push_back(function); + cb_state->queue_submit_functions.push_back(function); function = [=]() { SetImageMemoryValid(device_data, dst_image_state, true); return false; }; - cb_node->queue_submit_functions.push_back(function); + cb_state->queue_submit_functions.push_back(function); + AddCommandBufferCommandMemoryAccesses(cb_state, CMD_COPYIMAGE, mem_accesses); } // Returns true if sub_rect is entirely contained within rect @@ -1982,37 +2214,35 @@ static inline bool ContainsRect(VkRect2D rect, VkRect2D sub_rect) { return true; } -bool PreCallValidateCmdClearAttachments(layer_data *device_data, VkCommandBuffer commandBuffer, uint32_t attachmentCount, - const VkClearAttachment *pAttachments, uint32_t rectCount, const VkClearRect *pRects) { - GLOBAL_CB_NODE *cb_node = GetCBNode(device_data, commandBuffer); +bool PreCallValidateCmdClearAttachments(layer_data *device_data, GLOBAL_CB_NODE *cb_state, uint32_t attachmentCount, + const VkClearAttachment *pAttachments, uint32_t rectCount, const VkClearRect *pRects, + std::vector *mem_accesses) { const debug_report_data *report_data = core_validation::GetReportData(device_data); - + const auto commandBuffer = cb_state->commandBuffer; bool skip = false; - if (cb_node) { - skip |= ValidateCmdQueueFlags(device_data, cb_node, "vkCmdClearAttachments()", VK_QUEUE_GRAPHICS_BIT, - VALIDATION_ERROR_18602415); - skip |= ValidateCmd(device_data, cb_node, CMD_CLEARATTACHMENTS, "vkCmdClearAttachments()"); - // Warn if this is issued prior to Draw Cmd and clearing the entire attachment - if (!cb_node->hasDrawCmd && (cb_node->activeRenderPassBeginInfo.renderArea.extent.width == pRects[0].rect.extent.width) && - (cb_node->activeRenderPassBeginInfo.renderArea.extent.height == pRects[0].rect.extent.height)) { - // There are times where app needs to use ClearAttachments (generally when reusing a buffer inside of a render pass) - // This warning should be made more specific. It'd be best to avoid triggering this test if it's a use that must call - // CmdClearAttachments. - skip |= - log_msg(report_data, VK_DEBUG_REPORT_PERFORMANCE_WARNING_BIT_EXT, VK_DEBUG_REPORT_OBJECT_TYPE_COMMAND_BUFFER_EXT, + + skip |= + ValidateCmdQueueFlags(device_data, cb_state, "vkCmdClearAttachments()", VK_QUEUE_GRAPHICS_BIT, VALIDATION_ERROR_18602415); + skip |= ValidateCmd(device_data, cb_state, CMD_CLEARATTACHMENTS, "vkCmdClearAttachments()"); + // Warn if this is issued prior to Draw Cmd and clearing the entire attachment + if (!cb_state->hasDrawCmd && (cb_state->activeRenderPassBeginInfo.renderArea.extent.width == pRects[0].rect.extent.width) && + (cb_state->activeRenderPassBeginInfo.renderArea.extent.height == pRects[0].rect.extent.height)) { + // There are times where app needs to use ClearAttachments (generally when reusing a buffer inside of a render pass) + // This warning should be made more specific. It'd be best to avoid triggering this test if it's a use that must call + // CmdClearAttachments. + skip |= log_msg(report_data, VK_DEBUG_REPORT_PERFORMANCE_WARNING_BIT_EXT, VK_DEBUG_REPORT_OBJECT_TYPE_COMMAND_BUFFER_EXT, HandleToUint64(commandBuffer), 0, DRAWSTATE_CLEAR_CMD_BEFORE_DRAW, "DS", "vkCmdClearAttachments() issued on command buffer object 0x%p prior to any Draw Cmds." " It is recommended you use RenderPass LOAD_OP_CLEAR on Attachments prior to any Draw.", commandBuffer); - } - skip |= outsideRenderPass(device_data, cb_node, "vkCmdClearAttachments()", VALIDATION_ERROR_18600017); } + skip |= outsideRenderPass(device_data, cb_state, "vkCmdClearAttachments()", VALIDATION_ERROR_18600017); // Validate that attachment is in reference list of active subpass - if (cb_node->activeRenderPass) { - const VkRenderPassCreateInfo *renderpass_create_info = cb_node->activeRenderPass->createInfo.ptr(); - const VkSubpassDescription *subpass_desc = &renderpass_create_info->pSubpasses[cb_node->activeSubpass]; - auto framebuffer = GetFramebufferState(device_data, cb_node->activeFramebuffer); + if (cb_state->activeRenderPass) { + const VkRenderPassCreateInfo *renderpass_create_info = cb_state->activeRenderPass->createInfo.ptr(); + const VkSubpassDescription *subpass_desc = &renderpass_create_info->pSubpasses[cb_state->activeSubpass]; + auto framebuffer = GetFramebufferState(device_data, cb_state->activeFramebuffer); for (uint32_t i = 0; i < attachmentCount; i++) { auto clear_desc = &pAttachments[i]; @@ -2031,7 +2261,7 @@ bool PreCallValidateCmdClearAttachments(layer_data *device_data, VkCommandBuffer skip |= log_msg(report_data, VK_DEBUG_REPORT_ERROR_BIT_EXT, VK_DEBUG_REPORT_OBJECT_TYPE_COMMAND_BUFFER_EXT, HandleToUint64(commandBuffer), __LINE__, VALIDATION_ERROR_1860001e, "DS", "vkCmdClearAttachments() color attachment index %d out of range for active subpass %d. %s", - clear_desc->colorAttachment, cb_node->activeSubpass, + clear_desc->colorAttachment, cb_state->activeSubpass, validation_error_map[VALIDATION_ERROR_1860001e]); } else if (subpass_desc->pColorAttachments[clear_desc->colorAttachment].attachment == VK_ATTACHMENT_UNUSED) { skip |= log_msg(report_data, VK_DEBUG_REPORT_PERFORMANCE_WARNING_BIT_EXT, @@ -2070,13 +2300,13 @@ bool PreCallValidateCmdClearAttachments(layer_data *device_data, VkCommandBuffer } } if (image_view) { - auto image_view_state = GetImageViewState(device_data, image_view); + const auto image_view_state = GetImageViewState(device_data, image_view); for (uint32_t j = 0; j < rectCount; j++) { // The rectangular region specified by a given element of pRects must be contained within the render area of // the current render pass instance // TODO: This check should be moved to CmdExecuteCommands or QueueSubmit to cover secondary CB cases - if ((cb_node->createInfo.level == VK_COMMAND_BUFFER_LEVEL_PRIMARY) && - (false == ContainsRect(cb_node->activeRenderPassBeginInfo.renderArea, pRects[j].rect))) { + if ((cb_state->createInfo.level == VK_COMMAND_BUFFER_LEVEL_PRIMARY) && + (false == ContainsRect(cb_state->activeRenderPassBeginInfo.renderArea, pRects[j].rect))) { skip |= log_msg(report_data, VK_DEBUG_REPORT_ERROR_BIT_EXT, VK_DEBUG_REPORT_OBJECT_TYPE_COMMAND_BUFFER_EXT, HandleToUint64(commandBuffer), __LINE__, VALIDATION_ERROR_18600020, "DS", "vkCmdClearAttachments(): The area defined by pRects[%d] is not contained in the area of " @@ -2096,23 +2326,33 @@ bool PreCallValidateCmdClearAttachments(layer_data *device_data, VkCommandBuffer j, i, validation_error_map[VALIDATION_ERROR_18600022]); } } + // TODO: Just marking memory accesses as imprecise per-image binding basis for now, should be per-rect above + const auto image_state = GetImageState(device_data, image_view_state->create_info.image); + skip |= CreateAndValidateWriteMemoryAccess(report_data, CMD_CLEARATTACHMENTS, commandBuffer, cb_state->mem_accesses, + mem_accesses, image_state->binding, false, "vkCmdClearAttachments()"); } } } return skip; } -bool PreCallValidateCmdResolveImage(layer_data *device_data, GLOBAL_CB_NODE *cb_node, IMAGE_STATE *src_image_state, - IMAGE_STATE *dst_image_state, uint32_t regionCount, const VkImageResolve *pRegions) { +void PreCallRecordCmdClearAttachments(debug_report_data const *report_data, GLOBAL_CB_NODE *cb_state, + std::vector *mem_accesses) { + AddCommandBufferCommandMemoryAccesses(cb_state, CMD_CLEARATTACHMENTS, mem_accesses); +} + +bool PreCallValidateCmdResolveImage(layer_data *device_data, GLOBAL_CB_NODE *cb_state, IMAGE_STATE *src_image_state, + IMAGE_STATE *dst_image_state, uint32_t regionCount, const VkImageResolve *pRegions, + std::vector *mem_accesses) { const debug_report_data *report_data = core_validation::GetReportData(device_data); bool skip = false; - if (cb_node && src_image_state && dst_image_state) { + if (cb_state && src_image_state && dst_image_state) { skip |= ValidateMemoryIsBoundToImage(device_data, src_image_state, "vkCmdResolveImage()", VALIDATION_ERROR_1c800200); skip |= ValidateMemoryIsBoundToImage(device_data, dst_image_state, "vkCmdResolveImage()", VALIDATION_ERROR_1c800204); skip |= - ValidateCmdQueueFlags(device_data, cb_node, "vkCmdResolveImage()", VK_QUEUE_GRAPHICS_BIT, VALIDATION_ERROR_1c802415); - skip |= ValidateCmd(device_data, cb_node, CMD_RESOLVEIMAGE, "vkCmdResolveImage()"); - skip |= insideRenderPass(device_data, cb_node, "vkCmdResolveImage()", VALIDATION_ERROR_1c800017); + ValidateCmdQueueFlags(device_data, cb_state, "vkCmdResolveImage()", VK_QUEUE_GRAPHICS_BIT, VALIDATION_ERROR_1c802415); + skip |= ValidateCmd(device_data, cb_state, CMD_RESOLVEIMAGE, "vkCmdResolveImage()"); + skip |= insideRenderPass(device_data, cb_state, "vkCmdResolveImage()", VALIDATION_ERROR_1c800017); // For each region, the number of layers in the image subresource should not be zero // For each region, src and dest image aspect must be color only @@ -2120,17 +2360,17 @@ bool PreCallValidateCmdResolveImage(layer_data *device_data, GLOBAL_CB_NODE *cb_ if (pRegions[i].srcSubresource.layerCount == 0) { char const str[] = "vkCmdResolveImage: number of layers in source subresource is zero"; skip |= log_msg(report_data, VK_DEBUG_REPORT_WARNING_BIT_EXT, VK_DEBUG_REPORT_OBJECT_TYPE_COMMAND_BUFFER_EXT, - HandleToUint64(cb_node->commandBuffer), __LINE__, DRAWSTATE_MISMATCHED_IMAGE_ASPECT, "IMAGE", str); + HandleToUint64(cb_state->commandBuffer), __LINE__, DRAWSTATE_MISMATCHED_IMAGE_ASPECT, "IMAGE", str); } if (pRegions[i].dstSubresource.layerCount == 0) { char const str[] = "vkCmdResolveImage: number of layers in destination subresource is zero"; skip |= log_msg(report_data, VK_DEBUG_REPORT_WARNING_BIT_EXT, VK_DEBUG_REPORT_OBJECT_TYPE_COMMAND_BUFFER_EXT, - HandleToUint64(cb_node->commandBuffer), __LINE__, DRAWSTATE_MISMATCHED_IMAGE_ASPECT, "IMAGE", str); + HandleToUint64(cb_state->commandBuffer), __LINE__, DRAWSTATE_MISMATCHED_IMAGE_ASPECT, "IMAGE", str); } if (pRegions[i].srcSubresource.layerCount != pRegions[i].dstSubresource.layerCount) { skip |= log_msg( report_data, VK_DEBUG_REPORT_ERROR_BIT_EXT, VK_DEBUG_REPORT_OBJECT_TYPE_COMMAND_BUFFER_EXT, - HandleToUint64(cb_node->commandBuffer), __LINE__, VALIDATION_ERROR_0a200216, "IMAGE", + HandleToUint64(cb_state->commandBuffer), __LINE__, VALIDATION_ERROR_0a200216, "IMAGE", "vkCmdResolveImage: layerCount in source and destination subresource of pRegions[%d] does not match. %s", i, validation_error_map[VALIDATION_ERROR_0a200216]); } @@ -2139,31 +2379,37 @@ bool PreCallValidateCmdResolveImage(layer_data *device_data, GLOBAL_CB_NODE *cb_ char const str[] = "vkCmdResolveImage: src and dest aspectMasks for each region must specify only VK_IMAGE_ASPECT_COLOR_BIT"; skip |= log_msg(report_data, VK_DEBUG_REPORT_ERROR_BIT_EXT, VK_DEBUG_REPORT_OBJECT_TYPE_COMMAND_BUFFER_EXT, - HandleToUint64(cb_node->commandBuffer), __LINE__, VALIDATION_ERROR_0a200214, "IMAGE", "%s. %s", str, - validation_error_map[VALIDATION_ERROR_0a200214]); + HandleToUint64(cb_state->commandBuffer), __LINE__, VALIDATION_ERROR_0a200214, "IMAGE", "%s. %s", + str, validation_error_map[VALIDATION_ERROR_0a200214]); } } + // Add memory access for this image resolve + // TODO: Currently treating all image accesses as imprecise & covering the whole image area + skip |= CreateAndValidateReadMemoryAccess(report_data, CMD_RESOLVEIMAGE, cb_state->commandBuffer, cb_state->mem_accesses, + mem_accesses, src_image_state->binding, false, "vkCmdResolveImage()"); + skip |= CreateAndValidateWriteMemoryAccess(report_data, CMD_RESOLVEIMAGE, cb_state->commandBuffer, cb_state->mem_accesses, + mem_accesses, dst_image_state->binding, false, "vkCmdResolveImage()"); if (src_image_state->createInfo.format != dst_image_state->createInfo.format) { char const str[] = "vkCmdResolveImage called with unmatched source and dest formats."; skip |= log_msg(report_data, VK_DEBUG_REPORT_WARNING_BIT_EXT, VK_DEBUG_REPORT_OBJECT_TYPE_COMMAND_BUFFER_EXT, - HandleToUint64(cb_node->commandBuffer), __LINE__, DRAWSTATE_MISMATCHED_IMAGE_FORMAT, "IMAGE", str); + HandleToUint64(cb_state->commandBuffer), __LINE__, DRAWSTATE_MISMATCHED_IMAGE_FORMAT, "IMAGE", str); } if (src_image_state->createInfo.imageType != dst_image_state->createInfo.imageType) { char const str[] = "vkCmdResolveImage called with unmatched source and dest image types."; skip |= log_msg(report_data, VK_DEBUG_REPORT_WARNING_BIT_EXT, VK_DEBUG_REPORT_OBJECT_TYPE_COMMAND_BUFFER_EXT, - HandleToUint64(cb_node->commandBuffer), __LINE__, DRAWSTATE_MISMATCHED_IMAGE_TYPE, "IMAGE", str); + HandleToUint64(cb_state->commandBuffer), __LINE__, DRAWSTATE_MISMATCHED_IMAGE_TYPE, "IMAGE", str); } if (src_image_state->createInfo.samples == VK_SAMPLE_COUNT_1_BIT) { char const str[] = "vkCmdResolveImage called with source sample count less than 2."; skip |= log_msg(report_data, VK_DEBUG_REPORT_ERROR_BIT_EXT, VK_DEBUG_REPORT_OBJECT_TYPE_COMMAND_BUFFER_EXT, - HandleToUint64(cb_node->commandBuffer), __LINE__, VALIDATION_ERROR_1c800202, "IMAGE", "%s. %s", str, + HandleToUint64(cb_state->commandBuffer), __LINE__, VALIDATION_ERROR_1c800202, "IMAGE", "%s. %s", str, validation_error_map[VALIDATION_ERROR_1c800202]); } if (dst_image_state->createInfo.samples != VK_SAMPLE_COUNT_1_BIT) { char const str[] = "vkCmdResolveImage called with dest sample count greater than 1."; skip |= log_msg(report_data, VK_DEBUG_REPORT_ERROR_BIT_EXT, VK_DEBUG_REPORT_OBJECT_TYPE_COMMAND_BUFFER_EXT, - HandleToUint64(cb_node->commandBuffer), __LINE__, VALIDATION_ERROR_1c800206, "IMAGE", "%s. %s", str, + HandleToUint64(cb_state->commandBuffer), __LINE__, VALIDATION_ERROR_1c800206, "IMAGE", "%s. %s", str, validation_error_map[VALIDATION_ERROR_1c800206]); } // TODO: Need to validate image layouts, which will include layout validation for shared presentable images @@ -2173,29 +2419,31 @@ bool PreCallValidateCmdResolveImage(layer_data *device_data, GLOBAL_CB_NODE *cb_ return skip; } -void PreCallRecordCmdResolveImage(layer_data *device_data, GLOBAL_CB_NODE *cb_node, IMAGE_STATE *src_image_state, - IMAGE_STATE *dst_image_state) { +void PreCallRecordCmdResolveImage(layer_data *device_data, GLOBAL_CB_NODE *cb_state, IMAGE_STATE *src_image_state, + IMAGE_STATE *dst_image_state, std::vector *mem_accesses) { // Update bindings between images and cmd buffer - AddCommandBufferBindingImage(device_data, cb_node, src_image_state); - AddCommandBufferBindingImage(device_data, cb_node, dst_image_state); + AddCommandBufferBindingImage(device_data, cb_state, src_image_state); + AddCommandBufferBindingImage(device_data, cb_state, dst_image_state); std::function function = [=]() { return ValidateImageMemoryIsValid(device_data, src_image_state, "vkCmdResolveImage()"); }; - cb_node->queue_submit_functions.push_back(function); + cb_state->queue_submit_functions.push_back(function); function = [=]() { SetImageMemoryValid(device_data, dst_image_state, true); return false; }; - cb_node->queue_submit_functions.push_back(function); + cb_state->queue_submit_functions.push_back(function); + AddCommandBufferCommandMemoryAccesses(cb_state, CMD_RESOLVEIMAGE, mem_accesses); } -bool PreCallValidateCmdBlitImage(layer_data *device_data, GLOBAL_CB_NODE *cb_node, IMAGE_STATE *src_image_state, - IMAGE_STATE *dst_image_state, uint32_t regionCount, const VkImageBlit *pRegions, VkFilter filter) { +bool PreCallValidateCmdBlitImage(layer_data *device_data, GLOBAL_CB_NODE *cb_state, IMAGE_STATE *src_image_state, + IMAGE_STATE *dst_image_state, uint32_t regionCount, const VkImageBlit *pRegions, VkFilter filter, + std::vector *mem_accesses) { const debug_report_data *report_data = core_validation::GetReportData(device_data); bool skip = false; - if (cb_node && src_image_state && dst_image_state) { + if (cb_state && src_image_state && dst_image_state) { skip |= ValidateImageSampleCount(device_data, src_image_state, VK_SAMPLE_COUNT_1_BIT, "vkCmdBlitImage(): srcImage", VALIDATION_ERROR_184001d2); skip |= ValidateImageSampleCount(device_data, dst_image_state, VK_SAMPLE_COUNT_1_BIT, "vkCmdBlitImage(): dstImage", @@ -2206,9 +2454,9 @@ bool PreCallValidateCmdBlitImage(layer_data *device_data, GLOBAL_CB_NODE *cb_nod VALIDATION_ERROR_184001b6, "vkCmdBlitImage()", "VK_IMAGE_USAGE_TRANSFER_SRC_BIT"); skip |= ValidateImageUsageFlags(device_data, dst_image_state, VK_IMAGE_USAGE_TRANSFER_DST_BIT, true, VALIDATION_ERROR_184001c0, "vkCmdBlitImage()", "VK_IMAGE_USAGE_TRANSFER_DST_BIT"); - skip |= ValidateCmdQueueFlags(device_data, cb_node, "vkCmdBlitImage()", VK_QUEUE_GRAPHICS_BIT, VALIDATION_ERROR_18402415); - skip |= ValidateCmd(device_data, cb_node, CMD_BLITIMAGE, "vkCmdBlitImage()"); - skip |= insideRenderPass(device_data, cb_node, "vkCmdBlitImage()", VALIDATION_ERROR_18400017); + skip |= ValidateCmdQueueFlags(device_data, cb_state, "vkCmdBlitImage()", VK_QUEUE_GRAPHICS_BIT, VALIDATION_ERROR_18402415); + skip |= ValidateCmd(device_data, cb_state, CMD_BLITIMAGE, "vkCmdBlitImage()"); + skip |= insideRenderPass(device_data, cb_state, "vkCmdBlitImage()", VALIDATION_ERROR_18400017); // TODO: Need to validate image layouts, which will include layout validation for shared presentable images VkFormat src_format = src_image_state->createInfo.format; @@ -2222,7 +2470,7 @@ bool PreCallValidateCmdBlitImage(layer_data *device_data, GLOBAL_CB_NODE *cb_nod (tiling == VK_IMAGE_TILING_LINEAR ? props.linearTilingFeatures : props.optimalTilingFeatures); if (VK_FORMAT_FEATURE_BLIT_SRC_BIT != (flags & VK_FORMAT_FEATURE_BLIT_SRC_BIT)) { skip |= log_msg(report_data, VK_DEBUG_REPORT_ERROR_BIT_EXT, VK_DEBUG_REPORT_OBJECT_TYPE_COMMAND_BUFFER_EXT, - HandleToUint64(cb_node->commandBuffer), __LINE__, VALIDATION_ERROR_184001b4, "IMAGE", + HandleToUint64(cb_state->commandBuffer), __LINE__, VALIDATION_ERROR_184001b4, "IMAGE", "vkCmdBlitImage: source image format %s does not support VK_FORMAT_FEATURE_BLIT_SRC_BIT feature. %s", string_VkFormat(src_format), validation_error_map[VALIDATION_ERROR_184001b4]); } @@ -2230,7 +2478,7 @@ bool PreCallValidateCmdBlitImage(layer_data *device_data, GLOBAL_CB_NODE *cb_nod if ((VK_FILTER_LINEAR == filter) && (VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT != (flags & VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT))) { skip |= log_msg(report_data, VK_DEBUG_REPORT_ERROR_BIT_EXT, VK_DEBUG_REPORT_OBJECT_TYPE_COMMAND_BUFFER_EXT, - HandleToUint64(cb_node->commandBuffer), __LINE__, VALIDATION_ERROR_184001d6, "IMAGE", + HandleToUint64(cb_state->commandBuffer), __LINE__, VALIDATION_ERROR_184001d6, "IMAGE", "vkCmdBlitImage: source image format %s does not support linear filtering. %s", string_VkFormat(src_format), validation_error_map[VALIDATION_ERROR_184001d6]); } @@ -2238,14 +2486,14 @@ bool PreCallValidateCmdBlitImage(layer_data *device_data, GLOBAL_CB_NODE *cb_nod if ((VK_FILTER_CUBIC_IMG == filter) && (VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_CUBIC_BIT_IMG != (flags & VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_CUBIC_BIT_IMG))) { skip |= log_msg(report_data, VK_DEBUG_REPORT_ERROR_BIT_EXT, VK_DEBUG_REPORT_OBJECT_TYPE_COMMAND_BUFFER_EXT, - HandleToUint64(cb_node->commandBuffer), __LINE__, VALIDATION_ERROR_184001d8, "IMAGE", + HandleToUint64(cb_state->commandBuffer), __LINE__, VALIDATION_ERROR_184001d8, "IMAGE", "vkCmdBlitImage: source image format %s does not support cubic filtering. %s", string_VkFormat(src_format), validation_error_map[VALIDATION_ERROR_184001d8]); } if ((VK_FILTER_CUBIC_IMG == filter) && (VK_IMAGE_TYPE_3D != src_type)) { skip |= log_msg(report_data, VK_DEBUG_REPORT_ERROR_BIT_EXT, VK_DEBUG_REPORT_OBJECT_TYPE_COMMAND_BUFFER_EXT, - HandleToUint64(cb_node->commandBuffer), __LINE__, VALIDATION_ERROR_184001da, "IMAGE", + HandleToUint64(cb_state->commandBuffer), __LINE__, VALIDATION_ERROR_184001da, "IMAGE", "vkCmdBlitImage: source image type must be VK_IMAGE_TYPE_3D when cubic filtering is specified. %s", validation_error_map[VALIDATION_ERROR_184001da]); } @@ -2256,7 +2504,7 @@ bool PreCallValidateCmdBlitImage(layer_data *device_data, GLOBAL_CB_NODE *cb_nod if (VK_FORMAT_FEATURE_BLIT_DST_BIT != (flags & VK_FORMAT_FEATURE_BLIT_DST_BIT)) { skip |= log_msg(report_data, VK_DEBUG_REPORT_ERROR_BIT_EXT, VK_DEBUG_REPORT_OBJECT_TYPE_COMMAND_BUFFER_EXT, - HandleToUint64(cb_node->commandBuffer), __LINE__, VALIDATION_ERROR_184001be, "IMAGE", + HandleToUint64(cb_state->commandBuffer), __LINE__, VALIDATION_ERROR_184001be, "IMAGE", "vkCmdBlitImage: destination image format %s does not support VK_FORMAT_FEATURE_BLIT_DST_BIT feature. %s", string_VkFormat(dst_format), validation_error_map[VALIDATION_ERROR_184001be]); } @@ -2264,7 +2512,7 @@ bool PreCallValidateCmdBlitImage(layer_data *device_data, GLOBAL_CB_NODE *cb_nod if ((VK_SAMPLE_COUNT_1_BIT != src_image_state->createInfo.samples) || (VK_SAMPLE_COUNT_1_BIT != dst_image_state->createInfo.samples)) { skip |= log_msg(report_data, VK_DEBUG_REPORT_ERROR_BIT_EXT, VK_DEBUG_REPORT_OBJECT_TYPE_COMMAND_BUFFER_EXT, - HandleToUint64(cb_node->commandBuffer), __LINE__, VALIDATION_ERROR_184001c8, "IMAGE", + HandleToUint64(cb_state->commandBuffer), __LINE__, VALIDATION_ERROR_184001c8, "IMAGE", "vkCmdBlitImage: source or dest image has sample count other than VK_SAMPLE_COUNT_1_BIT. %s", validation_error_map[VALIDATION_ERROR_184001c8]); } @@ -2276,7 +2524,7 @@ bool PreCallValidateCmdBlitImage(layer_data *device_data, GLOBAL_CB_NODE *cb_nod << "the other one must also have unsigned integer format. " << "Source format is " << string_VkFormat(src_format) << " Destination format is " << string_VkFormat(dst_format); skip |= log_msg(report_data, VK_DEBUG_REPORT_ERROR_BIT_EXT, VK_DEBUG_REPORT_OBJECT_TYPE_COMMAND_BUFFER_EXT, - HandleToUint64(cb_node->commandBuffer), __LINE__, VALIDATION_ERROR_184001cc, "IMAGE", "%s. %s", + HandleToUint64(cb_state->commandBuffer), __LINE__, VALIDATION_ERROR_184001cc, "IMAGE", "%s. %s", ss.str().c_str(), validation_error_map[VALIDATION_ERROR_184001cc]); } @@ -2287,7 +2535,7 @@ bool PreCallValidateCmdBlitImage(layer_data *device_data, GLOBAL_CB_NODE *cb_nod << "the other one must also have signed integer format. " << "Source format is " << string_VkFormat(src_format) << " Destination format is " << string_VkFormat(dst_format); skip |= log_msg(report_data, VK_DEBUG_REPORT_ERROR_BIT_EXT, VK_DEBUG_REPORT_OBJECT_TYPE_COMMAND_BUFFER_EXT, - HandleToUint64(cb_node->commandBuffer), __LINE__, VALIDATION_ERROR_184001ca, "IMAGE", "%s. %s", + HandleToUint64(cb_state->commandBuffer), __LINE__, VALIDATION_ERROR_184001ca, "IMAGE", "%s. %s", ss.str().c_str(), validation_error_map[VALIDATION_ERROR_184001ca]); } @@ -2297,7 +2545,7 @@ bool PreCallValidateCmdBlitImage(layer_data *device_data, GLOBAL_CB_NODE *cb_nod ss << "vkCmdBlitImage: If the format of srcImage is a depth, stencil, or depth stencil " << "then filter must be VK_FILTER_NEAREST."; skip |= log_msg(report_data, VK_DEBUG_REPORT_ERROR_BIT_EXT, VK_DEBUG_REPORT_OBJECT_TYPE_COMMAND_BUFFER_EXT, - HandleToUint64(cb_node->commandBuffer), __LINE__, VALIDATION_ERROR_184001d0, "IMAGE", "%s. %s", + HandleToUint64(cb_state->commandBuffer), __LINE__, VALIDATION_ERROR_184001d0, "IMAGE", "%s. %s", ss.str().c_str(), validation_error_map[VALIDATION_ERROR_184001d0]); } @@ -2310,7 +2558,7 @@ bool PreCallValidateCmdBlitImage(layer_data *device_data, GLOBAL_CB_NODE *cb_nod << "Source format is " << string_VkFormat(src_format) << " Destination format is " << string_VkFormat(dst_format); skip |= log_msg(report_data, VK_DEBUG_REPORT_ERROR_BIT_EXT, VK_DEBUG_REPORT_OBJECT_TYPE_COMMAND_BUFFER_EXT, - HandleToUint64(cb_node->commandBuffer), __LINE__, VALIDATION_ERROR_184001ce, "IMAGE", "%s. %s", + HandleToUint64(cb_state->commandBuffer), __LINE__, VALIDATION_ERROR_184001ce, "IMAGE", "%s. %s", ss.str().c_str(), validation_error_map[VALIDATION_ERROR_184001ce]); } @@ -2325,7 +2573,7 @@ bool PreCallValidateCmdBlitImage(layer_data *device_data, GLOBAL_CB_NODE *cb_nod "VK_IMAGE_ASPECT_DEPTH_BIT " << "and VK_IMAGE_ASPECT_STENCIL_BIT set in srcImage and dstImage"; skip |= log_msg(report_data, VK_DEBUG_REPORT_ERROR_BIT_EXT, VK_DEBUG_REPORT_OBJECT_TYPE_COMMAND_BUFFER_EXT, - HandleToUint64(cb_node->commandBuffer), __LINE__, DRAWSTATE_INVALID_IMAGE_ASPECT, "IMAGE", + HandleToUint64(cb_state->commandBuffer), __LINE__, DRAWSTATE_INVALID_IMAGE_ASPECT, "IMAGE", "%s", ss.str().c_str()); } } @@ -2335,7 +2583,7 @@ bool PreCallValidateCmdBlitImage(layer_data *device_data, GLOBAL_CB_NODE *cb_nod ss << "vkCmdBlitImage: Stencil-only image formats must have only the VK_IMAGE_ASPECT_STENCIL_BIT " << "set in both the srcImage and dstImage"; skip |= log_msg(report_data, VK_DEBUG_REPORT_ERROR_BIT_EXT, VK_DEBUG_REPORT_OBJECT_TYPE_COMMAND_BUFFER_EXT, - HandleToUint64(cb_node->commandBuffer), __LINE__, DRAWSTATE_INVALID_IMAGE_ASPECT, "IMAGE", + HandleToUint64(cb_state->commandBuffer), __LINE__, DRAWSTATE_INVALID_IMAGE_ASPECT, "IMAGE", "%s", ss.str().c_str()); } } @@ -2345,7 +2593,7 @@ bool PreCallValidateCmdBlitImage(layer_data *device_data, GLOBAL_CB_NODE *cb_nod ss << "vkCmdBlitImage: Depth-only image formats must have only the VK_IMAGE_ASPECT_DEPTH " << "set in both the srcImage and dstImage"; skip |= log_msg(report_data, VK_DEBUG_REPORT_ERROR_BIT_EXT, VK_DEBUG_REPORT_OBJECT_TYPE_COMMAND_BUFFER_EXT, - HandleToUint64(cb_node->commandBuffer), __LINE__, DRAWSTATE_INVALID_IMAGE_ASPECT, "IMAGE", + HandleToUint64(cb_state->commandBuffer), __LINE__, DRAWSTATE_INVALID_IMAGE_ASPECT, "IMAGE", "%s", ss.str().c_str()); } } @@ -2363,7 +2611,7 @@ bool PreCallValidateCmdBlitImage(layer_data *device_data, GLOBAL_CB_NODE *cb_nod std::stringstream ss; ss << "vkCmdBlitImage: pRegions[" << i << "].srcOffsets specify a zero-volume area."; skip |= log_msg(report_data, VK_DEBUG_REPORT_WARNING_BIT_EXT, VK_DEBUG_REPORT_OBJECT_TYPE_COMMAND_BUFFER_EXT, - HandleToUint64(cb_node->commandBuffer), __LINE__, DRAWSTATE_INVALID_EXTENTS, "IMAGE", "%s", + HandleToUint64(cb_state->commandBuffer), __LINE__, DRAWSTATE_INVALID_EXTENTS, "IMAGE", "%s", ss.str().c_str()); } if ((rgn.dstOffsets[0].x == rgn.dstOffsets[1].x) || (rgn.dstOffsets[0].y == rgn.dstOffsets[1].y) || @@ -2371,39 +2619,39 @@ bool PreCallValidateCmdBlitImage(layer_data *device_data, GLOBAL_CB_NODE *cb_nod std::stringstream ss; ss << "vkCmdBlitImage: pRegions[" << i << "].dstOffsets specify a zero-volume area."; skip |= log_msg(report_data, VK_DEBUG_REPORT_WARNING_BIT_EXT, VK_DEBUG_REPORT_OBJECT_TYPE_COMMAND_BUFFER_EXT, - HandleToUint64(cb_node->commandBuffer), __LINE__, DRAWSTATE_INVALID_EXTENTS, "IMAGE", "%s", + HandleToUint64(cb_state->commandBuffer), __LINE__, DRAWSTATE_INVALID_EXTENTS, "IMAGE", "%s", ss.str().c_str()); } if (rgn.srcSubresource.layerCount == 0) { char const str[] = "vkCmdBlitImage: number of layers in source subresource is zero"; skip |= log_msg(report_data, VK_DEBUG_REPORT_WARNING_BIT_EXT, VK_DEBUG_REPORT_OBJECT_TYPE_COMMAND_BUFFER_EXT, - HandleToUint64(cb_node->commandBuffer), __LINE__, DRAWSTATE_MISMATCHED_IMAGE_ASPECT, "IMAGE", str); + HandleToUint64(cb_state->commandBuffer), __LINE__, DRAWSTATE_MISMATCHED_IMAGE_ASPECT, "IMAGE", str); } if (rgn.dstSubresource.layerCount == 0) { char const str[] = "vkCmdBlitImage: number of layers in destination subresource is zero"; skip |= log_msg(report_data, VK_DEBUG_REPORT_WARNING_BIT_EXT, VK_DEBUG_REPORT_OBJECT_TYPE_COMMAND_BUFFER_EXT, - HandleToUint64(cb_node->commandBuffer), __LINE__, DRAWSTATE_MISMATCHED_IMAGE_ASPECT, "IMAGE", str); + HandleToUint64(cb_state->commandBuffer), __LINE__, DRAWSTATE_MISMATCHED_IMAGE_ASPECT, "IMAGE", str); } // Check that src/dst layercounts match if (rgn.srcSubresource.layerCount != rgn.dstSubresource.layerCount) { skip |= log_msg(report_data, VK_DEBUG_REPORT_ERROR_BIT_EXT, VK_DEBUG_REPORT_OBJECT_TYPE_COMMAND_BUFFER_EXT, - HandleToUint64(cb_node->commandBuffer), __LINE__, VALIDATION_ERROR_09a001de, "IMAGE", + HandleToUint64(cb_state->commandBuffer), __LINE__, VALIDATION_ERROR_09a001de, "IMAGE", "vkCmdBlitImage: layerCount in source and destination subresource of pRegions[%d] does not match. %s", i, validation_error_map[VALIDATION_ERROR_09a001de]); } if (rgn.srcSubresource.aspectMask != rgn.dstSubresource.aspectMask) { skip |= log_msg(report_data, VK_DEBUG_REPORT_ERROR_BIT_EXT, VK_DEBUG_REPORT_OBJECT_TYPE_COMMAND_BUFFER_EXT, - HandleToUint64(cb_node->commandBuffer), __LINE__, VALIDATION_ERROR_09a001dc, "IMAGE", + HandleToUint64(cb_state->commandBuffer), __LINE__, VALIDATION_ERROR_09a001dc, "IMAGE", "vkCmdBlitImage: aspectMask members for pRegion[%d] do not match. %s", i, validation_error_map[VALIDATION_ERROR_09a001dc]); } if (!VerifyAspectsPresent(rgn.srcSubresource.aspectMask, src_format)) { skip |= log_msg(report_data, VK_DEBUG_REPORT_ERROR_BIT_EXT, VK_DEBUG_REPORT_OBJECT_TYPE_COMMAND_BUFFER_EXT, - HandleToUint64(cb_node->commandBuffer), __LINE__, VALIDATION_ERROR_09a001e2, "IMAGE", + HandleToUint64(cb_state->commandBuffer), __LINE__, VALIDATION_ERROR_09a001e2, "IMAGE", "vkCmdBlitImage: region [%d] source aspectMask (0x%x) specifies aspects not present in source " "image format %s. %s", i, rgn.srcSubresource.aspectMask, string_VkFormat(src_format), @@ -2413,7 +2661,7 @@ bool PreCallValidateCmdBlitImage(layer_data *device_data, GLOBAL_CB_NODE *cb_nod if (!VerifyAspectsPresent(rgn.dstSubresource.aspectMask, dst_format)) { skip |= log_msg( report_data, VK_DEBUG_REPORT_ERROR_BIT_EXT, VK_DEBUG_REPORT_OBJECT_TYPE_COMMAND_BUFFER_EXT, - HandleToUint64(cb_node->commandBuffer), __LINE__, VALIDATION_ERROR_09a001e4, "IMAGE", + HandleToUint64(cb_state->commandBuffer), __LINE__, VALIDATION_ERROR_09a001e4, "IMAGE", "vkCmdBlitImage: region [%d] dest aspectMask (0x%x) specifies aspects not present in dest image format %s. %s", i, rgn.dstSubresource.aspectMask, string_VkFormat(dst_format), validation_error_map[VALIDATION_ERROR_09a001e4]); } @@ -2423,7 +2671,7 @@ bool PreCallValidateCmdBlitImage(layer_data *device_data, GLOBAL_CB_NODE *cb_nod if (VK_IMAGE_TYPE_1D == src_type) { if ((0 != rgn.srcOffsets[0].y) || (1 != rgn.srcOffsets[1].y)) { skip |= log_msg(report_data, VK_DEBUG_REPORT_ERROR_BIT_EXT, VK_DEBUG_REPORT_OBJECT_TYPE_COMMAND_BUFFER_EXT, - HandleToUint64(cb_node->commandBuffer), __LINE__, VALIDATION_ERROR_09a001ea, "IMAGE", + HandleToUint64(cb_state->commandBuffer), __LINE__, VALIDATION_ERROR_09a001ea, "IMAGE", "vkCmdBlitImage: region [%d], source image of type VK_IMAGE_TYPE_1D with srcOffset[].y values " "of (%1d, %1d). These must be (0, 1). %s", i, rgn.srcOffsets[0].y, rgn.srcOffsets[1].y, validation_error_map[VALIDATION_ERROR_09a001ea]); @@ -2433,7 +2681,7 @@ bool PreCallValidateCmdBlitImage(layer_data *device_data, GLOBAL_CB_NODE *cb_nod if ((VK_IMAGE_TYPE_1D == src_type) || (VK_IMAGE_TYPE_2D == src_type)) { if ((0 != rgn.srcOffsets[0].z) || (1 != rgn.srcOffsets[1].z)) { skip |= log_msg(report_data, VK_DEBUG_REPORT_ERROR_BIT_EXT, VK_DEBUG_REPORT_OBJECT_TYPE_COMMAND_BUFFER_EXT, - HandleToUint64(cb_node->commandBuffer), __LINE__, VALIDATION_ERROR_09a001ee, "IMAGE", + HandleToUint64(cb_state->commandBuffer), __LINE__, VALIDATION_ERROR_09a001ee, "IMAGE", "vkCmdBlitImage: region [%d], source image of type VK_IMAGE_TYPE_1D or VK_IMAGE_TYPE_2D with " "srcOffset[].z values of (%1d, %1d). These must be (0, 1). %s", i, rgn.srcOffsets[0].z, rgn.srcOffsets[1].z, validation_error_map[VALIDATION_ERROR_09a001ee]); @@ -2446,7 +2694,7 @@ bool PreCallValidateCmdBlitImage(layer_data *device_data, GLOBAL_CB_NODE *cb_nod oob = true; skip |= log_msg( report_data, VK_DEBUG_REPORT_ERROR_BIT_EXT, VK_DEBUG_REPORT_OBJECT_TYPE_COMMAND_BUFFER_EXT, - HandleToUint64(cb_node->commandBuffer), __LINE__, VALIDATION_ERROR_09a001e6, "IMAGE", + HandleToUint64(cb_state->commandBuffer), __LINE__, VALIDATION_ERROR_09a001e6, "IMAGE", "vkCmdBlitImage: region [%d] srcOffset[].x values (%1d, %1d) exceed srcSubresource width extent (%1d). %s", i, rgn.srcOffsets[0].x, rgn.srcOffsets[1].x, src_extent.width, validation_error_map[VALIDATION_ERROR_09a001e6]); } @@ -2455,7 +2703,7 @@ bool PreCallValidateCmdBlitImage(layer_data *device_data, GLOBAL_CB_NODE *cb_nod oob = true; skip |= log_msg( report_data, VK_DEBUG_REPORT_ERROR_BIT_EXT, VK_DEBUG_REPORT_OBJECT_TYPE_COMMAND_BUFFER_EXT, - HandleToUint64(cb_node->commandBuffer), __LINE__, VALIDATION_ERROR_09a001e8, "IMAGE", + HandleToUint64(cb_state->commandBuffer), __LINE__, VALIDATION_ERROR_09a001e8, "IMAGE", "vkCmdBlitImage: region [%d] srcOffset[].y values (%1d, %1d) exceed srcSubresource height extent (%1d). %s", i, rgn.srcOffsets[0].y, rgn.srcOffsets[1].y, src_extent.height, validation_error_map[VALIDATION_ERROR_09a001e8]); } @@ -2464,18 +2712,18 @@ bool PreCallValidateCmdBlitImage(layer_data *device_data, GLOBAL_CB_NODE *cb_nod oob = true; skip |= log_msg( report_data, VK_DEBUG_REPORT_ERROR_BIT_EXT, VK_DEBUG_REPORT_OBJECT_TYPE_COMMAND_BUFFER_EXT, - HandleToUint64(cb_node->commandBuffer), __LINE__, VALIDATION_ERROR_09a001ec, "IMAGE", + HandleToUint64(cb_state->commandBuffer), __LINE__, VALIDATION_ERROR_09a001ec, "IMAGE", "vkCmdBlitImage: region [%d] srcOffset[].z values (%1d, %1d) exceed srcSubresource depth extent (%1d). %s", i, rgn.srcOffsets[0].z, rgn.srcOffsets[1].z, src_extent.depth, validation_error_map[VALIDATION_ERROR_09a001ec]); } if (rgn.srcSubresource.mipLevel >= src_image_state->createInfo.mipLevels) { skip |= log_msg(report_data, VK_DEBUG_REPORT_ERROR_BIT_EXT, VK_DEBUG_REPORT_OBJECT_TYPE_COMMAND_BUFFER_EXT, - HandleToUint64(cb_node->commandBuffer), __LINE__, VALIDATION_ERROR_184001ae, "IMAGE", + HandleToUint64(cb_state->commandBuffer), __LINE__, VALIDATION_ERROR_184001ae, "IMAGE", "vkCmdBlitImage: region [%d] source image, attempt to access a non-existant mip level %1d. %s", i, rgn.srcSubresource.mipLevel, validation_error_map[VALIDATION_ERROR_184001ae]); } else if (oob) { skip |= log_msg(report_data, VK_DEBUG_REPORT_ERROR_BIT_EXT, VK_DEBUG_REPORT_OBJECT_TYPE_COMMAND_BUFFER_EXT, - HandleToUint64(cb_node->commandBuffer), __LINE__, VALIDATION_ERROR_184001ae, "IMAGE", + HandleToUint64(cb_state->commandBuffer), __LINE__, VALIDATION_ERROR_184001ae, "IMAGE", "vkCmdBlitImage: region [%d] source image blit region exceeds image dimensions. %s", i, validation_error_map[VALIDATION_ERROR_184001ae]); } @@ -2485,7 +2733,7 @@ bool PreCallValidateCmdBlitImage(layer_data *device_data, GLOBAL_CB_NODE *cb_nod if (VK_IMAGE_TYPE_1D == dst_type) { if ((0 != rgn.dstOffsets[0].y) || (1 != rgn.dstOffsets[1].y)) { skip |= log_msg(report_data, VK_DEBUG_REPORT_ERROR_BIT_EXT, VK_DEBUG_REPORT_OBJECT_TYPE_COMMAND_BUFFER_EXT, - HandleToUint64(cb_node->commandBuffer), __LINE__, VALIDATION_ERROR_09a001f4, "IMAGE", + HandleToUint64(cb_state->commandBuffer), __LINE__, VALIDATION_ERROR_09a001f4, "IMAGE", "vkCmdBlitImage: region [%d], dest image of type VK_IMAGE_TYPE_1D with dstOffset[].y values of " "(%1d, %1d). These must be (0, 1). %s", i, rgn.dstOffsets[0].y, rgn.dstOffsets[1].y, validation_error_map[VALIDATION_ERROR_09a001f4]); @@ -2495,7 +2743,7 @@ bool PreCallValidateCmdBlitImage(layer_data *device_data, GLOBAL_CB_NODE *cb_nod if ((VK_IMAGE_TYPE_1D == dst_type) || (VK_IMAGE_TYPE_2D == dst_type)) { if ((0 != rgn.dstOffsets[0].z) || (1 != rgn.dstOffsets[1].z)) { skip |= log_msg(report_data, VK_DEBUG_REPORT_ERROR_BIT_EXT, VK_DEBUG_REPORT_OBJECT_TYPE_COMMAND_BUFFER_EXT, - HandleToUint64(cb_node->commandBuffer), __LINE__, VALIDATION_ERROR_09a001f8, "IMAGE", + HandleToUint64(cb_state->commandBuffer), __LINE__, VALIDATION_ERROR_09a001f8, "IMAGE", "vkCmdBlitImage: region [%d], dest image of type VK_IMAGE_TYPE_1D or VK_IMAGE_TYPE_2D with " "dstOffset[].z values of (%1d, %1d). These must be (0, 1). %s", i, rgn.dstOffsets[0].z, rgn.dstOffsets[1].z, validation_error_map[VALIDATION_ERROR_09a001f8]); @@ -2508,7 +2756,7 @@ bool PreCallValidateCmdBlitImage(layer_data *device_data, GLOBAL_CB_NODE *cb_nod oob = true; skip |= log_msg( report_data, VK_DEBUG_REPORT_ERROR_BIT_EXT, VK_DEBUG_REPORT_OBJECT_TYPE_COMMAND_BUFFER_EXT, - HandleToUint64(cb_node->commandBuffer), __LINE__, VALIDATION_ERROR_09a001f0, "IMAGE", + HandleToUint64(cb_state->commandBuffer), __LINE__, VALIDATION_ERROR_09a001f0, "IMAGE", "vkCmdBlitImage: region [%d] dstOffset[].x values (%1d, %1d) exceed dstSubresource width extent (%1d). %s", i, rgn.dstOffsets[0].x, rgn.dstOffsets[1].x, dst_extent.width, validation_error_map[VALIDATION_ERROR_09a001f0]); } @@ -2517,7 +2765,7 @@ bool PreCallValidateCmdBlitImage(layer_data *device_data, GLOBAL_CB_NODE *cb_nod oob = true; skip |= log_msg( report_data, VK_DEBUG_REPORT_ERROR_BIT_EXT, VK_DEBUG_REPORT_OBJECT_TYPE_COMMAND_BUFFER_EXT, - HandleToUint64(cb_node->commandBuffer), __LINE__, VALIDATION_ERROR_09a001f2, "IMAGE", + HandleToUint64(cb_state->commandBuffer), __LINE__, VALIDATION_ERROR_09a001f2, "IMAGE", "vkCmdBlitImage: region [%d] dstOffset[].y values (%1d, %1d) exceed dstSubresource height extent (%1d). %s", i, rgn.dstOffsets[0].y, rgn.dstOffsets[1].y, dst_extent.height, validation_error_map[VALIDATION_ERROR_09a001f2]); } @@ -2526,18 +2774,18 @@ bool PreCallValidateCmdBlitImage(layer_data *device_data, GLOBAL_CB_NODE *cb_nod oob = true; skip |= log_msg( report_data, VK_DEBUG_REPORT_ERROR_BIT_EXT, VK_DEBUG_REPORT_OBJECT_TYPE_COMMAND_BUFFER_EXT, - HandleToUint64(cb_node->commandBuffer), __LINE__, VALIDATION_ERROR_09a001f6, "IMAGE", + HandleToUint64(cb_state->commandBuffer), __LINE__, VALIDATION_ERROR_09a001f6, "IMAGE", "vkCmdBlitImage: region [%d] dstOffset[].z values (%1d, %1d) exceed dstSubresource depth extent (%1d). %s", i, rgn.dstOffsets[0].z, rgn.dstOffsets[1].z, dst_extent.depth, validation_error_map[VALIDATION_ERROR_09a001f6]); } if (rgn.dstSubresource.mipLevel >= dst_image_state->createInfo.mipLevels) { skip |= log_msg(report_data, VK_DEBUG_REPORT_ERROR_BIT_EXT, VK_DEBUG_REPORT_OBJECT_TYPE_COMMAND_BUFFER_EXT, - HandleToUint64(cb_node->commandBuffer), __LINE__, VALIDATION_ERROR_184001b0, "IMAGE", + HandleToUint64(cb_state->commandBuffer), __LINE__, VALIDATION_ERROR_184001b0, "IMAGE", "vkCmdBlitImage: region [%d] destination image, attempt to access a non-existant mip level %1d. %s", i, rgn.dstSubresource.mipLevel, validation_error_map[VALIDATION_ERROR_184001b0]); } else if (oob) { skip |= log_msg(report_data, VK_DEBUG_REPORT_ERROR_BIT_EXT, VK_DEBUG_REPORT_OBJECT_TYPE_COMMAND_BUFFER_EXT, - HandleToUint64(cb_node->commandBuffer), __LINE__, VALIDATION_ERROR_184001b0, "IMAGE", + HandleToUint64(cb_state->commandBuffer), __LINE__, VALIDATION_ERROR_184001b0, "IMAGE", "vkCmdBlitImage: region [%d] destination image blit region exceeds image dimensions. %s", i, validation_error_map[VALIDATION_ERROR_184001b0]); } @@ -2546,32 +2794,39 @@ bool PreCallValidateCmdBlitImage(layer_data *device_data, GLOBAL_CB_NODE *cb_nod if ((0 != rgn.srcSubresource.baseArrayLayer) || (1 != rgn.srcSubresource.layerCount) || (0 != rgn.dstSubresource.baseArrayLayer) || (1 != rgn.dstSubresource.layerCount)) { skip |= log_msg(report_data, VK_DEBUG_REPORT_ERROR_BIT_EXT, VK_DEBUG_REPORT_OBJECT_TYPE_COMMAND_BUFFER_EXT, - HandleToUint64(cb_node->commandBuffer), __LINE__, VALIDATION_ERROR_09a001e0, "IMAGE", + HandleToUint64(cb_state->commandBuffer), __LINE__, VALIDATION_ERROR_09a001e0, "IMAGE", "vkCmdBlitImage: region [%d] blit to/from a 3D image type with a non-zero baseArrayLayer, or a " "layerCount other than 1. %s", i, validation_error_map[VALIDATION_ERROR_09a001e0]); } } } // per-region checks + // Add memory access for this blit + // TODO: Currently treating all image accesses as imprecise & covering the whole image area + skip |= CreateAndValidateReadMemoryAccess(report_data, CMD_BLITIMAGE, cb_state->commandBuffer, cb_state->mem_accesses, + mem_accesses, src_image_state->binding, false, "vkCmdBlitImage()"); + skip |= CreateAndValidateWriteMemoryAccess(report_data, CMD_BLITIMAGE, cb_state->commandBuffer, cb_state->mem_accesses, + mem_accesses, dst_image_state->binding, false, "vkCmdBlitImage()"); } else { assert(0); } return skip; } -void PreCallRecordCmdBlitImage(layer_data *device_data, GLOBAL_CB_NODE *cb_node, IMAGE_STATE *src_image_state, - IMAGE_STATE *dst_image_state) { +void PreCallRecordCmdBlitImage(layer_data *device_data, GLOBAL_CB_NODE *cb_state, IMAGE_STATE *src_image_state, + IMAGE_STATE *dst_image_state, std::vector *mem_accesses) { // Update bindings between images and cmd buffer - AddCommandBufferBindingImage(device_data, cb_node, src_image_state); - AddCommandBufferBindingImage(device_data, cb_node, dst_image_state); + AddCommandBufferBindingImage(device_data, cb_state, src_image_state); + AddCommandBufferBindingImage(device_data, cb_state, dst_image_state); std::function function = [=]() { return ValidateImageMemoryIsValid(device_data, src_image_state, "vkCmdBlitImage()"); }; - cb_node->queue_submit_functions.push_back(function); + cb_state->queue_submit_functions.push_back(function); function = [=]() { SetImageMemoryValid(device_data, dst_image_state, true); return false; }; - cb_node->queue_submit_functions.push_back(function); + cb_state->queue_submit_functions.push_back(function); + AddCommandBufferCommandMemoryAccesses(cb_state, CMD_BLITIMAGE, mem_accesses); } // This validates that the initial layout specified in the command buffer for @@ -3459,8 +3714,9 @@ void PostCallRecordCreateImageView(layer_data *device_data, const VkImageViewCre sub_res_range.layerCount = ResolveRemainingLayers(&sub_res_range, image_state->createInfo.arrayLayers); } -bool PreCallValidateCmdCopyBuffer(layer_data *device_data, GLOBAL_CB_NODE *cb_node, BUFFER_STATE *src_buffer_state, - BUFFER_STATE *dst_buffer_state) { +bool PreCallValidateCmdCopyBuffer(layer_data *device_data, GLOBAL_CB_NODE *cb_state, BUFFER_STATE *src_buffer_state, + BUFFER_STATE *dst_buffer_state, uint32_t region_count, const VkBufferCopy *regions, + std::vector *read_accesses, std::vector *write_accesses) { bool skip = false; skip |= ValidateMemoryIsBoundToBuffer(device_data, src_buffer_state, "vkCmdCopyBuffer()", VALIDATION_ERROR_18c000ee); skip |= ValidateMemoryIsBoundToBuffer(device_data, dst_buffer_state, "vkCmdCopyBuffer()", VALIDATION_ERROR_18c000f2); @@ -3469,28 +3725,40 @@ bool PreCallValidateCmdCopyBuffer(layer_data *device_data, GLOBAL_CB_NODE *cb_no VALIDATION_ERROR_18c000ec, "vkCmdCopyBuffer()", "VK_BUFFER_USAGE_TRANSFER_SRC_BIT"); skip |= ValidateBufferUsageFlags(device_data, dst_buffer_state, VK_BUFFER_USAGE_TRANSFER_DST_BIT, true, VALIDATION_ERROR_18c000f0, "vkCmdCopyBuffer()", "VK_BUFFER_USAGE_TRANSFER_DST_BIT"); - skip |= ValidateCmdQueueFlags(device_data, cb_node, "vkCmdCopyBuffer()", + skip |= ValidateCmdQueueFlags(device_data, cb_state, "vkCmdCopyBuffer()", VK_QUEUE_TRANSFER_BIT | VK_QUEUE_GRAPHICS_BIT | VK_QUEUE_COMPUTE_BIT, VALIDATION_ERROR_18c02415); - skip |= ValidateCmd(device_data, cb_node, CMD_COPYBUFFER, "vkCmdCopyBuffer()"); - skip |= insideRenderPass(device_data, cb_node, "vkCmdCopyBuffer()", VALIDATION_ERROR_18c00017); + skip |= ValidateCmd(device_data, cb_state, CMD_COPYBUFFER, "vkCmdCopyBuffer()"); + skip |= insideRenderPass(device_data, cb_state, "vkCmdCopyBuffer()", VALIDATION_ERROR_18c00017); + const auto src_mem_obj = src_buffer_state->binding.mem; + const auto dst_mem_obj = dst_buffer_state->binding.mem; + for (uint32_t i = 0; i < region_count; ++i) { + const auto ® = regions[i]; + AddReadMemoryAccess(CMD_COPYBUFFER, read_accesses, {src_mem_obj, reg.srcOffset, reg.size}, true); + AddWriteMemoryAccess(CMD_COPYBUFFER, write_accesses, {dst_mem_obj, reg.dstOffset, reg.size}, true); + } + skip |= ValidateRWMemoryAccesses(core_validation::GetReportData(device_data), cb_state->commandBuffer, cb_state->mem_accesses, + read_accesses, write_accesses, "vkCmdCopyBuffer()", false, nullptr, nullptr); return skip; } -void PreCallRecordCmdCopyBuffer(layer_data *device_data, GLOBAL_CB_NODE *cb_node, BUFFER_STATE *src_buffer_state, - BUFFER_STATE *dst_buffer_state) { +void PreCallRecordCmdCopyBuffer(layer_data *device_data, GLOBAL_CB_NODE *cb_state, BUFFER_STATE *src_buffer_state, + BUFFER_STATE *dst_buffer_state, std::vector *read_accesses, + std::vector *write_accesses) { // Update bindings between buffers and cmd buffer - AddCommandBufferBindingBuffer(device_data, cb_node, src_buffer_state); - AddCommandBufferBindingBuffer(device_data, cb_node, dst_buffer_state); + AddCommandBufferBindingBuffer(device_data, cb_state, src_buffer_state); + AddCommandBufferBindingBuffer(device_data, cb_state, dst_buffer_state); std::function function = [=]() { return ValidateBufferMemoryIsValid(device_data, src_buffer_state, "vkCmdCopyBuffer()"); }; - cb_node->queue_submit_functions.push_back(function); + cb_state->queue_submit_functions.push_back(function); function = [=]() { SetBufferMemoryValid(device_data, dst_buffer_state, true); return false; }; - cb_node->queue_submit_functions.push_back(function); + cb_state->queue_submit_functions.push_back(function); + AddCommandBufferCommandMemoryAccesses(cb_state, CMD_COPYBUFFER, read_accesses); + AddCommandBufferCommandMemoryAccesses(cb_state, CMD_COPYBUFFER, write_accesses); } static bool validateIdleBuffer(layer_data *device_data, VkBuffer buffer) { @@ -3573,27 +3841,33 @@ void PostCallRecordDestroyBufferView(layer_data *device_data, VkBufferView buffe GetBufferViewMap(device_data)->erase(buffer_view); } -bool PreCallValidateCmdFillBuffer(layer_data *device_data, GLOBAL_CB_NODE *cb_node, BUFFER_STATE *buffer_state) { +bool PreCallValidateCmdFillBuffer(layer_data *device_data, GLOBAL_CB_NODE *cb_state, BUFFER_STATE *buffer_state, VkDeviceSize offset, + VkDeviceSize size, std::vector *mem_accesses) { bool skip = false; skip |= ValidateMemoryIsBoundToBuffer(device_data, buffer_state, "vkCmdFillBuffer()", VALIDATION_ERROR_1b40003e); - skip |= ValidateCmdQueueFlags(device_data, cb_node, "vkCmdFillBuffer()", + skip |= ValidateCmdQueueFlags(device_data, cb_state, "vkCmdFillBuffer()", VK_QUEUE_TRANSFER_BIT | VK_QUEUE_GRAPHICS_BIT | VK_QUEUE_COMPUTE_BIT, VALIDATION_ERROR_1b402415); - skip |= ValidateCmd(device_data, cb_node, CMD_FILLBUFFER, "vkCmdFillBuffer()"); + skip |= ValidateCmd(device_data, cb_state, CMD_FILLBUFFER, "vkCmdFillBuffer()"); // Validate that DST buffer has correct usage flags set skip |= ValidateBufferUsageFlags(device_data, buffer_state, VK_BUFFER_USAGE_TRANSFER_DST_BIT, true, VALIDATION_ERROR_1b40003a, "vkCmdFillBuffer()", "VK_BUFFER_USAGE_TRANSFER_DST_BIT"); - skip |= insideRenderPass(device_data, cb_node, "vkCmdFillBuffer()", VALIDATION_ERROR_1b400017); + skip |= insideRenderPass(device_data, cb_state, "vkCmdFillBuffer()", VALIDATION_ERROR_1b400017); + skip |= CreateAndValidateWriteMemoryAccess(core_validation::GetReportData(device_data), CMD_FILLBUFFER, cb_state->commandBuffer, + cb_state->mem_accesses, mem_accesses, {buffer_state->binding.mem, offset, size}, + true, "vkCmdFillBuffer()"); return skip; } -void PreCallRecordCmdFillBuffer(layer_data *device_data, GLOBAL_CB_NODE *cb_node, BUFFER_STATE *buffer_state) { +void PreCallRecordCmdFillBuffer(layer_data *device_data, GLOBAL_CB_NODE *cb_state, BUFFER_STATE *buffer_state, + std::vector *mem_accesses) { std::function function = [=]() { SetBufferMemoryValid(device_data, buffer_state, true); return false; }; - cb_node->queue_submit_functions.push_back(function); + cb_state->queue_submit_functions.push_back(function); // Update bindings between buffer and cmd buffer - AddCommandBufferBindingBuffer(device_data, cb_node, buffer_state); + AddCommandBufferBindingBuffer(device_data, cb_state, buffer_state); + AddCommandBufferCommandMemoryAccesses(cb_state, CMD_FILLBUFFER, mem_accesses); } bool ValidateBufferImageCopyData(const debug_report_data *report_data, uint32_t regionCount, const VkBufferImageCopy *pRegions, diff --git a/layers/buffer_validation.h b/layers/buffer_validation.h index dbc0127ee8..795237f4d1 100644 --- a/layers/buffer_validation.h +++ b/layers/buffer_validation.h @@ -59,14 +59,16 @@ void RecordClearImageLayout(layer_data *dev_data, GLOBAL_CB_NODE *cb_node, VkIma VkImageLayout dest_image_layout); bool PreCallValidateCmdClearColorImage(layer_data *dev_data, VkCommandBuffer commandBuffer, VkImage image, - VkImageLayout imageLayout, uint32_t rangeCount, const VkImageSubresourceRange *pRanges); + VkImageLayout imageLayout, uint32_t rangeCount, const VkImageSubresourceRange *pRanges, + std::vector *mem_accesses); void PreCallRecordCmdClearImage(layer_data *dev_data, VkCommandBuffer commandBuffer, VkImage image, VkImageLayout imageLayout, - uint32_t rangeCount, const VkImageSubresourceRange *pRanges); + uint32_t rangeCount, const VkImageSubresourceRange *pRanges, CMD_TYPE cmd, + std::vector *mem_accesses); bool PreCallValidateCmdClearDepthStencilImage(layer_data *dev_data, VkCommandBuffer commandBuffer, VkImage image, VkImageLayout imageLayout, uint32_t rangeCount, - const VkImageSubresourceRange *pRanges); + const VkImageSubresourceRange *pRanges, std::vector *mem_accesses); bool FindLayoutVerifyNode(layer_data const *device_data, GLOBAL_CB_NODE const *pCB, ImageSubresourcePair imgpair, IMAGE_CMD_BUF_LAYOUT_NODE &node, const VkImageAspectFlags aspectMask); @@ -133,24 +135,60 @@ bool VerifyDestImageLayout(layer_data *dev_data, GLOBAL_CB_NODE *cb_node, VkImag void TransitionFinalSubpassLayouts(layer_data *dev_data, GLOBAL_CB_NODE *pCB, const VkRenderPassBeginInfo *pRenderPassBegin, FRAMEBUFFER_STATE *framebuffer_state); +void AddMemoryAccess(CMD_TYPE cmd, std::vector *mem_accesses, MemoryAccess *mem_access, bool write, + uint32_t rw_index); + +void AddReadMemoryAccess(CMD_TYPE cmd, std::vector *mem_accesses, MEM_BINDING const &binding, bool precise); + +void AddWriteMemoryAccess(CMD_TYPE cmd, std::vector *mem_accesses, MEM_BINDING const &binding, bool precise); + +bool CreateAndValidateReadMemoryAccess(debug_report_data const *report_data, CMD_TYPE cmd, VkCommandBuffer command_buffer, + const MemAccessGroup &prev_mem_accesses, std::vector *mem_accesses, + MEM_BINDING const &binding, bool precise, const char *caller); + +bool CreateAndValidateWriteMemoryAccess(debug_report_data const *report_data, CMD_TYPE cmd, VkCommandBuffer command_buffer, + const MemAccessGroup &prev_mem_accesses, std::vector *mem_accesses, + MEM_BINDING const &binding, bool precise, const char *caller); + +bool MemoryConflict(MemoryAccess const *initial_access, MemoryAccess const *second_access); + +bool ValidateMemoryAccesses(debug_report_data const *report_data, VkCommandBuffer command_buffer, + std::unordered_map> &prev_mem_access_map, + std::vector *new_accesses, const char *caller, bool pre_check, + MemoryAccess **early_conflict, MemoryAccess **late_conflict); + +bool ValidateRWMemoryAccesses(debug_report_data const *report_data, VkCommandBuffer command_buffer, + MemAccessGroup &prev_mem_accesses, std::vector *read_accesses, + std::vector *write_accesses, const char *caller, bool pre_check, + MemoryAccess **early_conflict, MemoryAccess **late_conflict); + +void AddCommandBufferCommandMemoryAccesses(GLOBAL_CB_NODE *cb_state, CMD_TYPE cmd, std::vector *mem_accesses); + bool PreCallValidateCmdCopyImage(layer_data *device_data, GLOBAL_CB_NODE *cb_node, IMAGE_STATE *src_image_state, IMAGE_STATE *dst_image_state, uint32_t region_count, const VkImageCopy *regions, - VkImageLayout src_image_layout, VkImageLayout dst_image_layout); + VkImageLayout src_image_layout, VkImageLayout dst_image_layout, + std::vector *mem_accesses); + +bool PreCallValidateCmdClearAttachments(layer_data *device_data, GLOBAL_CB_NODE *cb_state, uint32_t attachmentCount, + const VkClearAttachment *pAttachments, uint32_t rectCount, const VkClearRect *pRects, + std::vector *mem_accesses); -bool PreCallValidateCmdClearAttachments(layer_data *device_data, VkCommandBuffer commandBuffer, uint32_t attachmentCount, - const VkClearAttachment *pAttachments, uint32_t rectCount, const VkClearRect *pRects); +void PreCallRecordCmdClearAttachments(debug_report_data const *report_data, GLOBAL_CB_NODE *cb_state, + std::vector *mem_accesses); bool PreCallValidateCmdResolveImage(layer_data *device_data, GLOBAL_CB_NODE *cb_node, IMAGE_STATE *src_image_state, - IMAGE_STATE *dst_image_state, uint32_t regionCount, const VkImageResolve *pRegions); + IMAGE_STATE *dst_image_state, uint32_t regionCount, const VkImageResolve *pRegions, + std::vector *mem_accesses); void PreCallRecordCmdResolveImage(layer_data *device_data, GLOBAL_CB_NODE *cb_node, IMAGE_STATE *src_image_state, - IMAGE_STATE *dst_image_state); + IMAGE_STATE *dst_image_state, std::vector *mem_accesses); bool PreCallValidateCmdBlitImage(layer_data *device_data, GLOBAL_CB_NODE *cb_node, IMAGE_STATE *src_image_state, - IMAGE_STATE *dst_image_state, uint32_t regionCount, const VkImageBlit *pRegions, VkFilter filter); + IMAGE_STATE *dst_image_state, uint32_t regionCount, const VkImageBlit *pRegions, VkFilter filter, + std::vector *mem_accesses); void PreCallRecordCmdBlitImage(layer_data *device_data, GLOBAL_CB_NODE *cb_node, IMAGE_STATE *src_image_state, - IMAGE_STATE *dst_image_state); + IMAGE_STATE *dst_image_state, std::vector *mem_accesses); bool ValidateCmdBufImageLayouts(layer_data *device_data, GLOBAL_CB_NODE *pCB, std::unordered_map const &globalImageLayoutMap, @@ -209,13 +247,16 @@ bool ValidateCopyBufferImageTransferGranularityRequirements(layer_data *device_d void PreCallRecordCmdCopyImage(layer_data *device_data, GLOBAL_CB_NODE *cb_node, IMAGE_STATE *src_image_state, IMAGE_STATE *dst_image_state, uint32_t region_count, const VkImageCopy *regions, - VkImageLayout src_image_layout, VkImageLayout dst_image_layout); + VkImageLayout src_image_layout, VkImageLayout dst_image_layout, + std::vector *mem_accesses); -bool PreCallValidateCmdCopyBuffer(layer_data *device_data, GLOBAL_CB_NODE *cb_node, BUFFER_STATE *src_buffer_state, - BUFFER_STATE *dst_buffer_state); +bool PreCallValidateCmdCopyBuffer(layer_data *device_data, GLOBAL_CB_NODE *cb_state, BUFFER_STATE *src_buffer_state, + BUFFER_STATE *dst_buffer_state, uint32_t region_count, const VkBufferCopy *regions, + std::vector *read_accesses, std::vector *write_accesses); void PreCallRecordCmdCopyBuffer(layer_data *device_data, GLOBAL_CB_NODE *cb_node, BUFFER_STATE *src_buffer_state, - BUFFER_STATE *dst_buffer_state); + BUFFER_STATE *dst_buffer_state, std::vector *read_accesses, + std::vector *write_accesses); bool PreCallValidateDestroyImageView(layer_data *device_data, VkImageView image_view, IMAGE_VIEW_STATE **image_view_state, VK_OBJECT *obj_struct); @@ -233,9 +274,11 @@ bool PreCallValidateDestroyBufferView(layer_data *device_data, VkBufferView buff void PostCallRecordDestroyBufferView(layer_data *device_data, VkBufferView buffer_view, BUFFER_VIEW_STATE *buffer_view_state, VK_OBJECT obj_struct); -bool PreCallValidateCmdFillBuffer(layer_data *device_data, GLOBAL_CB_NODE *cb_node, BUFFER_STATE *buffer_state); +bool PreCallValidateCmdFillBuffer(layer_data *device_data, GLOBAL_CB_NODE *cb_node, BUFFER_STATE *buffer_state, VkDeviceSize offset, + VkDeviceSize size, std::vector *mem_accesses); -void PreCallRecordCmdFillBuffer(layer_data *device_data, GLOBAL_CB_NODE *cb_node, BUFFER_STATE *buffer_state); +void PreCallRecordCmdFillBuffer(layer_data *device_data, GLOBAL_CB_NODE *cb_node, BUFFER_STATE *buffer_state, + std::vector *mem_accesses); bool PreCallValidateCmdCopyImageToBuffer(layer_data *device_data, VkImageLayout srcImageLayout, GLOBAL_CB_NODE *cb_node, IMAGE_STATE *src_image_state, BUFFER_STATE *dst_buff_state, uint32_t regionCount, diff --git a/layers/core_validation.cpp b/layers/core_validation.cpp index a56cbe36c2..4b630b8e03 100644 --- a/layers/core_validation.cpp +++ b/layers/core_validation.cpp @@ -1148,8 +1148,6 @@ static void UpdateDrawState(layer_data *dev_data, GLOBAL_CB_NODE *cb_state, cons if (!descriptor_set->IsPushDescriptor()) { // Bind this set and its active descriptor resources to the command buffer descriptor_set->BindCommandBuffer(cb_state, set_binding_pair.second); - // For given active slots record updated images & buffers - descriptor_set->GetStorageUpdates(set_binding_pair.second, &cb_state->updateBuffers, &cb_state->updateImages); } } } @@ -1720,6 +1718,9 @@ static void resetCB(layer_data *dev_data, const VkCommandBuffer cb) { pCB->activeSubpassContents = VK_SUBPASS_CONTENTS_INLINE; pCB->activeSubpass = 0; pCB->broken_bindings.clear(); + pCB->commands.clear(); + pCB->synch_commands.clear(); + pCB->mem_accesses.reset(); pCB->waitedEvents.clear(); pCB->events.clear(); pCB->writeEventsBeforeWait.clear(); @@ -1745,8 +1746,6 @@ static void resetCB(layer_data *dev_data, const VkCommandBuffer cb) { pSubCB->linkedCommandBuffers.erase(pCB); } pCB->linkedCommandBuffers.clear(); - pCB->updateImages.clear(); - pCB->updateBuffers.clear(); clear_cmd_buf_and_mem_references(dev_data, pCB); pCB->queue_submit_functions.clear(); pCB->cmd_execute_commands_functions.clear(); @@ -2619,6 +2618,81 @@ static void PostCallRecordQueueSubmit(layer_data *dev_data, VkQueue queue, uint3 } } +// Prototype +void ReplayMemoryAccessCommands(layer_data *, std::vector> *, size_t, size_t, + std::unordered_map> *, std::vector *); + +// Validate if the commands in the given cb_state conflict with any of the live memory access commands that were previously executed +// by commands in the replay_command_buffers vector and are stored in the mem_access_map mem_access_map will be updated to include +// any new live mem accesses from commands in the current cb_state +bool ValidateInterCmdBufferMemoryAccesses(layer_data *dev_data, GLOBAL_CB_NODE *cb_state, + const std::vector &replay_command_buffers, + std::unordered_map> *mem_access_map) { + bool skip = false; + // TODO: Currently constructing vector of live mem accesses for this CB on-the-fly. Really should have this pre-built + std::vector cb_live_accesses; + for (const auto &access_map : cb_state->mem_accesses.access_maps) { + for (const auto &mem_access_pair : access_map) { + for (const auto &mem_access : mem_access_pair.second) { + if (!mem_access.Visible()) { + // No applicable barrier so access is live + cb_live_accesses.push_back(mem_access); + } + } + } + } + // Run a pre-check and if there's no conflicts, just record updated accesses + MemoryAccess *early_conflict = nullptr, *late_conflict = nullptr; + if (ValidateMemoryAccesses(dev_data->report_data, cb_state->commandBuffer, *mem_access_map, &cb_live_accesses, + "vkQueueSubmit()", true, &early_conflict, &late_conflict)) { + std::vector> submit_cmds; + size_t start_replay_index = 0, end_replay_index = 0; // Indices that set bounds for replaying synch cmds + // This is the slow path + // We have to replay so copy cmd sequence up to this point into local vector + // Then mark seq replay start & seq replay end indices which are seq cmds between conflicting mem accesses that must be + // replayed + bool set_start_index = false, have_end_index = false; + for (const auto &cbstate : replay_command_buffers) { + for (const auto &cmd : cbstate->commands) { + if (cmd.get() == early_conflict->cmd) { + set_start_index = true; // We'll grab next synch cmd index + } else if (cmd.get() == late_conflict->cmd) { + have_end_index = true; + } + if (cmd->synch) { + if (set_start_index) { + start_replay_index = submit_cmds.size(); + } + if (!have_end_index) { // We'll just keep grabbing synch commands until we find late conflict + // command above + end_replay_index = submit_cmds.size(); + } + SynchCommand *synch_cmd = static_cast(cmd.get()); + submit_cmds.emplace_back(unique_ptr(new SynchCommand(*synch_cmd))); + } else { + submit_cmds.emplace_back(unique_ptr(new Command(*cmd))); + } + } + } + if (start_replay_index && end_replay_index) { + // We have synch commands between the conflict to replay. Clear current access map as well as live accesses + // which will both be filled on replay. + mem_access_map->clear(); + cb_live_accesses.clear(); + ReplayMemoryAccessCommands(dev_data, &submit_cmds, start_replay_index, end_replay_index, mem_access_map, + &cb_live_accesses); + } + // Now that any synch replays are done, validate the mem accesses for real + skip |= ValidateMemoryAccesses(dev_data->report_data, cb_state->commandBuffer, *mem_access_map, &cb_live_accesses, + "vkQueueSubmit()", false, nullptr, nullptr); + } + // We always update the access map with any outstanding live accesses for checks going fwd + for (const auto &mem_access : cb_live_accesses) { + (*mem_access_map)[mem_access.location.mem].push_back(mem_access); + } + return skip; +} + static bool PreCallValidateQueueSubmit(layer_data *dev_data, VkQueue queue, uint32_t submitCount, const VkSubmitInfo *pSubmits, VkFence fence) { auto pFence = GetFenceNode(dev_data, fence); @@ -2632,6 +2706,10 @@ static bool PreCallValidateQueueSubmit(layer_data *dev_data, VkQueue queue, uint vector current_cmds; unordered_map localImageLayoutMap; // Now verify each individual submit + // We'll store a map of submit's memory accesses as we submit cmd buffers in order to find cross-cmd-buffer memory conflicts + std::unordered_map> submit_mem_access_map; + // If we hit a potential cross-CB synch conflict, we'll replay CBs up to point of conflict to verify if conflict is real + std::vector replay_command_buffers; for (uint32_t submit_idx = 0; submit_idx < submitCount; submit_idx++) { const VkSubmitInfo *submit = &pSubmits[submit_idx]; for (uint32_t i = 0; i < submit->waitSemaphoreCount; ++i) { @@ -2673,6 +2751,7 @@ static bool PreCallValidateQueueSubmit(layer_data *dev_data, VkQueue queue, uint if (cb_node) { skip |= ValidateCmdBufImageLayouts(dev_data, cb_node, dev_data->imageLayoutMap, localImageLayoutMap); current_cmds.push_back(submit->pCommandBuffers[i]); + replay_command_buffers.push_back(cb_node); skip |= validatePrimaryCommandBufferState( dev_data, cb_node, (int)std::count(current_cmds.begin(), current_cmds.end(), submit->pCommandBuffers[i])); skip |= validateQueueFamilyIndices(dev_data, cb_node, queue); @@ -2692,6 +2771,7 @@ static bool PreCallValidateQueueSubmit(layer_data *dev_data, VkQueue queue, uint for (auto &function : cb_node->queryUpdates) { skip |= function(queue); } + skip |= ValidateInterCmdBufferMemoryAccesses(dev_data, cb_node, replay_command_buffers, &submit_mem_access_map); } } } @@ -3995,6 +4075,13 @@ VKAPI_ATTR VkResult VKAPI_CALL CreateQueryPool(VkDevice device, const VkQueryPoo lock_guard_t lock(global_lock); QUERY_POOL_NODE *qp_node = &dev_data->queryPoolMap[*pQueryPool]; qp_node->createInfo = *pCreateInfo; + // Count data elements per query for later size calculation + size_t data_count = 1; + if (VK_QUERY_TYPE_PIPELINE_STATISTICS == pCreateInfo->queryType) { + std::bitset<32> stat_bits(pCreateInfo->pipelineStatistics); + data_count = stat_bits.count(); + } + qp_node->data_count = data_count; } return result; } @@ -5691,8 +5778,10 @@ VKAPI_ATTR void VKAPI_CALL CmdBindVertexBuffers(VkCommandBuffer commandBuffer, u } // Expects global_lock to be held by caller -static void MarkStoreImagesAndBuffersAsWritten(layer_data *dev_data, GLOBAL_CB_NODE *pCB) { - for (auto imageView : pCB->updateImages) { +// Mark write buffers & images as valid & record all of the memory accesses into access_map for CB +static void UpdateDrawMemoryAccessState(layer_data *dev_data, GLOBAL_CB_NODE *cb_state, CMD_TYPE cmd, + std::vector *read_accesses, std::vector *write_accesses) { + for (auto imageView : cb_state->mem_accesses.write_images) { auto view_state = GetImageViewState(dev_data, imageView); if (!view_state) continue; @@ -5702,22 +5791,62 @@ static void MarkStoreImagesAndBuffersAsWritten(layer_data *dev_data, GLOBAL_CB_N SetImageMemoryValid(dev_data, image_state, true); return false; }; - pCB->queue_submit_functions.push_back(function); + cb_state->queue_submit_functions.push_back(function); } - for (auto buffer : pCB->updateBuffers) { + for (auto buffer : cb_state->mem_accesses.write_buffers) { auto buffer_state = GetBufferState(dev_data, buffer); assert(buffer_state); std::function function = [=]() { SetBufferMemoryValid(dev_data, buffer_state, true); return false; }; - pCB->queue_submit_functions.push_back(function); + cb_state->queue_submit_functions.push_back(function); + } + AddCommandBufferCommandMemoryAccesses(cb_state, cmd, read_accesses); + AddCommandBufferCommandMemoryAccesses(cb_state, cmd, write_accesses); +} + +// Update read & write mem_access vectors from r/w buffer/image sets in given DrawDispatchAccesses struct +static void UpdateRWMemoryAccessVectors(layer_data *device_data, CMD_TYPE cmd, std::vector *read_access_vector, + std::vector *write_access_vector, const MemAccessGroup &mem_accesses) { + // Parse through r/w sets and conservatively add MemAccesses across whole bindings + // TODO: Would like to limit these to actual size of updates where possible + for (const auto buffer : mem_accesses.read_buffers) { + const auto &buff_state = GetBufferState(device_data, buffer); + if (buff_state) { + AddReadMemoryAccess(cmd, read_access_vector, buff_state->binding, false); + } + } + for (const auto iv : mem_accesses.read_images) { + const auto &iv_state = GetImageViewState(device_data, iv); + if (iv_state) { + const auto &img_state = GetImageState(device_data, iv_state->create_info.image); + if (img_state) { + AddReadMemoryAccess(cmd, read_access_vector, img_state->binding, false); + } + } + } + for (const auto buffer : mem_accesses.write_buffers) { + const auto &buff_state = GetBufferState(device_data, buffer); + if (buff_state) { + AddWriteMemoryAccess(cmd, write_access_vector, buff_state->binding, false); + } + } + for (const auto iv : mem_accesses.write_images) { + const auto &iv_state = GetImageViewState(device_data, iv); + if (iv_state) { + const auto &img_state = GetImageState(device_data, iv_state->create_info.image); + if (img_state) { + AddWriteMemoryAccess(cmd, write_access_vector, img_state->binding, false); + } + } } } // Generic function to handle validation for all CmdDraw* type functions static bool ValidateCmdDrawType(layer_data *dev_data, VkCommandBuffer cmd_buffer, bool indexed, VkPipelineBindPoint bind_point, - CMD_TYPE cmd_type, GLOBAL_CB_NODE **cb_state, const char *caller, VkQueueFlags queue_flags, + CMD_TYPE cmd_type, GLOBAL_CB_NODE **cb_state, std::vector *read_accesses, + std::vector *write_accesses, const char *caller, VkQueueFlags queue_flags, UNIQUE_VALIDATION_ERROR_CODE queue_flag_code, UNIQUE_VALIDATION_ERROR_CODE msg_code, UNIQUE_VALIDATION_ERROR_CODE const dynamic_state_msg_code) { bool skip = false; @@ -5728,90 +5857,135 @@ static bool ValidateCmdDrawType(layer_data *dev_data, VkCommandBuffer cmd_buffer skip |= ValidateDrawState(dev_data, *cb_state, indexed, bind_point, caller, dynamic_state_msg_code); skip |= (VK_PIPELINE_BIND_POINT_GRAPHICS == bind_point) ? outsideRenderPass(dev_data, *cb_state, caller, msg_code) : insideRenderPass(dev_data, *cb_state, caller, msg_code); +#ifndef ENABLE_MEMORY_ACCESS_CALLBACK + // TODO : Early return here to skip the memory access checking below. The checks are functional but cause a perf hit + // and the callback that's used is disabled, so also turning off these checks for now. + // To re-enable the checks, just remove this early return + return skip; +#endif + // Grab mem accesses for this draw & check for missing synchs + auto const &state = (*cb_state)->lastBound[bind_point]; + PIPELINE_STATE *pPipe = state.pipeline_state; + if (VK_NULL_HANDLE != state.pipeline_layout.layout) { + MemAccessGroup mem_access_group; + for (const auto &set_binding_pair : pPipe->active_slots) { + uint32_t setIndex = set_binding_pair.first; + // Pull the set node + cvdescriptorset::DescriptorSet *descriptor_set = state.boundDescriptorSets[setIndex]; + if (descriptor_set) { + // For given active slots record updated images & buffers + descriptor_set->GetReadWriteBuffersAndImages(set_binding_pair.second, &mem_access_group.read_buffers, + &mem_access_group.read_images, &mem_access_group.write_buffers, + &mem_access_group.write_images); + UpdateRWMemoryAccessVectors(dev_data, cmd_type, read_accesses, write_accesses, mem_access_group); + } + } + skip |= ValidateRWMemoryAccesses(dev_data->report_data, cmd_buffer, (*cb_state)->mem_accesses, read_accesses, + write_accesses, caller, false, nullptr, nullptr); + } } return skip; } // Generic function to handle state update for all CmdDraw* and CmdDispatch* type functions -static void UpdateStateCmdDrawDispatchType(layer_data *dev_data, GLOBAL_CB_NODE *cb_state, VkPipelineBindPoint bind_point) { +static void UpdateStateCmdDrawDispatchType(layer_data *dev_data, GLOBAL_CB_NODE *cb_state, CMD_TYPE cmd, + VkPipelineBindPoint bind_point, std::vector *read_accesses, + std::vector *write_accesses) { UpdateDrawState(dev_data, cb_state, bind_point); - MarkStoreImagesAndBuffersAsWritten(dev_data, cb_state); + UpdateDrawMemoryAccessState(dev_data, cb_state, cmd, read_accesses, write_accesses); } // Generic function to handle state update for all CmdDraw* type functions -static void UpdateStateCmdDrawType(layer_data *dev_data, GLOBAL_CB_NODE *cb_state, VkPipelineBindPoint bind_point) { - UpdateStateCmdDrawDispatchType(dev_data, cb_state, bind_point); +static void UpdateStateCmdDrawType(layer_data *dev_data, GLOBAL_CB_NODE *cb_state, CMD_TYPE cmd, VkPipelineBindPoint bind_point, + std::vector *read_accesses, std::vector *write_accesses) { + UpdateStateCmdDrawDispatchType(dev_data, cb_state, cmd, bind_point, read_accesses, write_accesses); updateResourceTrackingOnDraw(cb_state); cb_state->hasDrawCmd = true; } static bool PreCallValidateCmdDraw(layer_data *dev_data, VkCommandBuffer cmd_buffer, bool indexed, VkPipelineBindPoint bind_point, - GLOBAL_CB_NODE **cb_state, const char *caller) { - return ValidateCmdDrawType(dev_data, cmd_buffer, indexed, bind_point, CMD_DRAW, cb_state, caller, VK_QUEUE_GRAPHICS_BIT, - VALIDATION_ERROR_1a202415, VALIDATION_ERROR_1a200017, VALIDATION_ERROR_1a200376); + GLOBAL_CB_NODE **cb_state, std::vector *read_accesses, + std::vector *write_accesses, const char *caller) { + return ValidateCmdDrawType(dev_data, cmd_buffer, indexed, bind_point, CMD_DRAW, cb_state, read_accesses, write_accesses, caller, + VK_QUEUE_GRAPHICS_BIT, VALIDATION_ERROR_1a202415, VALIDATION_ERROR_1a200017, + VALIDATION_ERROR_1a200376); } -static void PostCallRecordCmdDraw(layer_data *dev_data, GLOBAL_CB_NODE *cb_state, VkPipelineBindPoint bind_point) { - UpdateStateCmdDrawType(dev_data, cb_state, bind_point); +static void PreCallRecordCmdDraw(layer_data *dev_data, GLOBAL_CB_NODE *cb_state, CMD_TYPE cmd, VkPipelineBindPoint bind_point, + std::vector *read_accesses, std::vector *write_accesses) { + UpdateStateCmdDrawType(dev_data, cb_state, cmd, bind_point, read_accesses, write_accesses); } VKAPI_ATTR void VKAPI_CALL CmdDraw(VkCommandBuffer commandBuffer, uint32_t vertexCount, uint32_t instanceCount, uint32_t firstVertex, uint32_t firstInstance) { layer_data *dev_data = GetLayerDataPtr(get_dispatch_key(commandBuffer), layer_data_map); GLOBAL_CB_NODE *cb_state = nullptr; + std::vector read_accesses, write_accesses; unique_lock_t lock(global_lock); - bool skip = PreCallValidateCmdDraw(dev_data, commandBuffer, false, VK_PIPELINE_BIND_POINT_GRAPHICS, &cb_state, "vkCmdDraw()"); - lock.unlock(); + bool skip = PreCallValidateCmdDraw(dev_data, commandBuffer, false, VK_PIPELINE_BIND_POINT_GRAPHICS, &cb_state, &read_accesses, + &write_accesses, "vkCmdDraw()"); if (!skip) { - dev_data->dispatch_table.CmdDraw(commandBuffer, vertexCount, instanceCount, firstVertex, firstInstance); - lock.lock(); - PostCallRecordCmdDraw(dev_data, cb_state, VK_PIPELINE_BIND_POINT_GRAPHICS); + PreCallRecordCmdDraw(dev_data, cb_state, CMD_DRAW, VK_PIPELINE_BIND_POINT_GRAPHICS, &read_accesses, &write_accesses); lock.unlock(); + dev_data->dispatch_table.CmdDraw(commandBuffer, vertexCount, instanceCount, firstVertex, firstInstance); } } static bool PreCallValidateCmdDrawIndexed(layer_data *dev_data, VkCommandBuffer cmd_buffer, bool indexed, - VkPipelineBindPoint bind_point, GLOBAL_CB_NODE **cb_state, const char *caller) { - return ValidateCmdDrawType(dev_data, cmd_buffer, indexed, bind_point, CMD_DRAWINDEXED, cb_state, caller, VK_QUEUE_GRAPHICS_BIT, - VALIDATION_ERROR_1a402415, VALIDATION_ERROR_1a400017, VALIDATION_ERROR_1a40039c); + VkPipelineBindPoint bind_point, GLOBAL_CB_NODE **cb_state, + std::vector *read_accesses, std::vector *write_accesses, + const char *caller) { + return ValidateCmdDrawType(dev_data, cmd_buffer, indexed, bind_point, CMD_DRAWINDEXED, cb_state, read_accesses, write_accesses, + caller, VK_QUEUE_GRAPHICS_BIT, VALIDATION_ERROR_1a402415, VALIDATION_ERROR_1a400017, + VALIDATION_ERROR_1a40039c); } -static void PostCallRecordCmdDrawIndexed(layer_data *dev_data, GLOBAL_CB_NODE *cb_state, VkPipelineBindPoint bind_point) { - UpdateStateCmdDrawType(dev_data, cb_state, bind_point); +static void PreCallRecordCmdDrawIndexed(layer_data *dev_data, GLOBAL_CB_NODE *cb_state, CMD_TYPE cmd, + VkPipelineBindPoint bind_point, std::vector *read_accesses, + std::vector *write_accesses) { + UpdateStateCmdDrawType(dev_data, cb_state, cmd, bind_point, read_accesses, write_accesses); } VKAPI_ATTR void VKAPI_CALL CmdDrawIndexed(VkCommandBuffer commandBuffer, uint32_t indexCount, uint32_t instanceCount, uint32_t firstIndex, int32_t vertexOffset, uint32_t firstInstance) { layer_data *dev_data = GetLayerDataPtr(get_dispatch_key(commandBuffer), layer_data_map); GLOBAL_CB_NODE *cb_state = nullptr; + std::vector read_accesses, write_accesses; unique_lock_t lock(global_lock); bool skip = PreCallValidateCmdDrawIndexed(dev_data, commandBuffer, true, VK_PIPELINE_BIND_POINT_GRAPHICS, &cb_state, - "vkCmdDrawIndexed()"); - lock.unlock(); + &read_accesses, &write_accesses, "vkCmdDrawIndexed()"); if (!skip) { - dev_data->dispatch_table.CmdDrawIndexed(commandBuffer, indexCount, instanceCount, firstIndex, vertexOffset, firstInstance); - lock.lock(); - PostCallRecordCmdDrawIndexed(dev_data, cb_state, VK_PIPELINE_BIND_POINT_GRAPHICS); + PreCallRecordCmdDrawIndexed(dev_data, cb_state, CMD_DRAWINDEXED, VK_PIPELINE_BIND_POINT_GRAPHICS, &read_accesses, + &write_accesses); lock.unlock(); + dev_data->dispatch_table.CmdDrawIndexed(commandBuffer, indexCount, instanceCount, firstIndex, vertexOffset, firstInstance); } } static bool PreCallValidateCmdDrawIndirect(layer_data *dev_data, VkCommandBuffer cmd_buffer, VkBuffer buffer, bool indexed, VkPipelineBindPoint bind_point, GLOBAL_CB_NODE **cb_state, BUFFER_STATE **buffer_state, - const char *caller) { - bool skip = - ValidateCmdDrawType(dev_data, cmd_buffer, indexed, bind_point, CMD_DRAWINDIRECT, cb_state, caller, VK_QUEUE_GRAPHICS_BIT, - VALIDATION_ERROR_1aa02415, VALIDATION_ERROR_1aa00017, VALIDATION_ERROR_1aa003cc); + std::vector *read_accesses, std::vector *write_accesses, + VkDeviceSize offset, uint32_t count, uint32_t stride, const char *caller) { + bool skip = ValidateCmdDrawType(dev_data, cmd_buffer, indexed, bind_point, CMD_DRAWINDIRECT, cb_state, read_accesses, + write_accesses, caller, VK_QUEUE_GRAPHICS_BIT, VALIDATION_ERROR_1aa02415, + VALIDATION_ERROR_1aa00017, VALIDATION_ERROR_1aa003cc); *buffer_state = GetBufferState(dev_data, buffer); skip |= ValidateMemoryIsBoundToBuffer(dev_data, *buffer_state, caller, VALIDATION_ERROR_1aa003b4); + // TODO: This is temp code to test specific case that needs to be generalized for memory dependency checks + // First add mem read for indirect buffer + // make sure size is non-zero for count of 1 + auto size = stride * (count-1) + sizeof(VkDrawIndirectCommand); + skip |= CreateAndValidateReadMemoryAccess(dev_data->report_data, CMD_DRAWINDIRECT, cmd_buffer, (*cb_state)->mem_accesses, + read_accesses, {(*buffer_state)->binding.mem, offset, size}, false, caller); // TODO: If the drawIndirectFirstInstance feature is not enabled, all the firstInstance members of the // VkDrawIndirectCommand structures accessed by this command must be 0, which will require access to the contents of 'buffer'. return skip; } -static void PostCallRecordCmdDrawIndirect(layer_data *dev_data, GLOBAL_CB_NODE *cb_state, VkPipelineBindPoint bind_point, - BUFFER_STATE *buffer_state) { - UpdateStateCmdDrawType(dev_data, cb_state, bind_point); +static void PreCallRecordCmdDrawIndirect(layer_data *dev_data, GLOBAL_CB_NODE *cb_state, VkPipelineBindPoint bind_point, + BUFFER_STATE *buffer_state, std::vector *read_accesses, + std::vector *write_accesses) { + UpdateStateCmdDrawType(dev_data, cb_state, CMD_DRAWINDIRECT, bind_point, read_accesses, write_accesses); AddCommandBufferBindingBuffer(dev_data, cb_state, buffer_state); } @@ -5820,24 +5994,27 @@ VKAPI_ATTR void VKAPI_CALL CmdDrawIndirect(VkCommandBuffer commandBuffer, VkBuff layer_data *dev_data = GetLayerDataPtr(get_dispatch_key(commandBuffer), layer_data_map); GLOBAL_CB_NODE *cb_state = nullptr; BUFFER_STATE *buffer_state = nullptr; + std::vector read_accesses, write_accesses; unique_lock_t lock(global_lock); bool skip = PreCallValidateCmdDrawIndirect(dev_data, commandBuffer, buffer, false, VK_PIPELINE_BIND_POINT_GRAPHICS, &cb_state, - &buffer_state, "vkCmdDrawIndirect()"); - lock.unlock(); + &buffer_state, &read_accesses, &write_accesses, offset, count, stride, + "vkCmdDrawIndirect()"); if (!skip) { - dev_data->dispatch_table.CmdDrawIndirect(commandBuffer, buffer, offset, count, stride); - lock.lock(); - PostCallRecordCmdDrawIndirect(dev_data, cb_state, VK_PIPELINE_BIND_POINT_GRAPHICS, buffer_state); + PreCallRecordCmdDrawIndirect(dev_data, cb_state, VK_PIPELINE_BIND_POINT_GRAPHICS, buffer_state, &read_accesses, + &write_accesses); lock.unlock(); + dev_data->dispatch_table.CmdDrawIndirect(commandBuffer, buffer, offset, count, stride); } } static bool PreCallValidateCmdDrawIndexedIndirect(layer_data *dev_data, VkCommandBuffer cmd_buffer, VkBuffer buffer, bool indexed, VkPipelineBindPoint bind_point, GLOBAL_CB_NODE **cb_state, - BUFFER_STATE **buffer_state, const char *caller) { - bool skip = - ValidateCmdDrawType(dev_data, cmd_buffer, indexed, bind_point, CMD_DRAWINDEXEDINDIRECT, cb_state, caller, - VK_QUEUE_GRAPHICS_BIT, VALIDATION_ERROR_1a602415, VALIDATION_ERROR_1a600017, VALIDATION_ERROR_1a600434); + std::vector *read_accesses, + std::vector *write_accesses, BUFFER_STATE **buffer_state, + const char *caller) { + bool skip = ValidateCmdDrawType(dev_data, cmd_buffer, indexed, bind_point, CMD_DRAWINDEXEDINDIRECT, cb_state, read_accesses, + write_accesses, caller, VK_QUEUE_GRAPHICS_BIT, VALIDATION_ERROR_1a602415, + VALIDATION_ERROR_1a600017, VALIDATION_ERROR_1a600434); *buffer_state = GetBufferState(dev_data, buffer); skip |= ValidateMemoryIsBoundToBuffer(dev_data, *buffer_state, caller, VALIDATION_ERROR_1a60041c); // TODO: If the drawIndirectFirstInstance feature is not enabled, all the firstInstance members of the @@ -5846,9 +6023,10 @@ static bool PreCallValidateCmdDrawIndexedIndirect(layer_data *dev_data, VkComman return skip; } -static void PostCallRecordCmdDrawIndexedIndirect(layer_data *dev_data, GLOBAL_CB_NODE *cb_state, VkPipelineBindPoint bind_point, - BUFFER_STATE *buffer_state) { - UpdateStateCmdDrawType(dev_data, cb_state, bind_point); +static void PreCallRecordCmdDrawIndexedIndirect(layer_data *dev_data, GLOBAL_CB_NODE *cb_state, VkPipelineBindPoint bind_point, + BUFFER_STATE *buffer_state, std::vector *read_accesses, + std::vector *write_accesses) { + UpdateStateCmdDrawType(dev_data, cb_state, CMD_DRAWINDEXEDINDIRECT, bind_point, read_accesses, write_accesses); AddCommandBufferBindingBuffer(dev_data, cb_state, buffer_state); } @@ -5857,57 +6035,63 @@ VKAPI_ATTR void VKAPI_CALL CmdDrawIndexedIndirect(VkCommandBuffer commandBuffer, layer_data *dev_data = GetLayerDataPtr(get_dispatch_key(commandBuffer), layer_data_map); GLOBAL_CB_NODE *cb_state = nullptr; BUFFER_STATE *buffer_state = nullptr; + std::vector read_accesses, write_accesses; unique_lock_t lock(global_lock); - bool skip = PreCallValidateCmdDrawIndexedIndirect(dev_data, commandBuffer, buffer, true, VK_PIPELINE_BIND_POINT_GRAPHICS, - &cb_state, &buffer_state, "vkCmdDrawIndexedIndirect()"); - lock.unlock(); + bool skip = + PreCallValidateCmdDrawIndexedIndirect(dev_data, commandBuffer, buffer, true, VK_PIPELINE_BIND_POINT_GRAPHICS, &cb_state, + &read_accesses, &write_accesses, &buffer_state, "vkCmdDrawIndexedIndirect()"); if (!skip) { - dev_data->dispatch_table.CmdDrawIndexedIndirect(commandBuffer, buffer, offset, count, stride); - lock.lock(); - PostCallRecordCmdDrawIndexedIndirect(dev_data, cb_state, VK_PIPELINE_BIND_POINT_GRAPHICS, buffer_state); + PreCallRecordCmdDrawIndexedIndirect(dev_data, cb_state, VK_PIPELINE_BIND_POINT_GRAPHICS, buffer_state, &read_accesses, + &write_accesses); lock.unlock(); + dev_data->dispatch_table.CmdDrawIndexedIndirect(commandBuffer, buffer, offset, count, stride); } } static bool PreCallValidateCmdDispatch(layer_data *dev_data, VkCommandBuffer cmd_buffer, bool indexed, - VkPipelineBindPoint bind_point, GLOBAL_CB_NODE **cb_state, const char *caller) { - return ValidateCmdDrawType(dev_data, cmd_buffer, indexed, bind_point, CMD_DISPATCH, cb_state, caller, VK_QUEUE_COMPUTE_BIT, - VALIDATION_ERROR_19c02415, VALIDATION_ERROR_19c00017, VALIDATION_ERROR_UNDEFINED); + VkPipelineBindPoint bind_point, GLOBAL_CB_NODE **cb_state, + std::vector *read_accesses, std::vector *write_accesses, + const char *caller) { + return ValidateCmdDrawType(dev_data, cmd_buffer, indexed, bind_point, CMD_DISPATCH, cb_state, read_accesses, write_accesses, + caller, VK_QUEUE_COMPUTE_BIT, VALIDATION_ERROR_19c02415, VALIDATION_ERROR_19c00017, + VALIDATION_ERROR_UNDEFINED); } -static void PostCallRecordCmdDispatch(layer_data *dev_data, GLOBAL_CB_NODE *cb_state, VkPipelineBindPoint bind_point) { - UpdateStateCmdDrawDispatchType(dev_data, cb_state, bind_point); +static void PreCallRecordCmdDispatch(layer_data *dev_data, GLOBAL_CB_NODE *cb_state, VkPipelineBindPoint bind_point, + std::vector *read_accesses, std::vector *write_accesses) { + UpdateStateCmdDrawDispatchType(dev_data, cb_state, CMD_DISPATCH, bind_point, read_accesses, write_accesses); } VKAPI_ATTR void VKAPI_CALL CmdDispatch(VkCommandBuffer commandBuffer, uint32_t x, uint32_t y, uint32_t z) { layer_data *dev_data = GetLayerDataPtr(get_dispatch_key(commandBuffer), layer_data_map); GLOBAL_CB_NODE *cb_state = nullptr; + std::vector read_accesses, write_accesses; unique_lock_t lock(global_lock); - bool skip = - PreCallValidateCmdDispatch(dev_data, commandBuffer, false, VK_PIPELINE_BIND_POINT_COMPUTE, &cb_state, "vkCmdDispatch()"); - lock.unlock(); + bool skip = PreCallValidateCmdDispatch(dev_data, commandBuffer, false, VK_PIPELINE_BIND_POINT_COMPUTE, &cb_state, + &read_accesses, &write_accesses, "vkCmdDispatch()"); if (!skip) { - dev_data->dispatch_table.CmdDispatch(commandBuffer, x, y, z); - lock.lock(); - PostCallRecordCmdDispatch(dev_data, cb_state, VK_PIPELINE_BIND_POINT_COMPUTE); + PreCallRecordCmdDispatch(dev_data, cb_state, VK_PIPELINE_BIND_POINT_COMPUTE, &read_accesses, &write_accesses); lock.unlock(); + dev_data->dispatch_table.CmdDispatch(commandBuffer, x, y, z); } } static bool PreCallValidateCmdDispatchIndirect(layer_data *dev_data, VkCommandBuffer cmd_buffer, VkBuffer buffer, bool indexed, VkPipelineBindPoint bind_point, GLOBAL_CB_NODE **cb_state, - BUFFER_STATE **buffer_state, const char *caller) { - bool skip = - ValidateCmdDrawType(dev_data, cmd_buffer, indexed, bind_point, CMD_DISPATCHINDIRECT, cb_state, caller, VK_QUEUE_COMPUTE_BIT, - VALIDATION_ERROR_1a002415, VALIDATION_ERROR_1a000017, VALIDATION_ERROR_UNDEFINED); + BUFFER_STATE **buffer_state, std::vector *read_accesses, + std::vector *write_accesses, const char *caller) { + bool skip = ValidateCmdDrawType(dev_data, cmd_buffer, indexed, bind_point, CMD_DISPATCHINDIRECT, cb_state, read_accesses, + write_accesses, caller, VK_QUEUE_COMPUTE_BIT, VALIDATION_ERROR_1a002415, + VALIDATION_ERROR_1a000017, VALIDATION_ERROR_UNDEFINED); *buffer_state = GetBufferState(dev_data, buffer); skip |= ValidateMemoryIsBoundToBuffer(dev_data, *buffer_state, caller, VALIDATION_ERROR_1a000322); return skip; } -static void PostCallRecordCmdDispatchIndirect(layer_data *dev_data, GLOBAL_CB_NODE *cb_state, VkPipelineBindPoint bind_point, - BUFFER_STATE *buffer_state) { - UpdateStateCmdDrawDispatchType(dev_data, cb_state, bind_point); +static void PreCallRecordCmdDispatchIndirect(layer_data *dev_data, GLOBAL_CB_NODE *cb_state, VkPipelineBindPoint bind_point, + BUFFER_STATE *buffer_state, std::vector *read_accesses, + std::vector *write_accesses) { + UpdateStateCmdDrawDispatchType(dev_data, cb_state, CMD_DISPATCHINDIRECT, bind_point, read_accesses, write_accesses); AddCommandBufferBindingBuffer(dev_data, cb_state, buffer_state); } @@ -5915,21 +6099,23 @@ VKAPI_ATTR void VKAPI_CALL CmdDispatchIndirect(VkCommandBuffer commandBuffer, Vk layer_data *dev_data = GetLayerDataPtr(get_dispatch_key(commandBuffer), layer_data_map); GLOBAL_CB_NODE *cb_state = nullptr; BUFFER_STATE *buffer_state = nullptr; + std::vector read_accesses, write_accesses; unique_lock_t lock(global_lock); - bool skip = PreCallValidateCmdDispatchIndirect(dev_data, commandBuffer, buffer, false, VK_PIPELINE_BIND_POINT_COMPUTE, - &cb_state, &buffer_state, "vkCmdDispatchIndirect()"); - lock.unlock(); + bool skip = + PreCallValidateCmdDispatchIndirect(dev_data, commandBuffer, buffer, false, VK_PIPELINE_BIND_POINT_COMPUTE, &cb_state, + &buffer_state, &read_accesses, &write_accesses, "vkCmdDispatchIndirect()"); if (!skip) { - dev_data->dispatch_table.CmdDispatchIndirect(commandBuffer, buffer, offset); - lock.lock(); - PostCallRecordCmdDispatchIndirect(dev_data, cb_state, VK_PIPELINE_BIND_POINT_COMPUTE, buffer_state); + PreCallRecordCmdDispatchIndirect(dev_data, cb_state, VK_PIPELINE_BIND_POINT_COMPUTE, buffer_state, &read_accesses, + &write_accesses); lock.unlock(); + dev_data->dispatch_table.CmdDispatchIndirect(commandBuffer, buffer, offset); } } VKAPI_ATTR void VKAPI_CALL CmdCopyBuffer(VkCommandBuffer commandBuffer, VkBuffer srcBuffer, VkBuffer dstBuffer, uint32_t regionCount, const VkBufferCopy *pRegions) { layer_data *device_data = GetLayerDataPtr(get_dispatch_key(commandBuffer), layer_data_map); + std::vector read_accesses, write_accesses; unique_lock_t lock(global_lock); auto cb_node = GetCBNode(device_data, commandBuffer); @@ -5937,9 +6123,10 @@ VKAPI_ATTR void VKAPI_CALL CmdCopyBuffer(VkCommandBuffer commandBuffer, VkBuffer auto dst_buffer_state = GetBufferState(device_data, dstBuffer); if (cb_node && src_buffer_state && dst_buffer_state) { - bool skip = PreCallValidateCmdCopyBuffer(device_data, cb_node, src_buffer_state, dst_buffer_state); + bool skip = PreCallValidateCmdCopyBuffer(device_data, cb_node, src_buffer_state, dst_buffer_state, regionCount, pRegions, + &read_accesses, &write_accesses); if (!skip) { - PreCallRecordCmdCopyBuffer(device_data, cb_node, src_buffer_state, dst_buffer_state); + PreCallRecordCmdCopyBuffer(device_data, cb_node, src_buffer_state, dst_buffer_state, &read_accesses, &write_accesses); lock.unlock(); device_data->dispatch_table.CmdCopyBuffer(commandBuffer, srcBuffer, dstBuffer, regionCount, pRegions); } @@ -5954,6 +6141,7 @@ VKAPI_ATTR void VKAPI_CALL CmdCopyImage(VkCommandBuffer commandBuffer, VkImage s const VkImageCopy *pRegions) { bool skip = false; layer_data *device_data = GetLayerDataPtr(get_dispatch_key(commandBuffer), layer_data_map); + std::vector mem_accesses; unique_lock_t lock(global_lock); auto cb_node = GetCBNode(device_data, commandBuffer); @@ -5961,10 +6149,10 @@ VKAPI_ATTR void VKAPI_CALL CmdCopyImage(VkCommandBuffer commandBuffer, VkImage s auto dst_image_state = GetImageState(device_data, dstImage); if (cb_node && src_image_state && dst_image_state) { skip = PreCallValidateCmdCopyImage(device_data, cb_node, src_image_state, dst_image_state, regionCount, pRegions, - srcImageLayout, dstImageLayout); + srcImageLayout, dstImageLayout, &mem_accesses); if (!skip) { PreCallRecordCmdCopyImage(device_data, cb_node, src_image_state, dst_image_state, regionCount, pRegions, srcImageLayout, - dstImageLayout); + dstImageLayout, &mem_accesses); lock.unlock(); device_data->dispatch_table.CmdCopyImage(commandBuffer, srcImage, srcImageLayout, dstImage, dstImageLayout, regionCount, pRegions); @@ -5993,16 +6181,18 @@ VKAPI_ATTR void VKAPI_CALL CmdBlitImage(VkCommandBuffer commandBuffer, VkImage s VkImage dstImage, VkImageLayout dstImageLayout, uint32_t regionCount, const VkImageBlit *pRegions, VkFilter filter) { layer_data *dev_data = GetLayerDataPtr(get_dispatch_key(commandBuffer), layer_data_map); + std::vector mem_accesses; unique_lock_t lock(global_lock); auto cb_node = GetCBNode(dev_data, commandBuffer); auto src_image_state = GetImageState(dev_data, srcImage); auto dst_image_state = GetImageState(dev_data, dstImage); - bool skip = PreCallValidateCmdBlitImage(dev_data, cb_node, src_image_state, dst_image_state, regionCount, pRegions, filter); + bool skip = PreCallValidateCmdBlitImage(dev_data, cb_node, src_image_state, dst_image_state, regionCount, pRegions, filter, + &mem_accesses); if (!skip) { - PreCallRecordCmdBlitImage(dev_data, cb_node, src_image_state, dst_image_state); + PreCallRecordCmdBlitImage(dev_data, cb_node, src_image_state, dst_image_state, &mem_accesses); lock.unlock(); dev_data->dispatch_table.CmdBlitImage(commandBuffer, srcImage, srcImageLayout, dstImage, dstImageLayout, regionCount, pRegions, filter); @@ -6059,20 +6249,30 @@ VKAPI_ATTR void VKAPI_CALL CmdCopyImageToBuffer(VkCommandBuffer commandBuffer, V } } -static bool PreCallCmdUpdateBuffer(layer_data *device_data, const GLOBAL_CB_NODE *cb_state, const BUFFER_STATE *dst_buffer_state) { +static bool PreCallCmdUpdateBuffer(layer_data *device_data, VkCommandBuffer commandBuffer, VkBuffer dstBuffer, VkDeviceSize offset, + VkDeviceSize size, GLOBAL_CB_NODE **cb_state, BUFFER_STATE **dst_buffer_state, + std::vector *mem_accesses) { bool skip = false; - skip |= ValidateMemoryIsBoundToBuffer(device_data, dst_buffer_state, "vkCmdUpdateBuffer()", VALIDATION_ERROR_1e400046); + *cb_state = GetCBNode(device_data, commandBuffer); + assert(*cb_state); + *dst_buffer_state = GetBufferState(device_data, dstBuffer); + assert(*dst_buffer_state); + skip |= ValidateMemoryIsBoundToBuffer(device_data, *dst_buffer_state, "vkCmdUpdateBuffer()", VALIDATION_ERROR_1e400046); // Validate that DST buffer has correct usage flags set - skip |= ValidateBufferUsageFlags(device_data, dst_buffer_state, VK_BUFFER_USAGE_TRANSFER_DST_BIT, true, + skip |= ValidateBufferUsageFlags(device_data, *dst_buffer_state, VK_BUFFER_USAGE_TRANSFER_DST_BIT, true, VALIDATION_ERROR_1e400044, "vkCmdUpdateBuffer()", "VK_BUFFER_USAGE_TRANSFER_DST_BIT"); - skip |= ValidateCmdQueueFlags(device_data, cb_state, "vkCmdUpdateBuffer()", + skip |= ValidateCmdQueueFlags(device_data, *cb_state, "vkCmdUpdateBuffer()", VK_QUEUE_TRANSFER_BIT | VK_QUEUE_GRAPHICS_BIT | VK_QUEUE_COMPUTE_BIT, VALIDATION_ERROR_1e402415); - skip |= ValidateCmd(device_data, cb_state, CMD_UPDATEBUFFER, "vkCmdUpdateBuffer()"); - skip |= insideRenderPass(device_data, cb_state, "vkCmdUpdateBuffer()", VALIDATION_ERROR_1e400017); + skip |= ValidateCmd(device_data, *cb_state, CMD_UPDATEBUFFER, "vkCmdUpdateBuffer()"); + skip |= insideRenderPass(device_data, *cb_state, "vkCmdUpdateBuffer()", VALIDATION_ERROR_1e400017); + // Add mem access for writing buffer + skip |= CreateAndValidateWriteMemoryAccess(device_data->report_data, CMD_UPDATEBUFFER, commandBuffer, (*cb_state)->mem_accesses, + mem_accesses, (*dst_buffer_state)->binding, true, "vkCmdUpdateBuffer()"); return skip; } -static void PostCallRecordCmdUpdateBuffer(layer_data *device_data, GLOBAL_CB_NODE *cb_state, BUFFER_STATE *dst_buffer_state) { +static void PostCallRecordCmdUpdateBuffer(layer_data *device_data, GLOBAL_CB_NODE *cb_state, BUFFER_STATE *dst_buffer_state, + std::vector *mem_accesses) { // Update bindings between buffer and cmd buffer AddCommandBufferBindingBuffer(device_data, cb_state, dst_buffer_state); std::function function = [=]() { @@ -6080,24 +6280,24 @@ static void PostCallRecordCmdUpdateBuffer(layer_data *device_data, GLOBAL_CB_NOD return false; }; cb_state->queue_submit_functions.push_back(function); + AddCommandBufferCommandMemoryAccesses(cb_state, CMD_UPDATEBUFFER, mem_accesses); } VKAPI_ATTR void VKAPI_CALL CmdUpdateBuffer(VkCommandBuffer commandBuffer, VkBuffer dstBuffer, VkDeviceSize dstOffset, VkDeviceSize dataSize, const uint32_t *pData) { bool skip = false; layer_data *dev_data = GetLayerDataPtr(get_dispatch_key(commandBuffer), layer_data_map); + GLOBAL_CB_NODE *cb_state = nullptr; + BUFFER_STATE *dst_buff_state = nullptr; + std::vector mem_accesses; unique_lock_t lock(global_lock); - - auto cb_state = GetCBNode(dev_data, commandBuffer); - assert(cb_state); - auto dst_buff_state = GetBufferState(dev_data, dstBuffer); - assert(dst_buff_state); - skip |= PreCallCmdUpdateBuffer(dev_data, cb_state, dst_buff_state); + skip |= + PreCallCmdUpdateBuffer(dev_data, commandBuffer, dstBuffer, dstOffset, dataSize, &cb_state, &dst_buff_state, &mem_accesses); lock.unlock(); if (!skip) { dev_data->dispatch_table.CmdUpdateBuffer(commandBuffer, dstBuffer, dstOffset, dataSize, pData); lock.lock(); - PostCallRecordCmdUpdateBuffer(dev_data, cb_state, dst_buff_state); + PostCallRecordCmdUpdateBuffer(dev_data, cb_state, dst_buff_state, &mem_accesses); lock.unlock(); } } @@ -6105,14 +6305,15 @@ VKAPI_ATTR void VKAPI_CALL CmdUpdateBuffer(VkCommandBuffer commandBuffer, VkBuff VKAPI_ATTR void VKAPI_CALL CmdFillBuffer(VkCommandBuffer commandBuffer, VkBuffer dstBuffer, VkDeviceSize dstOffset, VkDeviceSize size, uint32_t data) { layer_data *device_data = GetLayerDataPtr(get_dispatch_key(commandBuffer), layer_data_map); + std::vector mem_accesses; unique_lock_t lock(global_lock); auto cb_node = GetCBNode(device_data, commandBuffer); auto buffer_state = GetBufferState(device_data, dstBuffer); if (cb_node && buffer_state) { - bool skip = PreCallValidateCmdFillBuffer(device_data, cb_node, buffer_state); + bool skip = PreCallValidateCmdFillBuffer(device_data, cb_node, buffer_state, dstOffset, size, &mem_accesses); if (!skip) { - PreCallRecordCmdFillBuffer(device_data, cb_node, buffer_state); + PreCallRecordCmdFillBuffer(device_data, cb_node, buffer_state, &mem_accesses); lock.unlock(); device_data->dispatch_table.CmdFillBuffer(commandBuffer, dstBuffer, dstOffset, size, data); } @@ -6127,22 +6328,34 @@ VKAPI_ATTR void VKAPI_CALL CmdClearAttachments(VkCommandBuffer commandBuffer, ui const VkClearRect *pRects) { bool skip = false; layer_data *dev_data = GetLayerDataPtr(get_dispatch_key(commandBuffer), layer_data_map); - { - lock_guard_t lock(global_lock); - skip = PreCallValidateCmdClearAttachments(dev_data, commandBuffer, attachmentCount, pAttachments, rectCount, pRects); + std::vector mem_accesses; + unique_lock_t lock(global_lock); + auto cb_state = GetCBNode(dev_data, commandBuffer); + if (cb_state) { + skip = + PreCallValidateCmdClearAttachments(dev_data, cb_state, attachmentCount, pAttachments, rectCount, pRects, &mem_accesses); + if (!skip) { + PreCallRecordCmdClearAttachments(dev_data->report_data, cb_state, &mem_accesses); + lock.unlock(); + dev_data->dispatch_table.CmdClearAttachments(commandBuffer, attachmentCount, pAttachments, rectCount, pRects); + } + } else { + lock.unlock(); + assert(0); } - if (!skip) dev_data->dispatch_table.CmdClearAttachments(commandBuffer, attachmentCount, pAttachments, rectCount, pRects); } VKAPI_ATTR void VKAPI_CALL CmdClearColorImage(VkCommandBuffer commandBuffer, VkImage image, VkImageLayout imageLayout, const VkClearColorValue *pColor, uint32_t rangeCount, const VkImageSubresourceRange *pRanges) { layer_data *dev_data = GetLayerDataPtr(get_dispatch_key(commandBuffer), layer_data_map); + std::vector mem_accesses; unique_lock_t lock(global_lock); - bool skip = PreCallValidateCmdClearColorImage(dev_data, commandBuffer, image, imageLayout, rangeCount, pRanges); + bool skip = PreCallValidateCmdClearColorImage(dev_data, commandBuffer, image, imageLayout, rangeCount, pRanges, &mem_accesses); if (!skip) { - PreCallRecordCmdClearImage(dev_data, commandBuffer, image, imageLayout, rangeCount, pRanges); + PreCallRecordCmdClearImage(dev_data, commandBuffer, image, imageLayout, rangeCount, pRanges, CMD_CLEARCOLORIMAGE, + &mem_accesses); lock.unlock(); dev_data->dispatch_table.CmdClearColorImage(commandBuffer, image, imageLayout, pColor, rangeCount, pRanges); } @@ -6152,11 +6365,14 @@ VKAPI_ATTR void VKAPI_CALL CmdClearDepthStencilImage(VkCommandBuffer commandBuff const VkClearDepthStencilValue *pDepthStencil, uint32_t rangeCount, const VkImageSubresourceRange *pRanges) { layer_data *dev_data = GetLayerDataPtr(get_dispatch_key(commandBuffer), layer_data_map); + std::vector mem_accesses; unique_lock_t lock(global_lock); - bool skip = PreCallValidateCmdClearDepthStencilImage(dev_data, commandBuffer, image, imageLayout, rangeCount, pRanges); + bool skip = + PreCallValidateCmdClearDepthStencilImage(dev_data, commandBuffer, image, imageLayout, rangeCount, pRanges, &mem_accesses); if (!skip) { - PreCallRecordCmdClearImage(dev_data, commandBuffer, image, imageLayout, rangeCount, pRanges); + PreCallRecordCmdClearImage(dev_data, commandBuffer, image, imageLayout, rangeCount, pRanges, CMD_CLEARDEPTHSTENCILIMAGE, + &mem_accesses); lock.unlock(); dev_data->dispatch_table.CmdClearDepthStencilImage(commandBuffer, image, imageLayout, pDepthStencil, rangeCount, pRanges); } @@ -6166,16 +6382,18 @@ VKAPI_ATTR void VKAPI_CALL CmdResolveImage(VkCommandBuffer commandBuffer, VkImag VkImage dstImage, VkImageLayout dstImageLayout, uint32_t regionCount, const VkImageResolve *pRegions) { layer_data *dev_data = GetLayerDataPtr(get_dispatch_key(commandBuffer), layer_data_map); + std::vector mem_accesses; unique_lock_t lock(global_lock); auto cb_node = GetCBNode(dev_data, commandBuffer); auto src_image_state = GetImageState(dev_data, srcImage); auto dst_image_state = GetImageState(dev_data, dstImage); - bool skip = PreCallValidateCmdResolveImage(dev_data, cb_node, src_image_state, dst_image_state, regionCount, pRegions); + bool skip = + PreCallValidateCmdResolveImage(dev_data, cb_node, src_image_state, dst_image_state, regionCount, pRegions, &mem_accesses); if (!skip) { - PreCallRecordCmdResolveImage(dev_data, cb_node, src_image_state, dst_image_state); + PreCallRecordCmdResolveImage(dev_data, cb_node, src_image_state, dst_image_state, &mem_accesses); lock.unlock(); dev_data->dispatch_table.CmdResolveImage(commandBuffer, srcImage, srcImageLayout, dstImage, dstImageLayout, regionCount, pRegions); @@ -6841,6 +7059,235 @@ bool ValidateStageMasksAgainstQueueCapabilities(layer_data *dev_data, GLOBAL_CB_ return skip; } +// Merge the given synch command with any previous synch cmds from given vector of synch commands +// This resolves dependency chains by pulling overlapping earlier barriers into current barrier +// If current barriers' srcMasks match with previous barriers destination masks, then incorporate the +// previous barrier src & dst Masks into our src/dst Masks & merge in all of the previous barriers. +// Return "true" if any merge occurs, "false" otherwise +// TODO: Need to analyze this more and verify that it's correct. There may be a bug here where previous +// barriers get hoisted to a current barrier, but can then be applied to mem accesses that follow the +// hoisted barriers. I think that's wrong and if so could potentially be fixed by tracking seq# (or ptr +// to cmd of origin) for barriers and check sequencing when clearing mem accesses. +static bool MergeSynchCommands(const std::vector &prev_synch_commands, SynchCommand *synch_command) { + bool merge = false; + for (const auto &psc : prev_synch_commands) { + if (psc->dst_stage_flags & synch_command->src_stage_flags) { + merge = true; + // scopes overlap so merge prev barrier mem masks into current masks + synch_command->src_stage_flags |= psc->src_stage_flags; + synch_command->dst_stage_flags |= psc->dst_stage_flags; + // Pull previous barriers into this barrier + for (const auto &gb : psc->global_barriers) { + synch_command->global_barriers.emplace_back(gb); + } + for (const auto &bb : psc->buffer_barriers) { + synch_command->buffer_barriers.emplace_back(bb); + } + for (const auto &ib : psc->image_barriers) { + synch_command->image_barriers.emplace_back(ib); + } + } + } + return merge; +} + +// Record given synch cmd vs. outstanding memory accesses up to the point of the synch command, +// for each barrier, compare given existing mem accesses and record any applicable barriers +static void ReplaySynchCommand(layer_data *device_data, SynchCommand *synch_cmd, + std::unordered_map> *access_map) { + auto src_stage_mask = synch_cmd->src_stage_flags; + // TODO: Make this barrier/access parsing smarter + for (auto &mem_access_pair : *access_map) { + for (auto &mem_access : mem_access_pair.second) { + if (mem_access.src_stage_flags & src_stage_mask) { + // Record any execution barrier overlaps + mem_access.pipe_barrier = true; + mem_access.dst_stage_flags |= synch_cmd->dst_stage_flags; + // For every global barrier that matches access mask, record the barrier + for (const auto &global_barrier : synch_cmd->global_barriers) { + if (0 != (global_barrier.srcAccessMask & mem_access.src_access_flags)) { + // This memory barrier applies to mem_access so record details + mem_access.mem_barrier = true; + mem_access.dst_access_flags |= global_barrier.dstAccessMask; + mem_access.synch_commands.push_back(synch_cmd); + } + } + } + } + } + for (const auto &buff_barrier : synch_cmd->buffer_barriers) { + const auto &buff_state = GetBufferState(device_data, buff_barrier.buffer); + const auto mem_obj = buff_state->binding.mem; + // Make an access struct with barrier range details to check for overlap + // TODO: This is hacky, creating tmp access struct from barrier to check conflict + // need to rework original function so this makes sense or write alternate function + MemoryAccess barrier_access = {}; + barrier_access.location.mem = mem_obj; + barrier_access.location.offset = buff_barrier.offset; + barrier_access.location.size = buff_barrier.size; + barrier_access.mem_barrier = false; + barrier_access.pipe_barrier = false; + const auto &mem_access_pair = access_map->find(mem_obj); + if (mem_access_pair != access_map->end()) { + for (auto &mem_access : mem_access_pair->second) { + // If the pipe & access masks overlap, then barrier applies + if (((mem_access.src_access_flags & buff_barrier.srcAccessMask) != 0) && + ((mem_access.src_stage_flags & src_stage_mask) != 0)) { + // Set buff access write opposite of actual access so conflict is flagged on overlap + barrier_access.write = !mem_access.write; + if (MemoryConflict(&barrier_access, &mem_access)) { + mem_access.mem_barrier = true; + mem_access.dst_stage_flags |= synch_cmd->dst_stage_flags; + mem_access.dst_access_flags |= buff_barrier.dstAccessMask; + mem_access.synch_commands.push_back(synch_cmd); + } + } + } + } + } + // TODO: Handle image barriers +} + +// For the given vector of commands, replay memory access commands, only replaying synch commands that occur between +// start/end_synch_index For memory commands that are hit, put them into an access map For Synch commands between the indices that +// are hit merge them with any previous synch commands then: +// 1. Record them against any outstanding accesses +// 2. Add them into a synch cmd vector for future merges +// When the synch command at end_synch_index is replayed, we then store any remaining mem access commands into remaining_accesses +// vector. +// This will include the previously conflicting late access, so the returned vector of live accesses can be checked against updated +// access_map. +void ReplayMemoryAccessCommands(layer_data *device_data, std::vector> *commands, size_t start_synch_index, + size_t end_synch_index, std::unordered_map> *access_map, + std::vector *remaining_accesses) { + if (commands->empty()) { + return; // early out + } + size_t index = 0; + // Store running list of synch commands for merge purposes + std::vector prev_synch_commands; + Command *cmd_ptr = nullptr; + while (index <= end_synch_index) { + cmd_ptr = (*commands)[index++].get(); + // 1. Merge synch cmd and build up synch vector + // Note that we only replay synch commands between the mem access points of interest where the potential conflict occurs + if (cmd_ptr->synch && (index >= start_synch_index && index <= end_synch_index)) { + auto synch_cmd_ptr = static_cast(cmd_ptr); + MergeSynchCommands(prev_synch_commands, synch_cmd_ptr); + prev_synch_commands.push_back(synch_cmd_ptr); + // Now check synch command against outstanding memory accesses + ReplaySynchCommand(device_data, synch_cmd_ptr, access_map); + } else if (!cmd_ptr->synch) { + // Currently all non-synch commands should be memory access commands, if this assert fails, this + // code must be updated to handle different command types + assert(!cmd_ptr->mem_accesses.empty()); + // Record memory accesses into access map going fwd + for (const auto &mem_access : cmd_ptr->mem_accesses) { + (*access_map)[mem_access.location.mem].push_back(mem_access); + } + } + // 2. If we do merge synch commands, we'll need to re-check accesses + } + // Any remaining mem access commands (after last synch) are added into live access vector + while (index < commands->size()) { + cmd_ptr = (*commands)[index++].get(); + // If there are no mem_accesses in the cmd (such as a later synch) this loop does nothing + for (const auto &mem_access : cmd_ptr->mem_accesses) { + if (!mem_access.Visible()) { + // We only want mem accesses that are still live + remaining_accesses->push_back(mem_access); + } + } + } +} + +// Record given set of barriers vs. outstanding memory accesses for this cmd buffer +// 1. First merge this barrier within any previous barriers in this CB +// 2. For each barrier, compare given existing mem accesses and record any applicable barriers +static void RecordBarrierMemoryAccess(layer_data *device_data, CMD_TYPE cmd, GLOBAL_CB_NODE *cb_state, + VkCommandBuffer command_buffer, VkPipelineStageFlags src_stage_mask, + VkPipelineStageFlags dst_stage_mask, uint32_t mem_barrier_count, + const VkMemoryBarrier *mem_barriers, uint32_t buffer_mem_barrier_count, + const VkBufferMemoryBarrier *buffer_mem_barriers, uint32_t image_memory_barrier_count, + const VkImageMemoryBarrier *image_memory_barriers) { + cb_state->commands.emplace_back( + unique_ptr(new SynchCommand(cmd, cb_state->commands.size(), cb_state, src_stage_mask, dst_stage_mask))); + Command *cmd_ptr = cb_state->commands.back().get(); + SynchCommand *synch_cmd_ptr = static_cast(cmd_ptr); + // First merge any overlapping previous barriers. + MergeSynchCommands(cb_state->synch_commands, synch_cmd_ptr); + // Now that we've merged previous synch commands, append this cmd to existing synch commands + cb_state->synch_commands.emplace_back(synch_cmd_ptr); + // TODO: Make this barrier/access parsing smarter + + for (auto &access_map : cb_state->mem_accesses.access_maps) { + for (auto &mem_access_pair : access_map) { + for (auto &mem_access : mem_access_pair.second) { + if (mem_access.src_stage_flags & src_stage_mask) { + // Record any execution barrier overlaps + mem_access.pipe_barrier = true; + mem_access.dst_stage_flags |= dst_stage_mask; + // For every global barrier that matches access mask, record the barrier + for (uint32_t i = 0; i < mem_barrier_count; ++i) { + const auto &mem_barrier = mem_barriers[i]; + if ((mem_barrier.srcAccessMask & mem_access.src_access_flags) != 0) { + // This memory barrier applies to earlier access so record details + mem_access.mem_barrier = true; + mem_access.dst_access_flags |= mem_barrier.dstAccessMask; + mem_access.synch_commands.push_back(cmd_ptr); + } + } + } + } + } + } + for (uint32_t i = 0; i < buffer_mem_barrier_count; ++i) { + const auto &buff_barrier = buffer_mem_barriers[i]; + const auto &buff_state = GetBufferState(device_data, buff_barrier.buffer); + const auto mem_obj = buff_state->binding.mem; + // Make an access struct with barrier range details to check for overlap + // TODO: This is hacky, creating tmp access struct from barrier to check conflict + // need to rework original function so this makes sense or write alternate function + MemoryAccess barrier_access = {}; + barrier_access.location.mem = mem_obj; + barrier_access.location.offset = buff_barrier.offset; + barrier_access.location.size = buff_barrier.size; + barrier_access.mem_barrier = false; + barrier_access.pipe_barrier = false; + for (auto &access_map : cb_state->mem_accesses.access_maps) { + const auto &mem_access_pair = access_map.find(mem_obj); + if (mem_access_pair != access_map.end()) { + for (auto &mem_access : mem_access_pair->second) { + // If the pipe & access masks overlap, then barrier applies + if (((mem_access.src_access_flags & buff_barrier.srcAccessMask) != 0) && + ((mem_access.src_stage_flags & src_stage_mask) != 0)) { + // Set buff access write opposite of actual access so conflict is flagged on overlap + barrier_access.write = !mem_access.write; + if (MemoryConflict(&barrier_access, &mem_access)) { + mem_access.mem_barrier = true; + mem_access.dst_stage_flags |= dst_stage_mask; + mem_access.dst_access_flags |= buff_barrier.dstAccessMask; + mem_access.synch_commands.push_back(cmd_ptr); + } + } + } + } + } + } + // TODO: Handle image barriers + + // Now need to record these barriers into the cmd for any future merges + for (uint32_t i = 0; i < mem_barrier_count; ++i) { + synch_cmd_ptr->global_barriers.emplace_back(mem_barriers[i]); + } + for (uint32_t i = 0; i < buffer_mem_barrier_count; ++i) { + synch_cmd_ptr->buffer_barriers.emplace_back(buffer_mem_barriers[i]); + } + for (uint32_t i = 0; i < image_memory_barrier_count; ++i) { + synch_cmd_ptr->image_barriers.emplace_back(image_memory_barriers[i]); + } +} + VKAPI_ATTR void VKAPI_CALL CmdWaitEvents(VkCommandBuffer commandBuffer, uint32_t eventCount, const VkEvent *pEvents, VkPipelineStageFlags sourceStageMask, VkPipelineStageFlags dstStageMask, uint32_t memoryBarrierCount, const VkMemoryBarrier *pMemoryBarriers, @@ -6879,6 +7326,9 @@ VKAPI_ATTR void VKAPI_CALL CmdWaitEvents(VkCommandBuffer commandBuffer, uint32_t return validateEventStageMask(q, cb_state, eventCount, first_event_index, sourceStageMask); }); TransitionImageLayouts(dev_data, commandBuffer, imageMemoryBarrierCount, pImageMemoryBarriers); + RecordBarrierMemoryAccess(dev_data, CMD_WAITEVENTS, cb_state, commandBuffer, sourceStageMask, dstStageMask, + memoryBarrierCount, pMemoryBarriers, bufferMemoryBarrierCount, pBufferMemoryBarriers, + imageMemoryBarrierCount, pImageMemoryBarriers); } } lock.unlock(); @@ -6918,8 +7368,18 @@ static bool PreCallValidateCmdPipelineBarrier(layer_data *device_data, GLOBAL_CB } static void PreCallRecordCmdPipelineBarrier(layer_data *device_data, GLOBAL_CB_NODE *cb_state, VkCommandBuffer commandBuffer, - uint32_t imageMemoryBarrierCount, const VkImageMemoryBarrier *pImageMemoryBarriers) { + VkPipelineStageFlags src_stage_mask, VkPipelineStageFlags dst_stage_mask, + VkDependencyFlags dependencyFlags, uint32_t mem_barrier_count, + const VkMemoryBarrier *mem_barriers, uint32_t buffer_mem_barrier_count, + const VkBufferMemoryBarrier *buffer_mem_barriers, uint32_t imageMemoryBarrierCount, + const VkImageMemoryBarrier *pImageMemoryBarriers) { TransitionImageLayouts(device_data, commandBuffer, imageMemoryBarrierCount, pImageMemoryBarriers); + + if (!cb_state->activeRenderPass) { // Barriers in a renderpass are only for subpass self-dep + RecordBarrierMemoryAccess(device_data, CMD_PIPELINEBARRIER, cb_state, commandBuffer, src_stage_mask, dst_stage_mask, + mem_barrier_count, mem_barriers, buffer_mem_barrier_count, buffer_mem_barriers, + imageMemoryBarrierCount, pImageMemoryBarriers); + } } VKAPI_ATTR void VKAPI_CALL CmdPipelineBarrier(VkCommandBuffer commandBuffer, VkPipelineStageFlags srcStageMask, @@ -6936,7 +7396,9 @@ VKAPI_ATTR void VKAPI_CALL CmdPipelineBarrier(VkCommandBuffer commandBuffer, VkP memoryBarrierCount, pMemoryBarriers, bufferMemoryBarrierCount, pBufferMemoryBarriers, imageMemoryBarrierCount, pImageMemoryBarriers); if (!skip) { - PreCallRecordCmdPipelineBarrier(device_data, cb_state, commandBuffer, imageMemoryBarrierCount, pImageMemoryBarriers); + PreCallRecordCmdPipelineBarrier(device_data, cb_state, commandBuffer, srcStageMask, dstStageMask, dependencyFlags, + memoryBarrierCount, pMemoryBarriers, bufferMemoryBarrierCount, pBufferMemoryBarriers, + imageMemoryBarrierCount, pImageMemoryBarriers); } } else { assert(0); @@ -7076,45 +7538,70 @@ static bool validateQuery(VkQueue queue, GLOBAL_CB_NODE *pCB, VkQueryPool queryP return skip; } +static bool PreCallValidateCmdCopyQueryPoolResults(layer_data *device_data, GLOBAL_CB_NODE *cb_state, VkQueryPool queryPool, + uint32_t firstQuery, uint32_t query_count, BUFFER_STATE *dst_buffer_state, + VkDeviceSize offset, VkDeviceSize stride, VkQueryResultFlags flags, + std::vector *mem_accesses) { + bool skip = false; + skip |= ValidateMemoryIsBoundToBuffer(device_data, dst_buffer_state, "vkCmdCopyQueryPoolResults()", VALIDATION_ERROR_19400674); + // Validate that DST buffer has correct usage flags set + skip |= ValidateBufferUsageFlags(device_data, dst_buffer_state, VK_BUFFER_USAGE_TRANSFER_DST_BIT, true, + VALIDATION_ERROR_19400672, "vkCmdCopyQueryPoolResults()", "VK_BUFFER_USAGE_TRANSFER_DST_BIT"); + skip |= ValidateCmdQueueFlags(device_data, cb_state, "vkCmdCopyQueryPoolResults()", + VK_QUEUE_GRAPHICS_BIT | VK_QUEUE_COMPUTE_BIT, VALIDATION_ERROR_19402415); + skip |= ValidateCmd(device_data, cb_state, CMD_COPYQUERYPOOLRESULTS, "vkCmdCopyQueryPoolResults()"); + skip |= insideRenderPass(device_data, cb_state, "vkCmdCopyQueryPoolResults()", VALIDATION_ERROR_19400017); + // Add mem write to buffer. + // TODO: Initially if data is not tightly packed, just mark as imprecise and cover whole as written + // we could make this case precise by splitting into a number of small accesses + auto const &qp_state = GetQueryPoolNode(device_data, queryPool); + auto num_bytes = (VK_QUERY_RESULT_64_BIT & flags) ? 8 : 4; + auto element_size = qp_state->data_count * num_bytes; + auto precise = (element_size == stride) ? true : false; + auto size = stride * (query_count - 1) + num_bytes; + skip |= CreateAndValidateWriteMemoryAccess(device_data->report_data, CMD_COPYQUERYPOOLRESULTS, cb_state->commandBuffer, + cb_state->mem_accesses, mem_accesses, {dst_buffer_state->binding.mem, offset, size}, + precise, "vkCmdCopyQueryPoolResults()"); + return skip; +} + +static void PreCallRecordCmdCopyQueryPoolResults(layer_data *device_data, GLOBAL_CB_NODE *cb_state, VkQueryPool queryPool, + uint32_t firstQuery, uint32_t queryCount, BUFFER_STATE *dst_buffer_state, + std::vector *mem_accesses) { + AddCommandBufferBindingBuffer(device_data, cb_state, dst_buffer_state); + cb_state->queue_submit_functions.emplace_back([=]() { + SetBufferMemoryValid(device_data, dst_buffer_state, true); + return false; + }); + cb_state->queryUpdates.emplace_back([=](VkQueue q) { return validateQuery(q, cb_state, queryPool, firstQuery, queryCount); }); + addCommandBufferBinding(&GetQueryPoolNode(device_data, queryPool)->cb_bindings, + {HandleToUint64(queryPool), kVulkanObjectTypeQueryPool}, cb_state); + AddCommandBufferCommandMemoryAccesses(cb_state, CMD_COPYQUERYPOOLRESULTS, mem_accesses); +} + VKAPI_ATTR void VKAPI_CALL CmdCopyQueryPoolResults(VkCommandBuffer commandBuffer, VkQueryPool queryPool, uint32_t firstQuery, uint32_t queryCount, VkBuffer dstBuffer, VkDeviceSize dstOffset, VkDeviceSize stride, VkQueryResultFlags flags) { bool skip = false; layer_data *dev_data = GetLayerDataPtr(get_dispatch_key(commandBuffer), layer_data_map); + std::vector mem_accesses; unique_lock_t lock(global_lock); - auto cb_node = GetCBNode(dev_data, commandBuffer); + auto cb_state = GetCBNode(dev_data, commandBuffer); auto dst_buff_state = GetBufferState(dev_data, dstBuffer); - if (cb_node && dst_buff_state) { - skip |= ValidateMemoryIsBoundToBuffer(dev_data, dst_buff_state, "vkCmdCopyQueryPoolResults()", VALIDATION_ERROR_19400674); - // Validate that DST buffer has correct usage flags set - skip |= - ValidateBufferUsageFlags(dev_data, dst_buff_state, VK_BUFFER_USAGE_TRANSFER_DST_BIT, true, VALIDATION_ERROR_19400672, - "vkCmdCopyQueryPoolResults()", "VK_BUFFER_USAGE_TRANSFER_DST_BIT"); - skip |= ValidateCmdQueueFlags(dev_data, cb_node, "vkCmdCopyQueryPoolResults()", - VK_QUEUE_GRAPHICS_BIT | VK_QUEUE_COMPUTE_BIT, VALIDATION_ERROR_19402415); - skip |= ValidateCmd(dev_data, cb_node, CMD_COPYQUERYPOOLRESULTS, "vkCmdCopyQueryPoolResults()"); - skip |= insideRenderPass(dev_data, cb_node, "vkCmdCopyQueryPoolResults()", VALIDATION_ERROR_19400017); - } - lock.unlock(); - - if (skip) return; - - dev_data->dispatch_table.CmdCopyQueryPoolResults(commandBuffer, queryPool, firstQuery, queryCount, dstBuffer, dstOffset, - stride, flags); - - lock.lock(); - if (cb_node && dst_buff_state) { - AddCommandBufferBindingBuffer(dev_data, cb_node, dst_buff_state); - cb_node->queue_submit_functions.emplace_back([=]() { - SetBufferMemoryValid(dev_data, dst_buff_state, true); - return false; - }); - cb_node->queryUpdates.emplace_back([=](VkQueue q) { - return validateQuery(q, cb_node, queryPool, firstQuery, queryCount); - }); - addCommandBufferBinding(&GetQueryPoolNode(dev_data, queryPool)->cb_bindings, - {HandleToUint64(queryPool), kVulkanObjectTypeQueryPool}, cb_node); + if (cb_state && dst_buff_state) { + skip |= PreCallValidateCmdCopyQueryPoolResults(dev_data, cb_state, queryPool, firstQuery, queryCount, dst_buff_state, + dstOffset, stride, flags, &mem_accesses); + if (!skip) { + PreCallRecordCmdCopyQueryPoolResults(dev_data, cb_state, queryPool, firstQuery, queryCount, dst_buff_state, + &mem_accesses); + lock.unlock(); + dev_data->dispatch_table.CmdCopyQueryPoolResults(commandBuffer, queryPool, firstQuery, queryCount, dstBuffer, dstOffset, + stride, flags); + } + } else { + lock.unlock(); + assert(0); } } diff --git a/layers/core_validation.h b/layers/core_validation.h index 460dba3736..aba7a8d118 100644 --- a/layers/core_validation.h +++ b/layers/core_validation.h @@ -138,6 +138,7 @@ class QUEUE_STATE { class QUERY_POOL_NODE : public BASE_NODE { public: VkQueryPoolCreateInfo createInfo; + size_t data_count; // Number of data items in this query }; struct PHYSICAL_DEVICE_STATE { diff --git a/layers/core_validation_error_enums.h b/layers/core_validation_error_enums.h index 12099c3275..746f7783f0 100644 --- a/layers/core_validation_error_enums.h +++ b/layers/core_validation_error_enums.h @@ -29,6 +29,7 @@ enum MEM_TRACK_ERROR { MEMTRACK_INVALID_CB, MEMTRACK_INVALID_MEM_OBJ, MEMTRACK_INVALID_ALIASING, + MEMTRACK_SYNCHRONIZATION_ERROR, MEMTRACK_INTERNAL_ERROR, MEMTRACK_FREED_MEM_REF, MEMTRACK_INVALID_OBJECT, diff --git a/layers/core_validation_types.h b/layers/core_validation_types.h index c62fb40c81..7affb3bc37 100644 --- a/layers/core_validation_types.h +++ b/layers/core_validation_types.h @@ -413,7 +413,6 @@ enum CMD_TYPE { CMD_BLITIMAGE, CMD_COPYBUFFERTOIMAGE, CMD_COPYIMAGETOBUFFER, - CMD_CLONEIMAGEDATA, CMD_UPDATEBUFFER, CMD_FILLBUFFER, CMD_CLEARCOLORIMAGE, @@ -438,6 +437,242 @@ enum CMD_TYPE { CMD_ENDRENDERPASS, CMD_EXECUTECOMMANDS, CMD_END, // Should be last command in any RECORDED cmd buffer + CMD_COUNT +}; + +// Struct for mapping a cmd to its permanent default pipe/access flags +// Draw/Dispatch/ClearAttachments commands will also add in some flags dynamically based on current state +struct CmdFlags { + VkPipelineStageFlags stage_flags; + VkAccessFlags access_flags; +}; + +static const uint32_t READ_INDEX = 0; +static const uint32_t WRITE_INDEX = 1; + +// Per-cmd read/write flags Read flags in slot 0, Write in 1 +static const CmdFlags CommandToFlags[CMD_COUNT][2] = { + // CMD_NONE, + {{0, 0}, {0, 0}}, + // CMD_BINDPIPELINE, + {{0, 0}, {0, 0}}, + // CMD_BINDPIPELINEDELTA, + {{0, 0}, {0, 0}}, + // CMD_SETVIEWPORTSTATE, + {{0, 0}, {0, 0}}, + // CMD_SETSCISSORSTATE, + {{0, 0}, {0, 0}}, + // CMD_SETLINEWIDTHSTATE, + {{0, 0}, {0, 0}}, + // CMD_SETDEPTHBIASSTATE, + {{0, 0}, {0, 0}}, + // CMD_SETBLENDSTATE, + {{0, 0}, {0, 0}}, + // CMD_SETDEPTHBOUNDSSTATE, + {{0, 0}, {0, 0}}, + // CMD_SETSTENCILREADMASKSTATE, + {{0, 0}, {0, 0}}, + // CMD_SETSTENCILWRITEMASKSTATE, + {{0, 0}, {0, 0}}, + // CMD_SETSTENCILREFERENCESTATE, + {{0, 0}, {0, 0}}, + // CMD_BINDDESCRIPTORSETS, + {{0, 0}, {0, 0}}, + // CMD_BINDINDEXBUFFER, + {{0, 0}, {0, 0}}, + // CMD_BINDVERTEXBUFFER, + {{0, 0}, {0, 0}}, + // CMD_DRAW, + {{VK_PIPELINE_STAGE_ALL_COMMANDS_BIT | VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT | VK_PIPELINE_STAGE_VERTEX_INPUT_BIT | + VK_PIPELINE_STAGE_VERTEX_SHADER_BIT, + VK_ACCESS_MEMORY_READ_BIT | VK_ACCESS_VERTEX_ATTRIBUTE_READ_BIT | VK_ACCESS_SHADER_READ_BIT}, + {VK_PIPELINE_STAGE_ALL_COMMANDS_BIT | VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT, 0}}, + // CMD_DRAWINDEXED, + {{VK_PIPELINE_STAGE_ALL_COMMANDS_BIT | VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT | VK_PIPELINE_STAGE_VERTEX_INPUT_BIT | + VK_PIPELINE_STAGE_VERTEX_SHADER_BIT, + VK_ACCESS_MEMORY_READ_BIT | VK_ACCESS_INDEX_READ_BIT | VK_ACCESS_VERTEX_ATTRIBUTE_READ_BIT | VK_ACCESS_SHADER_READ_BIT}, + {VK_PIPELINE_STAGE_ALL_COMMANDS_BIT | VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT, 0}}, + // CMD_DRAWINDIRECT, + {{VK_PIPELINE_STAGE_ALL_COMMANDS_BIT | VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT | VK_PIPELINE_STAGE_VERTEX_INPUT_BIT | + VK_PIPELINE_STAGE_VERTEX_SHADER_BIT, + VK_ACCESS_MEMORY_READ_BIT | VK_ACCESS_INDIRECT_COMMAND_READ_BIT | VK_ACCESS_VERTEX_ATTRIBUTE_READ_BIT | + VK_ACCESS_SHADER_READ_BIT}, + {VK_PIPELINE_STAGE_ALL_COMMANDS_BIT | VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT, 0}}, + // CMD_DRAWINDEXEDINDIRECT, + {{VK_PIPELINE_STAGE_ALL_COMMANDS_BIT | VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT | VK_PIPELINE_STAGE_VERTEX_INPUT_BIT | + VK_PIPELINE_STAGE_VERTEX_SHADER_BIT, + VK_ACCESS_MEMORY_READ_BIT | VK_ACCESS_INDEX_READ_BIT | VK_ACCESS_INDIRECT_COMMAND_READ_BIT | + VK_ACCESS_VERTEX_ATTRIBUTE_READ_BIT | VK_ACCESS_SHADER_READ_BIT}, + {VK_PIPELINE_STAGE_ALL_COMMANDS_BIT | VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT, 0}}, + // CMD_DISPATCH, + {{VK_PIPELINE_STAGE_ALL_COMMANDS_BIT | VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, + VK_ACCESS_MEMORY_READ_BIT | VK_ACCESS_SHADER_READ_BIT}, + {VK_PIPELINE_STAGE_ALL_COMMANDS_BIT | VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, 0}}, + // CMD_DISPATCHINDIRECT, + {{VK_PIPELINE_STAGE_ALL_COMMANDS_BIT | VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, + VK_ACCESS_MEMORY_READ_BIT | VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_INDIRECT_COMMAND_READ_BIT}, + {VK_PIPELINE_STAGE_ALL_COMMANDS_BIT | VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, 0}}, + // CMD_COPYBUFFER, + {{VK_PIPELINE_STAGE_ALL_COMMANDS_BIT | VK_PIPELINE_STAGE_TRANSFER_BIT, VK_ACCESS_MEMORY_READ_BIT | VK_ACCESS_TRANSFER_READ_BIT}, + {VK_PIPELINE_STAGE_ALL_COMMANDS_BIT | VK_PIPELINE_STAGE_TRANSFER_BIT, + VK_ACCESS_MEMORY_WRITE_BIT | VK_ACCESS_TRANSFER_WRITE_BIT}}, + // CMD_COPYIMAGE, + {{VK_PIPELINE_STAGE_ALL_COMMANDS_BIT | VK_PIPELINE_STAGE_TRANSFER_BIT, VK_ACCESS_MEMORY_READ_BIT | VK_ACCESS_TRANSFER_READ_BIT}, + {VK_PIPELINE_STAGE_ALL_COMMANDS_BIT | VK_PIPELINE_STAGE_TRANSFER_BIT, + VK_ACCESS_MEMORY_WRITE_BIT | VK_ACCESS_TRANSFER_WRITE_BIT}}, + // CMD_BLITIMAGE, + {{VK_PIPELINE_STAGE_ALL_COMMANDS_BIT | VK_PIPELINE_STAGE_TRANSFER_BIT, VK_ACCESS_MEMORY_READ_BIT | VK_ACCESS_TRANSFER_READ_BIT}, + {VK_PIPELINE_STAGE_ALL_COMMANDS_BIT | VK_PIPELINE_STAGE_TRANSFER_BIT, + VK_ACCESS_MEMORY_WRITE_BIT | VK_ACCESS_TRANSFER_WRITE_BIT}}, + // CMD_COPYBUFFERTOIMAGE, + {{VK_PIPELINE_STAGE_ALL_COMMANDS_BIT | VK_PIPELINE_STAGE_TRANSFER_BIT, VK_ACCESS_MEMORY_READ_BIT | VK_ACCESS_TRANSFER_READ_BIT}, + {VK_PIPELINE_STAGE_ALL_COMMANDS_BIT | VK_PIPELINE_STAGE_TRANSFER_BIT, + VK_ACCESS_MEMORY_WRITE_BIT | VK_ACCESS_TRANSFER_WRITE_BIT}}, + // CMD_COPYIMAGETOBUFFER, + {{VK_PIPELINE_STAGE_ALL_COMMANDS_BIT | VK_PIPELINE_STAGE_TRANSFER_BIT, VK_ACCESS_MEMORY_READ_BIT | VK_ACCESS_TRANSFER_READ_BIT}, + {VK_PIPELINE_STAGE_ALL_COMMANDS_BIT | VK_PIPELINE_STAGE_TRANSFER_BIT, + VK_ACCESS_MEMORY_WRITE_BIT | VK_ACCESS_TRANSFER_WRITE_BIT}}, + // CMD_UPDATEBUFFER, + {{0, 0}, + {VK_PIPELINE_STAGE_ALL_COMMANDS_BIT | VK_PIPELINE_STAGE_TRANSFER_BIT, + VK_ACCESS_MEMORY_WRITE_BIT | VK_ACCESS_TRANSFER_WRITE_BIT}}, + // CMD_FILLBUFFER, + {{0, 0}, + {VK_PIPELINE_STAGE_ALL_COMMANDS_BIT | VK_PIPELINE_STAGE_TRANSFER_BIT, + VK_ACCESS_MEMORY_WRITE_BIT | VK_ACCESS_TRANSFER_WRITE_BIT}}, + // CMD_CLEARCOLORIMAGE, + {{0, 0}, + {VK_PIPELINE_STAGE_ALL_COMMANDS_BIT | VK_PIPELINE_STAGE_TRANSFER_BIT, + VK_ACCESS_MEMORY_WRITE_BIT | VK_ACCESS_TRANSFER_WRITE_BIT}}, + // CMD_CLEARATTACHMENTS, + {{0, 0}, {VK_PIPELINE_STAGE_ALL_COMMANDS_BIT | VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT, 0}}, + // CMD_CLEARDEPTHSTENCILIMAGE, + {{0, 0}, + {VK_PIPELINE_STAGE_ALL_COMMANDS_BIT | VK_PIPELINE_STAGE_TRANSFER_BIT, + VK_ACCESS_MEMORY_WRITE_BIT | VK_ACCESS_TRANSFER_WRITE_BIT}}, + // CMD_RESOLVEIMAGE, + {{VK_PIPELINE_STAGE_ALL_COMMANDS_BIT | VK_PIPELINE_STAGE_TRANSFER_BIT, VK_ACCESS_MEMORY_READ_BIT | VK_ACCESS_TRANSFER_READ_BIT}, + {VK_PIPELINE_STAGE_ALL_COMMANDS_BIT | VK_PIPELINE_STAGE_TRANSFER_BIT, + VK_ACCESS_MEMORY_WRITE_BIT | VK_ACCESS_TRANSFER_WRITE_BIT}}, + // CMD_SETEVENT, + {{0, 0}, {0, 0}}, + // CMD_RESETEVENT, + {{0, 0}, {0, 0}}, + // CMD_WAITEVENTS, + {{0, 0}, {0, 0}}, + // CMD_PIPELINEBARRIER, + {{0, 0}, {0, 0}}, + // CMD_BEGINQUERY, + {{0, 0}, {0, 0}}, + // CMD_ENDQUERY, + {{0, 0}, {0, 0}}, + // CMD_RESETQUERYPOOL, + {{0, 0}, {0, 0}}, + // CMD_COPYQUERYPOOLRESULTS, + {{0, 0}, + {VK_PIPELINE_STAGE_ALL_COMMANDS_BIT | VK_PIPELINE_STAGE_TRANSFER_BIT, + VK_ACCESS_MEMORY_WRITE_BIT | VK_ACCESS_TRANSFER_WRITE_BIT}}, + // CMD_WRITETIMESTAMP, + {{0, 0}, {0, 0}}, + // CMD_PUSHCONSTANTS, + {{0, 0}, {0, 0}}, + // CMD_INITATOMICCOUNTERS, + {{0, 0}, {0, 0}}, + // CMD_LOADATOMICCOUNTERS, + {{0, 0}, {0, 0}}, + // CMD_SAVEATOMICCOUNTERS, + {{0, 0}, {0, 0}}, + // CMD_BEGINRENDERPASS, + {{VK_PIPELINE_STAGE_ALL_COMMANDS_BIT | VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT, 0}, {0, 0}}, + // CMD_NEXTSUBPASS, + {{VK_PIPELINE_STAGE_ALL_COMMANDS_BIT | VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT, 0}, {0, 0}}, + // CMD_ENDRENDERPASS, + {{VK_PIPELINE_STAGE_ALL_COMMANDS_BIT | VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT, 0}, {0, 0}}, + // CMD_EXECUTECOMMANDS, + {{0, 0}, {0, 0}}, + // CMD_END, // Should be last command in any RECORDED cmd buffer + {{0, 0}, {0, 0}}, +}; + +// fwd decl class for ptr below +class Command; +// Store details of memory access by a cmd +struct MemoryAccess { + MemoryAccess() + : precise(false), + write(false), + cmd(nullptr), + location{0, 0, 0}, + src_stage_flags(0), + src_access_flags(0), + dst_stage_flags(0), + dst_access_flags(0), + mem_barrier(false), + pipe_barrier(false), + synch_commands{} {}; + MemoryAccess(VkDeviceMemory mem, VkDeviceSize offset, VkDeviceSize size, bool precise) + : precise(precise), + write(false), + cmd(nullptr), + location{mem, offset, size}, + src_stage_flags(0), + src_access_flags(0), + dst_stage_flags(0), + dst_access_flags(0), + mem_barrier(false), + pipe_barrier(false), + synch_commands{} {}; + + // Once an appropriate barrier has been identified for an access, it will be Visible + bool Visible() const { + auto visible = (mem_barrier || (pipe_barrier && write)); + return visible; + }; + bool precise; // True if exact bounds of mem access are known, false if bounds are conservative + bool write; // False for read access + Command *cmd; // Ptr to cmd that makes this access + MEM_BINDING location; // mem/offset/size being accessed + // The source flags cover all access bits for the original memory access + // These flags are checked against synch objects to check if scope covers this access + VkPipelineStageFlags src_stage_flags; // Stage flags within scope of this access + VkAccessFlags src_access_flags; // Access flags within scope of this access + // The following members are only set when a valid synch object is added following this access + // When such an event occurs, the relevant dest scope flags are recorded here + VkPipelineStageFlags dst_stage_flags; // Stage flags within scope of this access + VkAccessFlags dst_access_flags; // Access flags within scope of this access + // These bool shortcuts indicate if memory and/or pipeline barriers have occurred since this access + bool mem_barrier; // True after relevant mem barrier added to CB following this access + bool pipe_barrier; // True after relevant mem barrier added to CB following this access + // A vector of all synch commands that affect this memory access + std::vector synch_commands; // Relevent synch commands +}; + +class Command { + public: + Command(CMD_TYPE type, size_t seq, GLOBAL_CB_NODE *gcb) : type(type), seq(seq), cb_state(gcb), replay(false), synch(false){}; + Command(CMD_TYPE type, size_t seq, GLOBAL_CB_NODE *gcb, bool synch) + : type(type), seq(seq), cb_state(gcb), replay(false), synch(synch){}; + virtual ~Command() {} + void AddMemoryAccess(MemoryAccess access) { mem_accesses.push_back(access); }; + void SetSeq(size_t seq_num) { seq = seq_num; }; + CMD_TYPE type; + size_t seq; // seq # of cmd in this cmd buffer + GLOBAL_CB_NODE *cb_state; + bool replay; // Track if cmd has been replayed during QueueSubmit + bool synch; + std::vector mem_accesses; // vector of all mem accesses by this cmd +}; + +class SynchCommand : public Command { + public: + SynchCommand(CMD_TYPE type, size_t seq, GLOBAL_CB_NODE *gcb, VkPipelineStageFlags src_stage_flags, + VkPipelineStageFlags dst_stage_flags) + : Command(type, seq, gcb, true), src_stage_flags(src_stage_flags), dst_stage_flags(dst_stage_flags){}; + VkPipelineStageFlags src_stage_flags; + VkPipelineStageFlags dst_stage_flags; + std::vector global_barriers; + std::vector buffer_barriers; + std::vector image_barriers; }; enum CB_STATE { @@ -634,6 +869,25 @@ struct LAST_BOUND_STATE { dynamicOffsets.clear(); } }; + +// Struct container to group sets of r/w buffers & images & r/w mem access maps +struct MemAccessGroup { + std::unordered_set read_images; + std::unordered_set read_buffers; + std::unordered_set write_images; + std::unordered_set write_buffers; + std::vector>> access_maps; + MemAccessGroup() { access_maps.resize(2); } + void reset() { + read_images.clear(); + read_buffers.clear(); + write_images.clear(); + write_buffers.clear(); + access_maps.clear(); + access_maps.resize(2); + } +}; + // Cmd Buffer Wrapper Struct - TODO : This desperately needs its own class struct GLOBAL_CB_NODE : public BASE_NODE { VkCommandBuffer commandBuffer; @@ -665,6 +919,11 @@ struct GLOBAL_CB_NODE : public BASE_NODE { // dependencies that have been broken : either destroyed objects, or updated descriptor sets std::unordered_set object_bindings; std::vector broken_bindings; + // + std::vector> commands; // Commands in this command buffer + std::vector synch_commands; // Synch Commands in this command buffer + // Track all mem_accesses by this CB at the point of a draw + MemAccessGroup mem_accesses; std::unordered_set waitedEvents; std::vector writeEventsBeforeWait; @@ -679,9 +938,6 @@ struct GLOBAL_CB_NODE : public BASE_NODE { DRAW_DATA currentDrawData; bool vertex_buffer_used; // Track for perf warning to make sure any bound vtx buffer used VkCommandBuffer primaryCommandBuffer; - // Track images and buffers that are updated by this CB at the point of a draw - std::unordered_set updateImages; - std::unordered_set updateBuffers; // If primary, the secondary command buffers we will call. // If secondary, the primary command buffers we will be called by. std::unordered_set linkedCommandBuffers; diff --git a/layers/descriptor_sets.cpp b/layers/descriptor_sets.cpp index cf527cd410..70099ab727 100644 --- a/layers/descriptor_sets.cpp +++ b/layers/descriptor_sets.cpp @@ -553,9 +553,11 @@ bool cvdescriptorset::DescriptorSet::ValidateDrawState(const std::map &bindings, - std::unordered_set *buffer_set, - std::unordered_set *image_set) const { +uint32_t cvdescriptorset::DescriptorSet::GetReadWriteBuffersAndImages(const std::map &bindings, + std::unordered_set *read_buffer_set, + std::unordered_set *read_image_set, + std::unordered_set *write_buffer_set, + std::unordered_set *write_image_set) const { auto num_updates = 0; for (auto binding_pair : bindings) { auto binding = binding_pair.first; @@ -564,15 +566,32 @@ uint32_t cvdescriptorset::DescriptorSet::GetStorageUpdates(const std::mapGetGlobalStartIndexFromBinding(binding); + auto &buffer_set = read_buffer_set; + auto &image_set = read_image_set; if (descriptors_[start_idx]->IsStorage()) { - if (Image == descriptors_[start_idx]->descriptor_class) { + buffer_set = write_buffer_set; + image_set = write_image_set; + } + switch (descriptors_[start_idx]->descriptor_class) { + case (Image): { for (uint32_t i = 0; i < p_layout_->GetDescriptorCountFromBinding(binding); ++i) { if (descriptors_[start_idx + i]->updated) { image_set->insert(static_cast(descriptors_[start_idx + i].get())->GetImageView()); num_updates++; } } - } else if (TexelBuffer == descriptors_[start_idx]->descriptor_class) { + break; + } + case (ImageSampler): { + for (uint32_t i = 0; i < p_layout_->GetDescriptorCountFromBinding(binding); ++i) { + if (descriptors_[start_idx + i]->updated) { + image_set->insert(static_cast(descriptors_[start_idx + i].get())->GetImageView()); + num_updates++; + } + } + break; + } + case (TexelBuffer): { for (uint32_t i = 0; i < p_layout_->GetDescriptorCountFromBinding(binding); ++i) { if (descriptors_[start_idx + i]->updated) { auto bufferview = static_cast(descriptors_[start_idx + i].get())->GetBufferView(); @@ -583,14 +602,20 @@ uint32_t cvdescriptorset::DescriptorSet::GetStorageUpdates(const std::mapdescriptor_class) { + break; + } + case (GeneralBuffer): { for (uint32_t i = 0; i < p_layout_->GetDescriptorCountFromBinding(binding); ++i) { if (descriptors_[start_idx + i]->updated) { buffer_set->insert(static_cast(descriptors_[start_idx + i].get())->GetBuffer()); num_updates++; } } + break; } + default: + // Don't need to do anything for Sampler case + break; } } return num_updates; diff --git a/layers/descriptor_sets.h b/layers/descriptor_sets.h index bcd1f6801e..e05a99176e 100644 --- a/layers/descriptor_sets.h +++ b/layers/descriptor_sets.h @@ -348,8 +348,10 @@ class DescriptorSet : public BASE_NODE { const char *caller, std::string *) const; // For given set of bindings, add any buffers and images that will be updated to their respective unordered_sets & return number // of objects inserted - uint32_t GetStorageUpdates(const std::map &, std::unordered_set *, - std::unordered_set *) const; + uint32_t GetReadWriteBuffersAndImages(const std::map &, std::unordered_set *read_buffer_set, + std::unordered_set *read_image_set, + std::unordered_set *write_buffer_set, + std::unordered_set *write_image_set) const; // Descriptor Update functions. These functions validate state and perform update separately // Validate contents of a WriteUpdate diff --git a/tests/layer_validation_tests.cpp b/tests/layer_validation_tests.cpp index ed072bcc54..afe2bfe871 100644 --- a/tests/layer_validation_tests.cpp +++ b/tests/layer_validation_tests.cpp @@ -10197,6 +10197,548 @@ TEST_F(VkLayerTest, UpdateBufferWithinRenderPass) { m_errorMonitor->VerifyFound(); } +#ifdef ENABLE_MEMORY_ACCESS_CALLBACK +TEST_F(VkLayerTest, DrawWithBufferWaRandRaWConflicts) { + TEST_DESCRIPTION( + "Attempt a draw that reads from a buffer that was written to and writes" + "to a buffer that was read from, both without any barrier present."); + + ASSERT_NO_FATAL_FAILURE(Init()); + ASSERT_NO_FATAL_FAILURE(InitViewport()); + ASSERT_NO_FATAL_FAILURE(InitRenderTarget()); + + m_commandBuffer->begin(); + + // Don't try this at home. Writing data into buffer that will be used as storage buffer + // for the draw. Before the Draw, though, copying data from the storage buffer into the + // uniform buffer, which will then be read during the draw. This will create a RaW issue + // for the uniform buffer and a WaR issue for the storage buffer. + VkDeviceSize buff_size = 1024; + uint32_t qfi = 0; + VkBufferCreateInfo bci = {}; + bci.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; + bci.usage = VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT; + bci.size = buff_size; + bci.queueFamilyIndexCount = 1; + bci.pQueueFamilyIndices = &qfi; + VkMemoryPropertyFlags reqs = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT; + vk_testing::Buffer storage_buffer; + storage_buffer.init(*m_device, bci, reqs); + bci.usage = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT; + vk_testing::Buffer uniform_buffer; + uniform_buffer.init(*m_device, bci, reqs); + + VkDeviceSize offset = 0; + uint32_t num_elements = (uint32_t)buff_size / sizeof(uint32_t); // Number of 32bit elements + std::vector Data(num_elements, 0); + // Fill in data buffer + for (uint32_t i = 0; i < num_elements; ++i) { + Data[i] = i; + } + VkDeviceSize data_size = Data.size() * sizeof(uint32_t); + // Write data into storage buffer + vkCmdUpdateBuffer(m_commandBuffer->handle(), storage_buffer.handle(), offset, data_size, Data.data()); + // Global Barrier to make sure buffer update completed (so we don't get RaW on copy) + VkMemoryBarrier mem_barrier = {}; + mem_barrier.sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER; + mem_barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; + mem_barrier.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT; + vkCmdPipelineBarrier(m_commandBuffer->handle(), VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 1, + &mem_barrier, 0, nullptr, 0, nullptr); + // Copy storage buffer contents to uniform buffer + VkBufferCopy buff_copy = {0, // srcOffset + 0, // dstOffset + buff_size}; + vkCmdCopyBuffer(m_commandBuffer->handle(), storage_buffer.handle(), uniform_buffer.handle(), 1, &buff_copy); + // + OneOffDescriptorSet ds(m_device->device(), { + {0, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 1, VK_SHADER_STAGE_ALL, nullptr}, + {1, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1, VK_SHADER_STAGE_ALL, nullptr}, + }); + + VkPipelineLayoutCreateInfo pipeline_layout_ci = {}; + pipeline_layout_ci.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO; + pipeline_layout_ci.setLayoutCount = 1; + pipeline_layout_ci.pSetLayouts = &ds.layout_; + + VkPipelineLayout pipeline_layout; + VkResult err = vkCreatePipelineLayout(m_device->device(), &pipeline_layout_ci, NULL, &pipeline_layout); + ASSERT_VK_SUCCESS(err); + + VkDescriptorBufferInfo buff_info = {}; + buff_info.buffer = uniform_buffer.handle(); + buff_info.range = buff_size; + VkWriteDescriptorSet descriptor_write = {}; + descriptor_write.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; + descriptor_write.dstBinding = 0; + descriptor_write.descriptorCount = 1; + descriptor_write.pTexelBufferView = nullptr; + descriptor_write.pBufferInfo = &buff_info; + descriptor_write.pImageInfo = nullptr; + descriptor_write.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; + descriptor_write.dstSet = ds.set_; + vkUpdateDescriptorSets(m_device->device(), 1, &descriptor_write, 0, NULL); + // + buff_info.buffer = storage_buffer.handle(); + descriptor_write.dstBinding = 1; + descriptor_write.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER; + vkUpdateDescriptorSets(m_device->device(), 1, &descriptor_write, 0, NULL); + + // Create PSO that uses the uniform buffers + char const *vsSource = + "#version 450\n" + "\n" + "void main(){\n" + " gl_Position = vec4(1);\n" + "}\n"; + char const *fsSource = + "#version 450\n" + "\n" + "layout(location=0) out vec4 color;\n" + "layout(set=0, binding=0) uniform block { vec4 read; };\n" + "layout(set=0, binding=1) buffer block { vec4 write; };\n" + "void main(){\n" + " write = read;\n" + " color = vec4(1);" + "}\n"; + VkShaderObj vs(m_device, vsSource, VK_SHADER_STAGE_VERTEX_BIT, this); + VkShaderObj fs(m_device, fsSource, VK_SHADER_STAGE_FRAGMENT_BIT, this); + + VkPipelineObj pipe(m_device); + pipe.AddShader(&vs); + pipe.AddShader(&fs); + pipe.AddColorAttachment(); + + err = pipe.CreateVKPipeline(pipeline_layout, renderPass()); + ASSERT_VK_SUCCESS(err); + m_commandBuffer->BeginRenderPass(m_renderPassBeginInfo); + + vkCmdBindPipeline(m_commandBuffer->handle(), VK_PIPELINE_BIND_POINT_GRAPHICS, pipe.handle()); + vkCmdBindDescriptorSets(m_commandBuffer->handle(), VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline_layout, 0, 1, &ds.set_, 0, + nullptr); + + VkViewport viewport = {0, 0, 16, 16, 0, 1}; + vkCmdSetViewport(m_commandBuffer->handle(), 0, 1, &viewport); + VkRect2D scissor = {{0, 0}, {16, 16}}; + vkCmdSetScissor(m_commandBuffer->handle(), 0, 1, &scissor); + // Should now trigger RaW and WaR errors at Draw time + m_errorMonitor->SetDesiredFailureMsg(VK_DEBUG_REPORT_WARNING_BIT_EXT, " causes a read after write conflict with "); + m_errorMonitor->SetDesiredFailureMsg(VK_DEBUG_REPORT_WARNING_BIT_EXT, " causes a write after read conflict with "); + vkCmdDraw(m_commandBuffer->handle(), 3, 1, 0, 0); + m_errorMonitor->VerifyFound(); + vkCmdEndRenderPass(m_commandBuffer->handle()); + m_commandBuffer->end(); + vkDestroyPipelineLayout(m_device->device(), pipeline_layout, nullptr); +} + +// This is a positive test to verify inter-CB memory access code is working correctly +// CB0 writes a SBuff +// CB1 has a synch for the write, copies SBuff to UBuff, and has a synch that doesn't affect copy +// CB2 has a synch that chains w/ last synch from prev CB and a Draw that reads UBuff & writes SBuff +TEST_F(VkPositiveLayerTest, MultiCBDrawWithBufferWaRandRaWSynchChain) { + TEST_DESCRIPTION( + "Create a sequence of 3 CBs that contain memory dependencies that are" + "cleared by synchs across CBs."); + + m_errorMonitor->ExpectSuccess(); + ASSERT_NO_FATAL_FAILURE(Init()); + ASSERT_NO_FATAL_FAILURE(InitViewport()); + ASSERT_NO_FATAL_FAILURE(InitRenderTarget()); + + VkCommandBuffer cmd_bufs[3]; + VkCommandBufferAllocateInfo alloc_info; + alloc_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; + alloc_info.pNext = nullptr; + alloc_info.commandBufferCount = 3; + alloc_info.commandPool = m_commandPool->handle(); + alloc_info.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; + vkAllocateCommandBuffers(m_device->device(), &alloc_info, cmd_bufs); + + // First command buffer will write into storage buffer + VkDeviceSize buff_size = 1024; + uint32_t qfi = 0; + VkBufferCreateInfo bci = {}; + bci.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; + bci.usage = VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT; + bci.size = buff_size; + bci.queueFamilyIndexCount = 1; + bci.pQueueFamilyIndices = &qfi; + VkMemoryPropertyFlags reqs = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT; + vk_testing::Buffer storage_buffer; + storage_buffer.init(*m_device, bci, reqs); + bci.usage = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT; + vk_testing::Buffer uniform_buffer; + uniform_buffer.init(*m_device, bci, reqs); + + VkCommandBufferBeginInfo cbbi; + cbbi.pNext = nullptr; + cbbi.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; + cbbi.pInheritanceInfo = VK_NULL_HANDLE; + cbbi.flags = 0; + vkBeginCommandBuffer(cmd_bufs[0], &cbbi); + + VkDeviceSize offset = 0; + uint32_t num_elements = (uint32_t)buff_size / sizeof(uint32_t); // Number of 32bit elements + std::vector Data(num_elements, 0); + // Fill in data buffer + for (uint32_t i = 0; i < num_elements; ++i) { + Data[i] = i; + } + VkDeviceSize data_size = Data.size() * sizeof(uint32_t); + // Write data into storage buffer + vkCmdUpdateBuffer(cmd_bufs[0], storage_buffer.handle(), offset, data_size, Data.data()); + vkEndCommandBuffer(cmd_bufs[0]); + + // Second command buffer starts with global barrier to clear buffer update + vkBeginCommandBuffer(cmd_bufs[1], &cbbi); + VkMemoryBarrier mem_barrier = {}; + mem_barrier.sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER; + mem_barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; + mem_barrier.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT; + vkCmdPipelineBarrier(cmd_bufs[1], VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 1, &mem_barrier, 0, + nullptr, 0, nullptr); + // Copy storage buffer contents to uniform buffer + VkBufferCopy buff_copy = {0, // srcOffset + 0, // dstOffset + buff_size}; + vkCmdCopyBuffer(cmd_bufs[1], storage_buffer.handle(), uniform_buffer.handle(), 1, &buff_copy); + // Now add a synch that doesn't affect outstanding R&W, but will chain with synch from next cmd buffer + // TODO: Would like to create synch chain here to verify synch merge code. Initial attempts cause other errors + // mem_barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT | VK_ACCESS_TRANSFER_READ_BIT; + // vkCmdPipelineBarrier(cmd_bufs[1], VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, 0, 1, + // &mem_barrier, 0, nullptr, 0, nullptr); + vkEndCommandBuffer(cmd_bufs[1]); + + // Last Cmd buffer has synch that should chain with previous synch to make buffer accesses visible + vkBeginCommandBuffer(cmd_bufs[2], &cbbi); + mem_barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT | VK_ACCESS_TRANSFER_READ_BIT; + mem_barrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT | VK_ACCESS_TRANSFER_READ_BIT; + vkCmdPipelineBarrier(cmd_bufs[2], VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 1, &mem_barrier, 0, + nullptr, 0, nullptr); + OneOffDescriptorSet ds(m_device->device(), { + {0, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 1, VK_SHADER_STAGE_ALL, nullptr}, + {1, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1, VK_SHADER_STAGE_ALL, nullptr}, + }); + + VkPipelineLayoutCreateInfo pipeline_layout_ci = {}; + pipeline_layout_ci.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO; + pipeline_layout_ci.setLayoutCount = 1; + pipeline_layout_ci.pSetLayouts = &ds.layout_; + + VkPipelineLayout pipeline_layout; + VkResult err = vkCreatePipelineLayout(m_device->device(), &pipeline_layout_ci, NULL, &pipeline_layout); + ASSERT_VK_SUCCESS(err); + + VkDescriptorBufferInfo buff_info = {}; + buff_info.buffer = uniform_buffer.handle(); + buff_info.range = buff_size; + VkWriteDescriptorSet descriptor_write = {}; + descriptor_write.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; + descriptor_write.dstBinding = 0; + descriptor_write.descriptorCount = 1; + descriptor_write.pTexelBufferView = nullptr; + descriptor_write.pBufferInfo = &buff_info; + descriptor_write.pImageInfo = nullptr; + descriptor_write.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; + descriptor_write.dstSet = ds.set_; + vkUpdateDescriptorSets(m_device->device(), 1, &descriptor_write, 0, NULL); + // + buff_info.buffer = storage_buffer.handle(); + descriptor_write.dstBinding = 1; + descriptor_write.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER; + vkUpdateDescriptorSets(m_device->device(), 1, &descriptor_write, 0, NULL); + + // Create PSO that uses the uniform buffers + char const *vsSource = + "#version 450\n" + "\n" + "void main(){\n" + " gl_Position = vec4(1);\n" + "}\n"; + char const *fsSource = + "#version 450\n" + "\n" + "layout(location=0) out vec4 color;\n" + "layout(set=0, binding=0) uniform block { vec4 read; };\n" + "layout(set=0, binding=1) buffer block { vec4 write; };\n" + "void main(){\n" + " write = read;\n" + " color = vec4(1);" + "}\n"; + VkShaderObj vs(m_device, vsSource, VK_SHADER_STAGE_VERTEX_BIT, this); + VkShaderObj fs(m_device, fsSource, VK_SHADER_STAGE_FRAGMENT_BIT, this); + + VkPipelineObj pipe(m_device); + pipe.AddShader(&vs); + pipe.AddShader(&fs); + pipe.AddColorAttachment(); + + err = pipe.CreateVKPipeline(pipeline_layout, renderPass()); + ASSERT_VK_SUCCESS(err); + vkCmdBeginRenderPass(cmd_bufs[2], &m_renderPassBeginInfo, VK_SUBPASS_CONTENTS_INLINE); + + vkCmdBindPipeline(cmd_bufs[2], VK_PIPELINE_BIND_POINT_GRAPHICS, pipe.handle()); + vkCmdBindDescriptorSets(cmd_bufs[2], VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline_layout, 0, 1, &ds.set_, 0, nullptr); + + VkViewport viewport = {0, 0, 16, 16, 0, 1}; + vkCmdSetViewport(cmd_bufs[2], 0, 1, &viewport); + VkRect2D scissor = {{0, 0}, {16, 16}}; + vkCmdSetScissor(cmd_bufs[2], 0, 1, &scissor); + vkCmdDraw(cmd_bufs[2], 3, 1, 0, 0); + vkCmdEndRenderPass(cmd_bufs[2]); + vkEndCommandBuffer(cmd_bufs[2]); + + // Submit command buffers and verify no error + VkSubmitInfo submit_info = {}; + submit_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; + submit_info.commandBufferCount = 3; + submit_info.pCommandBuffers = cmd_bufs; + vkQueueSubmit(m_device->m_queue, 1, &submit_info, VK_NULL_HANDLE); + vkQueueWaitIdle(m_device->m_queue); + m_errorMonitor->VerifyNotFound(); + vkDestroyPipelineLayout(m_device->device(), pipeline_layout, nullptr); +} +#endif +TEST_F(VkPositiveLayerTest, UpdateBufferRaWDependencyWithBarrier) { + TEST_DESCRIPTION("Update buffer used in CmdDrawIndirect w/ barrier before use"); + + m_errorMonitor->ExpectSuccess(); + ASSERT_NO_FATAL_FAILURE(Init()); + ASSERT_NO_FATAL_FAILURE(InitRenderTarget()); + + m_commandBuffer->begin(); + + VkMemoryPropertyFlags reqs = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT; + vk_testing::Buffer dst_buffer; + dst_buffer.init_as_dst(*m_device, (VkDeviceSize)1024, reqs); + + VkDeviceSize dstOffset = 0; + uint32_t Data[] = {1, 2, 3, 4, 5, 6, 7, 8}; + VkDeviceSize dataSize = sizeof(Data) / sizeof(uint32_t); + vkCmdUpdateBuffer(m_commandBuffer->handle(), dst_buffer.handle(), dstOffset, dataSize, &Data); + // Global Barrier to make sure buffer update completed + VkMemoryBarrier mem_barrier = {}; + mem_barrier.sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER; + mem_barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; + mem_barrier.dstAccessMask = VK_ACCESS_INDIRECT_COMMAND_READ_BIT; + vkCmdPipelineBarrier(m_commandBuffer->handle(), VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_DRAW_INDIRECT_BIT, 0, 1, + &mem_barrier, 0, nullptr, 0, nullptr); + // Start renderpass for DrawIndirect + m_commandBuffer->BeginRenderPass(m_renderPassBeginInfo); + // Bind gfx PSO to prevent unexpected errors + // Create PSO to be used for draw-time errors below + VkShaderObj vs(m_device, bindStateVertShaderText, VK_SHADER_STAGE_VERTEX_BIT, this); + VkShaderObj fs(m_device, bindStateFragShaderText, VK_SHADER_STAGE_FRAGMENT_BIT, this); + VkPipelineObj pipe(m_device); + pipe.AddShader(&vs); + pipe.AddShader(&fs); + VkViewport view_port = {}; + m_viewports.push_back(view_port); + pipe.SetViewport(m_viewports); + VkRect2D rect = {}; + m_scissors.push_back(rect); + pipe.SetScissor(m_scissors); + pipe.AddColorAttachment(); + VkPipelineLayoutCreateInfo pipeline_layout_ci = {}; + pipeline_layout_ci.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO; + pipeline_layout_ci.setLayoutCount = 0; + pipeline_layout_ci.pSetLayouts = NULL; + + VkPipelineLayout pipeline_layout; + VkResult err = vkCreatePipelineLayout(m_device->device(), &pipeline_layout_ci, NULL, &pipeline_layout); + ASSERT_VK_SUCCESS(err); + pipe.CreateVKPipeline(pipeline_layout, renderPass()); + + vkCmdBindPipeline(m_commandBuffer->handle(), VK_PIPELINE_BIND_POINT_GRAPHICS, pipe.handle()); + // Now issue indirect draw + vkCmdDrawIndirect(m_commandBuffer->handle(), dst_buffer.handle(), 0, 1, 0); + m_errorMonitor->VerifyNotFound(); + vkDestroyPipelineLayout(m_device->device(), pipeline_layout, nullptr); +} + +TEST_F(VkPositiveLayerTest, UpdateBufferWaRDependencyWithTwoBarrierChain) { + TEST_DESCRIPTION("Update buffer used in CmdDrawIndirect w/ barrier before use"); + + m_errorMonitor->ExpectSuccess(); + ASSERT_NO_FATAL_FAILURE(Init()); + ASSERT_NO_FATAL_FAILURE(InitRenderTarget()); + + m_commandBuffer->begin(); + + VkDeviceSize buff_size = 1024; + + VkMemoryPropertyFlags reqs = VK_MEMORY_HEAP_DEVICE_LOCAL_BIT; + vk_testing::Buffer src_buffer; + src_buffer.init_as_src_and_dst(*m_device, buff_size, reqs); + + reqs = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT; + vk_testing::Buffer dst_buffer; + dst_buffer.init_as_dst(*m_device, buff_size, reqs); + + VkBufferCopy buff_copy = {0, // srcOffset + 0, // dstOffset + buff_size}; + // Copy src to dst + vkCmdCopyBuffer(m_commandBuffer->handle(), src_buffer.handle(), dst_buffer.handle(), 1, &buff_copy); + + // Create an execution barrier chain (no mem barrier needed for WaR dependency) + vkCmdPipelineBarrier(m_commandBuffer->handle(), VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, 0, 0, + nullptr, 0, nullptr, 0, nullptr); + // Now 2nd global barrier introduces dependency chain with first barrier that should handle following WaR dependency + vkCmdPipelineBarrier(m_commandBuffer->handle(), VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, + nullptr, 0, nullptr, 0, nullptr); + // Now write into dst buffer that was read above + uint32_t Data[] = {1, 2, 3, 4, 5, 6, 7, 8}; + VkDeviceSize dataSize = sizeof(Data) / sizeof(uint32_t); + vkCmdUpdateBuffer(m_commandBuffer->handle(), src_buffer.handle(), 0, dataSize, &Data); + m_errorMonitor->VerifyNotFound(); +} + +#ifdef ENABLE_MEMORY_ACCESS_CALLBACK +TEST_F(VkLayerTest, UpdateBufferRaWDependencyMissingBarrier) { + TEST_DESCRIPTION("Update buffer used in CmdDrawIndirect without a barrier before use"); + + ASSERT_NO_FATAL_FAILURE(Init()); + ASSERT_NO_FATAL_FAILURE(InitRenderTarget()); + + m_commandBuffer->begin(); + + VkMemoryPropertyFlags reqs = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT; + vk_testing::Buffer dst_buffer; + dst_buffer.init_as_dst(*m_device, (VkDeviceSize)1024, reqs); + + VkDeviceSize dstOffset = 0; + uint32_t Data[] = {1, 2, 3, 4, 5, 6, 7, 8}; + VkDeviceSize dataSize = sizeof(Data) / sizeof(uint32_t); + vkCmdUpdateBuffer(m_commandBuffer->handle(), dst_buffer.handle(), dstOffset, dataSize, &Data); + // No barrier present here should lead to RaW error + + // Start renderpass for DrawIndirect + m_commandBuffer->BeginRenderPass(m_renderPassBeginInfo); + // Bind gfx PSO to prevent unexpected errors + // Create PSO to be used for draw-time errors below + VkShaderObj vs(m_device, bindStateVertShaderText, VK_SHADER_STAGE_VERTEX_BIT, this); + VkShaderObj fs(m_device, bindStateFragShaderText, VK_SHADER_STAGE_FRAGMENT_BIT, this); + VkPipelineObj pipe(m_device); + pipe.AddShader(&vs); + pipe.AddShader(&fs); + VkViewport view_port = {}; + m_viewports.push_back(view_port); + pipe.SetViewport(m_viewports); + VkRect2D rect = {}; + m_scissors.push_back(rect); + pipe.SetScissor(m_scissors); + pipe.AddColorAttachment(); + VkPipelineLayoutCreateInfo pipeline_layout_ci = {}; + pipeline_layout_ci.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO; + pipeline_layout_ci.setLayoutCount = 0; + pipeline_layout_ci.pSetLayouts = NULL; + + VkPipelineLayout pipeline_layout; + VkResult err = vkCreatePipelineLayout(m_device->device(), &pipeline_layout_ci, NULL, &pipeline_layout); + ASSERT_VK_SUCCESS(err); + pipe.CreateVKPipeline(pipeline_layout, renderPass()); + + vkCmdBindPipeline(m_commandBuffer->handle(), VK_PIPELINE_BIND_POINT_GRAPHICS, pipe.handle()); + // Now issue indirect draw + m_errorMonitor->SetDesiredFailureMsg(VK_DEBUG_REPORT_WARNING_BIT_EXT, " causes a read after write conflict with "); + vkCmdDrawIndirect(m_commandBuffer->handle(), dst_buffer.handle(), 0, 1, 0); + m_errorMonitor->VerifyFound(); + vkDestroyPipelineLayout(m_device->device(), pipeline_layout, nullptr); +} + +TEST_F(VkLayerTest, TwoCommandBufferUpdateBufferRaWDependencyMissingBarrier) { + TEST_DESCRIPTION( + "Update buffer in first command buffer then use buffer in CmdDrawIndirect in second command buffer with no barrier before " + "use"); + + ASSERT_NO_FATAL_FAILURE(Init()); + ASSERT_NO_FATAL_FAILURE(InitRenderTarget()); + + VkCommandBuffer cmd_bufs[2]; + VkCommandBufferAllocateInfo alloc_info; + alloc_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; + alloc_info.pNext = nullptr; + alloc_info.commandBufferCount = 2; + alloc_info.commandPool = m_commandPool->handle(); + alloc_info.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; + vkAllocateCommandBuffers(m_device->device(), &alloc_info, cmd_bufs); + + VkCommandBufferBeginInfo cbbi; + cbbi.pNext = nullptr; + cbbi.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; + cbbi.pInheritanceInfo = VK_NULL_HANDLE; + cbbi.flags = 0; + vkBeginCommandBuffer(cmd_bufs[0], &cbbi); + + VkMemoryPropertyFlags reqs = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT; + vk_testing::Buffer dst_buffer; + dst_buffer.init_as_dst(*m_device, (VkDeviceSize)1024, reqs); + + VkDeviceSize dstOffset = 0; + uint32_t Data[] = {1, 2, 3, 4, 5, 6, 7, 8}; + VkDeviceSize dataSize = sizeof(Data) / sizeof(uint32_t); + vkCmdUpdateBuffer(cmd_bufs[0], dst_buffer.handle(), dstOffset, dataSize, &Data); + // No barrier between here and use of buffer in follow-on CB should cause RaW issue + vkEndCommandBuffer(cmd_bufs[0]); + + vkBeginCommandBuffer(cmd_bufs[1], &cbbi); + // Start renderpass for DrawIndirect + vkCmdBeginRenderPass(cmd_bufs[1], &m_renderPassBeginInfo, VK_SUBPASS_CONTENTS_INLINE); + // Bind gfx PSO to prevent unexpected errors + // Create PSO to be used for draw-time errors below + VkShaderObj vs(m_device, bindStateVertShaderText, VK_SHADER_STAGE_VERTEX_BIT, this); + VkShaderObj fs(m_device, bindStateFragShaderText, VK_SHADER_STAGE_FRAGMENT_BIT, this); + VkPipelineObj pipe(m_device); + pipe.AddShader(&vs); + pipe.AddShader(&fs); + VkViewport view_port = {}; + m_viewports.push_back(view_port); + pipe.SetViewport(m_viewports); + VkRect2D rect = {}; + m_scissors.push_back(rect); + pipe.SetScissor(m_scissors); + pipe.AddColorAttachment(); + VkPipelineLayoutCreateInfo pipeline_layout_ci = {}; + pipeline_layout_ci.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO; + pipeline_layout_ci.setLayoutCount = 0; + pipeline_layout_ci.pSetLayouts = NULL; + + VkPipelineLayout pipeline_layout; + VkResult err = vkCreatePipelineLayout(m_device->device(), &pipeline_layout_ci, NULL, &pipeline_layout); + ASSERT_VK_SUCCESS(err); + pipe.CreateVKPipeline(pipeline_layout, renderPass()); + + vkCmdBindPipeline(cmd_bufs[1], VK_PIPELINE_BIND_POINT_GRAPHICS, pipe.handle()); + // Now issue indirect draw using buffer written in 1st cmd buffer + vkCmdDrawIndirect(cmd_bufs[1], dst_buffer.handle(), 0, 1, 0); + vkCmdEndRenderPass(cmd_bufs[1]); + vkEndCommandBuffer(cmd_bufs[1]); + // Now submit the CBs back-to-back and verify that error occurs + VkSubmitInfo submit_info[2] = {}; + submit_info[0].sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; + submit_info[0].commandBufferCount = 2; + submit_info[0].pCommandBuffers = cmd_bufs; + m_errorMonitor->SetDesiredFailureMsg(VK_DEBUG_REPORT_WARNING_BIT_EXT, " causes a read after write conflict with "); + vkQueueSubmit(m_device->m_queue, 1, submit_info, VK_NULL_HANDLE); + m_errorMonitor->VerifyFound(); + vkQueueWaitIdle(m_device->m_queue); + + // Submit the same cmd buffers in two separate submits + submit_info[0].sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; + submit_info[0].commandBufferCount = 1; + submit_info[0].pCommandBuffers = &cmd_bufs[0]; + submit_info[1].sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; + submit_info[1].commandBufferCount = 1; + submit_info[1].pCommandBuffers = &cmd_bufs[1]; + m_errorMonitor->SetDesiredFailureMsg(VK_DEBUG_REPORT_WARNING_BIT_EXT, " causes a read after write conflict with "); + vkQueueSubmit(m_device->m_queue, 2, submit_info, VK_NULL_HANDLE); + m_errorMonitor->VerifyFound(); + vkQueueWaitIdle(m_device->m_queue); + + vkDestroyPipelineLayout(m_device->device(), pipeline_layout, nullptr); +} +#endif TEST_F(VkLayerTest, ClearColorImageWithBadRange) { TEST_DESCRIPTION("Record clear color with an invalid VkImageSubresourceRange");