// Copyright (C) 2009-present, Panagiotis Christopoulos Charitos and contributors. // All rights reserved. // Code licensed under the BSD License. // http://www.anki3d.org/LICENSE #pragma once #include #include #include #include #include #include #include namespace anki { // Forward class GrObject; class GrManager; class GrManagerImpl; class TextureInitInfo; class SamplerInitInfo; class GrManagerInitInfo; class FramebufferInitInfo; class BufferInitInfo; class ShaderInitInfo; class ShaderProgramInitInfo; class CommandBufferInitInfo; class AccelerationStructureInitInfo; class GrUpscalerInitInfo; class PipelineQueryInitInfo; /// @addtogroup graphics /// @{ ANKI_CVAR(BoolCVar, Gr, Validation, false, "Enable or not validation") ANKI_CVAR(BoolCVar, Gr, GpuValidation, false, "Enable or not GPU validation") ANKI_CVAR(BoolCVar, Gr, Vsync, false, "Enable or not vsync") ANKI_CVAR(BoolCVar, Gr, DebugMarkers, false, "Enable or not debug markers"); ANKI_CVAR(BoolCVar, Gr, MeshShaders, false, "Enable or not mesh shaders"); ANKI_CVAR(NumericCVar, Gr, Device, 0, 0, 16, "Choose an available device. Devices are sorted by performance") ANKI_CVAR(BoolCVar, Gr, RayTracing, false, "Try enabling ray tracing") ANKI_CVAR(BoolCVar, Gr, Vrs, false, "Enable or not VRS") ANKI_CVAR(BoolCVar, Gr, WorkGraphcs, false, "Enable or not WorkGraphs") ANKI_CVAR(NumericCVar, Gr, MaxBindlessSampledTextureCount, 512, 16, kMaxU16) ANKI_CVAR(NumericCVar, Gr, GpuTimeout, 120.0, 0.0, 24.0 * 60.0, "Max time to wait for GPU fences or semaphores. More than that it must be a GPU timeout") ANKI_CVAR(NumericCVar, Gr, AsyncCompute, 0, 0, 2, "Control the async compute behaviour: 0: Try use separate queue family, 1: Use lower priority queue in the general's queue family, 2: Use " "the general queue") #if ANKI_GR_BACKEND_DIRECT3D ANKI_CVAR(NumericCVar, Gr, MaxRtvDescriptors, 1024, 8, kMaxU16, "Max number of RTVs") ANKI_CVAR(NumericCVar, Gr, MaxDsvDescriptors, 512, 8, kMaxU16, "Max number of DSVs"); ANKI_CVAR(NumericCVar, Gr, MaxCpuCbvSrvUavDescriptors, 16 * 1024, 8, kMaxU16, "Max number of CBV/SRV/UAV descriptors") ANKI_CVAR(NumericCVar, Gr, MaxCpuSamplerDescriptors, 512, 8, kMaxU16, "Max number of sampler descriptors") ANKI_CVAR(NumericCVar, Gr, MaxGpuCbvSrvUavDescriptors, 16 * 1024, 8, kMaxU16, "Max number of CBV/SRV/UAV descriptors") ANKI_CVAR(NumericCVar, Gr, MaxGpuSamplerDescriptors, 2 * 1024, 8, kMaxU16, "Max number of sampler descriptors") ANKI_CVAR(BoolCVar, Gr, Dred, false, "Enable DRED") #else ANKI_CVAR(NumericCVar, Gr, DiskShaderCacheMaxSize, 128_MB, 1_MB, 1_GB, "Max size of the pipeline cache file") ANKI_CVAR(BoolCVar, Gr, DebugPrintf, false, "Enable or not debug printf") ANKI_CVAR(BoolCVar, Gr, SamplerFilterMinMax, true, "Enable or not min/max sample filtering") ANKI_CVAR(StringCVar, Gr, VkLayers, "", "VK layers to enable. Seperated by :") #endif #define ANKI_GR_LOGI(...) ANKI_LOG("GR", kNormal, __VA_ARGS__) #define ANKI_GR_LOGE(...) ANKI_LOG("GR", kError, __VA_ARGS__) #define ANKI_GR_LOGW(...) ANKI_LOG("GR", kWarning, __VA_ARGS__) #define ANKI_GR_LOGF(...) ANKI_LOG("GR", kFatal, __VA_ARGS__) #define ANKI_GR_LOGV(...) ANKI_LOG("GR", kVerbose, __VA_ARGS__) class GrMemoryPool : public HeapMemoryPool, public MakeSingleton { template friend class MakeSingleton; private: GrMemoryPool(AllocAlignedCallback allocCb, void* allocCbUserData) : HeapMemoryPool(allocCb, allocCbUserData, "GrMemPool") { } ~GrMemoryPool() = default; }; ANKI_DEFINE_SUBMODULE_UTIL_CONTAINERS(Gr, GrMemoryPool) // Some constants constexpr U32 kMaxColorRenderTargets = 4; constexpr U32 kMaxRegisterSpaces = 3; ///< Groups that can be bound at the same time. constexpr U32 kMaxBindingsPerRegisterSpace = 32; constexpr U32 kMaxFramesInFlight = 3; ///< Triple buffering. constexpr U32 kMaxGrObjectNameLength = 61; constexpr U32 kMaxFastConstantsSize = 256; ///< Push/root constants size. /// The number of commands in a command buffer that make it a small batch command buffer. constexpr U32 kCommandBufferSmallBatchMaxCommands = 100; // Smart pointers class GrObjectDeleter { public: void operator()(GrObject* ptr); }; class GrObjectDeleterInternal { public: void operator()(GrObject* ptr); }; using GrObjectPtr = IntrusivePtr; using GrObjectInternalPtr = IntrusivePtr; #define ANKI_INSTANTIATE_GR_OBJECT(x_) \ class x_##Impl; \ class x_; \ using x_##Ptr = IntrusivePtr; \ using x_##InternalPtr = IntrusivePtr; #include #define ANKI_GR_OBJECT \ friend class GrManager; \ template \ friend class IntrusivePtr; \ template \ friend void callConstructor(T& p, TArgs&&... args); /// Shader block information. class ShaderVariableBlockInfo { public: I16 m_offset = -1; ///< Offset inside the block I16 m_arraySize = -1; ///< Number of elements. /// Stride between the each array element if the variable is array. I16 m_arrayStride = -1; /// Identifying the stride between columns of a column-major matrix or rows of a row-major matrix. I16 m_matrixStride = -1; }; /// Knowing the vendor allows some optimizations enum class GpuVendor : U8 { kUnknown, kArm, kNvidia, kAMD, kIntel, kQualcomm, kCount }; inline constexpr Array kGPUVendorStrings = {"unknown", "ARM", "nVidia", "AMD", "Intel", "Qualcomm"}; /// Device capabilities. ANKI_BEGIN_PACKED_STRUCT class GpuDeviceCapabilities { public: /// The alignment of offsets when bounding constant buffers. U32 m_constantBufferBindOffsetAlignment = kMaxU32; /// The alignment of offsets when bounding structured buffers. U32 m_structuredBufferBindOffsetAlignment = kMaxU32; /// The alignment of offsets when bounding texture buffers. U32 m_texelBufferBindOffsetAlignment = kMaxU32; /// Max push/root constant size. PtrSize m_fastConstantsSize = 128; /// The max combined size of shared variables (with paddings) in compute shaders. PtrSize m_computeSharedMemorySize = 16_KB; /// Each SBT record should be a multiple of this. U32 m_sbtRecordAlignment = kMaxU32; /// The size of a shader group handle that will be placed inside an SBT record. U32 m_shaderGroupHandleSize = 0; /// Min subgroup size of the GPU. U32 m_minWaveSize = 0; /// Max subgroup size of the GPU. U32 m_maxWaveSize = 0; /// Min size of a texel in the shading rate image. U32 m_minShadingRateImageTexelSize = 0; /// The max number of drawcalls in draw indirect count calls. U32 m_maxDrawIndirectCount = 0; /// GPU vendor. GpuVendor m_gpuVendor = GpuVendor::kUnknown; /// Descrete or integrated GPU. Bool m_discreteGpu = false; /// API version. U8 m_minorApiVersion = 0; /// API version. U8 m_majorApiVersion = 0; /// Align structured buffers using the structure's size and not the m_storageBufferBindOffsetAlignment. Bool m_structuredBufferNaturalAlignment = false; /// RT. Bool m_rayTracingEnabled = false; /// VRS. Bool m_vrs = false; /// Supports or not 24bit, 48bit or 96bit texture formats. Bool m_unalignedBbpTextureFormats = false; /// DLSS. Bool m_dlss = false; /// Mesh shaders. Bool m_meshShaders = false; /// Can create PipelineQuery objects. Bool m_pipelineQuery = false; /// Has access to barycentrics. Bool m_barycentrics = false; /// WorkGraphs Bool m_workGraphs = false; }; ANKI_END_PACKED_STRUCT /// The type of the allocator for heap allocations template using GrAllocator = HeapAllocator; /// The base of all init infos for GR. class GrBaseInitInfo { public: // The name of the object GrBaseInitInfo(CString name) { setName(name); } GrBaseInitInfo() { } GrBaseInitInfo(const GrBaseInitInfo& b) { m_name = b.m_name; } GrBaseInitInfo& operator=(const GrBaseInitInfo& b) { m_name = b.m_name; return *this; } CString getName() const { return m_name; } void setName(CString name) { m_name = (name.getLength()) ? name : "N/A"; } private: GrString m_name; }; enum class ColorBit : U8 { kNone = 0, kRed = 1 << 0, kGreen = 1 << 1, kBlue = 1 << 2, kAlpha = 1 << 3, kAll = kRed | kGreen | kBlue | kAlpha }; ANKI_ENUM_ALLOW_NUMERIC_OPERATIONS(ColorBit) enum class PrimitiveTopology : U8 { kPoints, kLines, kLineStip, kTriangles, kTriangleStrip, kPatches, kCount, kFirst = 0 }; enum class FillMode : U8 { kPoints, kWireframe, kSolid, kCount, kFirst = 0 }; enum class FaceSelectionBit : U8 { kNone = 0, kFront = 1 << 0, kBack = 1 << 1, kFrontAndBack = kFront | kBack }; ANKI_ENUM_ALLOW_NUMERIC_OPERATIONS(FaceSelectionBit) enum class CompareOperation : U8 { kAlways, kLess, kEqual, kLessEqual, kGreater, kGreaterEqual, kNotEqual, kNever, kCount }; enum class StencilOperation : U8 { kKeep, kZero, kReplace, kIncrementAndClamp, kDecrementAndClamp, kInvert, kIncrementAndWrap, kDecrementAndWrap, kCount }; enum class BlendFactor : U8 { kZero, kOne, kSrcColor, kOneMinusSrcColor, kDstColor, kOneMinusDstColor, kSrcAlpha, kOneMinusSrcAlpha, kDstAlpha, kOneMinusDstAlpha, kConstantColor, kOneMinusConstantColor, kConstantAlpha, kOneMinusConstantAlpha, kSrcAlphaSaturate, kSrc1Color, kOneMinusSrc1Color, kSrc1Alpha, kOneMinusSrc1Alpha, kCount }; enum class BlendOperation : U8 { kAdd, kSubtract, kReverseSubtract, kMin, kMax, kCount }; enum class VertexStepRate : U8 { kVertex, kInstance, kCount }; /// A way to distinguish the aspect of a depth stencil texture. enum class DepthStencilAspectBit : U8 { kNone = 0, kDepth = 1 << 0, kStencil = 1 << 1, kDepthStencil = kDepth | kStencil }; ANKI_ENUM_ALLOW_NUMERIC_OPERATIONS(DepthStencilAspectBit) /// Pixel or vertex format. /// WARNING: Keep it the same as vulkan (one conversion less). enum class Format : U32 { kNone = 0, #define ANKI_FORMAT_DEF(type, vk, d3d, componentCount, texelSize, blockWidth, blockHeight, blockSize, shaderType, depthStencil) k##type = vk, #include #undef ANKI_FORMAT_DEF }; ANKI_ENUM_ALLOW_NUMERIC_OPERATIONS(Format) /// Contains info for a specific Format. class FormatInfo { public: U8 m_componentCount; ///< The number of components. U8 m_texelSize; ///< The size of the texel. Only for incompressed, zero for compressed. U8 m_blockWidth; ///< The width of the block size of compressed formats. Zero otherwise. U8 m_blockHeight; ///< The height of the block size of compressed formats. Zero otherwise. U8 m_blockSize; ///< The size of the block of a compressed format. Zero otherwise. U8 m_shaderType; ///< It's 0 if the shader sees it as float, 1 if uint and 2 if signed int. DepthStencilAspectBit m_depthStencil; ///< Depth/stencil mask. const char* m_name; Bool isDepthStencil() const { return m_depthStencil != DepthStencilAspectBit::kNone; } Bool isDepth() const { return !!(m_depthStencil & DepthStencilAspectBit::kDepth); } Bool isStencil() const { return !!(m_depthStencil & DepthStencilAspectBit::kStencil); } Bool isCompressed() const { return m_blockSize > 0; } }; /// Get info for a specific Format. ANKI_PURE FormatInfo getFormatInfo(Format fmt); /// Compute the size in bytes of a texture surface surface. ANKI_PURE PtrSize computeSurfaceSize(U32 width, U32 height, Format fmt); /// Compute the size in bytes of the texture volume. ANKI_PURE PtrSize computeVolumeSize(U32 width, U32 height, U32 depth, Format fmt); /// Texture type. enum class TextureType : U8 { k1D, k2D, k3D, k2DArray, kCube, kCubeArray, kCount }; inline Bool textureTypeIsCube(const TextureType t) { return t == TextureType::kCube || t == TextureType::kCubeArray; } /// Texture usage hints. They are very important. enum class TextureUsageBit : U32 { kNone = 0, kSrvGeometry = 1 << 0, kSrvPixel = 1 << 1, kSrvCompute = 1 << 2, kSrvDispatchRays = 1 << 3, kUavGeometry = 1 << 4, kUavPixel = 1 << 5, kUavCompute = 1 << 6, kUavDispatchRays = 1 << 7, kRtvDsvRead = 1 << 8, kRtvDsvWrite = 1 << 9, kShadingRate = 1 << 10, kCopyDestination = 1 << 11, kPresent = 1 << 12, // Derived kAllSrv = kSrvGeometry | kSrvPixel | kSrvCompute | kSrvDispatchRays, kAllUav = kUavGeometry | kUavPixel | kUavCompute | kUavDispatchRays, kAllRtvDsv = kRtvDsvRead | kRtvDsvWrite, kAllGeometry = kSrvGeometry | kUavGeometry, kAllPixel = kSrvPixel | kUavPixel, kAllGraphics = kAllGeometry | kAllPixel | kRtvDsvRead | kRtvDsvWrite | kShadingRate, kAllCompute = kSrvCompute | kUavCompute, kAllCopy = kCopyDestination, kAllRead = kAllSrv | kAllUav | kRtvDsvRead | kShadingRate | kPresent, kAllWrite = kAllUav | kRtvDsvWrite | kCopyDestination, kAll = kAllRead | kAllWrite, kAllShaderResource = kAllSrv | kAllUav, }; ANKI_ENUM_ALLOW_NUMERIC_OPERATIONS(TextureUsageBit) enum class SamplingFilter : U8 { kNearest, kLinear, kMin, ///< It calculates the min of a 2x2 quad. Only if GpuDeviceCapabilities::m_samplingFilterMinMax is supported. kMax, ///< It calculates the max of a 2x2 quad. Only if GpuDeviceCapabilities::m_samplingFilterMinMax is supported. }; enum class SamplingAddressing : U8 { kClamp, kRepeat, kBlack, kWhite, kCount, kFirst = 0, kLast = kCount - 1, }; enum class ShaderType : U16 { kVertex, kHull, kDomain, kGeometry, kAmplification, kMesh, kPixel, kCompute, kRayGen, kAnyHit, kClosestHit, kMiss, kIntersection, kCallable, kWorkGraph, kCount, kFirst = 0, kLast = kCount - 1, kFirstGraphics = kVertex, kLastGraphics = kPixel, kFirstRayTracing = kRayGen, kLastRayTracing = kCallable, }; ANKI_ENUM_ALLOW_NUMERIC_OPERATIONS(ShaderType) inline Array g_shaderTypeNames = {{"Vertex", "Hull", "Domain", "Geometry", "Amplification", "Mesh", "Pixel", "Compute", "RayGen", "AnyHit", "ClosestHit", "Miss", "Intersection", "Callable", "WorkGraph"}}; enum class ShaderTypeBit : U16 { kVertex = 1 << 0, kHull = 1 << 1, kDomain = 1 << 2, kGeometry = 1 << 3, kAmplification = 1 << 4, kMesh = 1 << 5, kPixel = 1 << 6, kCompute = 1 << 7, kRayGen = 1 << 8, kAnyHit = 1 << 9, kClosestHit = 1 << 10, kMiss = 1 << 11, kIntersection = 1 << 12, kCallable = 1 << 13, kWorkGraph = 1 << 14, kNone = 0, kAllGraphics = kVertex | kHull | kDomain | kGeometry | kAmplification | kMesh | kPixel, kAllLegacyGeometry = kVertex | kHull | kDomain | kGeometry, kAllModernGeometry = kAmplification | kMesh, kAllRayTracing = kRayGen | kAnyHit | kClosestHit | kMiss | kIntersection | kCallable, kAllHit = kAnyHit | kClosestHit, kAll = kAllGraphics | kCompute | kAllRayTracing | kWorkGraph, }; ANKI_ENUM_ALLOW_NUMERIC_OPERATIONS(ShaderTypeBit) enum class ShaderVariableDataType : U8 { kNone, #define ANKI_SVDT_MACRO(type, baseType, rowCount, columnCount, isIntagralType) k##type, #define ANKI_SVDT_MACRO_OPAQUE(constant, type) k##constant, #include // Derived kCount, kNumericsFirst = kI32, kNumericsLast = kMat4, kNumeric1ComponentFirst = kI32, kNumeric1ComponentLast = kF32, kNumeric2ComponentFirst = kIVec2, kNumeric2ComponentLast = kVec2, kNumeric3ComponentFirst = kIVec3, kNumeric3ComponentLast = kVec3, kNumeric4ComponentFirst = kIVec4, kNumeric4ComponentLast = kVec4, kMatrixFirst = kMat3, kMatrixLast = kMat4, kTextureFirst = kTexture1D, kTextureLast = kTextureCubeArray, kImageFirst = kImage1D, kImageLast = kImageCubeArray, }; ANKI_ENUM_ALLOW_NUMERIC_OPERATIONS(ShaderVariableDataType) class ShaderVariableDataTypeInfo { public: const Char* m_name; U32 m_size; ///< Size of the type. Bool m_opaque; Bool m_isIntegral; ///< If true is integral type. Else it's float. }; ANKI_PURE const ShaderVariableDataTypeInfo& getShaderVariableDataTypeInfo(ShaderVariableDataType type); /// Occlusion query result bit. enum class OcclusionQueryResultBit : U8 { kNotAvailable = 1 << 0, kVisible = 1 << 1, kNotVisible = 1 << 2 }; ANKI_ENUM_ALLOW_NUMERIC_OPERATIONS(OcclusionQueryResultBit) /// Occlusion query result. enum class OcclusionQueryResult : U8 { kNotAvailable, kVisible, kNotVisible }; /// Timestamp query result. enum class TimestampQueryResult : U8 { kNotAvailable, kAvailable }; /// Pipeline query result. enum class PipelineQueryResult : U8 { kNotAvailable, kAvailable }; enum class PipelineQueryType : U8 { kPrimitivesPassedClipping, kCount }; /// Attachment load operation. enum class RenderTargetLoadOperation : U8 { kLoad, kClear, kDontCare }; /// Attachment store operation. enum class RenderTargetStoreOperation : U8 { kStore, kDontCare }; /// Buffer usage modes. /// The graphics work consists of the following pipes: indirect, geometry (all programmable and fixed function geometry stages) and finaly pixel. /// The compute from the consists of the following: indirect and compute. /// The trace rays from the: indirect and trace_rays /// !!WARNING!! If you change this remember to change PrivateBufferUsageBit. enum class BufferUsageBit : U64 { kNone = 0, kConstantGeometry = 1ull << 0ull, kConstantPixel = 1ull << 1ull, kConstantCompute = 1ull << 2ull, kConstantDispatchRays = 1ull << 3ull, kSrvGeometry = 1ull << 4ull, kSrvPixel = 1ull << 5ull, kSrvCompute = 1ull << 6ull, kSrvDispatchRays = 1ull << 7ull, kUavGeometry = 1ull << 8ull, kUavPixel = 1ull << 9ull, kUavCompute = 1ull << 10ull, kUavDispatchRays = 1ull << 11ull, kVertexOrIndex = 1ull << 12ull, kIndirectCompute = 1ull << 14ll, kIndirectDraw = 1ull << 15ull, kIndirectDispatchRays = 1ull << 16ull, kCopySource = 1ull << 17ull, kCopyDestination = 1ull << 18ull, kAccelerationStructureBuild = 1ull << 19ull, ///< Will be used as a position or index buffer in a BLAS build or instances buffer in a TLAS build. kShaderBindingTable = 1ull << 20ull, ///< Will be used as SBT in a dispatchRays() command. kAccelerationStructureBuildScratch = 1ull << 21ull, ///< Used in buildAccelerationStructureXXX commands. kAccelerationStructure = 1ull << 22ull, ///< Will be used as AS. // Derived kAllConstant = kConstantGeometry | kConstantPixel | kConstantCompute | kConstantDispatchRays, kAllSrv = kSrvGeometry | kSrvPixel | kSrvCompute | kSrvDispatchRays, kAllUav = kUavGeometry | kUavPixel | kUavCompute | kUavDispatchRays, kAllIndirect = kIndirectCompute | kIndirectDraw | kIndirectDispatchRays, kAllCopy = kCopySource | kCopyDestination, kAllGeometry = kConstantGeometry | kSrvGeometry | kUavGeometry | kVertexOrIndex, kAllPixel = kConstantPixel | kSrvPixel | kUavPixel, kAllGraphics = kAllGeometry | kAllPixel | kIndirectDraw, kAllCompute = kConstantCompute | kSrvCompute | kUavCompute | kIndirectCompute, kAllDispatchRays = kConstantDispatchRays | kSrvDispatchRays | kUavDispatchRays | kIndirectDispatchRays | kShaderBindingTable, kAllRayTracing = kAllDispatchRays | kAccelerationStructureBuild | kAccelerationStructureBuildScratch | kAccelerationStructure, kAllRead = kAllConstant | kAllSrv | kAllUav | kVertexOrIndex | kAllIndirect | kCopySource | kAccelerationStructureBuild | kShaderBindingTable | kAccelerationStructure, kAllWrite = kAllUav | kCopyDestination | kAccelerationStructureBuildScratch | kAccelerationStructure, kAllShaderResource = kAllConstant | kAllSrv | kAllUav, kAll = kAllRead | kAllWrite, }; ANKI_ENUM_ALLOW_NUMERIC_OPERATIONS(BufferUsageBit) /// Buffer access when mapped. enum class BufferMapAccessBit : U8 { kNone = 0, kRead = 1 << 0, kWrite = 1 << 1, kReadWrite = kRead | kWrite }; ANKI_ENUM_ALLOW_NUMERIC_OPERATIONS(BufferMapAccessBit) /// Index buffer's index type. enum class IndexType : U8 { kU16, kU32, kCount }; inline U32 getIndexSize(IndexType type) { ANKI_ASSERT(type < IndexType::kCount); return 2u << U32(type); } /// Acceleration structure type. enum class AccelerationStructureType : U8 { kTopLevel, kBottomLevel, kCount }; enum class AccelerationStructureUsageBit : U8 { kNone = 0, kBuild = 1 << 0, kAttach = 1 << 1, ///< Attached to a TLAS. Only for BLAS. kSrvGeometry = 1 << 2, kSrvPixel = 1 << 3, kSrvCompute = 1 << 4, kSrvDispatchRays = 1 << 5, // Derived kAllGraphics = kSrvGeometry | kSrvPixel, kAllSrv = kSrvGeometry | kSrvPixel | kSrvCompute | kSrvDispatchRays, kAllRead = kAttach | kSrvGeometry | kSrvPixel | kSrvCompute | kSrvDispatchRays, kAllWrite = kBuild }; ANKI_ENUM_ALLOW_NUMERIC_OPERATIONS(AccelerationStructureUsageBit) /// VRS rates. enum class VrsRate : U8 { k1x1, ///< Disable VRS. Always supported. k2x1, ///< Always supported. k1x2, k2x2, ///< Always supported. k4x2, k2x4, k4x4, kCount, kFirst = 0 }; ANKI_ENUM_ALLOW_NUMERIC_OPERATIONS(VrsRate) enum class GpuQueueType : U8 { kGeneral, kCompute, kCount, kFirst = 0 }; ANKI_ENUM_ALLOW_NUMERIC_OPERATIONS(GpuQueueType) enum class VertexAttributeSemantic : U8 { kPosition, kNormal, kTexCoord, kColor, kMisc0, kMisc1, kMisc2, kMisc3, kCount, kFirst = 0 }; ANKI_ENUM_ALLOW_NUMERIC_OPERATIONS(VertexAttributeSemantic) inline Array g_vertexAttributeSemanticNames = { {"Position", "Normal", "TexCoord", "Color", "Misc0", "Misc1", "Misc2", "Misc3"}}; enum class VertexAttributeSemanticBit : U8 { kNone, kPosition = 1u << 0u, kNormal = 1u << 1u, kTexCoord = 1u << 2u, kColor = 1u << 3u, kMisc0 = 1u << 4u, kMisc1 = 1u << 5u, kMisc2 = 1u << 6u, kMisc3 = 1u << 7u }; ANKI_ENUM_ALLOW_NUMERIC_OPERATIONS(VertexAttributeSemanticBit) /// This matches D3D. enum class DescriptorType : U8 { kConstantBuffer, kSrvStructuredBuffer, kUavStructuredBuffer, kSrvByteAddressBuffer, kUavByteAddressBuffer, kSrvTexelBuffer, kUavTexelBuffer, kSrvTexture, kUavTexture, kAccelerationStructure, kSampler, kCount, kFirst = 0 }; ANKI_ENUM_ALLOW_NUMERIC_OPERATIONS(DescriptorType) enum class HlslResourceType : U8 { kCbv, kSrv, kUav, kSampler, // !!!!WARNING!!! Keep it last kCount, kFirst = 0 }; ANKI_ENUM_ALLOW_NUMERIC_OPERATIONS(HlslResourceType) inline HlslResourceType descriptorTypeToHlslResourceType(DescriptorType type) { HlslResourceType out = HlslResourceType::kCount; switch(type) { case DescriptorType::kConstantBuffer: out = HlslResourceType::kCbv; break; case DescriptorType::kSrvStructuredBuffer: case DescriptorType::kSrvByteAddressBuffer: case DescriptorType::kSrvTexelBuffer: case DescriptorType::kSrvTexture: case DescriptorType::kAccelerationStructure: out = HlslResourceType::kSrv; break; case DescriptorType::kUavStructuredBuffer: case DescriptorType::kUavByteAddressBuffer: case DescriptorType::kUavTexelBuffer: case DescriptorType::kUavTexture: out = HlslResourceType::kUav; break; case DescriptorType::kSampler: out = HlslResourceType::kSampler; break; default: ANKI_ASSERT(0); } return out; } ANKI_BEGIN_PACKED_STRUCT class ShaderReflectionBinding { public: U32 m_registerBindingPoint = kMaxU32; U16 m_arraySize = 0; union { U16 m_vkBinding = kMaxU16; ///< Filled by the VK backend. U16 m_d3dStructuredBufferStride; }; DescriptorType m_type = DescriptorType::kCount; /// Order the bindings. THIS IS IMPORTANT because the backends expect them in a specific order. Bool operator<(const ShaderReflectionBinding& b) const { const HlslResourceType ahlsl = descriptorTypeToHlslResourceType(m_type); const HlslResourceType bhlsl = descriptorTypeToHlslResourceType(b.m_type); if(ahlsl != bhlsl) { return ahlsl < bhlsl; } if(m_registerBindingPoint != b.m_registerBindingPoint) { return m_registerBindingPoint < b.m_registerBindingPoint; } ANKI_ASSERT(!"Can't have 2 bindings with the same HlslResourceType and same binding point"); return false; } Bool operator==(const ShaderReflectionBinding& b) const { return memcmp(this, &b, sizeof(*this)) == 0; } void validate() const { ANKI_ASSERT(m_registerBindingPoint < kMaxU32); ANKI_ASSERT(m_type < DescriptorType::kCount); ANKI_ASSERT(m_arraySize > 0); ANKI_ASSERT(!(ANKI_GR_BACKEND_DIRECT3D && (m_type == DescriptorType::kSrvStructuredBuffer || m_type == DescriptorType::kUavStructuredBuffer) && m_d3dStructuredBufferStride == 0)); } }; ANKI_END_PACKED_STRUCT ANKI_BEGIN_PACKED_STRUCT class ShaderReflectionDescriptorRelated { public: /// The D3D backend expects bindings inside a space need to be ordered by HLSL type and then by register. Array2d m_bindings; Array m_bindingCounts = {}; U32 m_fastConstantsSize = 0; U32 m_d3dShaderBindingTableRecordConstantsSize = 0; Bool m_hasVkBindlessDescriptorSet = false; ///< Filled by the shader compiler. U8 m_vkBindlessDescriptorSet = kMaxU8; ///< Filled by the VK backend. Bool m_d3dHasDrawId = false; void validate() const { for(U32 set = 0; set < kMaxRegisterSpaces; ++set) { for(U32 ibinding = 0; ibinding < kMaxBindingsPerRegisterSpace; ++ibinding) { const ShaderReflectionBinding& binding = m_bindings[set][ibinding]; if(binding.m_type != DescriptorType::kCount) { ANKI_ASSERT(ibinding < m_bindingCounts[set]); binding.validate(); } else { ANKI_ASSERT(ibinding >= m_bindingCounts[set]); } } } } StringList toString() const; }; ANKI_END_PACKED_STRUCT class ShaderReflection { public: ShaderReflectionDescriptorRelated m_descriptor; class { public: Array m_vkVertexAttributeLocations; VertexAttributeSemanticBit m_vertexAttributeMask = VertexAttributeSemanticBit::kNone; } m_vertex; class { public: BitSet m_colorRenderTargetWritemask = {false}; Bool m_discards = false; } m_pixel; ShaderReflection() { m_vertex.m_vkVertexAttributeLocations.fill(kMaxU8); } void validate() const { m_descriptor.validate(); for([[maybe_unused]] VertexAttributeSemantic semantic : EnumIterable()) { ANKI_ASSERT(!(m_vertex.m_vertexAttributeMask & VertexAttributeSemanticBit(1 << semantic)) || m_vertex.m_vkVertexAttributeLocations[semantic] != kMaxU8); } const U32 attachmentCount = m_pixel.m_colorRenderTargetWritemask.getSetBitCount(); for(U32 i = 0; i < attachmentCount; ++i) { ANKI_ASSERT(m_pixel.m_colorRenderTargetWritemask.get(i) && "Should write to all attachments"); } } /// Combine shader reflection. static Error linkShaderReflection(const ShaderReflection& a, const ShaderReflection& b, ShaderReflection& c); StringList toString() const; }; /// Clear values for textures or attachments. class ClearValue { private: class Ds { public: F32 m_depth; I32 m_stencil; }; public: union { Array m_colorf; Array m_colori; Array m_coloru; Ds m_depthStencil; }; ClearValue() { zeroMemory(*this); } ClearValue(const ClearValue& b) { operator=(b); } ClearValue& operator=(const ClearValue& b) { memcpy(this, &b, sizeof(*this)); return *this; } }; class TextureRect { public: U32 m_offsetX = 0; U32 m_offsetY = 0; U32 m_offsetZ = 0; U32 m_width = kMaxU32; U32 m_height = kMaxU32; U32 m_depth = kMaxU32; }; /// Compute max number of mipmaps for a 2D texture. U8 computeMaxMipmapCount2d(U32 w, U32 h, U32 minSizeOfLastMip = 1); /// Compute max number of mipmaps for a 3D texture. U8 computeMaxMipmapCount3d(U32 w, U32 h, U32 d, U32 minSizeOfLastMip = 1); /// Visit a SPIR-V binary. template static void visitSpirv(TArray spv, TFunc func) { ANKI_ASSERT(spv.getSize() > 5); auto it = &spv[5]; do { const U32 instructionCount = *it >> 16u; const U32 opcode = *it & 0xFFFFu; TArray instructions(it + 1, instructionCount - 1); func(opcode, instructions); it += instructionCount; } while(it < spv.getEnd()); ANKI_ASSERT(it == spv.getEnd()); } /// @} } // end namespace anki