Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Combining multiple pixel shaders efficiently

So I'm making a thing with XNA 3.1, and I have a lot of separate effects that are applied via pixel shaders. These come from all sorts of sources, such as special attacks, environment, and so forth. The issue I'm having is that I'm noticing a significant reduction in frame rate.

At the moment, I'm drawing the entire scene to a RenderTarget2D, which I'm then applying all the effects to. I store a SortedDictionary containing the effects and their IDs (the IDs are used to change parameters at runtime), and I'm iterating over it and applying each effect one after the other:

foreach(KeyValuePair<Ref<int>,Effect> p in renderEffects)
{
    Effect r = p.Value;
    g.SetRenderTarget(0, MainGame.MainRenderTarget);
    //Change RenderTarget to allow code to grab existing texture in the same draw area.
    levelDraw = MainGame.LevelRenderTarget.GetTexture();
    //Change back to draw back to this texture, allowing render effects to be layered.
    g.SetRenderTarget(0, MainGame.LevelRenderTarget);

    MainGame.StartDraw(MainGame.GameBatch);
    //Starts the sprite batch and sets some parameters
    r.Begin();
    r.CurrentTechnique.Passes[0].Begin();
    MainGame.GameBatch.Draw(levelDraw, new Rectangle(0, 0, levelDraw.Width, levelDraw.Height), Color.White);
    r.CurrentTechnique.Passes[0].End();
    r.End();
    MainGame.GameBatch.End();
}

Now, this produces noticeable frame drops when layering just 3 effects, and when applying 10, it drops from 60FPS to 16FPS, which is of course unacceptable. I'm wondering if there is a more efficient way to do this. Considering I only have one texture, I considered that I may be able to combine the effects into one file and execute multiple passes without grabbing the texture back. I'm not sure if this is possible, however.

I'm not really sure exactly how the best way to do this is, though I imagine there must be a better way than the way I'm doing it.

like image 449
Hoeloe Avatar asked Dec 11 '12 14:12

Hoeloe


1 Answers

The method in the snippet is likely to be very slow, because you're doing a texture grab and a full screen draw for every effect, which stresses the memory bandwidth between the CPU and GPU on top of whatever is going on inside the shaders. You probably need, as you suggested in your post, to create a set of shaders which each contain multiple operations rather than running the read-write loop over and over again: one expensive shader will usually still be faster than many read-write-repeats of simple shaders.

You might want to look at Shawn Hargreaves article on shader fragments in HLSL and Tim Jones's code for doing this in XNA

like image 164
theodox Avatar answered Sep 22 '22 06:09

theodox