- Home /
Swapping Compute Shaders
I am editing some point cloud volume data in Unity, on the GPU.
My issue is that I have to edit my data in multiple passes (applying multiple different compute shaders to the same data). Is it possible to pass the output from one compute shader to another one? You know, stopping the current compute shader, starting another one, and pointing it to the same data in the GPU memory?
How would that be done? We have the ComputeShader.Dispatch
to begin with (in unity), which starts one compute shader, and then what? Can I somehow just make multiple ComputeShader
objects and then do secondComputeShader.SetBuffer(--existingGPUBuffer--)
and then .Dispatch()
? (note: it wouldn't be necessary or optimal for me to run multiple compute shaders in the same frame, unless there's no other option)
I am using DX11, with HLSL. My data structure is in a RWStructuredBuffer
, which according to MSDN, is synced across the entire GPU -- and indeed I can access this StructuredBuffer in my regular shader for display purposes.
Ideally I would be able to define a layering order "CS-A, CS-B, CS-A, CS-C"; if that is at all possible (but not all in the same frame).
Cheers!
[EDIT]
Alright, after Ben's comment, I'm posting what I'm doing right now:
public List<ComputeShader> computeShaderList;
ComputeBuffer particleBuffer;
struct Particle
{
public Vector3 position;
public Vector3 velocity;
};
ComputeShader currentComputeShader;
void Start ()
{
Particle[] particleArray = new Particle[particleCount];//remember to initialize for each Particle (position and velocity)
//create new compute buffer instance, and initialize it with your data.
particleBuffer = new ComputeBuffer(particleCount, 24); // 24 = "stride" = size allocated for each particle, probably in bytes, no idea what happens with this
particleBuffer.SetData(particleArray);
//so now we should have all our point data stored on the GPU, right?
foreach(ComputeShader cs in computeShaderList)
{
cs.SetBuffer(0, "particleBuffer", particleBuffer);
}
material.SetBuffer ("particleBuffer", particleBuffer);//this is for the regular shader
//and now we have told each of our (compute) shaders to reference that data we have stored on the GPU
}
void Update()
{
switch(currentState)// you don't need to run this each frame
{
case ComputeStates.CS_One:
currentComputeShader = computeShaderList[0];
break;
case ComputeStates.CS_Two:
currentComputeShader = computeShaderList[1];
break;
}
//currentComputeShader.SetFloat( ...set all your stuff
//Start the current compute shader to work this frame on your particles.
currentComputeShader.Dispatch(0, threadCount, 1, 1);
}
//note: you also need your OnRender with Graphics.DrawProcedural...
The Unity documentation on compute shaders is lacking, and the exposed ComputeShader object isn't very transparent. All I can say is that according to the Profiler, there seems to be no performance impact from having a bunch of different compute shaders, and choosing which one of them you want to dispatch at what time.
Given the way graphics cards work, it's very doubtful that if such a thing exists, you won't be able to "drag it out" over more than one frame. That would also negate the whole point of "stacking" compute shaders. So if "stacking" does exist, it will definitely have to happen in one "frame".
Perhaps Stacking isn't the right term. I just mean swapping shaders. That's possible, right? With regular shaders that is; you have $$anonymous$$aterial.Shader
. It's just that I want the new shader to access the same GPU Buffer already existing on the gpu...
Don't think of it in a videogame context; think of it more like a 3d modelling tool.
Using SetBuffer
won't cause a re-upload of the buffer to the GPU if that's why you're asking. Once you call SetData
all the uploading is done. If you use a different shader with the same data, this shader is simply passed the memory address of the already existing buffer.
Well I did not know that, Ben. ^_^ I have updated my question with some code. I can't verify if what I'm doing is good practice (no idea what magic happens in unity's backend), but it seems that I can have all the compute shaders I want, and just choose which one to Dispatch each frame. Do you see any negative impact in this?(compared to doing the same switching with regular shaders)
PS: feel free to convert your comments into an answer.
Answer by Benproductions1 · Apr 18, 2014 at 01:37 AM
As per all OpenGL buffers (IDK about D3D, but I'd assume it's the same), Unity uploads the data when you set it. For Texture2D
that would be Apply
, for compute buffers it's SetBuffer
. Once the buffer is uploaded to the GPU it will stay there until you either delete the buffer or use SetBuffer
again.
When the "data" for a material/shader is "set", if it's a pre-uploaded buffer, it gets simply passed a reference, like a pointer, to the data. Just like textures, compute buffers won't get re-uploaded when they are set on a shader. This is actually true for all buffers, as long as the implementation on the drivers is somewhat sane ;)
You should be able to freely pass around buffers between materials and shaders without much of an overhead.
That said, I am not familiar with the new OpenGL 4.X api, but I am versed in both OpenGL 3.X and OpenCL, so I shouldn't be too far from the truth.
Your answer
Follow this Question
Related Questions
A node in a childnode? 1 Answer
Visual Studio Community on Mac will not recognize HLSL 0 Answers
Compute shader loop in editor 1 Answer
Sharing code between shader and c# - how? 3 Answers
Unity 4.5 broke shader in DX11 mode 2 Answers