The RenderTargetRenderer class can be used to greatly increase the performance of FlatRedBall games which present complex scenes which never or rarely change. For static scenes a RenderTargetRenderer allows for near infinite complexity - specifically it allows for a very large number of visual elements to be drawn with almost no slowdown. Also, the RenderTargetRenderer is an easy way to render to a RenderTarget for post-processing.
This example will show how to render a very large number of Entities with essentially no slowdown. To set up the project:
Create a new Glue project
Import the following Entity: File:GraphicWithText.entz
Create a new Screen. I'll call mine GameScreen
Before we add anything to the game we'll want to turn off any framerate throttling so we can see actual performance differences. To do this open Game1.cs and add the following code to the Game1 constructor:
Next we'll add code to our GameScreen. To do this, open the GameScreen.cs file and modify the CustomInitialize and CustomActivity methods as follows:
Switch to Glue
Right-click on the Objects folder under the GameScreen
Select "Add Object"
Verify "FlatRedBall or Custom Type" is selected
Select the Sprite type
Call the Sprite RenderTargetSprite
Now we can switch to code to create a Texture2D for the Sprite. To do this, replace the CustomInitialize function with the following:
The example above shows how to perform a single render for all visuals and then to reuse that in a regular FRB Sprite for maximum performance. Of course, if the entities were being created only for a single render then we would want to destroy the entities after the render was finished. The CustomInitialize would be modified as follows:
The immediate destruction of the entities does not necessarily boost frame rate, but it does remove objects from the game which can reduce ram usage.
The ReRender function allows a render target to render itself again to the texture. The ReRender function has a number of conveniences built-in:
The initialization code does not need to be run again - the size of the texture, content managers, and name of the texture are all preserved
The Texture2D reference remains valid, so it does not need to be re-assigned
Objects do not need to be re-added to the RenderTargetRenderer's Layer - they are preserved.
The full code example for using ReRender is as follows:
The examples above show how to create entity instances and manually place them on the RenderTargetRenderer Layer. These examples illustrate how to work with the RenderTargetRenderer, but they do not reflect real Glue scenarios where objects may be defined in Glue or in files (such as Tiled TMX files) and added to Layers also created in Glue. Fortunately existing Layers can be added to RenderTargetRenderer instances. The following example code shows how to take a LayerInstance defined in Glue and to add it to the RenderTargetRenderer:
Now anything that is added to that Layer will be part of the RenderTargetRenderer. For more information see the FlatRedBall.SpriteManager.RemoveLayer and FlatRedBall.Camera.AddLayer pages.
This results in the game running around 7 frames per second (on my hardware at the time of this writing): Notice that the entities are converted to being manually updated, which reduces the update load significantly. The low frame rate in this case comes almost purely from rendering. For more information on ConvertToManuallyUpdated, see this page. In this case we'll assume that once the Entities have been placed they will not need to move or change otherwise. We can therefore take a "snapshot" of the screen using the RenderTargetRenderer and improve the performance significantly. Since the RenderTargetRender only creates a Texture2D, we will need to create an object to display it. To do this:
The result is a much higher framerate which fluctuated into the 2000's:
Since all objects receive a full update after rotation, updates can be expensive. However once updates are complete the game resumes at full framerate: