How to transmit lots of data to a Material efficiently?

I’m working on a project where I need to send a lot of data to a material (on the order of 64floats). The material will be used on an actor either with a static mesh, or skeletal mesh.
Data is changing per-frame, and so it is important that data is being transmitted from the CPU to the GPU efficiently.

Custom Primitive Data is apparently an efficient approach, however there is a hard limit of 32 floats. So that is not a viable option.

A dynamic material instance should work, but CPU->GPU communication doesn’t seem very performant when that much data is being updated every tick.

Does anyone know of a good approach? Are there any other better approaches?

1 Like

I wanted to post my progress on this problem.

My current approach involves storing my data in a render target, and then accessing the render target in my materials.

My main issue at the moment is trying to figure out how to efficiently write my TArray of integers to the render target.

1 Like

Try a Material Parameter Collection. You can send a bundle of 1k scalars + 4vecs to the GPU. You can reference these values like external-variables inside your materials.

As well, you can do whatever on the CPU and write updated values to the MPC so you can adjust whatever you tie into these numbers.

I use these all over and it make it very easy to organize. I can have all my ‘Environment’ stuff like time of day, rainyness, snowyness, etc as well as the scalingins for all my landscape textures, etc… Even things in a heightmesh material like a Time node, can be replicated by updating the MPC every tick.

ref: Material Parameter Collections | Unreal Engine Documentation

2 Likes

Quite honestly I don’t think that an MPC can transmit enough data for my usecase.
I need roughly 64 floats per object I have in the world (using my material).

That ends up hitting a hard limit of 80 objects as I need each object to be addressing a unique portion of the MPC.

There’s also the difficulty that I don’t believe I can easily change which region of the MPC I’m addressing in my material.

1 Like

each material can reference 2 MPC’s, and each MPC is a collection of (up to) 1000 scalar values + 1000 vec4 values.

How is that not enough? What kinds of data are you looking to store?

Make a BP object you can attach to the primitives that keeps track of all their stuff. Then have each thing update it’s own MPC?

1 Like

Yeah so an MPC has more than enough capacity for one object. I’ve already got a BP object to attach to the primitives and keep track of their stuff. The main issue is that it doesn’t seem possible to create MPCs dynamically.

Ideally, I would want an MPC per object, and then I would pass a reference to that MPC to the material. However, it doesn’t seem that new MPCs can be created at runtime.

I am storing lighting data. I’m working on material-based lighting.

1 Like

OK. I don’t believe they CAN be created dynamically. :frowning:

1 Like

Yep, that’s why I’m stuck with my janky render target approach

1 Like

Hey there, Sorry for necroing this thread but I’m looking at doing something similar and was curious if you’d made any progress?

1 Like

OP was on the right path here I think. As far as I’m aware the fastest way to pass a large amount of dynamic values to a material would be via a texture lookup. 64 floats could easily be encoded in an 8x8 RGB image. You can write values to render targets using blueprints, and then read those values in the material.

1 Like

Yeah, so my approach ended up being writing the data to a render target.
There’s a good amount of possible optimization that can be done to make writing to the render target faster, but the general gist of the issue is:

render target needs to be written to from render thread / game thread (if I remember correctly).
but we want to perform the minimal amount of computation within the renderthread/gamethread. Ideally we’d want to perform everything in a background thread.
So what we do is we create a new memory buffer that’s the same size as the render target, and we perform all computations on a background thread.
Then on the render thread it’s just a matter of locking the render target, memcpy, and then unlocking.

I don’t have any of my old code, so hopefully this helps enough. But feel free to ask any questions :slight_smile:

2 Likes

Hey! How would you write values to a render target from blueprint? I can’t find this information anywhere on the internet :frowning:

This video is a pretty good demo project to practice this kind of thing.

Thanks for the link :slight_smile:. Here is how I solved it in my case, where I needed to store and read Vector3s.

Render target settings

  • I had to set the Texture Group to 2D pixels to be able to properly read the data in the material graph
  • Set Size X and Size Y to the size of your “array”. In my case it’s a 2D array of 256x2

How to store the data

How to read data from the render target within the material graph

Add a custom node to the graph, with the following code and inputs:

  • Tex: your render target texture
  • PixelCoordinates: the coordinates of the pixel that holds the data you need
  • TexSize: the size of your render target (In my case 256x2). There is maybe a more dynamic way to get the texture size, but this is good enough for me.

HLSL Code:

float2 UV = PixelCoordinates/TexSize;
float3 data = Texture2DSample(Tex, TexSampler, UV);

return data;

The output will be the data that you stored earlier :slight_smile: