blob: 330b07ec31c8bb7b60d67ab244d3355e6f3f7ad6 [file] [log] [blame]
/* Copyright (c) 2018-2026 The Khronos Group Inc.
* Copyright (c) 2018-2026 Valve Corporation
* Copyright (c) 2018-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 "gpuav/core/gpuav.h"
#include "gpuav/core/gpuav_validation_pipeline.h"
#include "gpuav/validation_cmd/gpuav_validation_cmd_common.h"
#include "gpuav/resources/gpuav_state_trackers.h"
#include "gpuav/shaders/gpuav_error_header.h"
#include "gpuav/shaders/validation_cmd/push_data.h"
#include "generated/gpuav_offline_spirv.h"
#include "state_tracker/last_bound_state.h"
namespace gpuav {
namespace valcmd {
struct DispatchValidationShader {
static size_t GetSpirvSize() { return validation_cmd_dispatch_comp_size * sizeof(uint32_t); }
static const uint32_t *GetSpirv() { return validation_cmd_dispatch_comp; }
glsl::DispatchPushData push_constants{};
valpipe::BoundStorageBuffer indirect_buffer_binding = {glsl::kPreDispatchBinding_DispatchIndirectBuffer};
static std::vector<VkDescriptorSetLayoutBinding> GetDescriptorSetLayoutBindings() {
return {{glsl::kPreDispatchBinding_DispatchIndirectBuffer, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1,
VK_SHADER_STAGE_COMPUTE_BIT, nullptr}};
}
std::vector<VkWriteDescriptorSet> GetDescriptorWrites() const {
std::vector<VkWriteDescriptorSet> desc_writes(1);
desc_writes[0] = vku::InitStructHelper();
desc_writes[0].dstBinding = indirect_buffer_binding.binding;
desc_writes[0].dstArrayElement = 0;
desc_writes[0].descriptorCount = 1;
desc_writes[0].descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER;
desc_writes[0].pBufferInfo = &indirect_buffer_binding.info;
return desc_writes;
}
};
void DispatchIndirect(Validator &gpuav, const Location &loc, CommandBufferSubState &cb_state, const LastBound &last_bound,
VkBuffer indirect_buffer, VkDeviceSize indirect_offset) {
if (!gpuav.gpuav_settings.validate_indirect_dispatches_buffers) {
return;
}
ValidationCommandsGpuavState &val_cmd_gpuav_state =
gpuav.shared_resources_cache.GetOrCreate<ValidationCommandsGpuavState>(gpuav, loc);
valpipe::ComputePipeline<DispatchValidationShader> &validation_pipeline =
gpuav.shared_resources_cache.GetOrCreate<valpipe::ComputePipeline<DispatchValidationShader>>(
gpuav, loc, val_cmd_gpuav_state.error_logging_desc_set_layout_);
if (!validation_pipeline.valid) {
gpuav.InternalError(cb_state.VkHandle(), loc, "Failed to create DispatchValidationShader.");
return;
}
valpipe::RestorablePipelineState restorable_state(cb_state, VK_PIPELINE_BIND_POINT_COMPUTE);
// Setup shader resources
// ---
{
DispatchValidationShader shader_resources;
shader_resources.push_constants.limit_x = gpuav.phys_dev_props.limits.maxComputeWorkGroupCount[0];
shader_resources.push_constants.limit_y = gpuav.phys_dev_props.limits.maxComputeWorkGroupCount[1];
shader_resources.push_constants.limit_z = gpuav.phys_dev_props.limits.maxComputeWorkGroupCount[2];
shader_resources.push_constants.indirect_x_offset = static_cast<uint32_t>((indirect_offset / sizeof(uint32_t)));
shader_resources.indirect_buffer_binding.info = {indirect_buffer, 0, VK_WHOLE_SIZE};
if (!BindShaderResources(validation_pipeline, gpuav, cb_state, cb_state.compute_index, cb_state.GetErrorLoggerIndex(),
shader_resources)) {
gpuav.InternalError(cb_state.VkHandle(), loc, "Failed to GetManagedDescriptorSet in BindShaderResources");
return;
}
}
// Setup validation pipeline
// ---
{
DispatchCmdBindPipeline(cb_state.VkHandle(), VK_PIPELINE_BIND_POINT_COMPUTE, validation_pipeline.pipeline);
DispatchCmdDispatch(cb_state.VkHandle(), 1, 1, 1);
}
CommandBufferSubState::ErrorLoggerFunc error_logger =
[&gpuav](const uint32_t *error_record, const Location &loc_with_debug_region, const LogObjectList &objlist) {
bool skip = false;
using namespace glsl;
if (GetErrorGroup(error_record) != kErrorGroup_GpuPreDispatch) {
return skip;
}
const uint32_t error_sub_code = GetSubError(error_record);
switch (error_sub_code) {
case kErrorSubCode_PreDispatch_CountLimitX: {
uint32_t count = error_record[kValCmd_ErrorPayloadDword_0];
skip |= gpuav.LogError("VUID-VkDispatchIndirectCommand-x-00417", objlist, loc_with_debug_region,
"Indirect dispatch VkDispatchIndirectCommand::x of %" PRIu32
" would exceed maxComputeWorkGroupCount[0] limit of %" PRIu32 ".",
count, gpuav.phys_dev_props.limits.maxComputeWorkGroupCount[0]);
break;
}
case kErrorSubCode_PreDispatch_CountLimitY: {
uint32_t count = error_record[kValCmd_ErrorPayloadDword_0];
skip |= gpuav.LogError("VUID-VkDispatchIndirectCommand-y-00418", objlist, loc_with_debug_region,
"Indirect dispatch VkDispatchIndirectCommand::y of %" PRIu32
" would exceed maxComputeWorkGroupCount[1] limit of %" PRIu32 ".",
count, gpuav.phys_dev_props.limits.maxComputeWorkGroupCount[1]);
break;
}
case kErrorSubCode_PreDispatch_CountLimitZ: {
uint32_t count = error_record[kValCmd_ErrorPayloadDword_0];
skip |= gpuav.LogError("VUID-VkDispatchIndirectCommand-z-00419", objlist, loc_with_debug_region,
"Indirect dispatch VkDispatchIndirectCommand::z of %" PRIu32
" would exceed maxComputeWorkGroupCount[2] limit of %" PRIu32 ".",
count, gpuav.phys_dev_props.limits.maxComputeWorkGroupCount[0]);
break;
}
default:
break;
}
return skip;
};
cb_state.AddCommandErrorLogger(loc, &last_bound, std::move(error_logger));
}
} // namespace valcmd
} // namespace gpuav