Sign in to follow this  
Burnhard

On window resize?

Recommended Posts

Burnhard    96
Using C++ with D3D, when I recieve WM_SIZE messages, I check to see if the window actually has resized and trash all of my device objects in response. Obviously they are re-created, the device being reset, before the next WM_SIZE message. It seems to me that this could be a problem, especially as because I'm resizing I have nothing to paint the window with when I get a WM_PAINT message. Moreover, although I can restablish my device objects relatively quickly, actually populating them with usable data could in theory take some time. So, how do you manage window resizing? I don't mean full-screen, which is straight-forward, but in a context where you aren't rendering at 60 fps (unless there's actually some animation going on) and you're a control in the window of an application. How do you repaint the screen during a resize operation without have to reconstruct the entire scene first? Any tips?

Share this post


Link to post
Share on other sites
Burnhard    96
Ok, so I'm reading that I should really grab a backbuffer that's the size of the display and then render to it using a viewport, so I don't have to re-create all of my resources and can just do a render during a resize. Is this correct? Anyone have an example?

Share this post


Link to post
Share on other sites
standby01    154
I think usually you need to do these 5 steps in WM_SIZE.

1. Release some resources
mRenderTargetView->Release();
mDepthStencilView->Release();
mDepthStencilBuffer->Release();

2. Resize the swap chain and recreate the render target view.

mSwapChain->ResizeBuffers(1, mClientWidth, mClientHeight, DXGI_FORMAT_R8G8B8A8_UNORM, 0);

ID3D10Texture2D* backBuffer;
mSwapChain->GetBuffer(0, __uuidof(ID3D10Texture2D), reinterpret_cast<void**>(&backBuffer));
md3dDevice->CreateRenderTargetView(backBuffer, 0, &mRenderTargetView));
backBuffer->Release();


3. Re-create the depth/stencil buffer and view.

D3D10_TEXTURE2D_DESC depthStencilDesc;

depthStencilDesc.Width = mClientWidth;
depthStencilDesc.Height = mClientHeight;
...

md3dDevice->CreateTexture2D(&depthStencilDesc, 0, &mDepthStencilBuffer));
HR(md3dDevice->CreateDepthStencilView(mDepthStencilBuffer, 0, &mDepthStencilView);


4. Re-bind the render target view and depth/stencil view to the pipeline.

md3dDevice->OMSetRenderTargets(1, &mRenderTargetView, mDepthStencilView);


5. Set the viewport transform.

D3D10_VIEWPORT vp;
vp.TopLeftX = 0;
vp.TopLeftY = 0;
vp.Width = mClientWidth;
vp.Height = mClientHeight;
vp.MinDepth = 0.0f;
vp.MaxDepth = 1.0f;

md3dDevice->RSSetViewports(1, &vp);

Share this post


Link to post
Share on other sites
Evil Steve    2017
And if you're using D3D9, a common way to do this is to create the device with a 1x1 backbuffer, and then use IDirect3DDevice9::CreateAdditionalSwapChain to create a new swapchain which you resize (re-create). Because you're not resetting the entire device, you don't need to release and re-create all other objects.

Share this post


Link to post
Share on other sites
Burnhard    96
At the moment, I'm just clearing the back buffer to blue and then moving the window around/sizing it with the mouse. It basically flickers white while I'm moving it and it renders properly (blue) when I stop moving it. I've posted code below - I hope it makes sense! I've come from a commercial OpenGL background where I don't have to deal with issues like this, so was kind-of hoping there was a good solution whereby I could resize the window while still drawing an image inside it.

I create my window like this:



/*
Initialise Direct3D.
*/


HRESULT
Window3D::Initialise()
{
/*
Guard.
*/


assert (MyDevice == 0 && MyD3D9 == 0);
if (MyDevice != 0 || MyD3D9 != 0)
{
return(GRAPH3D_E_ALREADYCREATED);
}






/*
Get reference to D3D.
*/


if (MyD3D9 == 0)
{
MyD3D9.Attach(Direct3DCreate9(D3D_SDK_VERSION));
if (MyD3D9 == 0)
{
return(GRAPH3D_E_D3D9NOTAVAILABLE);
}
}

TRACE(L"Window3D :: Created D3D\n");






/*
Get Caps for the primary display, default adaptor - we assume HAL because we don't want to
be stuck with the Reference Rasterizer, which is CPU based and unbelievably slow. So, our
minimum basic requirement is HAL, which most graphics cards support.
*/


HRESULT Result;

Result = MyD3D9->GetDeviceCaps(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, &MyDeviceCaps);
if (FAILED(Result))
{
return(Result);
}

TRACE(L"Window3D :: Got device caps\n");





/*
Get the display mode, so we know what we are going to be using.
*/



Result = MyD3D9->GetAdapterDisplayMode(D3DADAPTER_DEFAULT, &MyDisplayMode);
if (FAILED(Result))
{
return(Result);
}

TRACE(L"Window3D :: Got adaptor display mode\n");





/*
Validate the device.
*/


Result = Validate(MyD3D9, MyDisplayMode, MyDeviceCaps);
if (FAILED(Result))
{
return(Result);
}

TRACE(L"Window3D :: Validated device against objects\n");





/*
Set up the structure used to create the D3DDevice.
*/


ZeroMemory(&MyPresentParameters, sizeof (MyPresentParameters));

MyPresentParameters.Windowed = TRUE;
MyPresentParameters.SwapEffect = D3DSWAPEFFECT_DISCARD;
MyPresentParameters.BackBufferFormat = D3DFMT_UNKNOWN;
MyPresentParameters.EnableAutoDepthStencil = TRUE;
MyPresentParameters.AutoDepthStencilFormat = D3DFMT_D16;



/*
TODO - Setup multisample by passing a parameter into this window.

m_D3DPP.MultiSampleType = D3DMULTISAMPLE_4_SAMPLES;
*/







/*
Create our device.
*/


Result = MyD3D9->CreateDevice ( D3DADAPTER_DEFAULT,
D3DDEVTYPE_HAL,
MyWindow,
D3DCREATE_HARDWARE_VERTEXPROCESSING,
&MyPresentParameters,
&MyDevice);
if (FAILED(Result))
{
return(Result);
}

TRACE(L"Window3D :: Created device instance\n");




/*
Store size for when check for size changes later.
*/


GetClientRect(MyWindow, &MyLastSize);





/*
Send self a paint message.
*/


::InvalidateRect(MyWindow, NULL, FALSE);



return S_OK;
}



Handling windows messages like this:



/*
Handle messages sent in from the outside world.
*/


/* virtual */
HRESULT
Window3D::Messages(UINT msg, WPARAM wParam, LPARAM lParam, bool& bHandled)

{

if (MyWindow == 0)
{
return S_OK;
}




switch(msg)

{
case WM_ENTERSIZEMOVE:
{

/*
Signal so that we don't re-create the device whenever a move occurs on WM_SIZE.
*/


MyIsEnterSizeMove = true;

bHandled = true;
}
break;

case WM_EXITSIZEMOVE:
{
/*
Check for window size change or monitor change.
*/


HRESULT Result;

Result = CheckForWindowSizeChange();
if (FAILED(Result))
{
return Result;
}

Result = CheckForWindowChangingMonitors();
if (FAILED(Result))
{
return Result;
}



// No longer moving or sizing.

MyIsEnterSizeMove = false;

bHandled = true;
}
break;

case WM_SIZE:
{
// Store current window size.

RECT rcCurrentClient;

GetClientRect(MyWindow, &rcCurrentClient);


if (MyIsEnterSizeMove)
{
/*
User is dragging size/move, so no need to change here, although it would be nice to be able to repaint it. Perhaps
if all of our objects were MANAGED we could do? Experiment to find out...
*/

}
else if(SIZE_MINIMIZED == wParam)
{
/*
We are minimized...
*/


MyLastSize = rcCurrentClient;
}
else
{
if (rcCurrentClient.top == 0 && rcCurrentClient.bottom == 0)
{


/* Rapidly clicking the task bar to minimize and restore a window
can cause a WM_SIZE message with SIZE_RESTORED when the window
has actually become minimized due to rapid change so just
ignore this message. */



}
else if (SIZE_MAXIMIZED == wParam)
{
HRESULT Result = CheckForWindowSizeChange();
if (FAILED(Result))
{
return(Result);
}

Result = CheckForWindowChangingMonitors();
if (FAILED(Result))
{
return(Result);
}
}
else if (SIZE_RESTORED == wParam)
{
if (MyIsEnterSizeMove)
{

}
else
{
HRESULT Result = CheckForWindowSizeChange();
if (FAILED(Result))
{
return Result;
}

Result = CheckForWindowChangingMonitors();
if (FAILED(Result))
{
return Result;
}
}
}
}

bHandled = true;
}
break;

case WM_PAINT:
{
/*
Only render when painting if we aren't a live window (live windows are rendered automatically).
*/


if (!MyIsLive)
{
HRESULT Result = Render();

if (FAILED(Result))
{
return Result;
}

::ValidateRect(MyWindow, &MyLastSize);

bHandled = true;
}
}
break;
}



return S_OK;
}



.... and CheckForWindowSizeChange() looks like this:



/*
Check to see if the window's size has changed.
*/


HRESULT
Window3D::CheckForWindowSizeChange()
{
HRESULT Result;



/*
Fetch current window size to compare with our last window size.
*/


RECT rcCurrentClient;

GetClientRect(MyWindow, &rcCurrentClient);





/*
If they're different, we need to reset our device.
*/


if(static_cast<UINT>(rcCurrentClient.right) != MyPresentParameters.BackBufferWidth ||
static_cast<UINT>(rcCurrentClient.bottom) != MyPresentParameters.BackBufferHeight)
{
/*
We may not have a device at the moment, so guard.
*/


if (MyIsDeviceCreated)
{
TRACE(L"Window3D :: Lost Device (window size change)\n");


/*
We have got a device, so tell derived classes to dispose of anything that's unmanaged.
*/


Result = DeviceLost();
if (FAILED(Result))
{
return(Result);
}

MyIsDeviceCreated = false;
}





/*
Make sure we flag not to do anything if we've got a very small window.
*/


int dX, dY;

dX = (rcCurrentClient.right - rcCurrentClient.left);
dY = (rcCurrentClient.bottom - rcCurrentClient.top);

if (dX < 1 || dY < 1)
{
MyIsTooSmall = true;

return S_OK;
}
else
{
MyIsTooSmall = false;
}




/*
Window is ok size, so setup present with the new window.
*/


MyLastSize = rcCurrentClient;

MyPresentParameters.BackBufferWidth = dX;
MyPresentParameters.BackBufferHeight = dY;





/*
Try and reset the device.
*/


Result = MyDevice->Reset(&MyPresentParameters);
if (FAILED(Result))
{
return Result;
}

TRACE(L"Window3D :: Device Reset (window size change)\n");





/*
Call size changed so derived classes get a go at setting their parameters too.
*/


OnSizeChanged(dX, dY);




/*
and reallocate our device dependent objects.
*/


Result = DeviceFound();
if (FAILED(Result))
{
return(Result);
}
else
{
MyIsDeviceCreated = true;
}

TRACE(L"Window3D :: Aquired Device (window size change)\n");





/*
Force a redraw.
*/


::InvalidateRect(MyWindow, NULL, FALSE);
}


return S_OK;
}


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