blob: 4388c0d1fa63d1fbf3e1c557e0a9bba22ec95d57 [file] [log] [blame]
/* Copyright (c) 2025-2026 The Khronos Group Inc.
* Copyright (c) 2025-2026 Valve Corporation
* Copyright (c) 2025-2026 LunarG, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "sync/sync_error_messages.h"
#include "sync/sync_commandbuffer.h"
#include "sync/sync_validation.h"
#include "error_message/error_strings.h"
#include "state_tracker/descriptor_sets.h"
#include "state_tracker/pipeline_state.h"
#include "state_tracker/render_pass_state.h"
#include <cassert>
#include <sstream>
namespace syncval {
ErrorMessages::ErrorMessages(SyncValidator& validator) : validator_(validator) {}
std::string ErrorMessages::Error(const HazardResult& hazard, const CommandExecutionContext& context, vvl::Func command,
const std::string& resource_description, const char* message_type,
const AdditionalMessageInfo& additional_info) const {
std::string message = FormatErrorMessage(hazard, context, command, resource_description, additional_info);
if (validator_.syncval_settings.message_extra_properties) {
if (!message.empty() && message.back() != '\n') {
message += '\n';
}
const ReportProperties properties = GetErrorMessageProperties(hazard, context, command, message_type, additional_info);
message += properties.FormatExtraPropertiesSection();
}
return message;
}
std::string ErrorMessages::BufferError(const HazardResult& hazard, const CommandBufferAccessContext& cb_context, vvl::Func command,
const std::string& resource_description, const AccessRange range,
AdditionalMessageInfo additional_info) const {
std::ostringstream ss;
ss << "\nBuffer access region: {\n";
ss << " offset = " << range.begin << "\n";
ss << " size = " << range.end - range.begin << "\n";
ss << "}\n";
additional_info.message_end_text += ss.str();
return Error(hazard, cb_context, command, resource_description, "BufferError", additional_info);
}
std::string ErrorMessages::BufferCopyError(const HazardResult& hazard, const CommandBufferAccessContext& cb_context,
const vvl::Func command, const std::string& resource_description, uint32_t region_index,
AccessRange range) const {
AdditionalMessageInfo additional_info;
additional_info.properties.Add(kPropertyRegionIndex, region_index);
std::ostringstream ss;
ss << "\nBuffer copy region " << region_index << ": {\n";
ss << " offset = " << range.begin << ",\n";
ss << " size = " << range.end - range.begin << "\n";
ss << "}\n";
additional_info.message_end_text = ss.str();
return Error(hazard, cb_context, command, resource_description, "BufferCopyError", additional_info);
}
std::string ErrorMessages::AccelerationStructureError(const HazardResult& hazard, const CommandBufferAccessContext& cb_context,
const vvl::Func command, const std::string& resource_description,
const AccessRange range, VkAccelerationStructureKHR as,
const Location& as_location) const {
AdditionalMessageInfo additional_info;
std::ostringstream ss;
ss << "The buffer backs ";
ss << as_location.Fields();
ss << " (" << validator_.FormatHandle(as) << "). ";
additional_info.pre_synchronization_text = ss.str();
std::ostringstream ss2;
ss2 << "\nBuffer access region: {\n";
ss2 << " offset = " << range.begin << "\n";
ss2 << " size = " << range.end - range.begin << "\n";
ss2 << "}\n";
additional_info.message_end_text += ss2.str();
return Error(hazard, cb_context, command, resource_description, "AccelerationStructureError", additional_info);
}
std::string ErrorMessages::ImageCopyResolveBlitError(const HazardResult& hazard, const CommandBufferAccessContext& cb_context,
vvl::Func command, const std::string& resource_description,
uint32_t region_index, const VkOffset3D& offset, const VkExtent3D& extent,
const VkImageSubresourceLayers& subresource) const {
const char* action = nullptr;
const char* message_type = nullptr;
if (IsValueIn(command, {vvl::Func::vkCmdBlitImage, vvl::Func::vkCmdBlitImage2, vvl::Func::vkCmdBlitImage2KHR})) {
action = "blit";
message_type = "ImageBlitError";
} else if (IsValueIn(command,
{vvl::Func::vkCmdResolveImage, vvl::Func::vkCmdResolveImage2, vvl::Func::vkCmdResolveImage2KHR})) {
action = "resolve";
message_type = "ImageResolveError";
} else {
action = "copy";
message_type = "ImageCopyError";
}
std::ostringstream ss;
ss << "\nImage " << action << " region " << region_index << ": {\n";
ss << " offset = {" << string_VkOffset3D(offset) << "},\n";
ss << " extent = {" << string_VkExtent3D(extent) << "},\n";
ss << " subresource = {" << string_VkImageSubresourceLayers(subresource) << "}\n";
ss << "}\n";
AdditionalMessageInfo additional_info;
additional_info.message_end_text = ss.str();
additional_info.properties.Add(kPropertyRegionIndex, region_index);
return Error(hazard, cb_context, command, resource_description, message_type, additional_info);
}
std::string ErrorMessages::ImageClearError(const HazardResult& hazard, const CommandBufferAccessContext& cb_context,
vvl::Func command, const std::string& resource_description,
uint32_t subresource_range_index,
const VkImageSubresourceRange& subresource_range) const {
std::ostringstream ss;
ss << "\nImage clear subresource range " << subresource_range_index << ": {\n";
ss << " " << string_VkImageSubresourceRange(subresource_range) << "\n";
ss << "}\n";
AdditionalMessageInfo additional_info;
additional_info.message_end_text = ss.str();
additional_info.properties.Add(kPropertyRegionIndex, subresource_range_index);
return Error(hazard, cb_context, command, resource_description, "ImageSubresourceRangeError", additional_info);
}
static void PrepareCommonDescriptorMessage(Logger& logger, const vvl::Pipeline& pipeline, uint32_t descriptor_set_number,
const vvl::DescriptorSet& descriptor_set, VkDescriptorType descriptor_type,
uint32_t descriptor_binding, uint32_t descriptor_array_element,
VkShaderStageFlagBits shader_stage, const char* resource_type,
AdditionalMessageInfo& additional_info, std::ostringstream& ss) {
const char* descriptor_type_str = string_VkDescriptorType(descriptor_type);
additional_info.properties.Add(kPropertyDescriptorType, descriptor_type_str);
additional_info.properties.Add(kPropertyDescriptorBinding, descriptor_binding);
additional_info.properties.Add(kPropertyDescriptorArrayElement, descriptor_array_element);
additional_info.access_initiator = std::string("Shader stage ") + string_VkShaderStageFlagBits(shader_stage);
ss << "\nThe " << resource_type << " is referenced by descriptor binding " << descriptor_binding;
ss << " (" << descriptor_type_str << ")";
if (descriptor_set.GetDescriptorCountFromBinding(descriptor_binding) > 1) {
ss << ", array element " << descriptor_array_element;
}
ss << " from descriptor set " << descriptor_set_number << " (" << logger.FormatHandle(descriptor_set) << ")";
ss << ", " << logger.FormatHandle(pipeline);
}
std::string ErrorMessages::BufferDescriptorError(const HazardResult& hazard, const CommandBufferAccessContext& cb_context,
vvl::Func command, const std::string& resource_description,
const vvl::Pipeline& pipeline, uint32_t set_number,
const vvl::DescriptorSet& descriptor_set, VkDescriptorType descriptor_type,
uint32_t descriptor_binding, uint32_t descriptor_array_element,
VkShaderStageFlagBits shader_stage) const {
AdditionalMessageInfo additional_info;
std::ostringstream ss;
PrepareCommonDescriptorMessage(validator_, pipeline, set_number, descriptor_set, descriptor_type, descriptor_binding,
descriptor_array_element, shader_stage, "buffer", additional_info, ss);
ss << ".";
additional_info.pre_synchronization_text = ss.str();
return Error(hazard, cb_context, command, resource_description, "BufferDescriptorError", additional_info);
}
std::string ErrorMessages::ImageDescriptorError(const HazardResult& hazard, const CommandBufferAccessContext& cb_context,
vvl::Func command, const std::string& resource_description,
const vvl::Pipeline& pipeline, uint32_t set_number,
const vvl::DescriptorSet& descriptor_set, VkDescriptorType descriptor_type,
uint32_t descriptor_binding, uint32_t descriptor_array_element,
VkShaderStageFlagBits shader_stage, VkImageLayout image_layout) const {
AdditionalMessageInfo additional_info;
std::ostringstream ss;
PrepareCommonDescriptorMessage(validator_, pipeline, set_number, descriptor_set, descriptor_type, descriptor_binding,
descriptor_array_element, shader_stage, "image", additional_info, ss);
ss << ", image layout " << string_VkImageLayout(image_layout) << ".";
additional_info.pre_synchronization_text = ss.str();
additional_info.properties.Add(kPropertyImageLayout, string_VkImageLayout(image_layout));
return Error(hazard, cb_context, command, resource_description, "ImageDescriptorError", additional_info);
}
std::string ErrorMessages::AccelerationStructureDescriptorError(
const HazardResult& hazard, const CommandBufferAccessContext& cb_context, vvl::Func command,
const std::string& resource_description, const vvl::Pipeline& pipeline, uint32_t set_number,
const vvl::DescriptorSet& descriptor_set, VkDescriptorType descriptor_type, uint32_t descriptor_binding,
uint32_t descriptor_array_element, VkShaderStageFlagBits shader_stage) const {
AdditionalMessageInfo additional_info;
additional_info.access_action = "traces rays against";
std::ostringstream ss;
PrepareCommonDescriptorMessage(validator_, pipeline, set_number, descriptor_set, descriptor_type, descriptor_binding,
descriptor_array_element, shader_stage, "acceleration structure", additional_info, ss);
ss << ".";
additional_info.pre_synchronization_text = ss.str();
return Error(hazard, cb_context, command, resource_description, "AccelerationStructureDescriptorError", additional_info);
}
std::string ErrorMessages::ClearAttachmentError(const HazardResult& hazard, const CommandBufferAccessContext& cb_context,
vvl::Func command, const std::string& resource_description,
VkImageAspectFlags clear_aspects, uint32_t clear_rect_index,
const VkClearRect& clear_rect) const {
std::ostringstream ss;
ss << "\nClear region: {\n";
ss << " region_index = " << clear_rect_index << ",\n";
ss << " rect = {" << string_VkRect2D(clear_rect.rect) << "},\n";
ss << " baseArrayLayer = " << clear_rect.baseArrayLayer << ",\n";
ss << " layerCount = " << clear_rect.layerCount << "\n";
ss << "}\n";
AdditionalMessageInfo additional_info;
additional_info.properties.Add(kPropertyImageAspects, string_VkImageAspectFlags(clear_aspects));
additional_info.access_action = "clears";
additional_info.message_end_text = ss.str();
return Error(hazard, cb_context, command, resource_description, "ClearAttachmentError", additional_info);
}
std::string ErrorMessages::RenderPassAttachmentError(const HazardResult& hazard, const CommandBufferAccessContext& cb_context,
vvl::Func command, const std::string& resource_description) const {
return Error(hazard, cb_context, command, resource_description, "RenderPassAttachmentError");
}
static const char* GetLoadOpActionName(VkAttachmentLoadOp load_op) {
if (load_op == VK_ATTACHMENT_LOAD_OP_LOAD) {
return "reads";
} else if (load_op == VK_ATTACHMENT_LOAD_OP_CLEAR) {
return "clears";
} else if (load_op == VK_ATTACHMENT_LOAD_OP_DONT_CARE) {
return "potentially modifies";
}
// If custon action name is not specified then it will be derived from hazard type (read or write)
return "";
}
static void CheckForLoadOpDontCareInsight(VkAttachmentLoadOp load_op, bool is_color, std::string& message_end_text) {
if (load_op == VK_ATTACHMENT_LOAD_OP_DONT_CARE) {
std::ostringstream ss;
ss << "\nVulkan insight: according to the specification VK_ATTACHMENT_LOAD_OP_DONT_CARE is a write access (";
if (is_color) {
ss << "VK_ACCESS_2_COLOR_ATTACHMENT_WRITE_BIT for color attachment";
} else {
ss << "VK_ACCESS_2_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT for depth/stencil attachment";
}
ss << ").";
message_end_text += ss.str();
}
}
std::string ErrorMessages::BeginRenderingError(const HazardResult& hazard, const CommandBufferAccessContext& cb_context,
vvl::Func command, const std::string& resource_description,
VkAttachmentLoadOp load_op) const {
AdditionalMessageInfo additional_info;
const char* load_op_str = string_VkAttachmentLoadOp(load_op);
additional_info.properties.Add(kPropertyLoadOp, load_op_str);
additional_info.access_action = GetLoadOpActionName(load_op);
return Error(hazard, cb_context, command, resource_description, "BeginRenderingError", additional_info);
}
std::string ErrorMessages::EndRenderingResolveError(const HazardResult& hazard, const CommandBufferAccessContext& cb_context,
vvl::Func command, const std::string& resource_description,
VkResolveModeFlagBits resolve_mode, bool resolve_write) const {
AdditionalMessageInfo additional_info;
const char* resolve_mode_str = string_VkResolveModeFlagBits(resolve_mode);
additional_info.properties.Add(kPropertyResolveMode, resolve_mode_str);
additional_info.access_action = resolve_write ? "writes to single sample resolve attachment" : "reads multisample attachment";
return Error(hazard, cb_context, command, resource_description, "EndRenderingResolveError", additional_info);
}
std::string ErrorMessages::EndRenderingStoreError(const HazardResult& hazard, const CommandBufferAccessContext& cb_context,
vvl::Func command, const std::string& resource_description,
VkAttachmentStoreOp store_op) const {
AdditionalMessageInfo additional_info;
const char* store_op_str = string_VkAttachmentStoreOp(store_op);
additional_info.properties.Add(kPropertyStoreOp, store_op_str);
return Error(hazard, cb_context, command, resource_description, "EndRenderingStoreError", additional_info);
}
std::string ErrorMessages::RenderPassLoadOpError(const HazardResult& hazard, const CommandBufferAccessContext& cb_context,
vvl::Func command, const std::string& resource_description, uint32_t subpass,
uint32_t attachment, VkAttachmentLoadOp load_op, bool is_color) const {
AdditionalMessageInfo additional_info;
const char* load_op_str = string_VkAttachmentLoadOp(load_op);
additional_info.properties.Add(kPropertyLoadOp, load_op_str);
additional_info.access_action = GetLoadOpActionName(load_op);
CheckForLoadOpDontCareInsight(load_op, is_color, additional_info.message_end_text);
return Error(hazard, cb_context, command, resource_description, "RenderPassLoadOpError", additional_info);
}
std::string ErrorMessages::RenderPassLoadOpVsLayoutTransitionError(const HazardResult& hazard,
const CommandBufferAccessContext& cb_context, vvl::Func command,
const std::string& resource_description,
VkAttachmentLoadOp load_op, bool is_color) const {
AdditionalMessageInfo additional_info;
const char* load_op_str = string_VkAttachmentLoadOp(load_op);
additional_info.properties.Add(kPropertyLoadOp, load_op_str);
additional_info.hazard_overview = "attachment loadOp access is not synchronized with the attachment layout transition";
additional_info.access_action = GetLoadOpActionName(load_op);
CheckForLoadOpDontCareInsight(load_op, is_color, additional_info.message_end_text);
return Error(hazard, cb_context, command, resource_description, "RenderPassLoadOpVsLayoutTransitionError", additional_info);
}
std::string ErrorMessages::RenderPassResolveError(const HazardResult& hazard, const CommandBufferAccessContext& cb_context,
vvl::Func command, const std::string& resource_description) const {
return Error(hazard, cb_context, command, resource_description, "RenderPassResolveError");
}
std::string ErrorMessages::RenderPassStoreOpError(const HazardResult& hazard, const CommandBufferAccessContext& cb_context,
vvl::Func command, const std::string& resource_description,
VkAttachmentStoreOp store_op) const {
AdditionalMessageInfo additional_info;
const char* store_op_str = string_VkAttachmentStoreOp(store_op);
additional_info.properties.Add(kPropertyStoreOp, store_op_str);
return Error(hazard, cb_context, command, resource_description, "RenderPassStoreOpError", additional_info);
}
std::string ErrorMessages::RenderPassLayoutTransitionError(const HazardResult& hazard, const CommandBufferAccessContext& cb_context,
vvl::Func command, const std::string& resource_description,
VkImageLayout old_layout, VkImageLayout new_layout) const {
const char* old_layout_str = string_VkImageLayout(old_layout);
const char* new_layout_str = string_VkImageLayout(new_layout);
AdditionalMessageInfo additional_info;
additional_info.properties.Add(kPropertyOldLayout, old_layout_str);
additional_info.properties.Add(kPropertyNewLayout, new_layout_str);
additional_info.access_action = "performs image layout transition";
return Error(hazard, cb_context, command, resource_description, "RenderPassLayoutTransitionError", additional_info);
}
std::string ErrorMessages::RenderPassLayoutTransitionVsStoreOrResolveError(const HazardResult& hazard,
const CommandBufferAccessContext& cb_context,
vvl::Func command,
const std::string& resource_description,
VkImageLayout old_layout, VkImageLayout new_layout,
uint32_t store_resolve_subpass) const {
const char* old_layout_str = string_VkImageLayout(old_layout);
const char* new_layout_str = string_VkImageLayout(new_layout);
AdditionalMessageInfo additional_info;
additional_info.properties.Add(kPropertyOldLayout, old_layout_str);
additional_info.properties.Add(kPropertyNewLayout, new_layout_str);
additional_info.access_action =
"performs image layout transition during " +
validator_.FormatHandle(cb_context.GetCurrentRenderPassContext()->GetRenderPassState()->Handle());
additional_info.brief_description_end_text = "during store/resolve operation in subpass ";
additional_info.brief_description_end_text += std::to_string(store_resolve_subpass);
return Error(hazard, cb_context, command, resource_description, "RenderPassLayoutTransitionVsStoreOrResolveError",
additional_info);
}
std::string ErrorMessages::RenderPassFinalLayoutTransitionError(const HazardResult& hazard,
const CommandBufferAccessContext& cb_context, vvl::Func command,
const std::string& resource_description, VkImageLayout old_layout,
VkImageLayout new_layout) const {
const char* old_layout_str = string_VkImageLayout(old_layout);
const char* new_layout_str = string_VkImageLayout(new_layout);
AdditionalMessageInfo additional_info;
additional_info.properties.Add(kPropertyOldLayout, old_layout_str);
additional_info.properties.Add(kPropertyNewLayout, new_layout_str);
additional_info.access_action =
"performs final image layout transition during " +
validator_.FormatHandle(cb_context.GetCurrentRenderPassContext()->GetRenderPassState()->Handle());
return Error(hazard, cb_context, command, resource_description, "RenderPassFinalLayoutTransitionError", additional_info);
}
std::string ErrorMessages::RenderPassFinalLayoutTransitionVsStoreOrResolveError(const HazardResult& hazard,
const CommandBufferAccessContext& cb_context,
vvl::Func command,
const std::string& resource_description,
VkImageLayout old_layout, VkImageLayout new_layout,
uint32_t store_resolve_subpass) const {
const char* old_layout_str = string_VkImageLayout(old_layout);
const char* new_layout_str = string_VkImageLayout(new_layout);
AdditionalMessageInfo additional_info;
additional_info.properties.Add(kPropertyOldLayout, old_layout_str);
additional_info.properties.Add(kPropertyNewLayout, new_layout_str);
additional_info.access_action =
"performs final image layout transition during " +
validator_.FormatHandle(cb_context.GetCurrentRenderPassContext()->GetRenderPassState()->Handle());
additional_info.brief_description_end_text = "during store/resolve operation in subpass ";
additional_info.brief_description_end_text += std::to_string(store_resolve_subpass);
return Error(hazard, cb_context, command, resource_description, "RenderPassFinalLayoutTransitionVsStoreOrResolveError",
additional_info);
}
std::string ErrorMessages::ImageBarrierError(const HazardResult& hazard, const CommandExecutionContext& context, vvl::Func command,
const std::string& resource_description, const SyncImageBarrier& barrier) const {
AdditionalMessageInfo additional_info;
additional_info.access_action = "performs image layout transition on the";
std::ostringstream ss;
ss << "\npImageMemoryBarriers[" << barrier.barrier_index << "]: {\n";
ss << " srcStageMask = " << string_VkPipelineStageFlags2(barrier.barrier.src_exec_scope.mask_param) << ",\n";
ss << " srcAccessMask = " << string_VkAccessFlags2(barrier.barrier.original_src_access) << ",\n";
ss << " dstStageMask = " << string_VkPipelineStageFlags2(barrier.barrier.dst_exec_scope.mask_param) << ",\n";
ss << " dstAccessMask = " << string_VkAccessFlags2(barrier.barrier.original_dst_access) << ",\n";
ss << "}\n";
additional_info.message_end_text = ss.str();
return Error(hazard, context, command, resource_description, "ImageBarrierError", additional_info);
}
std::string ErrorMessages::FirstUseError(const HazardResult& hazard, const CommandExecutionContext& exec_context,
const CommandBufferAccessContext& recorded_context, uint32_t command_buffer_index) const {
const ResourceUsageInfo prior_usage_info = exec_context.GetResourceUsageInfo(hazard.TagEx());
const ResourceUsageInfo recorded_usage_info = recorded_context.GetResourceUsageInfo(hazard.RecordedAccess()->TagEx());
AdditionalMessageInfo additional_info;
additional_info.properties.Add(kPropertyCommandBufferIndex, command_buffer_index);
std::ostringstream ss;
ss << vvl::String(recorded_usage_info.command);
if (!recorded_usage_info.debug_region_name.empty()) {
ss << "[" << recorded_usage_info.debug_region_name << "]";
}
if (exec_context.Handle().type == kVulkanObjectTypeQueue) {
ss << " (from " << validator_.FormatHandle(recorded_context.Handle());
ss << " submitted on the current ";
ss << validator_.FormatHandle(exec_context.Handle()) << ")";
} else { // primary command buffer executes secondary one
assert(exec_context.Handle().type == kVulkanObjectTypeCommandBuffer);
ss << " (from the secondary " << validator_.FormatHandle(recorded_context.Handle()) << ")";
}
additional_info.access_initiator = ss.str();
std::ostringstream ss2;
if (prior_usage_info.queue) {
if (prior_usage_info.cb) {
ss2 << "(from " << validator_.FormatHandle(prior_usage_info.cb->Handle());
ss2 << " submitted on " << validator_.FormatHandle(prior_usage_info.queue->Handle()) << ")";
} else { // QueuePresent case (not recorded into command buffer)
ss2 << "(submitted on " << validator_.FormatHandle(prior_usage_info.queue->Handle()) << ")";
}
} else if (prior_usage_info.cb) {
// TODO: distinuish between "native" primary command buffer commands and
// command recorded from the secondary command buffers.
ss2 << "(from the primary " << validator_.FormatHandle(prior_usage_info.cb->Handle()) << ")";
}
additional_info.brief_description_end_text = ss2.str();
if (!recorded_usage_info.debug_region_name.empty()) {
additional_info.properties.Add(kPropertyDebugRegion, recorded_usage_info.debug_region_name);
}
// Use generic "resource" when resource handle is not specified for some reason (likely just a missing code).
// TODO: specify resources in EndRenderPass (NegativeSyncVal.QSOBarrierHazard).
const std::string resource_description = (recorded_usage_info.resource_handle != NullVulkanTypedHandle)
? validator_.FormatHandle(recorded_usage_info.resource_handle)
: "resource";
return Error(hazard, exec_context, recorded_usage_info.command, resource_description, "SubmitTimeError", additional_info);
}
std::string ErrorMessages::PresentError(const HazardResult& hazard, const QueueBatchContext& batch_context, vvl::Func command,
const std::string& resource_description, uint32_t swapchain_index) const {
AdditionalMessageInfo additional_info;
additional_info.access_action = "presents";
additional_info.properties.Add(kPropertySwapchainIndex, swapchain_index);
return Error(hazard, batch_context, command, resource_description, "PresentError", additional_info);
}
std::string ErrorMessages::VideoError(const HazardResult& hazard, const CommandBufferAccessContext& cb_context, vvl::Func command,
const std::string& resource_description) const {
return Error(hazard, cb_context, command, resource_description, "VideoError");
}
} // namespace syncval