/* * Copyright (c) Contributors to the Open 3D Engine Project. * For complete copyright and license terms please see the LICENSE at the root of this distribution. * * SPDX-License-Identifier: Apache-2.0 OR MIT * */ #pragma once #include #include #include #include #include namespace AZ { namespace Metal { class ShaderPlatformInterface : public RHI::ShaderPlatformInterface { public: explicit ShaderPlatformInterface(uint32_t apiUniqueIndex); RHI::APIType GetAPIType() const override; AZ::Name GetAPIName() const override; RHI::Ptr CreateShaderStageFunction( const StageDescriptor& stageDescriptor) override; bool IsShaderStageForRaster(RHI::ShaderHardwareStage shaderStageType) const override; bool IsShaderStageForCompute(RHI::ShaderHardwareStage shaderStageType) const override; bool IsShaderStageForRayTracing(RHI::ShaderHardwareStage shaderStageType) const override; RHI::Ptr CreatePipelineLayoutDescriptor() override; bool BuildPipelineLayoutDescriptor( RHI::Ptr pipelineLayoutDescriptor, const ShaderResourceGroupInfoList& srgInfoList, const RootConstantsInfo& rootConstantsInfo, const RHI::ShaderCompilerArguments& shaderCompilerArguments) override; bool VariantCompilationRequiresSrgLayoutData() const override { return true; } bool CompilePlatformInternal( const AssetBuilderSDK::PlatformInfo& platform, const AZStd::string& shaderSourcePath, const AZStd::string& functionName, RHI::ShaderHardwareStage shaderStage, const AZStd::string& tempFolderPath, StageDescriptor& outputDescriptor, const RHI::ShaderCompilerArguments& shaderCompilerArguments) const override; AZStd::string GetAzslCompilerParameters(const RHI::ShaderCompilerArguments& shaderCompilerArguments) const; AZStd::string GetAzslCompilerWarningParameters(const RHI::ShaderCompilerArguments& shaderCompilerArguments) const; bool BuildHasDebugInfo(const RHI::ShaderCompilerArguments& shaderCompilerArguments) const override; const char* GetAzslHeader(const AssetBuilderSDK::PlatformInfo& platform) const override; private: ShaderPlatformInterface() = delete; bool CompileHLSLShader( const AZStd::string& shaderSourceFile, const AZStd::string& tempFolder, const AZStd::string& entryPoint, const RHI::ShaderHardwareStage shaderAssetType, const RHI::ShaderCompilerArguments& shaderCompilerArguments, AZStd::vector& compiledShader, AZStd::vector& compiledByteCode, const AssetBuilderSDK::PlatformInfo& platform, ByProducts& byproducts) const; bool UpdateCompiledShader(AZ::IO::FileIOStream& fileStream, const char* platformName, const char* fileName, AZStd::vector& compiledShader) const; bool CreateMetalLib(const char* platformName, const AZStd::string& shaderSourceFile, const AZStd::string& tempFolder, AZStd::vector& compiledByteCode, AZStd::vector& sourceMetalShader, const AssetBuilderSDK::PlatformInfo& platform) const; using ArgBufferEntries = AZStd::pair; struct compareByRegisterId { bool operator()(const ArgBufferEntries& lhs, const ArgBufferEntries& rhs) { return lhs.second < rhs.second; } }; /** * These set of functions will parse the final metal source code and add unused variables * within the declaration of argument buffers so that they match ShaderResourceGroupLayout * This is necessary because metal drivers expect the argbuffer layout bound at runtime to match * the one declared in the shaders or we get undefined behaviour (hard crashes/weird lags/etc). * * For example - Given SRG like so * ShaderResourceGroup ObjectSrg : SRG_PerObject * { * Texture2D FontImage; * * Sampler LinearSampler * { * MinFilter = Linear; * MagFilter = Linear; * MipFilter = Linear; * AddressU = Clamp; * AddressV = Clamp; * AddressW = Clamp; * }; * * column_major float4x4 m_projectionMatrix; * } * * the imgui vertex shader that only uses m_projectionMatrix will modify the following metal shader * * struct spvDescriptorSetBuffer0 * { * constant type_ConstantBuffer_PerObject_SRGConstantsStruct* PerObject_SRGConstantBuffer [[id(2)]]; * }; * * to * * struct spvDescriptorSetBuffer0 * { * texture2d dummyImage0 [[id(0)]]; * sampler dummySampler1 [[id(1)]]; * constant type_ConstantBuffer_PerObject_SRGConstantsStruct* PerObject_SRGConstantBuffer [[id(2)]]; * }; */ bool AddUnusedResources(AZStd::vector& compiledShader) const; //Insert all the static sampler resource string entries into a set. bool AddSamplerEntries(const RHI::ShaderResourceGroupLayout& groupLayout, AZStd::string& argBufferStr) const; //! Insert all the image resource string entries into the set. Use ProcessSamplerEntry as a helper method. bool AddImageEntries(const RHI::ShaderResourceGroupLayout& groupLayout, AZStd::string& argBufferStr) const; bool ProcessSamplerEntry(uint32_t regId, AZStd::string& argBufferStr, uint32_t samplercount) const; //! Insert all the constant buffer resource string entries into the set. bool AddConstantBufferEntries(const RHI::ShaderResourceGroupLayout& groupLayout, AZStd::string& constantBufferTempStructs, AZStd::string& argBufferStr, uint32_t groupLayoutIndex) const; //! Insert all the buffer resource string entries into the set. bool AddBufferEntries(const RHI::ShaderResourceGroupLayout& groupLayout, AZStd::string& structuredBufferTempStructs, AZStd::string& argBufferStr, uint32_t groupLayoutIndex) const; //! Helper function to add already existing resource entry into the set bool AddExistingResourceEntry(const char* resourceStr, size_t resourceStartPos, uint32_t regId, AZStd::string& argBufferStr) const; //! This is to cache the srg layout which is needed to add the unused variables mutable AZStd::fixed_vector m_srgLayouts; //! The set helps remove duplicates and keeps the entries sorted based on register id mutable AZStd::set m_argBufferEntries; const Name m_apiName{APINameString}; }; } }