[MDX] Device Resize

Started by
16 comments, last by alex_myrpg 17 years, 10 months ago
Hi, my problem is currently that running Managed DirectX 9.0 (MDX 1.1 I believe) in windowed mode causes an InvalidCallException whenever the form (and therefore device) is resized. I'm not using any VertexBuffers which usually seem to cause the problem, only Sprites and Textures. At the moment, I recreate the presentation parameters and reset the device in the DeviceResizing event. I've tried most of the simple things already but feel free to suggest anything still. Here's my code: [Device Initialisation]
Protected Overridable Sub InitializeGraphics()
        If _disposed Then Throw New ObjectDisposedException(Me.GetType().Name)

        Trace.WriteLine("Initializing Graphics Device")

        ' Get display mode and capibilities
        Dim displayMode As DisplayMode
        Dim displayCaps As Direct3D.Caps

        displayMode = Direct3D.Manager.Adapters.Default.CurrentDisplayMode
        displayCaps = Direct3D.Manager.GetDeviceCaps(Direct3D.Manager.Adapters.Default.Adapter, Direct3D.DeviceType.Hardware)

        ' Check for hardware processing
        Dim flags As CreateFlags

        If displayCaps.DeviceCaps.SupportsHardwareRasterization Then
            flags = CreateFlags.HardwareVertexProcessing
        Else
            flags = CreateFlags.SoftwareVertexProcessing
        End If

        ' Check for pure device
        If displayCaps.DeviceCaps.SupportsPureDevice AndAlso _
        ((flags AndAlso CreateFlags.HardwareVertexProcessing) <> 0) Then
            flags = flags Or CreateFlags.PureDevice
        End If

        ' Create device from presentation parameters
        BuildPresentParameters()

        _graphics = New Direct3D.Device( _
            Direct3D.Manager.Adapters.Default.Adapter, _
            displayCaps.DeviceType, _
            Me, flags, _presentParams)

        ' Set form size
        Me.Size = _resolution
    End Sub

And here's the DeviceResizing code plus additional helper functions need for init & resizing:
Public Sub ResetGraphics()
        If _graphics Is Nothing Or _deviceLost Then Exit Sub

        ' Stop engine timer
        _timer.Stop()

        ' Recreate presentation parameters
        BuildPresentParameters()

        ' Reset graphics device
        Try
            _graphics.Reset(_presentParams)
        Catch ex As DeviceLostException
            ' Device lost
            _deviceLost = True

            ' Write to trace
            Trace.WriteLine("Device lost in game loop")
        End Try
    End Sub

    Protected Sub BuildPresentParameters()
        If _disposed Then Throw New ObjectDisposedException(Me.GetType().Name)

        Trace.WriteLine("Build Presentation Parameters")

        ' Create presentation parameters for graphics device
        _presentParams = New PresentParameters()

        _presentParams.BackBufferCount = 1
        _presentParams.BackBufferFormat = Format.A8R8G8B8
        _presentParams.BackBufferWidth = _resolution.Width
        _presentParams.BackBufferHeight = _resolution.Height
        _presentParams.BackBufferCount = 1
        _presentParams.ForceNoMultiThreadedFlag = False
        _presentParams.SwapEffect = SwapEffect.Discard
        _presentParams.PresentationInterval = PresentInterval.Immediate
        _presentParams.PresentFlag = PresentFlag.None
        _presentParams.Windowed = _isWindowed
    End Sub

    Private Sub _graphics_DeviceResizing(ByVal sender As Object, ByVal e As System.ComponentModel.CancelEventArgs) Handles _graphics.DeviceResizing
        '' Stop device resizing
        'e.Cancel = True

        ResetGraphics()
    End Sub

I hope this is a simple problem...you should have all the code/information here but please tell me if you need more. Thanks in advance for any help.
Advertisement
This sound familiarly.

The default behavior of managed Direct3D is to reset the device after the window is resized. This is done to make sure that the back buffer has the same size as your window. Unfortunately such a reset will invalidate some of your DirectX objects. Mainly everything that was created in the default pool or use a state block. Your Sprite object is part of this invalidation, too.
There are at least three possible solutions for this problem.

1. Disable the managed Direct3D event handling. Look for the Device.IsUsingEventHandlers property
2. You can handle the Device.DeviceResizing event and cancel it. If the device is not reset your sprite object will not lost.
3. Handle the Device.DeviceLost and Device.DeviceReset event. Dispose your sprite object during the lost and recreate at the reset event.

I don’t recommend the second solution because there are other reasons that will cause a device reset.
If you look for a fast solution use the third one. As the event handlers have some other drawbacks solution one would be the best but force you to handle device lost situations and back buffer scaling fully by your own.
thanks for your reply. i will try solution #3 for now, but maybe you could explain breifly how i could accomplish #1 either yourself or with a link? anyway your answer makes sense, so i will post about my success with it.
yes - the disposing of resources seems to be the problem, thx.
I'm having trouble understanding what functions I should call when...
Would you mind clarifying what I have to do for DeviceLost and DeviceReset for these types of objects: Sprites, Textures (Pool.Default & Pool.Managed), Direct3D.Font please?
In the device lost handler you simply need to call Dispose for any Direct3D object that lives in the video memory or in the driver. This are all Pool.Default resources and objects that use state blocks like the font and sprite objects.

The device reset handler should recreate anything you have disposed during the lost. It is recommended to write one method that create such resources and call it one time after you have created the device and every time you get a device reset.

The same is valid for the disposing of the objects. This method should called during a device lost and before you finally dispose your device.

In the case you set some render states after you have created the device you should this do in an additional method that is called during the reset. The reason for this is that a reset will reset your render states, too.
Great, that explains it perfectly, thanks. Except what are the OnDeviceLost and OnDeviceReset methods of a Sprite/Font object for?

EDIT: The problem I find is that it takes very long to recreate some resources...
I believe it's to do with the GUI system, so it could involve VertexBuffers. Thanks for your help again...

[Edited by - alex_myrpg on May 29, 2006 3:32:01 PM]
Ideas anyone? Surely this problem has come up before.
How many d3d objects do you have?

How much of them need to be recreated?
I think an important thing to note is that in MDX the recreation of the device is handled automatically. If you've attached the device to a form itself then there is no need to override the resize event.

Any vertex buffers and such should be created with the managed flag in their constructors. Render targets are the only exception from what I know.

I struggled with the same problem for some time. I used complex algorithms to try and handle this resize event. Then I read Tom Millers MDX book and he explains that it's best to just allow MDX to handle it's own resize managment.

The only thing you will have to reset are things like view, world, projection matrix and other custom data that you've stored in the device object. MDX will automatically eject that data on resize. Also renderstates need to be reset.

So hook to the deviceResize event and handle reseting the custom data but don't cancel it or override it.

-Devin

This topic is closed to new replies.

Advertisement