Organizing rendering supporting multiple shaders

Hello all,

I’ve been working in MonoGame for a while, but I have just started getting into shaders. I wrote my first shader, which replicates this blended rainbow effect.

I want this shader to be used only on entities that have performed a certain action, and entities have their own Draw methods with a solid base that can be overridden. I have a class wrapped around SpriteBatch, called SpriteRenderer, that allows me to stop and start new batches from anywhere, but I don’t want to have separate batches for each entity that is using that shader. Essentially, I want to be able to batch all entities using a particular shader.

I came up with an idea for the following:

  1. Store all SpriteBatch info into a new struct (SpriteBatch, Texture, Position, SourceRect, Color, etc.)
  2. Add to a dictionary which has the shader as the key (null is a valid key) and a list of those structs as the value
  3. Handle all rendering at once with the data in the dictionary, starting new batches for each shader
  4. Clear the dictionary after finishing rendering

The entity would pass that information into each call of SpriteRenderer, which would handle everything else. An example would be something like:

SpriteRenderer.Instance.Draw(myShader, myTex, Position, TintColor);

However, I’m unsure if this approach would work very well. My main concerns are:

-Changing shader variables for individual objects. If one object wants to change the values of a shader for itself, the new values will be applied to all objects using that shader. I’m using a FrontToBack SpriteSortMode. Ideally, I’d like to have only one instance of the shader (if that’s possible).
-Likely too much memory usage (will need to be tested).

What would be a good practice for handling rendering with numerous shaders in a flexible manner like this? Are there any examples out there?

You can find my wrapper and open source code here.

Thanks in advance!

Ain’t this effect in an ‘DoSomething/Action’ class? And called when an event is thrown ? (oop) this event knowing who fired it with the ‘sender’ argument

Sorry, I’m not understanding. I already have a system handling the numerous different actions the entities can take. My question is asking for good practices regarding managing batches with multiple shaders.

If you use front to back rendering the sprites get sorted regardless of order you submit them, you can just draw your special shaders afterwards.

You can use different shader inputs and start with multiple begin draw end queues, not much of a performance problem.

If you don’t use front to back, you can just go through all elements and if they are not using the current shader then spritebatch. End, begin, draw again. No biggie if you only switch a couple of times

From

It is not only a problem of managing batches. If ahead of the manager, you can direct the data to the correct list, instead of
looking over a list of tasks to do, and with branches assigning to the right renderer, you can save time and efforts I think.

If an event for ex, “fire_a_bullet” happens, it also could add its owner to the list of spritebatches to draw this effect. (a class managing lists of things to do: a list for alphatested, a list for alphablended, a list for fully opaque etc)

As suggested by kosmonautgames, it is faster to use batches of objects using a same effect, instead of switch between each one if they have a different effect to apply.

If this is for just one sprite in a menu as shown in your link, you can do it in the simplest manner.
If you plan to do massive drawing with many effects (> 3 or 4 is many ?), sorting data may not be an option.

I think I understand a little more. From what I gathered, it’s something along the lines of this:

public List<BattleEntity> AlphaBatched; public List<BattleEntity> RainbowBatched;

In the case I’m covering, when an entity uses the “Charge” move, I send them to the RainbowBatched list, which renders them using that rainbow shader I wrote. Does that sound correct?

That’s it :wink:.
The main goal is to to dispatch and prepare the drawing in the update() method, so the draw() only draws with the lists assigned to a spritebatch using effect A or another with B etc

I see how this approach may work, and I greatly appreciate the help! However, there are still a few things I can’t wrap my head around with it:

  1. Some of my objects are rendered vastly differently from others. For example, my NineSlicedTexture2D class uses destRectangles instead of position and scale. I’d like shaders to be applied to any sort of object, and the approach I see here doesn’t show how I can account for that.
  2. If I have 10+ shaders, then I will need 10+ lists with numerous branches of conditional checks. This sounds like a pain to manage; there must be a more elegant solution.
  3. I’m not rendering with SpriteSortMode.Immediate, so I still need individual information passed to shaders. The shader I wrote requires some parameters: the entity’s color, and the texture coordinates for their current animation frame (I’m using spritesheets). This is needed so the charge effect looks consistent regardless of the frame being played since it offsets it from the top-left of the spritesheet and also accounts for whatever color they have.

These list are only relevant to solve the sorting needed by alphablended and alphatest drawing.

When it is not needed it is also better to batch models to limit the swaping (unreal does, unity does and has communicated/published a lot on this)
I’m not sure even AAA 3d games use 10 effects on a model and render it 10 times They generally bake many into 1effect to render onto different rendertargets to achieve many results from just one model/sprite applied (for particles for ex.) avoiding to render many times the same geometry/sprite/scene.

Sorry, I should’ve mentioned that I’m dealing with 2D here. As for the 10+ effects, it’s not on a single sprite that I’m concerned with. It’s for managing multiple that may need them. If I was vague about my specification, please let me know since I feel there’s a disconnect. I’ll give an example:

-Character1 did some action and needs to render with the rainbow shader
-Character2 did some action and needs to render with an alphatest shader
-Character3 is always rendered with some sort of liquid-like shader
-Character4 did some action that makes it render with a shader that produces some sort of glowing effect
-Character5 is also rendered with the same rainbow shader Character1 is using

Essentially, I want a scalable system to manage batching the effects. So in this instance, Characters 2, 3, and 4 are in their own batches with their own shaders, and Characters 1 and 5 are in the same batch being rendered with the rainbow shader.

Ideally, I wouldn’t want to have to worry about creating a new list that is mapped to a large conditional branch each time I decide I want to use a new shader.

At the bottom of this base class is a Draw method that works for most BattleEntities in my project. Other objects have their own Draw methods to uniquely render what they need (Ex. moves in this project show button prompts). Since I want to add shaders, I’d like to keep this sort of flexibility while being able to batch objects that want to use the same shader together. I hope this clears things up a bit (and if it doesn’t please ask).

The 3D principle can be applyed to 2D
First things first,

How do you send your charactersX to draw in a batch ?

Start RainbowEffect
foreach(character c in characters)
{
 if(c.action != "rainbowaction")
  continue;
 c.Draw()
}
End Effect

Start AlphablendEffect
foreach(character c in characters)
{
 if(c.action != "alphatestaction")
  continue;
 c.Draw()
}
End Effect

etc.

Do you loop over your characters X times ?

I don’t see how you can batch without lists at one time or another.

Moreover, lists can be created at loadtime, because you know in advance all the effects you are going to use (unless you load your effects/models asynchronously in an infinite/open world manner) because you have filled your “LoadContent()” method. And as you fill this method, you can also add the list for the new effect.

For ex (very simplified):

List<RenderEffectItem> ls = new List<RenderEffectItem>(); 
//for each effect, add it to the list
//each RenderEffectItem is just a class

class RenderEffectItem
{
 Effect _EffectToApply;
 List<xyz> _LsItemsToDrawWith_EffectToApply;
}

This eliminates the conditionnal statements to be added when a new effect is needed :wink:
To draw, it only needs to loop avec ls and apply the attached effect.

There is not one single manner to do batching, but “if” and lists isn’t avoidable. Or an ubershader being able to draw all the effects, without any conditionnal in draw() but the technique to apply.

How do you plan to apply the existing/new shader ? In each sprite’s Draw method ?

What i dont catch is why you need so many effects, when you can do one with multiple techniques:
alphablend,
Alphablend+rotation (if done in the shader and not in c#)
Etc
A more “elegant way” would be to render on different rendertargets “water”, “blur”, “rainbow” (alphatest needs to be sorted anyway) so you can apply the effect 1 one on many sprites.

Does your water effect uses tiles textures or is it a ‘distortion’ effect?
You can start by se1rching which of you shader use the part of code as another one to regroup in techniques

Okay, I think I’m starting to understand now. Part of my problem is I’m thinking of this wrong and I’m trying for some one-size-fits-all solution that isn’t possible. I’m approaching rendering in the same way as game logic, which won’t work.

I’ll very clearly state how everything currently works and what I want to do.

Right now at the start of Draw(), I have clear the screen with GraphicsDevice.Clear and begin drawing my SpriteBatch with my settings of no Effect, AlphaBlend, etc. Then, I call Draw() on higher level objects, which simply call Draw() on the objects contained in them. Every object aside from UI currently draws to the same SpriteBatch; UI has a separate batch.

One of the main types of objects in my game are BattleEntities, which have animations. All animations have a Draw method that the BattleEntity passes its data to (position, rotation, etc.) in its own Draw method. Animation’s Draw method draws to the SpriteBatch with the data that was passed in.

At the end of the game’s main Draw method, I call End on the SpriteBatch.

That’s how everything works. Now, I have the rainbow shader that I wrote, and I want to apply it when the BattleEntity uses the Charge move. The problem is my current system only does a single SpriteBatch Begin at the start of drawing, and an End at the very end of drawing. If two BattleEntities used the Charge move, I want to batch them both together in the same Begin call that uses that shader.

What would you say is a good way to change my code to support this? Should I add an EffectMaterial to each BattleEntity, then add them to the correct list at the end of their Draw() methods?

Thanks again for your help; I really appreciate it!

Sorry for the late reply, got a lot of work to do within a small time window.

To start with, you will need different spritebatch objects if you do alphablend, alphatest and other (rainbow in different effects.)
Concerning the rainbow, you can pass an effect to the SpriteBatch.Draw, with just a simple useRainbow flag:

if(useRainbow){
SpriteBatch.Draw(Texture2D, Rectangle, Nullable<Rectangle>, Color, Single, Vector2, SpriteEffects, Single)
} 

you can try your effect without much changes in the structure. playing with the last param tells the layer to draw the sprite onto

refs:
https://msdn.microsoft.com/en-us/library/ff433992.aspx
https://msdn.microsoft.com/en-us/library/ff433988.aspx
https://msdn.microsoft.com/en-us/library/ff433989.aspx
from
https://msdn.microsoft.com/en-us/library/microsoft.xna.framework.graphics.spritebatch.draw.aspx

Optimizations will come later (RenderTargets, MRTs, etc :wink: )

Hey sorry, I didn’t forget about this thread. I spent a week or so experimenting with this in a new project since I wanted to try different approaches to working this out. On the plus side, I managed to separate the rendering from the game logic. Here are my results:

A renderer is attached to each object, and each renderer can have an Effect on it. This is one simple renderer:

At the end of the main Update, the RenderingManager takes in all the visible and active renderers in the scene in a method called SetupRendering. This places all the renderers in a RenderBatch, which is no more than a class that holds an Effect and a list of Renderers. Renderers are placed into RenderBatches that have the same shader. This solves my problem of placing objects with the same Effects together automatically: simply set the renderer’s Effect in the object when necessary, and the RenderingManager will handle it at all.

In the main Draw, the RenderingManager’s PerformRendering method is called, which goes through each RenderBatch, starting a SpriteBatch.Begin() call for each with the Effect applied, then calls Render() on all the renderers in that batch. It calls SpriteBatch.End() after it’s done and repeats for the rest of the RenderBatches.

So far I tested with several different shaders and didn’t notice any problems. Thanks a lot for your help, and I’d love to hear your thoughts!