DrawTargetScreen, DrawTargetTexture2D and Scenegraphs

Feb 3, 2010 at 12:28 PM

Hi StatusUnknown,

I'm now trying to implement some custom shaders that use multiple DrawTargetTexture2D instances to render a Sun effect as you will find here: http://www.youtube.com/watch?v=gkV3raj93_o

The original implementation on Xna used multiple RenderTarget instances so I decided to use DrawTargetTexture2D for my own implementation.

I however have a problem certainly due some misunderstanding on how to use DrawTargetTexture2D and my own scene graph.

The rendering pipeline in my project would look similar to:

  • MyApplication.Initialize : inherits from Xen.Application
    • Creates a DrawTargetScreen instance and adds a GameState instance (GameStates allow me to subdivided my application logic and rendering depending on which state my game is (Introduction, Mainmenu or playing for instance))
  • MyApplication.Draw(state)
    • GameState.Draw(state)
      • SceneManager.Draw(state) : the SceneManager holds my game entities
        • Sun.Draw(state)

Within my Sun class, which inherits from IDraw, I am simply rendering a Xen.Ex.Geometry.Sphere instance with a Xen.Ex.Material.SolidColourFillShader binded shader.
I also added a DrawTargetTexture2D directly within the Sun class and implemented my drawing code as explained in the Tutorials.

Now that is when my problem occurs: when my Sun tries to draw using the DrawTargetTexture2D, it throws an exception saying that a DrawTarget is already in use.

That seems pretty logic as all my scene graph receives his draw calls from the DrawTargetScreen.Draw method that is called from my Application instance.

It seems that I cannot implement such render to texture code within my Scene entities rendering code and would have to attach my entity to a DrawTargetTexture2D instance managed at the Application level.

Is it really the case? Do you see any other way to use Xen to perform such thing?

Thanks

Philippe

Coordinator
Feb 3, 2010 at 1:57 PM
Edited Feb 3, 2010 at 2:39 PM

For all but a few rare exceptions, all rendering to a render target (screen included) should happen in one go. This is especially true on the Xbox.
By design, xen does not allow you to call Draw() on a DrawTarget while drawing another DrawTarget. This wasn't an accident, I very specifically designed it that way.

However, it is a valid thing to want to do - it's not a good way to go about it. A good example is the particle system - these require rendering to render target(s) each frame. However it would be very bad design if they did this rendering in the middle of another draw target process.

For such a case, there is a work around. But you should obviously be careful:

Both DrawState and UpdateState have a method called 'PreFrameDraw'. This method is very simple, it simply takes in an IDraw object and adds it to a list. At the beginning of the next rendered frame - before Draw() is called on the main application - each item in the list will have Draw() called, and the list will be cleared. That is; if you call PreFrameDraw(), then the object you pass in will have Draw() called at the start of the next frame.

Note however, if you call this 60 times per second (ie, from an 60hz Update loop) and the frame rate is 20fps, then it will draw the object three times each frame. This is actually desirable thing in the case of the particle system. It also helps with other things like cache and such.

However I'd also suggest having a think about your design. It may be there is a more elegant design that would solve the problem (such as implementing the pattern yourself in a more controlled manner). There may not either.

Feb 3, 2010 at 3:29 PM

I thank you for your quick reply and I totally understand your design decisions which make the API clear, clean and clever ;)

Now, that brings me back to my own design... Which direction would you recommend?

Avoiding multiple rendering passes in my scenegraph directly, how would a smart developer manage that using Xen?

Philippe

Feb 3, 2010 at 9:02 PM

Why not draw the Sun before the draw to screen is called and stuff it into a texture, as you draw an opaque mask anyway as part of the crepescular rays you can use that to obscure the Sun in texture you draw to. You end with only the parts of the Sun you could actually see in your scene in the texture.

Then you can draw your scene without the Sun and blend the Sun texture back over the top. The Sun should then obscure any objects that were behind it but not any that were in front of it because you already masked them.

Feb 4, 2010 at 10:21 AM

Hi Feckinotter,

As Statusunknown mentioned, my issue isn't really on how I can succeed rendering the Sun to get the result I'm looking for but really on my game engine design. I need to find out the best way to integrate my entities rendering pipeline within the constraints of the Xen design.

If I solve the issue I have with my Sun class, I will certainly meet other issues with other entities.

My first research on the topic concludes to manage multiple rendering passes at the Application level by creating a DrawTargetScreen instance to render to the screen as usual and add a set of DrawTargetTexture2D instances that can be used inside the Application.Draw call by submitting the IDraw objects each frame they need to be rendered.

Therefore, I would have a Scene graph containing all my objects and updating themselves every frame and if the CullTest is successfull, I would add them to the DrawTargets to be renderered.

The only constraint for me to use such design is the time required to populate the DrawTarget instances each frame.

I'm about to test it but maybe Statusunknown or one of you could tell me if that would be expensive CPU wise?

Philippe

Coordinator
Feb 4, 2010 at 10:38 AM

"The only constraint for me to use such design is the time required to populate the DrawTarget instances each frame."

The one thing I'll mention: You don't need to add everything to a draw target. It's perfectly OK to have a single proxy object added to the target which performs logic to decide what needs drawing itself.
This is why the draw target is so restrictive in how you can manipulate it's list, because ideally you don't.

In general, do what is the best fit for your game, not what is theoretically the fastest.

Feb 4, 2010 at 2:01 PM
Edited Feb 4, 2010 at 2:02 PM

I knew about it StatusUnknown but I was mentionning this as a potential (not proven) technical design for my engine to allow me to render my game entities using DrawTargetScreen and DrawTargetTexture2D instances.

For instance:

 

public class Sun : ISceneNode, IUpdate // The game entity that is updated by my SceneManager class
{
     // Maintains entity properties for game play, positions, rotations etc...
}

public class SunRenderer : IDraw
{
private Sun _SunGameEntity; // a valid Sun instance

public void Draw(DrawState state) { if(state.DrawTarget is DrawTargetTexture2D) // Perform the special effects rendering to the given DrawTargetTexture2D if(state.DrawTarget is DrawTargetScreen) // Render final compositing getting textures from my DrawTargetTexture2D instances. } } public class MyXenApplication : Xen.Application { DrawTargetScreen _RenderToScreen; // Final scene compositing and rendering DrawTargetTexture2D[] _RenderToTextures; // Stack of DrawTargetTexture2D instances that Entity renderers can use to render to texture public override void Draw(DrawState state) { for(int i = 0; i < _RenderToTexture.Lenght; i++) _RenderToTexture[i].Draw(state); _RenderToScreen.Draw(state); } }

 

This is just an idea on how I could achieve it... Does it makes sense?

Philippe

Coordinator
Feb 4, 2010 at 3:55 PM

That's certainly a possibility, but it would easily break (eg, if you started rendering the scene to a texture, for post processing effects, etc).

There is nothing stopping you creating your own interface, ie, IDrawPreProcess, or something like that. It's a possibility.