I'm making a 2D, top-down platformer, in which four or five layers are rendered each time the game draws. Everything is fairly performant except for the bottom layer, which is the game map. This is because the map uses a modified version of this effect to draw shadows under the solid objects on the map.
The effect provides different options for the light attenuation and edge blending, which can make pretty realistic looking shadows. I originally had it blurring edges as much as possible, but not attenuating light.
In my game, only a small portion of the screen is initially visible (most of the rest of the screen is black), so I was able to set the size on the components used to create the shadows to very small. Ideally, however, I would like the visible size to be able to grow/shrink - meaning I can't set the size for these components and rendertargets once, unless I set them to the full size of the window. Notice that in the demonstration of the effect, they have a fixed size for the visible region.
Here's where the problem comes in. If I use edge blending on the entire visible screen, the FPS dies - especially since it's fullscreen.
I've been trying to think of different approaches to mitigate this. So far I've come up with:
- Disabling the edge blending on shadows - this is what we're currently doing as a workaround. The original effect is performant enough to draw hard-edged shadows without dropping frames noticeably
- Multi-threading - I took a look at my processor usage and noticed that although I have eight cores in my dev machine, only one was being used significantly by my application. If it's possible to offload some of the work being done to render shadows, that'd be great. The problem is that currently, I am rendering everything to the same target (null, or the default target). What I thought of doing is rendering each layer to a separate render target and threading all of that - the divide and conquer approach. The problem is that I believe you can only have one active render target at a time, since you can only have one
GraphicsDevice. I'm a bit of a newbie concerning XNA's finer points and threading in C#. Would it be possible to lock the
GraphicsDeviceinstance so only one thread is using it at a time? Then I could just try to make sure that each layer is performing all
[spritebatch].Draw()calls in as concentrated of a space as possible. I'm not sure if this would help, though, since the most time-consuming function call is
- Only rendering the effect on a portion of the screen - I didn't have a very fully-formed idea here. But it would be nice to be able to only apply the effect on a specific
Rectangle. That way I could set the size of the components to the whole screen, and just pass in the necessary area that is actually visible at the time of rendering. Ultimately, though, this wouldn't be a great solution, since the visible area in my game has no theoretical upper bound.
- Rendering multiple effects at once - I noticed that the effect I linked to has to render six times each time it draws the shadows (if you use edge blending). The first time is to distort the image and compute the distance to each point (see the link for a more in-depth explanation), then it applies a horizontal reduction, then it actually draws the shadows with the selected attenuation settings, then it blurs them horizontally, and finally, it blurs them vertically. Not only does this mean setting up six identical sets of vertices for a triangle list, but it also means applying six separate effects for six separate
DrawUserIndexedPrimitivescalls. Is there some way to apply multiple effects to a single draw call?
I'm open to any other suggestions, too - or any references you have which may help optimize and improve my game's performance.