Search Unity

Question Is it possible to use NVIDIA HLSL Extensions in Unity?

Discussion in 'Shaders' started by BastianUrbach, Jan 19, 2023.

  1. BastianUrbach

    BastianUrbach

    Joined:
    Sep 7, 2017
    Posts:
    2
    NVIDIA has some cool HLSL extensions, e.g. for measuring time inside of a ray tracing shader. The HLSL-side setup and usage is simple but I don't know if or how I can do the CPU-side setup in Unity. I tried calling NvAPI_D3D12_SetNvShaderExtnSlotSpace and NvAPI_D3D12_SetNvShaderExtnSlotSpaceLocalThread in a native plugin in UnityGfxDeviceEventInitialize and got NVAPI_OK but the extensions are still not working. One problem might be that Unity forces me to bind an actual ComputeBuffer or GraphicsBuffer to the fake UAV slot used by NVAPI instead of leaving it empty. I don't know if that's the (only) problem though, I just know that I didn't get it working so far. NvGetSpecial(NV_SPECIALOP_GLOBAL_TIMER_LO) seems to output garbage. I'm using DirectX 12 and an RTX 4070 Ti.

    I'm aware that this is a very specific question but maybe someone here has done something similar before or has more experience with native plugins or DirectX 12 than I do. I'd appreciate any help I can get.

    Plugin:
    Code (CPP):
    1. #include <d3d12.h>
    2. #include <dxgi.h>
    3. #include <sstream>
    4.  
    5. #include "nvapi/nvapi.h"
    6. #include "nvapi/nvapi_lite_d3dext.h"
    7. #include "unity/IUnityInterface.h"
    8. #include "unity/IUnityGraphics.h"
    9. #include "unity/IUnityGraphicsD3D12.h"
    10. #include "unity/IUnityLog.h"
    11.  
    12. const NvU32 SLOT = 3;
    13. const NvU32 SPACE = 0;
    14.  
    15. IUnityInterfaces* unity = NULL;
    16. IUnityGraphics* unityGraphics = NULL;
    17. IUnityGraphicsD3D12v2* unityD3D12 = NULL;
    18.  
    19. void Log(const char* message) {
    20.     auto debug = unity->Get<IUnityLog>();
    21.     debug->Log(kUnityLogTypeLog, message, "", 0);
    22. }
    23.  
    24. void LogNvapiStatus(const char* message, NvAPI_Status status) {
    25.     std::stringstream s;
    26.     s << message << " " << status;
    27.     Log(s.str().c_str());
    28. }
    29.  
    30. void UNITY_INTERFACE_API OnGraphicsDeviceEvent(UnityGfxDeviceEventType eventType) {
    31.     NvAPI_Status status = NVAPI_OK;
    32.  
    33.     switch (eventType) {
    34.         case kUnityGfxDeviceEventInitialize:
    35.             unityD3D12 = unity->Get<IUnityGraphicsD3D12v2>();
    36.  
    37.             status = NvAPI_Initialize();
    38.             LogNvapiStatus("NVAPI Init", status);
    39.  
    40.             status = NvAPI_D3D12_SetNvShaderExtnSlotSpace(
    41.                 unityD3D12->GetDevice(), SLOT, SPACE
    42.             );
    43.             LogNvapiStatus("NVAPI Enable", status);
    44.  
    45.             status = NvAPI_D3D12_SetNvShaderExtnSlotSpaceLocalThread(
    46.                 unityD3D12->GetDevice(), SLOT, SPACE
    47.             );
    48.             LogNvapiStatus("NVAPI Enable Local Thread", status);
    49.  
    50.             break;
    51.  
    52.         case kUnityGfxDeviceEventShutdown:
    53.             status = NvAPI_D3D12_SetNvShaderExtnSlotSpace(
    54.                 unityD3D12->GetDevice(), ~0u, 0
    55.             );
    56.             LogNvapiStatus("NVAPI Disable", status);
    57.  
    58.             status = NvAPI_D3D12_SetNvShaderExtnSlotSpaceLocalThread(
    59.                 unityD3D12->GetDevice(), ~0u, 0
    60.             );
    61.             LogNvapiStatus("NVAPI Disable Local Thread", status);
    62.  
    63.             break;
    64.     };
    65. }
    66.  
    67. extern "C"
    68. void UNITY_INTERFACE_EXPORT UNITY_INTERFACE_API UnityPluginLoad(
    69.     IUnityInterfaces* unityInterfaces
    70. ) {
    71.     unity = unityInterfaces;
    72.  
    73.     Log("Load");
    74.  
    75.     unityGraphics = unityInterfaces->Get<IUnityGraphics>();
    76.     unityGraphics->RegisterDeviceEventCallback(OnGraphicsDeviceEvent);
    77.  
    78.     OnGraphicsDeviceEvent(kUnityGfxDeviceEventInitialize);
    79. }
    80.  
    81. extern "C"
    82. void UNITY_INTERFACE_EXPORT UNITY_INTERFACE_API UnityPluginUnload() {
    83.     Log("Unload");
    84.  
    85.     unityGraphics->UnregisterDeviceEventCallback(OnGraphicsDeviceEvent);
    86. }
    RayTracingShader:
    Code (HLSL):
    1. #define NV_SHADER_EXTN_SLOT u3
    2. #define NV_SHADER_EXTN_REGISTER_SPACE space0
    3. #include "nvHLSLExtns.hlsl"
    4. ...
    5. uint time = NvGetSpecial(NV_SPECIALOP_GLOBAL_TIMER_LO);
     
  2. guoxx_

    guoxx_

    Joined:
    Mar 16, 2021
    Posts:
    55
    You can use Nvidia shader extension in Unity. We use it for shader execution reordering.

    You already have everything set up on C++ side. in C#, you can bind an empty buffer to slot "g_NvidiaExt".

    Code (CSharp):
    1.  
    2.             // Bind a dummy buffer in order to stop Unity from complaining
    3.             cmd.SetGlobalBuffer("g_NvidiaExt", RenderResources.EmptyBuffer);
    4.  
    One last thing, NV_SHADER_EXTN_REGISTER_SPACE should use space1
     
  3. INedelcu

    INedelcu

    Unity Technologies

    Joined:
    Jul 14, 2015
    Posts:
    173
  4. BastianUrbach

    BastianUrbach

    Joined:
    Sep 7, 2017
    Posts:
    2
    Thanks! I got it working in a very simple RayTracingShader now. I actually don't really need it anymore but it's still good to know for future projects. The same code doesn't seem to work in a ComputeShader though. If I use space1 in a ComputeShader in any way (whether for the extensions or just a normal buffer), I get this error:
    Code (csharp):
    1. D3D12 CreateComputePipelineState failed.