// 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;

// Descriptor Heap
// ---
// This dword is split up as
// | 31 ......... 28 | 27 ..........20 | 19 ........ 4 | 3 ........... 0 |
// | Descriptor Type | Descriptor Size | Mapping Index | Alignment Shift |
// - |Descriptor Type| is 4 bits defined in gpuav_descriptor_validation.h
// - |Descriptor Size| is 8 bits to hold the value returned from vkGetPhysicalDeviceDescriptorSizeEXT
//   (if using untyped, will use the property fields instead).
//   These SHOULD be at max 256
//   Also not guaranteed to be a power of 2, so can't compress like the |Alignment Shift|
// - |Mapping Index| is the index into VkShaderDescriptorSetAndBindingMappingInfoEXT::pMapping
//   this is to allow us to get the information that state tracking already has for the error message
// - |Alignment Shift| is used to store the exponent to shift to get the alignment
//    ex. if alignment is 16, we store 4 (1 << 4)
//    Note - Alignment MUST be a power of 2 according the the spec
//    Note - Alignment value SHOULD be at max 256, (this will support 32k anyway)
const int kInst_DescriptorHeap_DescriptorTypeShift = 28;
const int kInst_DescriptorHeap_DescriptorTypeMask = 0xF << kInst_DescriptorHeap_DescriptorTypeShift;
const int kInst_DescriptorHeap_DescriptorSizeShift = 20;
const int kInst_DescriptorHeap_DescriptorSizeMask = 0xFF << kInst_DescriptorHeap_DescriptorSizeShift;
const int kInst_DescriptorHeap_MappingIndexShift = 4;
const int kInst_DescriptorHeap_MappingIndexMask = 0xFFFF << kInst_DescriptorHeap_MappingIndexShift;
const int kInst_DescriptorHeap_MappingIndexUntyped = 0xFFFF;  // used to notify we are using untyped pointers here (no mapping)
const int kInst_DescriptorHeap_AlignmentShiftMask = 0xF;

// 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
