My first impression with porting beta 1 to beta 2

At the time I began writing this it was 2:13am. I happily finished downloading XNA beta 2 at 11pm and have spent the last 3 hours porting my beta 1 code to beta 2. Most of it has been a fairly painless process however there are a few key areas which have been a huge pain in the rear.

To start I had 3 applications and 2 libraries to port:

1) DEMO_FractalViewer (app) - win32 app for testing various fractal terrain generation algorithms
2) DEMO_PlanetViewer (app) - win32 app for testing the procedural generation of planets
3) DEMO_Sandbox (app) - XNA game application for testing my deferred shading engine
4) Engine.Graphics (dll) - deferred shading engine along w/ everything else gfx oriented
5) Engine.Math (dll) - port of XNA's math library to double's (needed for generating planets)

To start I tackled Engine.Math as I figured it would be the simplest to port. It was as the only problem I had was the removal of the A, B, C, and D properties from XNA.Framework.Plane. Not a big deal and it was fixed within a few minutes. I then ported DEMO_Sandbox to the new beta 2 way of doing things which again only took a few minutes. Then the nightmare began...

Since my engine is built around deferred shading it relies heavily on using multiple render targets. I started by changing all of my Texture2D's to RenderTarget2D and all of my GetSurfaceLevel()'s to GetTexture(). My first problem was trying to figure out how to store a pointer to the backbuffer. I decided to call m_device.GetRenderTarget(0) as I assumed that the backbuffer must be a RenderTarget2D since there's no other way to get at it. This quickly exploded with a NullReferenceException as I had been using my pointer to the backbuffer immediately thereafter to get at it's Width and Height for generating the rest of the render targets. I investigated every possibly way I could think of to retrieve the backbuffer but alas it was all for naught. I resorted to using m_device.Viewport.Width and m_device.Viewport.Height and just left my backbuffer pointer null.

The next problem was an InvalidOperationException when calling GetTexture() after the first rendering pass. I was totally stumped with this one. After going through the GraphicsDevice docs for a few minutes I had a hunch I needed to call ResolveRenderTarget(). Conveniently the documentation for ResolveRenderTarget() informs you that the function "Resolves the render target at the specified index". I had kind of figured that out from the function header but I decided I'd give it a shot as I was otherwise clueless. After putting a call to ResolveRenderTarget() immediately after every call to SetRenderTarget() I was pleasantly surprised to see it solved the problem - despite the fact it was extremely annoying to find all of my calls to SetRenderTarget() and put a call to ResolveRenderTarget() afterward as there were quite a few.

This initial success was quickly destroyed by the fact that you cannot call SetRenderTarget(0, target) with target being null. I already knew this from beta 1 and DirectX but since I had no idea how I was supposed to set the render target back to the backbuffer without being able to get a pointer to it I figured I'd give it a shot and see what happens. At this point I said "screw porting the deferred shading" and just commented it out so I could focus on my other 2 applications and then come back to it later.

Porting my other 2 applications was extremely short lived. There was almost nothing that needed to be ported however I was stopped dead by one thing: BeginScene() and EndScene() were removed from GraphicsDevice. Both DEMO_*Viewer applications were standard C# form applications where I was using XNA to render into a panel within a splitter container. I'll be honest I was quite incredulous. I couldn't (and still can't) believe BeginScene() and EndScene() were removed. I searched and searched for a possible solution but came up empty handed. In the end I decided to just comment it out and see what happens. I never made it that far. I get an InvalidOperationException as soon as I try to create an index buffer (after making no changes to how the device is created from beta 1). All of the initialization is done in the constructor of the form after InitializeComponent() is called. At this point I decided I was done. It's getting late and I figure i'll just tell my tale of woe and see if anyone else is having similar problems :p.

So - to sum all of this up:

1) What are ResolveBackBuffer() and ResolveRenderTarget() for? The documentation restates their names and is of absolutely no help. I did read on a thread by someone else that you're supposed to call ResolveRenderTarget() after you're done rendering to it? I am currently calling it immediately after calling SetRenderTarget() and since I havn't been able to get past a null backbuffer I never got to a point where it would matter.

2) How do I set render target 0 back to the back buffer if I cannot get a pointer to it? I noticed getting at the SwapChain has also been removed so definitely stumped with this one atm.

3) VertexElementUsage.PositionTransformed is gone. I am using VertexElementUsage.Position instead and my initial assumption is it shouldn't matter but can anyone confirm that? I havn't been able to test it because of the aforementioned problems.

4) The removal of BeginScene() and EndScene(). Is this the death of all win32 apps that want to use XNA to render in a child control?

5) What are GraphicsDevice.Textures and GraphicsDevice.VertexTextures for? There is 0 documentation on it other than the fact it returns a TextureCollection and I was just curious :p (though i'll admit I havn't tried playing with it yet).

6) If render targets have to be recreated every time the device is reset then why do we have to manually recreate RenderTarget2D's ourselves? It takes a pointer to a GraphicsDevice in its constructor so if it's universal functionality why can't it register with the event and recreate its internal Texture2D itself? I apologise if this is in fact how it works but the documentation provides little information on this other than that it needs to be recreated after the device is lost.

I apologise if any of this is in the documentation and I missed it. I just wanted to post this now because it is an uncontaminated version of my initial 3 hour impression of beta 2 and I figured waiting until I had more time would, for better or for worse, skew it a bit. Unfortunately I havn't gotten to testing the content pipeline yet so i'll write another "first impressions" thread when I get to it. Hopefully this post can help people who are experiencing similar obstacles while porting their own code.

[6853 byte] By [KeithNewton] at [2007-12-27]
# 1
Keith Newton wrote:

1) What are ResolveBackBuffer() and ResolveRenderTarget() for? The documentation restates their names and is of absolutely no help. I did read on a thread by someone else that you're supposed to call ResolveRenderTarget() after you're done rendering to it? I am currently calling it immediately after calling SetRenderTarget() and since I havn't been able to get past a null backbuffer I never got to a point where it would matter.

I don't know what these are, and yes the documentation is of no help on the subject. I hope someone at Microsoft does a writeup on how to use this functionality sooner rather than later.

DerekNedelman at 2007-9-4 > top of Msdn Tech,Game Technologies: DirectX, XNA, XACT, etc.,XNA Framework...
# 2

Rendering to a texture can be done like this:

// in LoadGraphicsContent, initialize the render target
rt = new RenderTarget2D(graphics.GraphicsDevice, 320, 200, 1, SurfaceFormat.Color, MultiSampleType.None, 0);

// In UnloadGraphicsContent, dispose of the render target. A render target
// is created in the Default pool in D3D9 (ResourceManagementMode.Manual)
// so it needs to be disposed manually like this.
rt.Dispose();

// In the Draw method, set the render target
graphics.GraphicsDevice.SetRenderTarget(0, rt);
graphics.GraphicsDevice.Clear(
Color.CornflowerBlue);

// Draw stuff
testBatch.Begin();
testBatch.Draw(sharky,
new Vector2(0, 0), Color.White);
testBatch.End();

// Resolve it and set it to null to get the backbuffer back
// (QUESTION: how does this work with MRTs?)
graphics.GraphicsDevice.ResolveRenderTarget(0);
graphics.GraphicsDevice.SetRenderTarget(0,
null);

// Draw as normal, using the texture associated with the rt
// (QUESTION: doesn't this texture have to be disposed after use?)

graphics.GraphicsDevice.Clear(Color.Red);
testBatch.Begin();
testBatch.Draw(rt.GetTexture(),
new Vector2(0, 0), Color.White);
testBatch.End();

RimvanWersch at 2007-9-4 > top of Msdn Tech,Game Technologies: DirectX, XNA, XACT, etc.,XNA Framework...
# 3

And how do you copy from one RenderTarget2D to another?

And how do you do your own sprite/screen quad class without many redundant (and seemingly uneccesary) vertex functions?

X-Tatic at 2007-9-4 > top of Msdn Tech,Game Technologies: DirectX, XNA, XACT, etc.,XNA Framework...
# 4
The builtin backbuffer is no longer directly accessible: just set null if you want to go back to rendering onto it after using a custom rendertarget.

The easiest way to look up the current screen size is via Viewport.Width and Height, as you figured out.

The XNA pattern for rendering to textures is:

- Set render target(s)
- Draw
- Resolve
- Reset render target
- Now you can call GetTexture on the rendertarget you just drew onto

You may be lucky getting this to work calling Resolve immediately after setting the rendertarget, but that won't work on Xbox or if you are using a multisampled rendertarget on Windows.

> This initial success was quickly destroyed by the fact that you

cannot
> call SetRenderTarget(0, target) with target being null.

Really? That should work. If you can't get it going please file a bug over on connect.

You shouldn't need BeginScene and EndScene for anything: they actually don't even do anything in native D3D most of the time! Just draw stuff, then call Present when you're done rendering your scene.
ShawnHargreaves-MSFT at 2007-9-4 > top of Msdn Tech,Game Technologies: DirectX, XNA, XACT, etc.,XNA Framework...
# 5

Thanks Shawn I appreciate the response. I discovered the reason calling SetRenderTarget() with the render target being set to null was failing is because I was calling ResolveRenderTarget() immediately afterward - though in beta 1 when I did this it threw an exception. Moving ResolveRenderTarget() to the end of the rendering pass, immediately before the next call to SetRenderTarget(), while not calling it after rendering to the back buffer fixed all of my problems. I removed BeginScene() and EndScene() and as you said it would it worked just fine. I'll admit I was a bit skeptical at first, probably due to my experiences with DirectX, but in the end I can see how this makes sense. To fix my problems with pre-transformed vertices being removed I changed my screen coordinates to be in homogenous clip space and it seems to do the trick when rendering a full screen quad (if you see something wrong please let me know):

float xOff = 1.0f / targetWidth;

float yOff = 1.0f / targetHeight;

ScreenVert[] vert = new ScreenVert[4];

vert[0].m_pos = new Vector4(-1.0f - xOff, 1.0f + yOff, 0.0f, 1.0f);

vert[0].m_texCoords = new Vector2(0.0f, 0.0f);

vert[1].m_pos = new Vector4(1.0f - xOff, 1.0f + yOff, 0.0f, 1.0f);

vert[1].m_texCoords = new Vector2(1.0f, 0.0f);

vert[2].m_pos = new Vector4(-1.0f - xOff, -1.0f + yOff, 0.0f, 1.0f);

vert[2].m_texCoords = new Vector2(0.0f, 1.0f);

vert[3].m_pos = new Vector4(1.0f - xOff, -1.0f + yOff, 0.0f, 1.0f);

vert[3].m_texCoords = new Vector2(1.0f, 1.0f);

All in all these fixes only took me a few minutes once I found out what was wrong. The total time it took to port beta 1 to beta 2, if I had known these things beforehand, would have been extremely small. I hope this helps for anybody else out there having similar problems =). Time to check out the content pipeline!

KeithNewton at 2007-9-4 > top of Msdn Tech,Game Technologies: DirectX, XNA, XACT, etc.,XNA Framework...
# 6

Hmm, thanks for enlightening us.

Now we can render to a RenderTarget2D, but how can this actually be applied? And can you even draw directly to, say, a Texture2D or associate a RenderTarget2D with a Texture2D?

Uhhh..?

Bapa at 2007-9-4 > top of Msdn Tech,Game Technologies: DirectX, XNA, XACT, etc.,XNA Framework...
# 7
Bapa wrote:

Hmm, thanks for enlightening us.

Now we can render to a RenderTarget2D, but how can this actually be applied? And can you even draw directly to, say, a Texture2D or associate a RenderTarget2D with a Texture2D?

Uhhh..?

If you are struggling you can think of a RenderTarget2D as a Surface and a Texture2D together.
When you have rendered to you RenderTarget2D with device.SetRenderTarget() then device.ResolveTarget(n) you can call renderTarget2D.GetTexture()

X-Tatic at 2007-9-4 > top of Msdn Tech,Game Technologies: DirectX, XNA, XACT, etc.,XNA Framework...
# 8

But does the method GetTexture() not return a Texture2D? How do you associate the RenderTarget with a Texture2D?

....or am I misunderstanding this completely?

Bapa at 2007-9-4 > top of Msdn Tech,Game Technologies: DirectX, XNA, XACT, etc.,XNA Framework...
# 9
You are thinking of it the wrong way around. Rendering to a RenderTarget2D produces a Texture2D internally, GetTexture() retreives it (the old way YOU would create the Texture2D and render to its surface).
X-Tatic at 2007-9-4 > top of Msdn Tech,Game Technologies: DirectX, XNA, XACT, etc.,XNA Framework...
# 10

OH. Thanks!

So then I can say: thisTexuture2D = thisRenderTarget.GetTexture()?

Yay!

Bapa at 2007-9-4 > top of Msdn Tech,Game Technologies: DirectX, XNA, XACT, etc.,XNA Framework...