blob: 9e04d21f1df751aa46a400c939bf42705624986b [file]
// Copyright (c) 2021-2026 The Khronos Group Inc.
// Copyright (c) 2021-2026 Valve Corporation
// Copyright (c) 2021-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.
// Values used between the GLSL shaders and the GPU-AV logic
// NOTE: This header is included by the instrumentation shaders and glslang doesn't support #pragma once
#ifndef GPU_ERROR_HEADER_H
#define GPU_ERROR_HEADER_H
#include "gpuav_error_codes.h"
#ifdef __cplusplus
#include <cstdint>
namespace gpuav {
namespace glsl {
using uint = unsigned int;
#endif
// GPU-AV Error record structure:
// ------------------------------
// /---------------------------------
// | Error header
// | - Record size
// | - Shader Id
// | - Instruction Id
// | - Shader stage Id
// | - Shader stage info (3 integers)
// | - Action command index in command buffer
// | - Command resources index
// | - Error group (Id unique to the shader/instrumentation code that wrote the error)
// | - subcode (maps to VUIDs)
// | --------------------------------
// | Error specific parameters
// \---------------------------------
// Error Header offsets:
// ---------------------
// The following are offsets to fields common to all records
// Each record first contains the size of the record in 32-bit words, including
// the size word.
const int kHeader_ErrorRecordSizeOffset = 0;
// We compress the |shader id|, |error group| and |error sub code| in a single dword
// |shader id| is used to map back the VkShaderMoudle/Pipeline/ShaderObject
// |error group| maps to which instrumentation pass is from
// |error sub code| is how we can map things to a single VUID
const int kHeader_ShaderIdErrorOffset = 1;
// This is the ordinal position of the instruction within the SPIR-V shader
// which generated the validation error.
//
// This also is the stage which generated the validation error.
const int kHeader_StageInstructionIdOffset = 2;
// Each stage will contain different values in the next set of words of the
// record used to identify which instantiation of the shader generated the
// validation error.
const int kHeader_StageInfoOffset_0 = 3;
const int kHeader_StageInfoOffset_1 = 4;
const int kHeader_StageInfoOffset_2 = 5;
// Compressed dword to know where the error came from in the API
const int kHeader_ActionIdErrorLoggerIdOffset = 6;
const int kHeaderSize = 7;
const int kInst_LogError_ParameterOffset_0 = kHeaderSize;
const int kInst_LogError_ParameterOffset_1 = kHeaderSize + 1;
const int kInst_LogError_ParameterOffset_2 = kHeaderSize + 2;
// kHeader_ShaderIdErrorOffset
// ---
// This dword is split up as
// | 31 ..... 24 | 23 ......... 18 | 17 ................. 0 |
// | Error Group | Error Sub Group | Instrumented Shader Id |
//
// These limits are reasonable and will be awhile until we go past them
// When moved to slang from GLSL, can add staic asserts
const int kErrorSubCode_Shift = 18;
const int kErrorSubCode_Mask = 0x3F << kErrorSubCode_Shift; // 32 slot
const int kErrorGroup_Shift = 24;
const int kErrorGroup_Mask = 0xFF << kErrorGroup_Shift; // 256 slots
const uint kMaxInstrumentedShaders = 1u << kErrorSubCode_Shift; // 256k
const uint kShaderIdMask = 0x3FFFF; // Used to get unique_shader_id
// kHeader_StageInstructionIdOffset
// ---
// This dword is split up as
// | 31 .. 27 | 26 ...... 0 |
// | Stage Id | Instruction Id |
// We control and know there are under 32 shader stages
// We can assume shader are under 128MB
const int kStageId_Shift = 27;
const int kStageId_Mask = 0x1F << kStageId_Shift; // 32 slot
const int kInstructionId_Mask = 0x7FFFFFF;
// kHeader_ActionIdErrorLoggerIdOffset
// ---
// This dword is split up as
// | 31 ..... 16 | 15 ................. 0 |
// | Error Group | Instrumented Shader Id |
// Note we have a limit (GpuAVSettings::indices_count) but for simplicity, divide in half until find need to adjust.
const int kActionId_Shift = 16;
const int kActionId_Mask = 0xFFFF << kActionId_Shift; // 64k slot
const int kErrorLoggerId_Mask = 0xFFFF;
// Error specific parameters offsets:
// ----------------------------------
// Descriptor Indexing
// ---
// This dword is split up as
// | 31 ........ 27 | 26 ............................................ 0 |
// | Descriptor Set | Global Descriptor index (with binding LUT offset) |
// We only allow for 32 sets (see kDebugInputBindlessMaxDescSets)
// We use a LUT per set that knows which binding the "global" Descriptor Index value lives
const int kInst_DescriptorIndexing_SetShift = 27;
const int kInst_DescriptorIndexing_SetMask = 0x1F << kInst_DescriptorIndexing_SetShift; // 32 slot
const int kInst_DescriptorIndexing_IndexMask = 0x7FFFFFF;
// Buffer device addresses
// ---
// Payload contains 3 pieces of data, compressed into a single uint32_t
// We can be safe that assume alignment we don't need these upper 2 bytes
// Note - if needed, we could use log2(alignment) as it must be a Power-of-Two
// | 31 | 30 | 29 ........................ 0 |
// | is write | is struct | Alignment OR Access Byte Size |
const uint kInst_BuffAddrAccess_PayloadShiftIsWrite = 31;
const uint kInst_BuffAddrAccess_PayloadShiftIsStruct = 30;
const uint kInst_BuffAddrAccess_PayloadMaskAccessInfo = 0x3FFFFFFF;
// kErrorGroup_InstMeshShading
// ---
// This dword is split up as
// | 31 ........ 16 | 15 .............. 0 |
// | OutputVertices | OutputPrimitivesEXT |
// This is because the limits for both of these on all known devices is 1024
const int kInst_MeshShading_OutputVerticesShift = 16;
const int kInst_MeshShading_OutputPrimitivesMask = 0xFFFF;
// Validation commands shaders
// ---
const int kValCmd_ErrorPayloadDword_0 = kHeaderSize;
const int kValCmd_ErrorPayloadDword_1 = kHeaderSize + 1;
const int kValCmd_ErrorPayloadDword_2 = kHeaderSize + 2;
const int kValCmd_ErrorPayloadDword_3 = kHeaderSize + 3;
const int kValCmd_ErrorPayloadDword_4 = kHeaderSize + 4;
// Sizes/Counts
// -------------------
const int kErrorRecordSize = kHeaderSize + 5;
const int kErrorRecordCounts = 4096; // Maximum number of errors a command buffer can hold. Arbitrary value
const int kErrorBufferByteSize = 4 * kErrorRecordSize * kErrorRecordCounts + 2 * 4; // 2 * 4 bytes to store flags and errors count
// We know the size of the uint32_t array for SpecConstantInstErrorBufferLengthId
const int kErrorBufferDataLength = kErrorRecordSize * kErrorRecordCounts;
#ifdef __cplusplus
} // namespace glsl
#endif
// DebugPrintf
// ---
const int kDebugPrintf_OutputBuffer_DWordsCount = 0;
const int kDebugPrintf_OutputBuffer_Data = 1;
#ifdef __cplusplus
[[maybe_unused]] static inline uint32_t GetErrorGroup(const uint32_t* error_record) {
return error_record[glsl::kHeader_ShaderIdErrorOffset] >> glsl::kErrorGroup_Shift;
}
[[maybe_unused]] static inline uint32_t GetSubError(const uint32_t* error_record) {
return (error_record[glsl::kHeader_ShaderIdErrorOffset] & glsl::kErrorSubCode_Mask) >> glsl::kErrorSubCode_Shift;
}
} // namespace gpuav
#endif
#endif