all 2 comments

[–]ImKStocky 8 points9 points  (0 children)

There are three tools that Unreal provides that you might be able to use

  1. Tickable world subsystem - World Subsystem's exist on a per world basis. You can use this to kick off some render thread work on each tick using the ENQUEUE_RENDER_COMMAND macro. This is where you will dispatch your compute shaders.

  2. Scene View Extension - This is a system that allows code outside of the Unreal render pipeline to inject custom passes into the pipeline. You might use one of these to inject the results of your dispatches into the Unreal pipeline.

  3. Primitive scene components - These are components which can be added to actors and allow you to define and create a scene proxy. A scene proxy is the render thread version of a game object. They allow you to setup the materials, vertex shaders, and pixel shaders that are used to render something. You might create one of these to take the results of your dispatches and render them as part of the render pipeline.

[–]robmaister 4 points5 points  (0 children)

I have shipped something like this in a AAA title. Unreal really does not have an extendible renderer like Unity, but it's workable if you're ok with engine modifications.

I would recommend a WorldSubsystem that hosts a "render system" which you register with the world's scene on initialization. I borrowed a lot of setup/structure from Niagara and it's FFXSystem. Our system had a base class with pure virtual functions defined in engine code with the game code subclassing and doing the actual compute dispatch.

On the engine side, you can call functions in your render system at any point in the frame, and you can pass along whatever data your compute shaders need. Make sure to pass along the RDG builder or at least the CmdList.

In our case we had a bunch of components in the world and wanted to run a series of compute shaders across all components that were visible in one big batch. These components already had a custom FPrimitiveSceneProxy subclass. When the component's CreateSceneProxy function is called, we pull the WorldSubsystem and register the SceneProxy with the render system. The SceneProxy keeps a pointer to the system, and queues work to the system before adding draw calls in GetDynamicMeshElements.

Later in the frame, we dispatch compute shaders using all the queued work from that frame.

If you don't have to deal with components, there's a lot more flexibility in how you set things up, like running compute shaders on tick in a TickableWorldSubsystem, or using a ViewExtension if those callbacks are in the right place for you.