Swap Chains

JP Bridges

Newcomer
Joined
Jul 19, 2003
Messages
11
Folks,

I'm trying to get D3D views running in multiple panels of my windows forms application.

I derive a class from panel and set as a member variable a SwapChain object. I then pass a valid D3D Device to this class in the constructor and steal it's present parameters (changing DeviceWindow and DeviceWindow handle to this and this->Handle respectively).

This works fine and I can create multiple instances of these controls all of which display valid D3D data.

I do the drawing (currently only Device::Clear) in the panels Paint event handler, this works great.

However when I resize the window things go pear shaped and I get an exception thrown saying the swap chain is trying to dispose itself.

Now I guess that the swap chain is destroying itself because the back buffer size no longer matches the size of the control. Which is fine.

However I can't seem to handle this gracefully, I mean do I try and watch if the SwapChain::Disposed property is true, and if it is then create a whole new SwapChain, or is there a better way to do it... the documentation seems to offer no clues.

Anybody?

JP :confused:
 
Upon resize, the swap chain will dispose itself (at least, I've noticed). And I haven't found any way around re-creating it. However, resizing should occur relatively infrequently, so I can't see this as much of a problem, performance-wise.

As for the back buffer, you will have to make sure that your actual viewport (say, a PictureBox, if that's what you're rendering to), its backbuffer(s), and all related surfaces are of the same size when you perform the Clear(), or it will die a painful death. If Clear() is throwing exceptions, this is usually the problem.

It's late, so take those last 2 paragraphs w/ a grain of salt. If you're still having trouble, I'll post some code here later. (I'm doing a 3D animation app right now which has 4 ortho and perspective viewports. It's a 4-th year University project that I'll be GPL'ing in August. Hopefully it will help people out with Managed Direct3D, since the docs...uh... don't exist. :) )
 
Yeah thanks Steve, it's sorted.

Upon a resize the D3D Device resets itself causing a chain reaction that causes the swap chains to flag themselves for disposal.

The problem I had was that I wasn't handling this at the right time and it was messing things up.

One of the things that did have me worried though was that this was happenning multiple times when the window was resized via dragging... and the recreation of the swap chains seemed to take forever.

An hour or so passes with me trying to work out how to make it faster.

Then I decide to run the application in Release mode and it runs like lightning.

Moral of the story... debug mode with Managed DX (or managed stuff in general?) is VERY VERY slow, if things seem to take an unacceptably long time to run in code then, before panicking, it's well worth compiling a release version to see if you will get acceptable speeds because there is a HUGE difference.

JP
 
Okay although I make no claim to tidy or well ordered code here you go...

First step is of course correctly setting up a D3D device, which I assume you've done.

Next you have to set up your swap chain. In the code below ViewPanel is inherited from Panel and has a swap chain associated with it. pSwapChain is a private member of ViewPanel.

Code:
  Void ViewPanel::InitializeSwapChain(DirectX::Direct3D::Device * pDev)
    {
    // Create a new set of presentation parameters based on our main Direct3D device.
    // Point the new presentation parameters at the panel, and then create a new swap chain.    
    PresentParameters * pp = new PresentParameters(pDev->PresentationParameters);
    pp->DeviceWindow       = this;
    pp->DeviceWindowHandle = this->Handle;  

    try
      {
      pSwapChain = new SwapChain(pDev, pp);
      }
    catch(InvalidCallException * dxCatch)
      {
      Console::WriteLine(dxCatch->ToString());
      }

    } // End METHOD InitializeSwapChain

Now to test this I used the Paint event of the panel...
I had to put a test in to make sure we didn't try to use the swap chain if it was marked for garbage collection because it was fairly bad karma if we did (see original post).

Code:
  Void ViewPanel::OnPaintBackground(Object * pObject, PaintEventArgs * pea)
    {
    DirectX::Direct3D::Surface * pSurf;

    // If the swap chain is marked for disposal, due to a device reset or something, then don't
    // do anything.
    if(pSwapChain->Disposed)
      {
      return;
      }

    pSurf = pSwapChain->GetBackBuffer(0, BackBufferType::Mono);

    pSurf->Device->SetRenderTarget(0, pSurf);
    pSurf->Device->Clear(ClearFlags::Target, Drawing::Color::Black, 0.0f, 0);
    pSurf->Device->EndScene();
    pSwapChain->Present();

    } // End EVENT OnPaintBackground

Now the only other thing is that you have to remember to handle the Reset event for your main D3D device.

When a device is reset you must re-initialise all of your swap chains again using the first method I posted. In this code I keep track of my swap chains by keeping an ArrayList of pointers to the active ViewPanels... although I'm sure there are better ways to do this.

Hope this helps...

JP
 
JP,

I know you are probably going to want to shoot me, but I can't seem to get this to work...

Is there anyway I could see your Device Presentation Parameters?

It is as though my SetRenderTarget call is not working...

One thing though, is that I wasn't assigning the new DeviceWindowHandle... I was just assigning the DeviceWindow.

That seemed to clear up creating the new Swap Chain...

I am supposed to call BeginScene, EndScene, and Present off of the base Device and not the SwapChain.Device right? (don't they both referred to the same instance anyways?)

What's happening is that when I call ClearScreen it is just renderes the plain Green and Pinkish screens that get rendered if you don't clear the back buffer.... But I am...

device.Clear(ClearFlags.Target | ClearFlags.ZBuffer, color, 1.0f, 0);

I have also tried:

swapChain.Device.Clear(ClearFlags.Target | ClearFlags.ZBuffer, System.Drawing.Color.Turquoise, 1.0f, 0);
It is not bueno.

Anyhoo...

I am doing this in C# as well...

But I can not see any differences in our code...

Thanks again.
 
My PresentParams and device creation method call is as follows...

Code:
    // Basic implementation of Direct X... change this later.
    PresentParameters * pp = new PresentParameters;    

    pp->Windowed         = true;
    pp->BackBufferCount  = 1;
    pp->BackBufferWidth  = 0;
    pp->BackBufferHeight = 0;
    pp->BackBufferFormat = Format::Unknown;
    pp->EnableAutoDepthStencil = false;
    pp->DeviceWindow      = this;
    pp->DeviceWindowHandle = this->Handle;
    pp->PresentFlag  = PresentFlag::None;
    pp->FullScreenRefreshRateInHz = 0;
    pp->MultiSample = MultiSampleType::None;
    pp->PresentationInterval = PresentInterval::Default;
    pp->SwapEffect = SwapEffect::Discard;
 
   
    // Create our device based on the presentation parameters.
    pDev = new Microsoft::DirectX::Direct3D::Device(0, DeviceType::Hardware, this->Handle,
                                                    CreateFlags::SoftwareVertexProcessing, pp);

This is called from within the main applications constructor so 'this' refers to the main windows form. As far as I remember this was pretty much the bare minimum I could get away with without the Device call failing.

The reason I assigned both the DeviceWindow and the DeviceWindowHandle was that I was having trouble getting the swap chain to associate with the panel. I agree that one or the other should do the job and you probably don't/shouldn't need both.

If you're still having trouble feel free to email and I'll send you some source files...

JP
*still grumbling that MS still haven't published any significant documentation for this stuff*
 
Thanks again,


No bueno on the Present Parameters....

Was I correct about calling Device.Present instead of swapchain.Present?

And do you set the RenderTarget before or after x.BeginScene()?
 
As far as I understand you present the swap chain since that is actually your off screen surface...

My drawing loop looks like this... (in C#ish lingo)

Code:
pSurf = pSwapChain.GetBackBuffer(0, BackBufferType.Mono);

pSurf.Device.SetRenderTarget(0, pSurf);
pSurf.Device.Clear(ClearFlags.Target, Drawing.Color.Black, 0.0f, 0);

pSurf.Device.BeginScene(); 
pSurf.Device.EndScene();
pSwapChain.Present();

pSurf device is just a pointer to the main graphics device so don't let it confuse you.

JP
 
Thanks again for your help...


Still no luck..
Here's my code. (C#)



SwapChain swapChain = new SwapChain(deviceManager.Device, this.presentParameters);


// Tried setting the depthStencil as well.
Surface depthStencil =
deviceManager.Device.CreateDepthStencilSurface(
Parent.Width,
Parent.Height,
deviceManager.Device.PresentationParameters.AutoDepthStencilFormat,
deviceManager.Device.PresentationParameters.MultiSample,
deviceManager.Device.PresentationParameters.MultiSampleQuality,
true );


Surface renderSurface = swapChain.GetBackBuffer(0, BackBufferType.Mono);

deviceManager.Device.SetRenderTarget(0, renderSurface);
// deviceManager.Device.DepthStencilSurface = depthStencil;

renderSurface.Device.BeginScene();

renderSurface.Device.Clear(ClearFlags.Target | ClearFlags.ZBuffer, System.Drawing.Color.Turquoise, 1.0f, 0);


renderSurface.Device.EndScene();
renderSurface.Device.Present();


P.S How do you insert code and make it white?
 
Use the code tag... have a look at the help next to vB Code, it's explianed there.

As for your code I don't know what's wrong. My experience with DX9 (Managed) so far has been mostly frustration, things don't seem to work unless you get them just right.

The lack of documentation is terrible too.

One thing you may want to try though is looking in your debug window while you are running particular function calls... often it'll flash up messages there that you'd otherwise miss.

JP
 
elikakohen,

I feel your pain - Managed DX swap chains are no fun. The C# project I spoke of earlier in this thread is GPL'd at: midget3d.com.

Download the source and have a look at DeviceManager.cs. It's a tad archaic due to Ryan's firm belief in building everything with design patterns (which worked out really well for us, in the end). If you have any questions, feel free to ask. Also, if you feel you could improve Midget - our feelings won't be hurt if you send us some code. ;)

Good luck!
 
steved,

I have pulled that application to shreds...

We seem to be doing everything the same except for the patterns we are using.

Right now, all I am trying to get to work is a single window with two panel controls in it.

I want to clear one panel to Turqouise and the other to WhiteSmoke. =)

Can't get it to work at all.

Sounds pretty simple I guess.

But what I want to do after that is to use multiple viewports with each SwapChain.

I am not exactly certain what ViewPorts do at this point. But I have a good idea.

Hopefully its a Device->SwapChins->ViewPorts heirarchy.

If not, then I am totally confused.

Anyhoo.

I can strip my code down to show you what I have.

Let me know if you want me to post it.
 
Feel free to post it, it might give me a clearer idea of how our code is differing. From your description, I'm confused as to what could be going wrong.

(Sorry for the late reply - haven't been on in a while. :) )
 
Back
Top