Sign in to follow this  
Dom_152

Swap chain hell

Recommended Posts

Currently I'm using Direct3Ds swap chains to allow multiple windows to be rendered to. It all worked well up until the point where I decided I needed to handle the resizing of the swap chains backbufferr when the window resized. So I am now creating the Direct3D device with a dummy 1x1 window which isn't shown. Then for each proper rendering window I create a new swap chain. My first problem is that now I've done this the screen doesn't seem to be clearing properly or something because as the polygon on the screen rotates it leaves a nasty trial then disappears. Here is the code I use for creating the dummy window for the Direct3D device:
HWND hWnd = CreateWindowA("dfWndClass", "DFGE", WS_POPUP, 0, 0, 1, 1, NULL, NULL, 0, NULL);

//Clear out the Presentation Paremeters structure
ZeroMemory(&m_d3d9PP, sizeof(m_d3d9PP));
//And fill it out with dummy settings
*Taken out general presentation paremeters stuff*
m_d3d9PP.BackBufferWidth = 1;
m_d3d9PP.BackBufferHeight = 1;
m_d3d9PP.hDeviceWindow = hWnd;				

m_d3d9->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, hWnd, D3DCREATE_HARDWARE_VERTEXPROCESSING, &m_d3d9PP, &m_d3d9Device);

And later on in the same function I create the swap chain for the real window:
m_d3d9->GetDeviceCaps(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, &m_d3d9Caps);

//Clear out the Presentation Paremeters structure
ZeroMemory(&m_d3d9PP, sizeof(m_d3d9PP));
//And fill it out with proper settings
m_d3d9PP.Windowed = !settings.fullscreen;
m_d3d9PP.BackBufferCount = 1;
*Taken out presentation paremeters stuff*
Pos2D<int> size = RootPtr->GetPlatform()->GetDFWindowSize(rWindow->GetRenderWindow());
m_d3d9PP.BackBufferWidth = size.X;
m_d3d9PP.BackBufferHeight = size.Y;		

m_clearColour = D3DCOLOR_ARGB(0, 0, 0, 1);

m_activeWindow = rWindow->GetRenderWindow();

//Windowed so create a swapchain for the window
if(!settings.fullscreen)
{
	LPDIRECT3DSWAPCHAIN9 swapChain = 0;
	LPDIRECT3DSURFACE9 backBuffer, depthStencilBuffer  =NULL;

	if(FAILED(m_d3d9Device->CreateAdditionalSwapChain(&m_d3d9PP, &swapChain)))
	{
		RootPtr->GetLogger()->Log("Could not create swapchain for window!", EMT_ERROR);
		return false;
	}		
				
	m_swapChains.push_back(swapChain);
	m_numSwapChains++;

	if(FAILED(m_swapChains.at(0)->GetBackBuffer(0, D3DBACKBUFFER_TYPE_MONO, &backBuffer)))
		return false;

	m_d3d9Device->SetRenderTarget(0, backBuffer);		

	//Create depth/stencil buffer for this swapchain
	m_d3d9Device->CreateDepthStencilSurface(m_d3d9PP.BackBufferWidth, m_d3d9PP.BackBufferHeight, m_d3d9PP.AutoDepthStencilFormat, m_d3d9PP.MultiSampleType, 
																				m_d3d9PP.MultiSampleQuality, true, &depthStencilBuffer, NULL);
	//And set it
	m_d3d9Device->SetDepthStencilSurface(depthStencilBuffer);
}
Is there anything obviously wrong there? I don't think there is but I'm usually wrong about such things. The second problem comes when I try to call my Resize() function:
	void D3D9RenderDevice::Resize()
	{
		if(m_d3d9Device)
		{
			LPDIRECT3DSWAPCHAIN9 swapChain = 0;
			LPDIRECT3DSURFACE9 backBuffer, depthStencilBuffer = NULL;	
			D3DPRESENT_PARAMETERS pp;
			
			m_swapChains.at(m_activeSwapChain)->GetPresentParameters(&pp);

			//Get the new size of the window
			//We use the current device window as a guide for this as if it's being resized it must be the active window
			Pos2D<int> size = RootPtr->GetPlatform()->GetDFWindowSize(m_activeWindow);
			pp.BackBufferWidth = size.X;
			pp.BackBufferHeight = size.Y;
			pp.hDeviceWindow = m_activeWindow;

			//Re-create the swapchain using the new settings
			m_swapChains.at(m_activeSwapChain)->Release();
			m_swapChains.at(m_activeSwapChain) = 0;

			if(FAILED(m_d3d9Device->CreateAdditionalSwapChain(&m_d3d9PP, &swapChain)))
			{
				RootPtr->GetLogger()->Log("Could not create new swapchain for resize!", EMT_ERROR);
				return;
			}

			m_swapChains.at(m_activeSwapChain) = swapChain;

			if(FAILED(m_swapChains.at(m_activeSwapChain)->GetBackBuffer(0, D3DBACKBUFFER_TYPE_MONO, &backBuffer)))
			{
				RootPtr->GetLogger()->Log("Could not create new swapchain for resize!", EMT_ERROR);
				return;
			}

			m_d3d9Device->SetRenderTarget(0, backBuffer);			
			backBuffer->Release();

			m_swapChains.at(m_activeSwapChain)->GetPresentParameters(&pp);
			m_d3d9PP = pp;

			//Create depth/stencil buffer for this swapchain
			m_d3d9Device->CreateDepthStencilSurface(pp.BackBufferWidth, pp.BackBufferHeight, pp.AutoDepthStencilFormat, pp.MultiSampleType, 
																				pp.MultiSampleQuality, true, &depthStencilBuffer, NULL);
			//And set it
			m_d3d9Device->SetDepthStencilSurface(depthStencilBuffer);
		}
	}

It always fails on this line: m_d3d9Device->CreateAdditionalSwapChain(&m_d3d9PP, &swapChain); Even though in the debugger swapChain does look like it has been filled out properly. Any help is much appreciated. My first time really looking into this swap chain lark.

Share this post


Link to post
Share on other sites
Quote:
It always fails on this line:


Without looking at the code, there's one thing you should be doing as a first step for debugging this:

If you're getting a failure code from a D3D call, the debug Direct3D runtime will tell you why it is unhappy.

Switch to the Debug runtimes in the DirectX control panel (in the utilities part of the SDK install folder in newer SDKs or in the system control panel in older SDKs) with a reasonable output level (halfway is always good for me), run your app in a debugger, look at the 'output' window of your debugger after the D3D call fails - in almost all cases, D3D will give you some details of why it failed the call.

Personally I always run with the debug runtimes enabled unless I need to do something like performance profiling.

[edit]
As for the trails, how are you clearing each render target? You should do the SetRenderTarget()/SetDepthStencilSurface() for each swap chains render target and do a Clear() afterwards for each one (remembering to clear the depth buffer too - forgetting to do that is often the cause of something appearing for a few frames then dissappearing).

Share this post


Link to post
Share on other sites
Thanks, I have to say I didn't know about the control panel so your post was very helpful thanks.

EDIT: About the clearing I have all the buffers cleared at the end of each frame after the render target has been set. Are you saying I should call Clear straight after creating the swap chain and buffers before drawing to it?

EDIT2: I just did a debug run with the DirectX Debug libraries and I have to say thanks again! I had no idea that I could get D3D to output this sort of information. This may help me figure out a solution to another problem I'm having!

EDIT3: I think the reason for the clearing problems is that the Z-Buffer and Stencil buffer aren't being created properly and I've heard if you try to clear an invalid buffer you can get weird results.

OK According to the debug output this is the reason:

Direct3D9: (ERROR) :Failed to create driver surface
Direct3D9: (ERROR) :Error during initialization of surface. CreateRenderTarget/CreateDepthStencil failed
Direct3D9: (ERROR) :Failure trying to create zstencil surface
D3D9 Helper: IDirect3DDevice9::CreateDepthStencilSurface failed: D3DERR_OUTOFVIDEOMEMORY

Out of video memory? How did that happen :S

The clearing problem:

Direct3D9: (ERROR) :Invalid flag D3DCLEAR_ZBUFFER: no zbuffer is associated with device. Clear failed.

Direct3D9: (ERROR) :Invalid flag D3DCLEAR_STENCIL: no zbuffer is associated with device. Clear failed.

But this is before I get the out of memory error and I think I get this error after I set the render target using my own function:

void D3D9RenderDevice::SetRenderTarget(int targetNum)
{
LPDIRECT3DSURFACE9 backBuffer, depthStencilBuffer = NULL;

if(m_d3d9PP.Windowed)
{
if(m_swapChains.at(targetNum))
{
if(FAILED(m_swapChains.at(targetNum)->GetBackBuffer(0, D3DBACKBUFFER_TYPE_MONO, &backBuffer)))
return;

m_d3d9Device->SetRenderTarget(0, backBuffer);
backBuffer->Release();

D3DPRESENT_PARAMETERS pp;
m_swapChains.at(targetNum)->GetPresentParameters(&pp);

m_activeWindow = pp.hDeviceWindow;
m_activeSwapChain = targetNum;

//Create depth/stencil buffer for this swapchain
m_d3d9Device->CreateDepthStencilSurface(pp.BackBufferWidth, pp.BackBufferHeight, pp.AutoDepthStencilFormat, pp.MultiSampleType,
pp.MultiSampleQuality, true, &depthStencilBuffer, NULL);
//And set it
m_d3d9Device->SetDepthStencilSurface(depthStencilBuffer);
}
}
}


This is called every frame before drawing to enable multiple window rendering.

[Edited by - Dom_152 on April 7, 2007 11:31:23 AM]

Share this post


Link to post
Share on other sites
Is there a reason that you're manually creating and assigning the depth stencil? It sounds to me like that's where the problem is.

Why not set EnableAutoDepthStencil in your presentation parameters to true and assign an appropriate AutoDepthStencilFormat? D24S8 works well. (These are the mdx terms, but looking up the c++ equivalents should be simple enough). You might have to set PresentFlag flag as well. I have it set to discard.

Also, check out the swap chains demo at codesampler.com.

[Edited by - gharen2 on April 7, 2007 12:45:42 PM]

Share this post


Link to post
Share on other sites
Trust me, you can use auto depth stencils with swap chains. I've done it before.

But if you don't believe me, look at the codesampler.com demo. It uses auto depth stencils.

The only thing you have to do manually with swap chains is make the chain's back buffer the current render target.

Share this post


Link to post
Share on other sites
Alrighty thanks. I'll get rid of all the depth/stencil buffer stuff and try again.

EDIT: OK Done that but now I get this:

Direct3D9: (ERROR) :DepthStencil Buffer must be at least as big as the RenderTarget.
First-chance exception at 0x7c81eb33 in Test 1.exe: Microsoft C++ exception: long at memory location 0x0012f2b0..
Direct3D9: (ERROR) :DrawIndexedPrimitiveUP failed.

This is before any re-sizing is done. The backbuffer is the right size so why isn't the DepthStencil buffer correct?

EDIT2: After looking around it seems I was right. See this thread on gamedev:

HERE

In particular the post by Namethatnobodyelsetook:

Quote:

You cannot use AutoDepthStencil on a swapchain, only the main device. Why? There's no way to access the depth buffer to set it as the active depth surface.


So I must create a DepthStencil surface but I must've been doing it wrong.

[Edited by - Dom_152 on April 7, 2007 1:01:00 PM]

Share this post


Link to post
Share on other sites
Success! I've got it all working now. I wasn't saving the pointers to the depthStencil surfaces I was creating so there being lost and therefore couldn't be used. I now store them in another vector alongside the swap chains themselves. Now to fix some problem with the projection matrix.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this