注意:(我知道管理代码的DX已被弃用,我计划在我有一些额外时间时用SlimDX重写代码。)我在尝试处理DeviceLostException时遇到了很多麻烦。与大多数DX应用程序不同,我不是在不断渲染帧,而是只在需要更改屏幕上的内容时才渲染。这使得遵循处理DeviceLostExceptions的指南有点棘手。我正在编写一个程序,使用Nvidia的3D Vision技术来显示立体图像。我的显示类是一个无边框的窗体类,拉伸到全屏。我有一个名为BaseStimulusDisplay的基类,用于设置基本表单和DirectX设备,然后我继承它以创建用于显示立体图像的StereoStimulusDisplay对象。 (我这样做是因为我有一些其他类型的显示器用于程序的不同方面。)类的工作方式是我有两个对象列表(stereoObjects和singleEyeObjects),它们基本上只是包含一些DX表面和一些位置的对象info以确定应在屏幕上绘制曲面的位置。当我想在屏幕上显示对象时,我调用一个RenderScene方法,该方法循环遍历对象列表中的每个对象,并使用StretchRectangle方法将对象表面绘制到后备缓冲区,然后调用预设()。为了尝试处理DeviceLostException,我将一个位于Render Scene方法末尾的device.Present()放在一个如下所示的try块中:
Try
Me._device.Present()
Catch ex As DeviceLostException
'Indicate that the device has been lost
deviceIsLost = True
'Send a message to the debug output that the device was lost
Debug.WriteLine("Device was lost... Attempting to recover device")
waitForDeviceRecovery()
renderScene()
End Try
这是waitForDeviceRecovry。我确保在调用device.Reset()之前处理所有使用视频内存的对象,并在调用后重新分配表面。
Public Overridable Sub waitForDeviceRecovery()
Do
Try
_device.TestCooperativeLevel()
Catch ex As DeviceLostException
Catch ex2 As DeviceNotResetException
Try
For Each Val As StereoObject In stereoObjectList
Val.Dispose()
Next
For Each Val As SingleEyeImage In singleEyeList
Val.Dispose()
Next
Me._offScreenSurface.Dispose()
Me._backBuffer.Dispose()
Me._blankScreenSurface.Dispose()
Me._leftEyeSurface.Dispose()
Me._rightEyeSurface.Dispose()
Me._backBuffer.Dispose()
_device.Reset(presentParams)
deviceIsLost = False
For Each Val As StereoObject In stereoObjectList
Val.loadSurfaces(Val.objName)
Next
For Each Val As SingleEyeImage In singleEyeList
Val.loadSurface(Val.objName)
Next
createSurfaces()
'Send a message to debug out put that the device was recovered
Debug.WriteLine("The device was successfully recoverd")
Catch ex As DeviceLostException
End Try
End Try
Loop Until deviceIsLost = False
End Sub
现在,如果我运行代码并按Alt-Tab循环浏览我的打开程序并返回DX应用程序,则代码在renderScene方法中的以下行崩溃,并带有InvalidCallException;
Me._device.ColorFill(Me._leftEyeSurface, New Rectangle(0, 0, Me._size.Width, Me._size.Height), color)
此外,如果我将Alt-Tab转换为另一个程序(不只是将所有应用程序列表循环回我的应用程序)然后尝试重新启动应用程序。程序永远不会恢复它只是卡在waitForDeviceRecovery方法的do循环中。我的意思是device.TestCooperativeLevel()调用不断抛出DeviceLostException,从不抛出DeviceNotResetException。我正在为下面的两个显示类添加完整的代码。
以下是基本刺激显示的代码。
Imports System
Imports System.Collections.Generic
Imports System.ComponentModel
Imports System.Data
Imports System.Drawing
Imports System.Text
Imports System.Windows.Forms
Imports System.IO
Imports Microsoft.DirectX
Imports Microsoft.DirectX.Direct3D
Imports Microsoft.DirectX.Direct3D.PresentParameters
Public MustInherit Class BaseStimulusDisplay
Public displaySettings As DisplaySettings
Public _size As Rectangle 'Size of screen
Public _device As Device 'DX device object to interface with graphics card
Protected _offScreenSurface As Surface 'Surface on which to draw the SteroScreen images
Protected _blankScreenSurface As Surface 'Surface of all background color
Protected _backBuffer As Surface 'The DX backbuffer
Protected _adapter As Integer = Direct3D.Manager.Adapters.Default.Adapter
Protected presentParams As PresentParameters
Protected deviceIsLost As Boolean = False
Public Sub New(ByVal displaySettings As DisplaySettings)
'Make windows happy
Me.components = Nothing
Me.InitializeComponent()
Me.displaySettings = displaySettings
Me._size = New Rectangle(0, 0, Me.displaySettings.xResolution, Me.displaySettings.yResolution)
MyBase.SetStyle((ControlStyles.AllPaintingInWmPaint Or ControlStyles.Opaque Or ControlStyles.ContainerControl), True)
Me.InitializeDevice() 'Set the device parameters and create the device
Invalidate()
End Sub
Public Overridable Sub InitializeDevice()
presentParams = New PresentParameters
presentParams.Windowed = False
presentParams.BackBufferFormat = Format.X8R8G8B8
presentParams.BackBufferWidth = Me._size.Width
presentParams.BackBufferHeight = Me._size.Height
presentParams.BackBufferCount = 1
presentParams.SwapEffect = Direct3D.SwapEffect.Discard
presentParams.PresentationInterval = PresentInterval.One
Me._device = New Device(_adapter, DeviceType.Hardware, CType(Me, Control), CreateFlags.HardwareVertexProcessing, New PresentParameters() {presentParams})
End Sub
Public Overridable Sub createSurfaces()
Me._offScreenSurface = Me._device.CreateOffscreenPlainSurface(Me._size.Width, Me._size.Height, Format.X8R8G8B8, Pool.Default)
Me._backBuffer = Me._device.CreateOffscreenPlainSurface(Me._size.Width, Me._size.Height, Format.X8R8G8B8, Pool.Default)
Me._blankScreenSurface = Me._device.CreateOffscreenPlainSurface(Me._size.Width, Me._size.Height, Format.X8R8G8B8, Pool.Default)
End Sub
Public Overridable Overloads Sub Dispose()
Me._offScreenSurface.Dispose()
Me._backBuffer.Dispose()
Me._blankScreenSurface.Dispose()
MyBase.Dispose()
End Sub
Public MustOverride Sub renderScene()
Public MustOverride Sub closeDisplay()
End Class
以下是我的StereoStimulsDisplay以及StereoObject和SingleEyeObject的代码:
Imports System
Imports System.Collections.Generic
Imports System.ComponentModel
Imports System.Data
Imports System.Drawing
Imports System.Text
Imports System.Windows.Forms
Imports System.IO
Imports Microsoft.DirectX
Imports Microsoft.DirectX.Direct3D
Imports Microsoft.DirectX.Direct3D.PresentParameters
Public Class StereoStimulusDisplay
Private Const colorFormat As Format = Format.A8B8G8R8 'The sufrace color format to use
Private Const poolType As Pool = Pool.Default 'The sufrace memory pool type NOT SURE WHAT THIS IS FOR
Dim stereoObjectList As List(Of StereoObject)
Dim singleEyeList As List(Of SingleEyeImage)
Dim _leftEyeSurface As Surface
Dim _rightEyeSurface As Surface
Public Sub New(ByVal displaySettings As DisplaySettings)
MyBase.New(displaySettings)
InitializeComponent()
stereoObjectList = New List(Of StereoObject)
singleEyeList = New List(Of SingleEyeImage)
createSurfaces()
renderScene() 'Double render call is a hack to make sure the stereo display gets triggerd
renderScene()
End Sub
Public Sub addStereoObject(ByRef sObject As StereoObject)
stereoObjectList.Add(sObject)
End Sub
Public Sub addSingleEyeObject(ByRef sObject As SingleEyeImage)
singleEyeList.Add(sObject)
End Sub
Public Function removeStereoObject(ByRef sObject As StereoObject)
sObject.Dispose()
Return stereoObjectList.Remove(sObject)
End Function
Public Function removeSingleEyeObject(ByRef sObject As SingleEyeImage)
sObject.Dispose()
Return singleEyeList.Remove(sObject)
End Function
Public Overrides Sub createSurfaces()
MyBase.createSurfaces()
Me._offScreenSurface = Me._device.CreateOffscreenPlainSurface((Me._size.Width * 2), (Me._size.Height + 1), Format.A8R8G8B8, Pool.Default)
Me._leftEyeSurface = Me._device.CreateOffscreenPlainSurface((Me._size.Width), (Me._size.Height), Format.A8R8G8B8, Pool.Default)
Me._rightEyeSurface = Me._device.CreateOffscreenPlainSurface((Me._size.Width), (Me._size.Height), Format.A8R8G8B8, Pool.Default)
Me._blankScreenSurface = Me._device.CreateOffscreenPlainSurface((Me._size.Width * 2), (Me._size.Height + 1), Format.A8R8G8B8, Pool.Default)
End Sub
Public Sub displayBlankScene(ByVal color As Color)
'Erase the lase frame from surfaces
Me._device.ColorFill(Me._leftEyeSurface, New Rectangle(0, 0, Me._size.Width, Me._size.Height), color)
Me._device.ColorFill(Me._rightEyeSurface, New Rectangle(0, 0, Me._size.Width, Me._size.Height), color)
Dim destRect As New Rectangle(0, 0, Me._size.Width, Me._size.Height)
'If Not useData Then
Me._device.StretchRectangle(Me._leftEyeSurface, CType(Me._size, Rectangle), Me._offScreenSurface, CType(destRect, Rectangle), TextureFilter.None)
destRect.X = Me._size.Width
Me._device.StretchRectangle(Me._rightEyeSurface, CType(Me._size, Rectangle), Me._offScreenSurface, CType(destRect, Rectangle), TextureFilter.None)
'End If
Dim gStream As GraphicsStream = Me._offScreenSurface.LockRectangle(LockFlags.None)
Dim data As Byte() = New Byte() {78, 86, 51, 68, 0, 15, 0, 0, 56, 4, 0, 0, 32, 0, 0, 0, 2, 0, 0, 0}
gStream.Seek(CLng((((Me._size.Width * 2) * Me._size.Height) * 4)), SeekOrigin.Begin)
gStream.Write(data, 0, data.Length)
gStream.Close()
Me._offScreenSurface.UnlockRectangle()
Me._device.BeginScene()
Me._backBuffer = Me._device.GetBackBuffer(0, 0, BackBufferType.Mono)
Me._device.StretchRectangle(
Me._offScreenSurface, CType(New Rectangle(0, 0, (Me._size.Width * 2), (Me._size.Height + 1)), Rectangle),
Me._backBuffer, CType(New Rectangle(0, 0, Me._size.Width, Me._size.Height), Rectangle), TextureFilter.None)
Me._backBuffer.ReleaseGraphics()
Me._device.EndScene()
Try
Me._device.Present()
Catch ex As DeviceLostException
'Indicate that the device has been lost
deviceIsLost = True
'Send a message to the debug output that the device was lost
Debug.WriteLine("Device was lost... Attempting to recover device")
waitForDeviceRecovery()
displayBlankScene(color)
End Try
MyBase.Invalidate()
End Sub
Public Overrides Sub renderScene()
'Erase the last frame from surfaces
Me._device.ColorFill(Me._leftEyeSurface, New Rectangle(0, 0, Me._size.Width, Me._size.Height), Color.Black)
Me._device.ColorFill(Me._rightEyeSurface, New Rectangle(0, 0, Me._size.Width, Me._size.Height), Color.Black)
'Write all steroscopic objects to both eye surfaces
For Each stereoObj As StereoObject In stereoObjectList
Me._device.StretchRectangle(stereoObj.leftSurface, CType(stereoObj.leftSourRect, Rectangle), Me._leftEyeSurface, CType(stereoObj.leftDestRec, Rectangle), TextureFilter.None)
Me._device.StretchRectangle(stereoObj.rightSurface, CType(stereoObj.rightSourRect, Rectangle), Me._rightEyeSurface, CType(stereoObj.rightDestRec, Rectangle), TextureFilter.None)
Next
'Write all single eye objects to their proper surface
For Each SingleEyeObj In singleEyeList
If SingleEyeObj.displayEye = SingleEyeImage.EYE.Left Then
Me._device.StretchRectangle(SingleEyeObj.surface, CType(SingleEyeObj.sourRect, Rectangle), Me._leftEyeSurface, CType(SingleEyeObj.destRec, Rectangle), TextureFilter.None)
ElseIf SingleEyeObj.displayEye = SingleEyeImage.EYE.Right Then
Me._device.StretchRectangle(SingleEyeObj.surface, CType(SingleEyeObj.sourRect, Rectangle), Me._rightEyeSurface, CType(SingleEyeObj.destRec, Rectangle), TextureFilter.None)
ElseIf SingleEyeObj.displayEye = SingleEyeImage.EYE.Both Then
Me._device.StretchRectangle(SingleEyeObj.surface, CType(SingleEyeObj.sourRect, Rectangle), Me._leftEyeSurface, CType(SingleEyeObj.destRec, Rectangle), TextureFilter.None)
Me._device.StretchRectangle(SingleEyeObj.surface, CType(SingleEyeObj.sourRect, Rectangle), Me._rightEyeSurface, CType(SingleEyeObj.destRec, Rectangle), TextureFilter.None)
End If
Next
Dim destRect As New Rectangle(0, 0, Me._size.Width, Me._size.Height)
Me._device.StretchRectangle(Me._leftEyeSurface, CType(Me._size, Rectangle), Me._offScreenSurface, CType(destRect, Rectangle), TextureFilter.None)
destRect.X = Me._size.Width
Me._device.StretchRectangle(Me._rightEyeSurface, CType(Me._size, Rectangle), Me._offScreenSurface, CType(destRect, Rectangle), TextureFilter.None)
Dim gStream As GraphicsStream = Me._offScreenSurface.LockRectangle(LockFlags.None)
Dim data As Byte() = New Byte() {78, 86, 51, 68, 0, 15, 0, 0, 56, 4, 0, 0, 32, 0, 0, 0, 2, 0, 0, 0}
gStream.Seek(CLng((((Me._size.Width * 2) * Me._size.Height) * 4)), SeekOrigin.Begin)
gStream.Write(data, 0, data.Length)
gStream.Close()
Me._offScreenSurface.UnlockRectangle()
Me._device.BeginScene()
Me._backBuffer = Me._device.GetBackBuffer(0, 0, BackBufferType.Mono)
Me._device.StretchRectangle(
Me._offScreenSurface, CType(New Rectangle(0, 0, (Me._size.Width * 2), (Me._size.Height + 1)), Rectangle),
Me._backBuffer, CType(New Rectangle(0, 0, Me._size.Width, Me._size.Height), Rectangle), TextureFilter.None)
Me._backBuffer.ReleaseGraphics()
Me._device.EndScene()
Try
Me._device.Present()
Catch ex As DeviceLostException
'Indicate that the device has been lost
deviceIsLost = True
'Send a message to the debug output that the device was lost
Debug.WriteLine("Device was lost... Attempting to recover device")
waitForDeviceRecovery()
renderScene()
End Try
MyBase.Invalidate()
End Sub
Public Overridable Sub waitForDeviceRecovery()
Do
Try
_device.TestCooperativeLevel()
Catch ex As DeviceLostException
Catch ex2 As DeviceNotResetException
Try
For Each Val As StereoObject In stereoObjectList
Val.Dispose()
Next
For Each Val As SingleEyeImage In singleEyeList
Val.Dispose()
Next
Me._offScreenSurface.Dispose()
Me._backBuffer.Dispose()
Me._blankScreenSurface.Dispose()
Me._leftEyeSurface.Dispose()
Me._rightEyeSurface.Dispose()
Me._backBuffer.Dispose()
_device.Reset(presentParams)
deviceIsLost = False
For Each Val As StereoObject In stereoObjectList
Val.loadSurfaces(Val.objName)
Next
For Each Val As SingleEyeImage In singleEyeList
Val.loadSurface(Val.objName)
Next
createSurfaces()
'Send a message to debug out put that the device was recovered
Debug.WriteLine("The device was successfully recoverd")
Catch ex As DeviceLostException
End Try
End Try
Loop Until deviceIsLost = False
End Sub
Public Overrides Sub closeDisplay()
For Each val As StereoObject In stereoObjectList
val.Dispose()
Next
For Each val As SingleEyeImage In singleEyeList
val.Dispose()
Next
MyBase.Dispose()
Dispose()
Close()
End Sub
Public Shared Function computeDispayableDisparity(ByVal requestedLevel As Double,
ByVal displaySettings As DisplaySettings,
ByVal numSubPixelShifts As Integer) As Double
Dim arcPerSub As Double = displaySettings.arcSecsPerPixel / numSubPixelShifts
Dim leftOver As Double = requestedLevel Mod arcPerSub
If leftOver < arcPerSub / 2 Then
Return requestedLevel - leftOver
Else
Return requestedLevel - leftOver + arcPerSub
End If
End Function
End Class
Public Class StereoObject 公共场所作为点 Public objName As String 公共实际的Disparty As Double Public numSubPixelLevels As Integer
Private subPixelSurfaces As Surface()
Private subPixelImages As Image()
Private parentDisplay As StereoStimulusDisplay
Public leftDestRec As Rectangle
Public rightDestRec As Rectangle
Public leftSourRect As Rectangle
Public rightSourRect As Rectangle
Public leftSurface As Surface
Public rightSurface As Surface
Public Sub New(ByVal objName As String, ByVal pos As Point, ByVal disparity As Double, ByVal numSubPixelLevels As Integer, ByRef parentDisplay As StereoStimulusDisplay)
Me.objName = objName
Me.pos = pos
Me.numSubPixelLevels = numSubPixelLevels
Me.parentDisplay = parentDisplay
ReDim subPixelSurfaces(numSubPixelLevels - 1)
ReDim subPixelImages(numSubPixelLevels - 1)
' Create and load the surfaces for all of the diffrent sub pixels images
loadSurfaces(objName)
Me.actualDisparty = updateDisparity(disparity)
End Sub
Public Function updateDisparity(ByVal newDisparty As Double) As Double
Dim disparityDirection As Integer, pxlDisparity As Double, halfShift As Double, nPixelShift As Integer, fracDisparity As Double
Dim nSubPixelShift As Integer, halfSubR As Double, halfSubL As Double, pxlR As Integer, pxlL As Integer, subR As Integer, subL As Integer
Dim actualDisparity As Double
'disparityDirection + backward; - forward(crossed)
If newDisparty > 0.0 Then
disparityDirection = 1
Else
disparityDirection = -1
newDisparty *= -1
End If
Dim test As Double = Me.parentDisplay.displaySettings.arcSecsPerPixel()
pxlDisparity = newDisparty * Me.parentDisplay.displaySettings.pixelsPerArcSec()
halfShift = pxlDisparity / 2.0
nPixelShift = CType(Math.Truncate(halfShift), Integer)
fracDisparity = halfShift - nPixelShift
nSubPixelShift = CType(Math.Round(fracDisparity * 32), Integer)
halfSubR = nSubPixelShift \ 2
halfSubL = nSubPixelShift - halfSubR
pxlR = nPixelShift
subR = halfSubR
pxlL = -nPixelShift
If nSubPixelShift = 0 Then
subL = 0
Else
subL = Me.numSubPixelLevels - halfSubL
pxlL -= 1
End If
If pxlR = 0 And subR = 0 And pxlL = 0 And subL = 0 Then
subR = 1
End If
actualDisparity = Me.parentDisplay.displaySettings.arcSecsPerPixel() * (Math.Abs(pxlL * Me.numSubPixelLevels + subL) + Math.Abs(pxlR * Me.numSubPixelLevels + subR)) / Me.numSubPixelLevels
If actualDisparity = 0 Then
subR = 1
actualDisparity = Me.parentDisplay.displaySettings.arcSecsPerPixel() / Me.numSubPixelLevels
End If
If disparityDirection > 0 Then
Me.leftDestRec = New Rectangle(pos.X + pxlL, pos.Y, subPixelImages(subL).Width, subPixelImages(subL).Height)
Me.rightDestRec = New Rectangle(pos.X + pxlR, pos.Y, subPixelImages(subR).Width, subPixelImages(subR).Height)
Me.leftSourRect = New Rectangle(0, 0, subPixelImages(subL).Width, subPixelImages(subL).Height)
Me.rightSourRect = New Rectangle(0, 0, subPixelImages(subR).Width, subPixelImages(subR).Height)
Me.leftSurface = subPixelSurfaces(subL)
Me.rightSurface = subPixelSurfaces(subR)
Else
Me.leftDestRec = New Rectangle(pos.X + pxlR, pos.Y, subPixelImages(subR).Width, subPixelImages(subR).Height)
Me.rightDestRec = New Rectangle(pos.X + pxlL, pos.Y, subPixelImages(subL).Width, subPixelImages(subL).Height)
Me.rightSourRect = New Rectangle(0, 0, subPixelImages(subL).Width, subPixelImages(subL).Height)
Me.leftSourRect = New Rectangle(0, 0, subPixelImages(subR).Width, subPixelImages(subR).Height)
Me.leftSurface = subPixelSurfaces(subR)
Me.rightSurface = subPixelSurfaces(subL)
End If
Return actualDisparity
End Function
Public Sub loadSurfaces(ByVal fileName As String)
Dim name As String = ""
For i As Integer = 0 To numSubPixelLevels - 1
name = Application.StartupPath & "\input\StereoObjectFiles\" & fileName & "\" & fileName & "_" & i.ToString() & ".bmp"
subPixelImages(i) = Image.FromFile(name)
subPixelSurfaces(i) = Surface.FromBitmap(parentDisplay._device, CType(DirectCast(subPixelImages(i), Bitmap), Bitmap), Pool.Default)
Next
End Sub
Public Sub Dispose()
leftSurface.Dispose()
rightSurface.Dispose()
For Each surf As Surface In subPixelSurfaces
surf.Dispose()
Next
For Each img As Image In subPixelImages
img.Dispose()
Next
End Sub
Public Sub setPos(ByVal pos As Point)
Me.pos = pos
Me.updateDisparity(Me.actualDisparty)
End Sub
Public Sub setToTrueZeroDepth()
Me.leftDestRec = New Rectangle(pos.X, pos.Y, subPixelImages(0).Width, subPixelImages(0).Height)
Me.rightDestRec = Me.leftDestRec
Me.leftSourRect = New Rectangle(0, 0, subPixelImages(0).Width, subPixelImages(0).Height)
Me.leftSourRect = New Rectangle(0, 0, subPixelImages(0).Width, subPixelImages(0).Height)
Me.leftSurface = subPixelSurfaces(0)
Me.rightSurface = subPixelSurfaces(0)
End Sub
结束班
Public Class SingleEyeImage Enum EYE As Integer 左= 0 对= 1 两者都= 3 结束枚举
Public pos As Point
Public objName As String
Private parentDisplay As StereoStimulusDisplay
Public displayEye As Integer
Public destRec As Rectangle
Public sourRect As Rectangle
Public surface As Surface
Public Sub New(ByVal objName As String, ByVal pos As Point, ByVal eye As EYE, ByRef parentDisplay As StereoStimulusDisplay)
Me.objName = objName
Me.pos = pos
displayEye = eye
Me.parentDisplay = parentDisplay
loadSurface(objName)
End Sub
Public Sub loadSurface(ByVal fileName As String)
'Dim path As String = Application.StartupPath & "\input\SingleEyeObjectFiles\" & fileName & ".bmp"
Dim path As String = Application.StartupPath & "\input\SingleEyeObjectFiles\" & fileName
Dim img As Image = Image.FromFile(path)
surface = surface.FromBitmap(parentDisplay._device, CType(DirectCast(img, Bitmap), Bitmap), Pool.Default)
sourRect = New Rectangle(0, 0, img.Width, img.Height)
destRec = New Rectangle(Me.pos.X, Me.pos.Y, img.Width, img.Height)
img.Dispose()
End Sub
Public Sub setPos(ByVal pos As Point)
Me.pos = pos
Me.destRec.X = pos.X
Me.destRec.Y = pos.Y
End Sub
Public Sub Dispose()
surface.Dispose()
End Sub
结束班