PDA

View Full Version : DirectX 9, ALT-TAB and mode changes?



FusionWolf
22-01-2005, 02:35 PM
Hi,

I'm currently programming 2D game by using DirectX ID3DXSprite interface. I have created window by using CreateWindow() function and I have message handler and a message loop up and running. So, skeleton for the rendering is ready and working, but....

If I want to do so, that user can press ALT-ENTER to switch between windowed and fullscreen mode. What I have to do? I know I should read proper messages for when the keys is pressed - okey, but then what? Set the size of window somehow (SetWindowLong()) by how? And what about DirectX? Should I stop rendering (which occurs in message loop). How to do that?

And what about if I'm rendering fullscreen and user presses ALT-TAB or "windows logo" key? How to handle situation like that?

I have tried to find tutorials, but these are very hard to find for DirectX 9.

I appreciate your answers - thanks.

FusionWolf
22-01-2005, 06:58 PM
What the heck I'm doing wrong? I'm trying to avoid a lost device when alt-tabbing a fullscreen application.

I have BeginScene function which is called begining of main loop. In BeginScene function I check for lost device with TestCooperativeLevel function. If device is lost BeginScene returns result of TestCooperativelevel and exists.

In main loop, when calling the BeginScene I check if it fails and then it doen't enter in the game loop. And when the BeginScene is called again. I check the CooperativeLevel (of course) and if it's "device no reset" state I reset the device, return "failure" to main loop and exit from function.

And next time, when main loop calls BeginScene all should be fine and rendering should occur, but that never happens. What I'm doing wrong?



function BeginScene: HRESULT;
var
hr: HRESULT;
begin

hr := Device.TestCooperativeLevel;

if hr = D3DERR_DEVICELOST then begin
Result := hr;
Exit;
end;

if hr = D3DERR_DEVICENOTRESET then begin
Device.Reset(FD3DPP);
Result := hr;
Exit;
end;

Device.Clear(0, nil, D3DCLEAR_TARGET, $000000, 1, 0);

Result := Device.BeginScene;
end;


While uMsg <> WM_QUIT do begin
if GameWindow.Active then
MsgWaiting &#58;= PeekMessage&#40;uMsg, 0, 0, 0, PM_REMOVE&#41;
else
MsgWaiting &#58;= GetMessage&#40;uMsg, 0, 0, 0&#41;;

if MsgWaiting then begin
TranslateMessage&#40;uMsg&#41;;
DispatchMessage&#40;uMsg&#41;;
Continue;
end;

GameTimer.Update;

// Check if we could begun 3D drawing.
if GameDX.BeginScene = D3D_OK then begin

// Game code here. If application is ALT-TABed then this is section is never ran.

GameDX.EndScene;
end;
end;

Sly
23-01-2005, 02:56 AM
This is how I handle changing from full-screen to windowed and back.

When I detect the key-press to switch modes, I set the Windowed property as appropriate and call the Reset method of my TDK3DDevice object.
procedure TDK3DDevice.Reset;
var
d3dpp: TD3DPresentParameters;
begin
DoDeviceReset;
FillPresentParameters(d3dpp);
Assert(FDevice.Reset(d3dpp) = D3D_OK);
DoDeviceLost;
end;
DoDeviceReset triggers the OnDeviceReset event of the TDK3DDevice object. In the event handler for OnDeviceReset, I invalidate any loaded resources that require invalidating and change the window style.
procedure TfrmMain.DeviceReset(Sender: TObject);
begin
gSystem.Fonts.InvalidateDeviceObjects;

if gSystem.Device.Windowed then
begin
BorderStyle := FWindowedBorderStyle;
Left := FWindowedLeft;
Top := FWindowedTop;
end
else
begin
FWindowedBorderStyle := BorderStyle;
FWindowedLeft := Left;
FWindowedTop := Top;

BorderStyle := bsNone;
Left := 0;
Top := 0;
end;

gSystem.Device.WindowHandle := Handle;

ClientWidth := gSystem.Device.Width;
ClientHeight := gSystem.Device.Height;
end;
The only resources I have that require invalidating are the font objects.
procedure TDKFont.InvalidateDeviceObjects;
begin
m_pVB := nil;
m_pSavedStateBlock := nil;
m_pDrawTextStateBlock := nil;
end;
Here it destroys the vertex buffer and state blocks.

In the OnDeviceLost event handler, I restore the objects that were destroyed previously.
procedure TfrmMain.DeviceLost(Sender: TObject);
begin
gSystem.Fonts.RestoreDeviceObjects;
end;
Again, it is only the font objects that require restoring.
function TDKFont.RestoreDeviceObjects: HRESULT;
var
which: LongWord;
begin
// Create vertex buffer for the letters
Result:= m_pd3dDevice.CreateVertexBuffer(MAX_NUM_VERTICES*S izeOf(TFont2DVertex),
D3DUSAGE_WRITEONLY or D3DUSAGE_DYNAMIC,
0, D3DPOOL_DEFAULT, m_pVB, nil);
if FAILED(Result) then Exit;

// Create the state blocks for rendering text
for which:= 0 to 1 do
begin
m_pd3dDevice.BeginStateBlock;
FMaterial.Texture.Use(m_pd3dDevice);

if FZEnable then
m_pd3dDevice.SetRenderState(D3DRS_ZENABLE, iTrue)
else
m_pd3dDevice.SetRenderState(D3DRS_ZENABLE, iFalse);

m_pd3dDevice.SetRenderState(D3DRS_ALPHABLENDENABLE , iTrue);
m_pd3dDevice.SetRenderState(D3DRS_SRCBLEND, D3DBLEND_SRCALPHA);
m_pd3dDevice.SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA);
m_pd3dDevice.SetRenderState(D3DRS_ALPHATESTENABLE, iFalse);
m_pd3dDevice.SetRenderState(D3DRS_FILLMODE, D3DFILL_SOLID);
m_pd3dDevice.SetRenderState(D3DRS_CULLMODE, D3DCULL_CCW);
m_pd3dDevice.SetRenderState(D3DRS_STENCILENABLE, iFalse);
m_pd3dDevice.SetRenderState(D3DRS_CLIPPING, iTrue);
m_pd3dDevice.SetRenderState(D3DRS_MULTISAMPLEANTIA LIAS, iFalse);
m_pd3dDevice.SetRenderState(D3DRS_CLIPPLANEENABLE, iFalse);
m_pd3dDevice.SetRenderState(D3DRS_VERTEXBLEND, iFalse);
m_pd3dDevice.SetRenderState(D3DRS_INDEXEDVERTEXBLE NDENABLE, iFalse);
m_pd3dDevice.SetRenderState(D3DRS_FOGENABLE, iFalse);

m_pd3dDevice.SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_MODULATE);
m_pd3dDevice.SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_TEXTURE);
m_pd3dDevice.SetTextureStageState(0, D3DTSS_COLORARG2, D3DTA_DIFFUSE);
m_pd3dDevice.SetTextureStageState(0, D3DTSS_ALPHAOP, D3DTOP_MODULATE);
m_pd3dDevice.SetTextureStageState(0, D3DTSS_ALPHAARG1, D3DTA_TEXTURE);
m_pd3dDevice.SetTextureStageState(0, D3DTSS_ALPHAARG2, D3DTA_DIFFUSE);

m_pd3dDevice.SetSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_POINT);
m_pd3dDevice.SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_POINT);
m_pd3dDevice.SetSamplerState(0, D3DSAMP_MIPFILTER, D3DTEXF_NONE);

m_pd3dDevice.SetTextureStageState(0, D3DTSS_TEXCOORDINDEX, 0);
m_pd3dDevice.SetTextureStageState(0, D3DTSS_TEXTURETRANSFORMFLAGS, D3DTTFF_DISABLE);
m_pd3dDevice.SetTextureStageState(1, D3DTSS_COLOROP, D3DTOP_DISABLE);
m_pd3dDevice.SetTextureStageState(1, D3DTSS_ALPHAOP, D3DTOP_DISABLE);

if (which = 0) then
m_pd3dDevice.EndStateBlock(m_pSavedStateBlock)
else
m_pd3dDevice.EndStateBlock(m_pDrawTextStateBlock);
end;

Result:= S_OK;
end;
This method recreates the vertex buffer and the state blocks used by the fonts.

This means that with those methods in place, to switch modes all I do is the following:
gSystem.Device.Windowed := not gSystem.Device.Windowed;
gSystem.Device.Reset;
This allows me to switch back and forth from windowed to full-screen as much as I want. Note that I have not handled the case where the call to BeginScene fails yet due to Alt+Tab. That is yet to be done.

Hope that helps a bit.

FusionWolf
23-01-2005, 10:15 AM
This is how I handle changing from full-screen to windowed and back.

Hope that helps a bit.

Thank you Sly again for your help. I finally managed to get it work. Now I can ALT-TAB or press "logokey" to change my application to idle mode and reset it back to working state.

I Also got ALT-ENTER combination to work. The problem was that I assumed that, when I set window properties with GetWindowLong() WinAPI function (I don't use VCL components) it automaticly looses my device, but that didn't happend. So I have to call Reset() by my self.

Thanks very mutch. ;)