/*
 *  Copyright 2019-2021 Diligent Graphics LLC
 *  Copyright 2015-2019 Egor Yusov
 *  
 *  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.
 *
 *  In no event and under no legal theory, whether in tort (including negligence), 
 *  contract, or otherwise, unless required by applicable law (such as deliberate 
 *  and grossly negligent acts) or agreed to in writing, shall any Contributor be
 *  liable for any damages, including any direct, indirect, special, incidental, 
 *  or consequential damages of any character arising as a result of this License or 
 *  out of the use or inability to use the software (including but not limited to damages 
 *  for loss of goodwill, work stoppage, computer failure or malfunction, or any and 
 *  all other commercial damages or losses), even if such Contributor has been advised 
 *  of the possibility of such damages.
 */

#pragma once

/// \file
/// Definition of Diligent::IShaderBindingTable interface and related data structures

#include "../../../Primitives/interface/Object.h"
#include "../../../Primitives/interface/FlagEnum.h"
#include "GraphicsTypes.h"
#include "Constants.h"
#include "Buffer.h"
#include "PipelineState.h"
#include "TopLevelAS.h"

DILIGENT_BEGIN_NAMESPACE(Diligent)

// {1EE12101-7010-4825-AA8E-AC6BB9858BD6}
static const INTERFACE_ID IID_ShaderBindingTable =
    {0x1ee12101, 0x7010, 0x4825, {0xaa, 0x8e, 0xac, 0x6b, 0xb9, 0x85, 0x8b, 0xd6}};

// clang-format off

/// Shader binding table description.
struct ShaderBindingTableDesc DILIGENT_DERIVE(DeviceObjectAttribs)
    
    /// Ray tracing pipeline state object from which shaders will be taken.
    IPipelineState* pPSO                  DEFAULT_INITIALIZER(nullptr);
    
#if DILIGENT_CPP_INTERFACE
    ShaderBindingTableDesc() noexcept {}
#endif
};
typedef struct ShaderBindingTableDesc ShaderBindingTableDesc;


/// Defines shader binding table validation flags, see IShaderBindingTable::Verify().
DILIGENT_TYPED_ENUM(VERIFY_SBT_FLAGS, Uint32)
{
    /// Check that all shaders are bound or inactive.
    VERIFY_SBT_FLAG_SHADER_ONLY   = 0x1,

    /// Check that shader record data are initialized.
    VERIFY_SBT_FLAG_SHADER_RECORD = 0x2,
        
    /// Check that all TLASes that were used in the SBT are alive and
    /// shader binding indices have not changed.
    VERIFY_SBT_FLAG_TLAS          = 0x4,

    /// Enable all validations.
    VERIFY_SBT_FLAG_ALL           = VERIFY_SBT_FLAG_SHADER_ONLY   |
                                    VERIFY_SBT_FLAG_SHADER_RECORD |
                                    VERIFY_SBT_FLAG_TLAS
};
DEFINE_FLAG_ENUM_OPERATORS(VERIFY_SBT_FLAGS)


#define DILIGENT_INTERFACE_NAME IShaderBindingTable
#include "../../../Primitives/interface/DefineInterfaceHelperMacros.h"

#define IShaderBindingTableInclusiveMethods      \
    IDeviceObjectInclusiveMethods;               \
    IShaderBindingTableMethods ShaderBindingTable

/// Shader binding table interface

/// Defines the methods to manipulate an SBT object
DILIGENT_BEGIN_INTERFACE(IShaderBindingTable, IDeviceObject)
{
#if DILIGENT_CPP_INTERFACE
    /// Returns the shader binding table description that was used to create the object
    virtual const ShaderBindingTableDesc& DILIGENT_CALL_TYPE GetDesc() const override = 0;
#endif
    
    /// Checks that all shaders are bound, instances and geometries have not changed, shader record data are initialized.
    
    /// \param [in] Flags - Flags that specify which type of validation to perform.
    /// \return     True if SBT content is valid, and false otherwise.
    /// 
    /// \note Access to the SBT must be externally synchronized.
    ///       This method implemented only for development build and has no effect in release build.
    VIRTUAL Bool METHOD(Verify)(THIS_
                                VERIFY_SBT_FLAGS Flags) CONST PURE;
    

    /// Resets the SBT with the new pipeline state. This is more effecient than creating a new SBT.
    
    /// \note Access to the SBT must be externally synchronized.
    VIRTUAL void METHOD(Reset)(THIS_
                               IPipelineState* pPSO) PURE;
    

    /// After TLAS or BLAS was rebuilt or updated, hit group shader bindings may have become invalid,
    /// you can reset hit groups only and keep ray-gen, miss and callable shader bindings intact.
    
    /// \note Access to the SBT must be externally synchronized.
    VIRTUAL void METHOD(ResetHitGroups)(THIS) PURE;
    

    /// Binds a ray-generation shader.
    
    /// \param [in] pShaderGroupName - Ray-generation shader name that was specified in RayTracingGeneralShaderGroup::Name
    ///                                when the pipeline state was created.
    /// \param [in] pData            - Shader record data, can be null.
    /// \param [in] DataSize         - Shader record data size, should be equal to RayTracingPipelineDesc::ShaderRecordSize.
    /// 
    /// \note Access to the SBT must be externally synchronized.
    VIRTUAL void METHOD(BindRayGenShader)(THIS_
                                          const char* pShaderGroupName,
                                          const void* pData            DEFAULT_INITIALIZER(nullptr),
                                          Uint32      DataSize         DEFAULT_INITIALIZER(0)) PURE;
    

    /// Binds a ray-miss shader.
    
    /// \param [in] pShaderGroupName - Ray-miss shader name that was specified in RayTracingGeneralShaderGroup::Name
    ///                                when the pipeline state was created. Can be null to make the shader inactive.
    /// \param [in] MissIndex        - Miss shader offset in the shader binding table (aka ray type). This offset will
    ///                                correspond to 'MissShaderIndex' argument of TraceRay() function in HLSL, 
    ///                                and 'missIndex' argument of traceRay() function in GLSL.
    /// \param [in] pData            - Shader record data, can be null.
    /// \param [in] DataSize         - Shader record data size, should be equal to RayTracingPipelineDesc::ShaderRecordSize.
    /// 
    /// \note Access to the SBT must be externally synchronized.
    VIRTUAL void METHOD(BindMissShader)(THIS_
                                        const char* pShaderGroupName,
                                        Uint32      MissIndex,
                                        const void* pData            DEFAULT_INITIALIZER(nullptr),
                                        Uint32      DataSize         DEFAULT_INITIALIZER(0)) PURE;
    

    /// Binds a hit group for the the specified geometry in the instance.
    
    /// \param [in] pTLAS                    - Top-level AS that contains the given instance.
    /// \param [in] pInstanceName            - Instance name that contains the geometry. This is the name that was used
    ///                                        when the TLAS was created, see TLASBuildInstanceData::InstanceName.
    /// \param [in] pGeometryName            - Geometry name in the instance, for which to bind the hit group.
    ///                                        This is the name that was given to geometry when BLAS was created,
    ///                                        see BLASBuildTriangleData::GeometryName and BLASBuildBoundingBoxData::GeometryName.
    /// \param [in] RayOffsetInHitGroupIndex - Ray offset in the shader binding table (aka ray type). This offset will correspond
    ///                                        to 'RayContributionToHitGroupIndex' argument of TraceRay() function in HLSL, and 
    ///                                        'sbtRecordOffset' argument of traceRay() function in GLSL.
    ///                                        Must be less than HitShadersPerInstance.
    /// \param [in] pShaderGroupName         - Hit group name that was specified in RayTracingTriangleHitShaderGroup::Name or
    ///                                        RayTracingProceduralHitShaderGroup::Name when the pipeline state was created.
    ///                                        Can be null to make the shader group inactive.
    /// \param [in] pData                    - Shader record data, can be null.
    /// \param [in] DataSize                 - Shader record data size, should be equal to RayTracingPipelineDesc::ShaderRecordSize.
    /// 
    /// \note Access to the SBT must be externally synchronized.
    ///       Access to the TLAS must be externally synchronized.
    ///       Access to the BLAS that was used in the TLAS instance with name pInstanceName must be externally synchronized.
    VIRTUAL void METHOD(BindHitGroupForGeometry)(THIS_
                                                 ITopLevelAS* pTLAS,
                                                 const char*  pInstanceName,
                                                 const char*  pGeometryName,
                                                 Uint32       RayOffsetInHitGroupIndex,
                                                 const char*  pShaderGroupName,
                                                 const void*  pData            DEFAULT_INITIALIZER(nullptr),
                                                 Uint32       DataSize         DEFAULT_INITIALIZER(0)) PURE;
    

    /// Binds a hit group to the specified location in the table.
    
    /// \param [in] BindingIndex     - Location of the hit group in the table. 
    /// \param [in] pShaderGroupName - Hit group name that was specified in RayTracingTriangleHitShaderGroup::Name or
    ///                                RayTracingProceduralHitShaderGroup::Name when the pipeline state was created.
    ///                                Can be null to make the shader group inactive.
    /// \param [in] pData            - Shader record data, can be null.
    /// \param [in] DataSize         - Shader record data size, should equal to RayTracingPipelineDesc::ShaderRecordSize.
    /// 
    /// \note Access to the SBT must be externally synchronized.
    /// 
    /// \remarks    Use IBottomLevelAS::GetGeometryIndex(), ITopLevelAS::GetBuildInfo(), 
    ///             ITopLevelAS::GetInstanceDesc().ContributionToHitGroupIndex to calculate the binding index.
    VIRTUAL void METHOD(BindHitGroupByIndex)(THIS_
                                             Uint32      BindingIndex,
                                             const char* pShaderGroupName,
                                             const void* pData            DEFAULT_INITIALIZER(nullptr),
                                             Uint32      DataSize         DEFAULT_INITIALIZER(0)) PURE;


    /// Binds a hit group for all geometries in the specified instance.
    
    /// \param [in] pTLAS                    - Top-level AS that contains the given instance.
    /// \param [in] pInstanceName            - Instance name, for which to bind the hit group. This is the name that was used
    ///                                        when the TLAS was created, see TLASBuildInstanceData::InstanceName.
    /// \param [in] RayOffsetInHitGroupIndex - Ray offset in the shader binding table (aka ray type). This offset will
    ///                                        correspond to 'RayContributionToHitGroupIndex' argument of TraceRay() function
    ///                                        in HLSL, and 'sbtRecordOffset' argument of traceRay() function in GLSL.
    ///                                        Must be less than HitShadersPerInstance.
    /// \param [in] pShaderGroupName         - Hit group name that was specified in RayTracingTriangleHitShaderGroup::Name or
    ///                                        RayTracingProceduralHitShaderGroup::Name when the pipeline state was created.
    ///                                        Can be null to make the shader group inactive.
    /// \param [in] pData                    - Shader record data, can be null.
    /// \param [in] DataSize                 - Shader record data size, should be equal to RayTracingPipelineDesc::ShaderRecordSize.
    /// 
    /// \note Access to the SBT must be externally synchronized.
    ///       Access to the TLAS must be externally synchronized.
    VIRTUAL void METHOD(BindHitGroupForInstance)(THIS_
                                                 ITopLevelAS* pTLAS,
                                                 const char*  pInstanceName,
                                                 Uint32       RayOffsetInHitGroupIndex,
                                                 const char*  pShaderGroupName,
                                                 const void*  pData            DEFAULT_INITIALIZER(nullptr),
                                                 Uint32       DataSize         DEFAULT_INITIALIZER(0)) PURE;
    
    
    /// Binds a hit group for all instances in the given top-level AS.
    
    /// \param [in] pTLAS                    - Top-level AS, for which to bind the hit group.
    /// \param [in] RayOffsetInHitGroupIndex - Ray offset in the shader binding table (aka ray type). This offset will
    ///                                        correspond to 'RayContributionToHitGroupIndex' argument of TraceRay()
    ///                                        function in HLSL, and 'sbtRecordOffset' argument of traceRay() function in GLSL.
    ///                                        Must be less than HitShadersPerInstance.
    /// \param [in] pShaderGroupName         - Hit group name that was specified in RayTracingTriangleHitShaderGroup::Name or
    ///                                        RayTracingProceduralHitShaderGroup::Name when the pipeline state was created.
    ///                                        Can be null to make the shader group inactive.
    /// \param [in] pData                    - Shader record data, can be null.
    /// \param [in] DataSize                 - Shader record data size, should be equal to RayTracingPipelineDesc::ShaderRecordSize.
    /// 
    /// \note Access to the SBT must be externally synchronized.
    ///       Access to the TLAS must be externally synchronized.
    VIRTUAL void METHOD(BindHitGroupForTLAS)(THIS_
                                             ITopLevelAS* pTLAS,
                                             Uint32       RayOffsetInHitGroupIndex,
                                             const char*  pShaderGroupName,
                                             const void*  pData            DEFAULT_INITIALIZER(nullptr),
                                             Uint32       DataSize         DEFAULT_INITIALIZER(0)) PURE;


    /// Binds a callable shader.
    
    /// \param [in] pShaderGroupName - Callable shader name that was specified in RayTracingGeneralShaderGroup::Name
    ///                                when the pipeline state was created. Can be null to make the shader inactive.
    /// \param [in] CallableIndex    - Callable shader offset in the shader binding table. This offset will correspond to
    ///                                'ShaderIndex' argument of CallShader() function in HLSL, 
    ///                                and 'callable' argument of executeCallable() function in GLSL.
    /// \param [in] pData            - Shader record data, can be null.
    /// \param [in] DataSize         - Shader record data size, should be equal to RayTracingPipelineDesc::ShaderRecordSize.
    /// 
    /// \note Access to the SBT must be externally synchronized.
    VIRTUAL void METHOD(BindCallableShader)(THIS_
                                            const char* pShaderGroupName,
                                            Uint32      CallableIndex,
                                            const void* pData           DEFAULT_INITIALIZER(nullptr),
                                            Uint32      DataSize        DEFAULT_INITIALIZER(0)) PURE;
};
DILIGENT_END_INTERFACE

#include "../../../Primitives/interface/UndefInterfaceHelperMacros.h"

#if DILIGENT_C_INTERFACE

// clang-format off

#    define IShaderBindingTable_Verify(This, ...)                  CALL_IFACE_METHOD(ShaderBindingTable, Verify,                  This, __VA_ARGS__)
#    define IShaderBindingTable_Reset(This, ...)                   CALL_IFACE_METHOD(ShaderBindingTable, Reset,                   This, __VA_ARGS__)
#    define IShaderBindingTable_ResetHitGroups(This)               CALL_IFACE_METHOD(ShaderBindingTable, ResetHitGroups,          This)
#    define IShaderBindingTable_BindRayGenShader(This, ...)        CALL_IFACE_METHOD(ShaderBindingTable, BindRayGenShader,        This, __VA_ARGS__)
#    define IShaderBindingTable_BindMissShader(This, ...)          CALL_IFACE_METHOD(ShaderBindingTable, BindMissShader,          This, __VA_ARGS__)
#    define IShaderBindingTable_BindHitGroupByIndex(This, ...)     CALL_IFACE_METHOD(ShaderBindingTable, BindHitGroupByIndex,     This, __VA_ARGS__)
#    define IShaderBindingTable_BindHitGroupForGeometry(This, ...) CALL_IFACE_METHOD(ShaderBindingTable, BindHitGroupForGeometry, This, __VA_ARGS__)
#    define IShaderBindingTable_BindHitGroupForInstance(This, ...) CALL_IFACE_METHOD(ShaderBindingTable, BindHitGroupForInstance, This, __VA_ARGS__)
#    define IShaderBindingTable_BindHitGroupForTLAS(This, ...)     CALL_IFACE_METHOD(ShaderBindingTable, BindHitGroupForTLAS,     This, __VA_ARGS__)
#    define IShaderBindingTable_BindCallableShader(This, ...)      CALL_IFACE_METHOD(ShaderBindingTable, BindCallableShader,      This, __VA_ARGS__)

// clang-format on

#endif

DILIGENT_END_NAMESPACE // namespace Diligent
