DirectDraw:
Enumeration
By: Jack Hoxley
Written: May 2000
Download:
DD_Enum.Zip
(165kb)
Enumeration is a very important
process and any professional application should use it.
Assuming that you've done (or
know about) DirectDraw Fullscreen mode, you will be aware that you need to specify
a screen width, height and colour depth. If you're using one of the common resolutions
(640x480, 800x600) then you can be fairly confident that most computers will
support that resolution. But you can't be 100% sure.
Enumeration is the process of
checking the available capabilities; it is widely used in DirectX and it is
good practise to use it. Using enumeration you can collect information on what
display modes are available and what colour depths can be used; more importantly
when you get onto the higher level functions, particularly hardware specific
functions - You can use enumeration to check the computer to see if it is capable
of doing what you want it to do. It can also be used a a safety net; for example,
if you attempt to use a gamma control and the hardware does not support it you'll
get errors and your program can crash. If, before hand, you'd enumerated the
hardware you will know that the hardware does/doesn't support gamma controls.
Enumeration is used by all the
big games, look through any popular game and you'll see various options relating
to hardware - use Triple-buffering, use 32bit textures, use 32bit display modes,
enable 3D sound... and so on. This is all done through enumeration.
The Code
This tutorial will cover the basics of enumeration. At the end of this tutorial
you will have a simple program that lists the important hardware capabilites
of the current computer and the available display modes.
Step 1: The interface
It is probably easier if you download the above zip file for this part,
but if you can't/won't then follow these steps.
1. Add two frames to the form, as shown in the picture. Make the captions "Display
Mode" and "Other Details".
2. Add a dropdown combo box to the first frame, name it "cmbArray"
and makes it's "style" property to equal 2 Add the label in if you
want to - but it isn't necessary.
3. Add a Label to the same frame and call it lblinfo
4. Add a Listbox to the second frame and call it "lstHardware"
5. Add a command button outside the two frames and at the bottom of the window.
6. Make the command button have a caption of "Run A Test DirectDraw Application
in the selected Display Mode"
You're window should now look something like:
Step 2: The Variables
The varaibles are almost identical
to the fullscreen tutorial - Apart from the top two sections it is identical.
As is a lot of the code. For more information on the variables, see the fullscreen
mode tutorial.
Dim dx As
New DirectX7
'This data structure is to simplify getting/using
the display
'modes AFTER they have been enumerated.
Private Type DisplayModeDesc
Width As Long
Height As Long
BPP As Byte
End Type
Dim arr_DisplayModes() As DisplayModeDesc 'note
the fact that I haven't
'specified how big the array is to be. This allows me to resize it later
on.
Dim binit As Boolean
Dim dd As DirectDraw7
Dim Mainsurf As DirectDrawSurface7
Dim primary As DirectDrawSurface7
Dim backbuffer As DirectDrawSurface7
Dim ddsd1 As DDSURFACEDESC2
Dim ddsd2 As DDSURFACEDESC2
Dim ddsd3 As DDSURFACEDESC2
Dim ddsd4 As DDSURFACEDESC2
Dim brunning As Boolean
Dim CurModeActiveStatus As Boolean
Dim bRestore As Boolean |
|
|
|
Step 3: The procedures
This section is for
all the core-code procedures. All apart from two are directly copied from the
DirectDraw: Fullscreen mode tutorial, but most of the comments have been taken
out. Please go to the Fullscreen tutorial for
a full explanation
Sub Init()
On Local Error GoTo errOut 'If there is an error
we end the program.
Set dd = dx.DirectDrawCreate("")
frmDummy.Show
Call dd.SetCooperativeLevel(frmDummy.hWnd, DDSCL_FULLSCREEN Or DDSCL_ALLOWMODEX
Or DDSCL_EXCLUSIVE)
Dim reqWidth As Long, reqHeight As Long, reqDepth As Byte
reqWidth = arr_DisplayModes(cmbArray.ListIndex + 1).Width
reqHeight = arr_DisplayModes(cmbArray.ListIndex + 1).Height
reqDepth = arr_DisplayModes(cmbArray.ListIndex + 1).BPP
Call dd.SetDisplayMode(reqWidth, reqHeight, reqDepth, 0, DDSDM_DEFAULT)
ddsd1.lFlags = DDSD_CAPS Or DDSD_BACKBUFFERCOUNT
ddsd1.ddsCaps.lCaps = DDSCAPS_PRIMARYSURFACE Or DDSCAPS_FLIP Or DDSCAPS_COMPLEX
ddsd1.lBackBufferCount = 1
Set primary = dd.CreateSurface(ddsd1)
'Get the backbuffer
Dim caps As DDSCAPS2
caps.lCaps = DDSCAPS_BACKBUFFER
Set backbuffer = primary.GetAttachedSurface(caps)
backbuffer.GetSurfaceDesc ddsd3
backbuffer.SetFontTransparency True
backbuffer.SetForeColor RGB(0, 0, 0)
' init the surfaces
InitSurfaces
binit = True
brunning = True
Do While brunning
blt
DoEvents
Loop
errOut:
EndIt
End Sub
Sub blt()
On Local Error GoTo errOut
If binit = False Then Exit Sub
Dim ddrval As Long
Dim rBack As RECT
bRestore = False
Do Until ExModeActive
DoEvents
bRestore = True
Loop
' if we lost and got back the surfaces, then restore them
DoEvents
If bRestore Then bRestore = False
dd.RestoreAllSurfaces
InitSurfaces
End If
'get the area of the screen where our window is
'this sets the rectangle to be the size of the screen.
rBack.Bottom = ddsd3.lHeight
rBack.Right = ddsd3.lWidth
ddrval = backbuffer.BltFast(0, 0, Mainsurf, rBack, DDBLTFAST_WAIT)
Call backbuffer.DrawText(10, 10, "Press Any Key To Continue", False)
'flip the back buffer to the screen
primary.Flip Nothing, DDFLIP_WAIT
errOut:
End Sub
Sub EndIt()
Call dd.RestoreDisplayMode
Call dd.SetCooperativeLevel(Me.hWnd, DDSCL_NORMAL)
Unload frmDummy
End Sub
Function ExModeActive() As Boolean
'This is used to test if we're in the correct
resolution.
Dim TestCoopRes As Long
TestCoopRes = dd.TestCooperativeLevel
If (TestCoopRes = DD_OK) Then
ExModeActive = True
Else
ExModeActive = False
End If
End Function
Sub InitSurfaces()
Set Mainsurf = Nothing
'load the bitmap into a surface - backdrop.bmp
ddsd4.lFlags = DDSD_CAPS Or DDSD_HEIGHT Or DDSD_WIDTH
ddsd4.ddsCaps.lCaps = DDSCAPS_OFFSCREENPLAIN
ddsd4.lWidth = ddsd3.lWidth
ddsd4.lHeight = ddsd3.lHeight
Set Mainsurf = dd.CreateSurfaceFromFile(App.Path & "\backdrop.bmp",
ddsd4)
End Sub
Sub GetDisplayModes()
'This is the actual code that reports back what
display modes are available.
'You could modify this to be a function, which enumerates the display
modes
'and runs through the list until it finds the one that you want. ie.
You're program
'runs in 800x600 in 32bpp mode; create a function that searches through
the
'available modes UNTIL it finds the one you want (800x600x32), at this
point it
'reports back True or false......
Dim DisplayModesEnum As DirectDrawEnumModes
Dim ddsd2 As DDSURFACEDESC2
Dim dd As DirectDraw7 'These two lines can also
be seen in the Init sub. This time
'it doesn't go to fullscreen mode
Set dd = dx.DirectDrawCreate("")
dd.SetCooperativeLevel Me.hWnd, DDSCL_NORMAL
'Create the Enumeration object
Set DisplayModesEnum = dd.GetDisplayModesEnum(0, ddsd2)
'Remember the array that wasn't defined? At this
point
'we set the size of the array.
ReDim arr_DisplayModes(DisplayModesEnum.GetCount()) As DisplayModeDesc
'This loop runs through the display modes, retrieving
the data.
'Height/Width/BPP aren't the only things that you can retrieve here....
For i = 1 To DisplayModesEnum.GetCount()
DisplayModesEnum.GetItem i, ddsd2
cmbArray.AddItem CStr(ddsd2.lWidth) & "x" & CStr(ddsd2.lHeight) & "
" & Str(ddsd2.ddpfPixelFormat.lRGBBitCount) & "bpp"
cmbArray.Text = CStr(ddsd2.lWidth) & "x" & CStr(ddsd2.lHeight) & " "
& CStr(ddsd2.ddpfPixelFormat.lRGBBitCount) & "bpp"
'This fills out the data structure to include
information
'on the current display mode.
arr_DisplayModes(i).Height = ddsd2.lHeight
arr_DisplayModes(i).Width = ddsd2.lWidth
arr_DisplayModes(i).BPP = ddsd2.ddpfPixelFormat.lRGBBitCount
Next i
'Directdraw is no longer needed - destroy it.
Set dd = Nothing
'Fill out the user-display label
lblinfo.Caption = "Display Mode index = " & cmbArray.ListIndex + 1 &
vbCr
lblinfo.Caption = lblinfo.Caption & " Height = " & CStr(arr_DisplayModes(cmbArray.ListIndex
+ 1).Height) & vbCr lblinfo.Caption = lblinfo.Caption & " Width = "
& CStr(arr_DisplayModes(cmbArray.ListIndex + 1).Width) & vbCr lblinfo.Caption
= lblinfo.Caption & " Colour Depth = " & CStr(arr_DisplayModes(cmbArray.ListIndex
+ 1).BPP) & vbCr
End Sub
Sub GetDDCaps()
'This part returns the capabilities of DirectDraw
'You only really need this information if you're going to do
'any technical stuff.
Dim dd As DirectDraw7
Dim hwCaps As DDCAPS 'HARDWARE
Dim helCaps As DDCAPS 'SOFTWARE EMULATION
Set dd = dx.DirectDrawCreate("")
dd.SetCooperativeLevel Me.hWnd, DDSCL_NORMAL
dd.GetCaps hwCaps, helCaps
'how much video memory is available
lstHardware.AddItem "GENERAL INFORMATION"
'The memory amount can be useful. If you know
that you're surfaces require
'450kb of memory then you can check if the host computer has this much
memory.
lstHardware.AddItem " total video memory " & CStr(hwCaps.lVidMemTotal)
& " bytes (" & CStr(Format$(hwCaps.lVidMemTotal / 1024, "#.0")) & "Kb)"
lstHardware.AddItem " free video memory " & CStr(hwCaps.lVidMemFree)
& " bytes (" & CStr(Format$(hwCaps.lVidMemFree / 1024, "#.0")) & "Kb)"
lstHardware.AddItem " There are " & hwCaps.lNumFourCCCodes & " FourCC
codes available"
lstHardware.AddItem ""
lstHardware.AddItem "HARDWARE CAPABILITIES"
'You can get a list of what these constants mean
in the
'sdk help file. If you don't have the help file you're a bit stuck!
lVal = hwCaps.ddsCaps.lCaps2
If lVal And DDCAPS2_CANCALIBRATEGAMMA Then
lstHardware.AddItem " Supports gamma correction"
Else
lstHardware.AddItem " No support for gamma correction"
End If
If lVal And DDCAPS2_CERTIFIED Then
lstHardware.AddItem " The driver is certified"
Else
lstHardware.AddItem " The driver is not certified"
End If
If lVal And DDCAPS2_WIDESURFACES Then
lstHardware.AddItem " support for surfaces wider than the screen"
Else
lstHardware.AddItem " No support for surfaces wider than the screen"
End If
lVal = hwCaps.lSVBFXCaps
If lVal And DDFXCAPS_BLTALPHA Then
lstHardware.AddItem " Support for Alpha Blended Blit operations"
Else
lstHardware.AddItem " No support for Alpha Blended Blit operations"
End If
If lVal And DDFXCAPS_BLTROTATION Then
lstHardware.AddItem " Support for rotation Blit operations"
Else
lstHardware.AddItem " No support for rotation Blit operations"
End If
lVal = hwCaps.lSSBCaps
If lVal And DDCAPS_3D Then
lstHardware.AddItem " Support for 3D Acceleration"
Else
lstHardware.AddItem " No support for 3D acceleration"
End If
If lVal And DDCAPS_BLTQUEUE Then
lstHardware.AddItem " Support for asynchronous blitting"
Else
lstHardware.AddItem " No support for asynchronous blitting"
End If
If lVal And DDCAPS_BLTSTRETCH Then
lstHardware.AddItem " Support for stretching during Blit operations"
Else
lstHardware.AddItem " No support for stretching during blit operations"
End If
If lVal And DDCAPS_NOHARDWARE Then
lstHardware.AddItem " Hardware support is available"
Else
lstHardware.AddItem " No hardware support"
End If
'//////////////////////SOFTWARE\\\\\\\\\\\\\\\\\\\\\\\\\\\\
lstHardware.AddItem "SOFTWARE CAPABILITIES"
lVal = helCaps.ddsCaps.lCaps2
If lVal And DDCAPS2_WIDESURFACES Then
lstHardware.AddItem " The device supports surfaces wider than the screen"
Else
lstHardware.AddItem " The device does not support surfaces wider than
the screen"
End
If lVal = helCaps.lSVBFXCaps
If lVal And DDFXCAPS_BLTALPHA Then
lstHardware.AddItem " Software supports Alpha Blended Blit operations"
Else
lstHardware.AddItem " No Software support for Alpha Blended Blit operations"
End If
If lVal And DDFXCAPS_BLTROTATION Then
lstHardware.AddItem " Software supports rotation Blit operations"
Else
lstHardware.AddItem " No software support for rotation Blit operations"
End If
lVal = helCaps.lSSBCaps
If lVal And DDCAPS_3D Then
lstHardware.AddItem " Software supports 3D Acceleration"
Else
lstHardware.AddItem " No software support for 3D acceleration"
End If
If lVal And DDCAPS_BLTQUEUE Then
lstHardware.AddItem " Software supports asynchronous blitting"
Else
lstHardware.AddItem " No software support for asynchronous blitting"
End If
Set dd = Nothing
End Sub |
|
|
|
Step 4: Linking
code and interface code
This is the last section of this tutorial. It deals with linking the procedures
together and finishing off the interface.
'These Next
three procedures just update the user-display with the
'information he/she has just selected.
Private Sub cmbArray_Click()
lblinfo.Caption = "Display Mode index = " & cmbArray.ListIndex + 1 & vbCr
lblinfo.Caption = lblinfo.Caption & " Height = " & CStr(arr_DisplayModes(cmbArray.ListIndex
+ 1).Height) & vbCr lblinfo.Caption = lblinfo.Caption & " Width = " &
CStr(arr_DisplayModes(cmbArray.ListIndex + 1).Width) & vbCr lblinfo.Caption
= lblinfo.Caption & " Colour Depth = " & CStr(arr_DisplayModes(cmbArray.ListIndex
+ 1).BPP) & vbCr
End Sub
Private Sub cmbArray_KeyDown(KeyCode As Integer, Shift As Integer)
lblinfo.Caption = "Display Mode index = " & cmbArray.ListIndex + 1 & vbCr
lblinfo.Caption = lblinfo.Caption & " Height = " & CStr(arr_DisplayModes(cmbArray.ListIndex
+ 1).Height) & vbCr lblinfo.Caption = lblinfo.Caption & " Width = " &
CStr(arr_DisplayModes(cmbArray.ListIndex + 1).Width) & vbCr lblinfo.Caption
= lblinfo.Caption & " Colour Depth = " & CStr(arr_DisplayModes(cmbArray.ListIndex
+ 1).BPP) & vbCr
End Sub
Private Sub cmbArray_Scroll()
lblinfo.Caption = "Display Mode index = " & cmbArray.ListIndex + 1 & vbCr
lblinfo.Caption = lblinfo.Caption & " Height = " & CStr(arr_DisplayModes(cmbArray.ListIndex
+ 1).Height) & vbCr lblinfo.Caption = lblinfo.Caption & " Width = " &
CStr(arr_DisplayModes(cmbArray.ListIndex + 1).Width) & vbCr lblinfo.Caption
= lblinfo.Caption & " Colour Depth = " & CStr(arr_DisplayModes(cmbArray.ListIndex
+ 1).BPP) & vbCr
End Sub
Private Sub Command1_Click()
'This will start the test display mode
Init
End Sub
Private Sub Form_Load()
Me.Show
'This lists the display modes
GetDisplayModes
'This lists the hardware/software CAPabilities
GetDDCaps
End Sub |
|
|
|
Step 5: frmDummy.frm
One last thing, the dummy form. This is used by DirectDraw when it goes to fullscreen
mode. It is a simple form with these attributes:
1. Pixel Scale Mode
2. Maximises when loaded
3. No Caption or control box.
The only code that is needed for this form is to make it disappear, when the
test resolution is run the user has to press any key before it will close again.
Private
Sub Form_KeyDown(KeyCode As Integer, Shift As Integer)
Call frmMain.EndIt
End Sub |
|
|
|
Simple as that.
The endit procedure was written into the other form, and this just makes a call
to that code. The form_Keydown event is called whenever the user presses any
key (and our window has the focus).
Step 6: A Recursive
Function
It should be fairly easy to make a recursive function that searches for a particular
resolution. Try this following code, everything is contained within a single
module, and is designed to be added into your projects.
Private
enum_dx As New DirectX7
Private enum_DModes As DirectDrawEnumModes
Private enum_ddsd As DDSURFACEDESC2
Private enum_dd As DirectDraw7
Private enum_i As Integer
Public Function FindResolution(reqWidth As Long, reqHeight As Long, reqBPP
As Byte, oHostForm As Form) As Boolean
Set enum_dd = enum_dx.DirectDrawCreate("")
enum_dd.SetCooperativeLevel oHostForm.hWnd, DDSCL_NORMAL
Set enum_DModes = enum_dd.GetDisplayModesEnum(0, enum_ddsd)
FindResolution = False
For enum_i = 1 To enum_DModes.GetCount()
enum_DModes.GetItem enum_i, enum_ddsd
If CLng(enum_ddsd.lHeight) = CLng(reqHeight) Then
If CLng(enum_ddsd.lWidth) = CLng(reqWidth) Then
If CLng(enum_ddsd.ddpfPixelFormat.lRGBBitCount) = CLng(reqBPP) Then
FindResolution = True
Exit Function
End If
End If
End If
Next enum_i
Set enum_dd = Nothing
End Function |
|
|
|
Although this isn't
the simplest code, it is almost identical to the code used earlier; this time
it has none of the array methods or user-interface code - this function is intended
to go on in the background with no intervention by the user.
Feel free to use
this procedure in your DirectDraw projects.
Finished
|