How do I get the dispatch call for a Compute Shader to show in RenderDoc?

Hi,

I am trying to debug a compute shader using the RenderDoc integration in 5.3. My script issues one dispatch on each call to update, but it never shows up in the event browser.

In previous versions of Unity, RenderDoc would sometimes catch the dispatch call from the editor. I would guess that with the tighter integration, RenderDoc is now more selective about what events are considered ‘within’ a frame.

Where must I place the call to dispatch to ensure it is captured?

I have tried OnPreRender(), OnPostRender() and Update() but it shows in none.

Sj

Hey sebj. The RenderDoc integration in 5.3 is currently doing something along the lines of:

BeginRenderDocCapture();
DrawScene();
EndRenderDocCapture();

But it seems like the DrawScene(); call only does rendering, it doesn’t call script updates. I’ve found what I need to change to make it do a full scene render, so I will get that committed. I’m not sure yet what Unity versions that will make it into as it depends on cutoff dates, but I’ll keep you posted. If you want to file a bug about this with a minimal repro project and post the number here, I’ll make sure that the fix works on your project then get it assigned to me and kept up to date.

As a workaround for the moment unfortunately the only thing I can suggest is to custom build a version of RenderDoc from source with the integration disabled. Then you can use the default overlays and keybinds (F11 to switch the scene or game view, F12/PrintScreen to capture). It’s a bit of a hack but there isn’t an alternative to bypass the integration.

Hi unity_baldurk,

Thanks for getting back to me. I have submitted a bug report along with my project (its in a very early stage and so quite minimal); the case number is 762795.

I will try and build an alternative version of RenderDoc as you suggest for now.

Sj

A quick update for anyone else with the same problem:

I modified RenderDoc as suggested, but instead of disabling the integration, I altered the API slightly so that I could control when a capture begins and ends. When Unity is started from renderdocui, the local/modified renderdoc DLL is loaded into Unitys process, meaning its trivial to use PInvoke in component scripts to call any, potentially new, exported functions.

RenderDoc has StartFrameCapture and EndFrameCapture methods, but they need device and window pointers, which only Unity knows, so I modified StartFrameCapture to also set the active window whenever it is called, and created two additional parameterless functions which just call the original methods with the properties of the active window.

The new functions were exported & declared in Unity like so:

[DllImport(“renderdoc.dll”, CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl)]
public static extern void RENDERDOCEX_StartCapture();

[DllImport(“renderdoc.dll”, CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl)]
public static extern void RENDERDOCEX_StopCapture();

Now, to capture the dispatch call, all you have to do its make sure the original integration captures a frame once (to set the m_activeWindow member), then surround the ComputeShader.Dispatch() call with RENDERDOCEX_StartCapture()/RENDERDOCEX_StopCapture() (I trigger this using a button on a custom inspector in Editor mode).

This was all very easy to do - Unity Technologies have picked a really great tool in RenderDoc to integrate!

Hi sebj, I must admit that is pretty cool! I hadn’t thought about the ability to P/Invoke to renderdoc’s application API once it’s loaded, but I imagine it would be quite useful for catching one-off bake processes and such.

Knowing that, there’s a workaround that doesn’t even need renderdoc modifications in this case - you should be able to just pass NULL for both device and window in start and end capture, and this will just pick the first pair it comes across. That works fine if you are just doing offscreen rendering or compute as you don’t care which backbuffer you capture and you control the start and end points of the capture explicitly.

I made a similar-ish change upstream in renderdoc’s repository. StartCapture() will now set the captured window active, as a kind of insurance against any future problems to make sure that clicking ‘trigger capture’ from the renderdoc UI will at least be able to capture the correct window:
When StartFrameCapture() is called on API, also make that wnd active · baldurk/renderdoc@278a977 · GitHub. This isn’t quite the same as what you did, so I might add something to the API to allow for the precise behaviour you have just in case.

I’m glad it’s working for you now though! If you have any other feedback about the Unity integration, let me know. If you have feedback about renderdoc you can get in touch with me that way ;).

Hi unity_baldurk,

I imagine it would be quite useful for catching one-off bake processes and such.

Yes, this is exactly what I wanted it for in the Editor. That and being able to quickly & easily send off a shader under tightly controlled conditions for debugging purposes.

> Knowing that, there’s a workaround that doesn’t even need renderdoc modifications in this case - you should be able to just pass NULL for both device and window in start and end capture

Thanks for that! For anyone whos not aware, RenderDoc doesn’t export the StartFrameCapture and EndFrameCapture (even parametrised) directly, but its simple to get references to them via the GetAPI function that is exported… (I really like RenderDoc…):

[StructLayout(LayoutKind.Sequential)]
struct RENDERDOC_API_1_0_0
{
public IntPtr GetAPIVersion;

public IntPtr SetCaptureOptionU32;
public IntPtr SetCaptureOptionF32;

public IntPtr GetCaptureOptionU32;
public IntPtr GetCaptureOptionF32;

public IntPtr SetFocusToggleKeys;
public IntPtr SetCaptureKeys;

public IntPtr GetOverlayBits;
public IntPtr MaskOverlayBits;

public IntPtr Shutdown;
public IntPtr UnloadCrashHandler;

public IntPtr SetLogFilePathTemplate;
public IntPtr GetLogFilePathTemplate;

public IntPtr GetNumCaptures;
public IntPtr GetCapture;

public IntPtr TriggerCapture;

public IntPtr IsRemoteAccessConnected;
public IntPtr LaunchReplayUI;

public IntPtr SetActiveWindow;

public StartFrameCapture StartFrameCapture;
public IntPtr IsFrameCapturing;
public EndFrameCapture EndFrameCapture;
}

//typedef void (RENDERDOC_CC *pRENDERDOC_StartFrameCapture)(RENDERDOC_DevicePointer device, RENDERDOC_WindowHandle wndHandle);
public delegate void StartFrameCapture(IntPtr device, IntPtr window);

//typedef uint32_t (RENDERDOC_CC *pRENDERDOC_EndFrameCapture)(RENDERDOC_DevicePointer device, RENDERDOC_WindowHandle wndHandle);
public delegate int EndFrameCapture(IntPtr device, IntPtr window);

[DllImport(“renderdoc.dll”, CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl)]
public static extern int RENDERDOC_GetAPI(int version, out IntPtr outAPIPointers);

private static int eRENDERDOC_API_Version_1_0_0 = 10000;

public static void StartCapture()
{
IntPtr pAPI = new IntPtr();
int ret = RENDERDOC_GetAPI(eRENDERDOC_API_Version_1_0_0, out pAPI);
RENDERDOC_API_1_0_0 api = (RENDERDOC_API_1_0_0)Marshal.PtrToStructure(pAPI, typeof(RENDERDOC_API_1_0_0));
api.StartFrameCapture(new IntPtr(), new IntPtr());
}

public static void EndCapture()
{
IntPtr pAPI = new IntPtr();
int ret = RENDERDOC_GetAPI(eRENDERDOC_API_Version_1_0_0, out pAPI);
RENDERDOC_API_1_0_0 api = (RENDERDOC_API_1_0_0)Marshal.PtrToStructure(pAPI, typeof(RENDERDOC_API_1_0_0));
api.EndFrameCapture(new IntPtr(), new IntPtr());
}

Now vanilla 0.26 can be used when starting the Editor directly, and the capturing works just as it did with the modified API.

> I made a similar-ish change upstream in renderdoc’s repository. StartCapture() will now set the captured window active, as a kind of insurance against any future problems to make sure that clicking ‘trigger capture’ from the renderdoc UI will at least be able to capture the correct window:
When StartFrameCapture() is called on API, also make that wnd active · baldurk/renderdoc@278a977 · GitHub. This isn’t quite the same as what you did, so I might add something to the API to allow for the precise behaviour you have just in case.

Awesome!

Thanks for all your assistance with this, it is great to have it working.

1 Like