Managed DirectX First Steps: Direct 3D Basics and DirectX vs. GDI+ - The Coding Phase
(Page 8 of 14 )
Before you start any coding in your project, you need to set a reference to the Direct3D and DX3D components of Managed DirectX. To add the references, choose Project ? Add Reference, and locate the appropriate components on the list in the .NET components tab. If the components aren’t in the list, then you possibly don’t have the Managed DirectX interface installed on your computer. Because this interface is included with DirectX 9.0, you’ll need to download and install the latest version of the DirectX SDK from the Microsoft DirectX developer site at http://msdn.microsoft.com/directx.
First Step: Coding the Main Window You’ll start by coding the main window, which will allow you to see your hardware capabilities, and then you can code the tests one by one, from the simpler to the more complex ones.
After creating the main window, as shown in the visual prototype in the project phase, you need to know the functions that list the adapters, devices, display modes supported, and capabilities. A quick look in SDK help shows you these methods and properties of the Manager object:
- Adapters.Count: Returns the number of adapters in the machine. Remember that it’s possible for a single graphics board to have more than one adapter.
- Adapter[n].Information: Returns the adapter characteristics, according to an ordinal adapter number.
- GetDeviceCaps: Returns the device capabilities in a complex structure. The function receives the ordinal number of the adapter and the type of the device (Hardware or Reference). Remember, Reference is software-based and always supported; Hardware is hardware-based and depends on the boards installed.
- Adapters[n].SupportedDisplayModes: Returns the characteristics of a specific display mode, given its ordinal number.
- CheckDeviceType: Checks if a specific display mode is supported by the current device.
A quick look in the DirectX SDK help will also show you that most of these functions don’t return a readable description (which could be used to fill the list), so you’ll need to create some functions to return display names where appropriate.
Because all the information between the lists are related (the devices supported may vary for each adapter, and the display modes and device characteristics may vary depending on the device), it’s better to force an update of the related list every time a new item is selected on a high-order list. Your program’s basic structure will be as follows:
On the "load" event:
Load the adapters list
Select the first list item, in order to fire the
selection changed event
On the adapter list "selected item changed" event:
Reload the device list
Select the first list item, in order to fire the
selection changed event
On the device list "selected item changed" event:
Reload the display modes list
Reload the device capabilities list
Because you’ll be using the Device object all over the form, you can create the variable at form level.
Device device;
In the Load event, you can call the ListAdapters function:
private void FrmDirectX_Load(object sender,
System.EventArgs e) {
// Fill the Adapters list.
ListAdapters();
}
public void ListAdapters() {
AdapterDetails adapterInfo;
// Add each adapter to the LstAdapters list box.
foreach(AdapterInformation info in Manager.Adapters) {
AdaptersListBox.Items.Add (info. Information.
Description);
}
// Select the first available index, in order to fire
the change event.
AdaptersListBox.SelectedIndex = 0;
}
private void FrmDirectX_Closing(object sender,
CancelEventArgs e) {
if(device != null) {
device.Dispose();
}
device = null;
{
If you run your code now, you’ll see the first list filled with the adapters’ descriptions. The devices list, which must be filled for each adapter chosen, will always have one or two members: the Reference Rasterizer, which will always be present, and a hardware abstraction layer (HAL) rasterizer, which will be present only if supported by a 3-D board. To check the presence of hardware acceleration, you can query the device capacities using the previously shown function, and if there’s no error, then you can add the HAL to your list.
The function for filling the devices list and the code for calling it (in the event that handles the selected item change at the adapters list) is shown in the following code sample:
private void AdaptersListBox_SelectedIndexChanged
(object sender,System.EventArgs e){
// Update the devices list every time a new adapter is
chosen.
ListDevices(AdaptersListBox.SelectedIndex);
}
public void ListDevices(int adapter) {
Caps deviceCaps;
// Add each supported device to the DevicesListBox list
box.
DevicesListBox.Items.Clear();
// The Reference Rasterizer will always be supported.
DevicesListBox.Items.Add("Reference Rasterizer (REF)");
// If there’s no error when getting the HAL
capabilities,
// then you have a hardware acceleration board
installed.
try {
deviceCaps = Manager.GetDeviceCaps(adapter,
DeviceType.Hardware);
DevicesListBox.Items.Add("Hardware Acceleration
(HAL)");
}
catch {
}
// Select the first available index, in order to fire
the change event.
DevicesListBox.SelectedIndex = 0;
}
The display modes will depend on the adapter and the device chosen, so you can create a function (ListDisplayModes) that will receive this information as parameters, and call it on the selection change event of the devices list box.
private void DevicesListBox_SelectedIndexChanged
(object sender, System.EventArgs e) {
// The first entry in DevicesListBox is the Reference
Rasterizer.
DeviceType deviceType = (DevicesListBox.SelectedIndex
== 0) ?
DeviceType.Reference : DeviceType.Hardware;
ListDisplayModes(AdaptersListBox.SelectedIndex,
deviceType, Format.X8R8G8B8);
ListDisplayModes(AdaptersListBox.SelectedIndex,
deviceType, Format.X1R5G5B5);
ListDisplayModes(AdaptersListBox.SelectedIndex,
deviceType, Format.R5G6B5);
ListDeviceCaps(AdaptersListBox.SelectedIndex,
deviceType);
}
Listing the display modes isn’t as straightforward as listing the adapters. First you must check if every mode returned by the adapter is supported by the device, and then you must compose each list item with a combination of various properties that will uniquely identify each display mode as listed here:
- Width, Height: The width and height of the screen. If creating a full-screen device, these properties will define the resolution of the screen; when in windowed mode, Direct3D will manage to create the device without errors only if the current display is one of these resolutions.
- Format: The format of the display mode, as explained in the section “Understanding Display Modes.”
- RefreshRate: The monitor refresh rate, in MHz, or 0 if the default. Usually you don’t have to care about this, but it’s possible for a device to support the same resolution with different refresh rates, so it’s better to list it in your list box, or you could finish with duplicated entries.
Because the Format property returns a member of the Format enumeration, you simply use the ToString() method, available by default to all classes to display the enumeration value. You can now complete the Display Modes list box as follows:
private void ListDisplayModes(int adapter, DeviceType
renderer,
Format adapterFormat) {
DisplayModesListBox.Items.Clear();
foreach(DisplayMode dispMode in
Manager.Adapters[adapter]. Supported
DisplayModes) {
// Check to see if the display mode is supported by the
device.
if(Manager.CheckDeviceType
(adapter, renderer, dispMode.Format, dispMode.Format,
false)) {
// Fill the display modes list with the width, height,
// mode name, and refresh rate.
DisplayModesListBox.Items.Add(dispMode.Width +
"x" + dispMode.Height +" ( "+
DirectXLists.DisplayModeName(dispMode.Format) +
" - " + dispMode.RefreshRate + "Khz)");
}
}
}
Running your program now, you can see the first three list boxes filled with information, as shown in Figure 3-22.

Figure 3-22. The filled Adapters, Rendering Devices, and Display Modes list boxes
The last list box, which will list the device capabilities, will be a tougher one to fill if you want to have explicit control over what you list. The simplest way to list device capabilities is to simply call the ToString() function of the devCaps variable. However, you want to learn how to access different capabilities in a customized way. Because the function GetDeviceCaps returns a complex structure with many dozens of flags, organized in many different enumerations, you must create functions to return readable strings for each property. You’ll use the descriptions provided in SDK help to create the functions that will list the most important flags for the purposes of this example, but there are some you’ll leave aside. If you want to create a comprehensive list, just follow the steps explained here.
The first function you create checks for some simple flags in the Caps structure and adds the appropriate strings to the Device Capabilities list box.
public static void ListGeneralCaps(Caps devCaps, ListBox
listCaps) {
listCaps.Items.Add(" -----General Caps -----------------
--------");
if(devCaps.MaxActiveLights == -1) {
listCaps.Items.Add("Maximum Active Lights:
Unlimited");
}
else {
listCaps.Items.Add("Maximum Active Lights: " +
devCaps.MaxActiveLights);
}
if(devCaps.MaxPointSize == 1){
listCaps.Items.Add("Device does not support point
size control");
}
else {
listCaps.Items.Add("Maximum point primitive size: "+
devCaps.MaxPointSize);
}
listCaps.Items.Add("Maximum Primitives in each
DrawPrimitives call: " +
devCaps.MaxPrimitiveCount);
listCaps.Items.Add("Maximum textures simultaneously
bound: " +
devCaps.MaxSimultaneousTextures);
listCaps.Items.Add("Maximum Texture aspect ratio: " +
devCaps.MaxTextureAspectRatio);
listCaps.Items.Add("Maximum Texture size: " +
devCaps.MaxTextureWidth +
"x" + devCaps.MaxTextureHeight);
listCaps.Items.Add("Maximum matrixes blending: " +
devCaps.MaxVertexBlendMatrices);
listCaps.Items.Add("Maximum vertex shaders registers: "
+
devCaps.MaxVertexShaderConst);
}
To help you understand specific device capabilities, create many other functions with the same basic structure: a simple sequence of if commands, each one testing for a specific flag within the composed flag members. The following code shows an example of such a function, one that lists the flags that compose the Caps member of DriverCaps:
public static void ListDriverCaps(DriverCaps driverCaps,
ListBox listCaps) { listCaps.Items.Add(" -----Driver
Caps ------------------------");
if(driverCaps.SupportsDynamicTextures);
listCaps.Items.Add("The driver support Dynamic
textures");
}
if(driverCaps.CanCalibrateGamma) {
listCaps.Items.Add("The driver can automatically
adjust the gamma ramp");
}
if(driverCaps.SupportsFullscreenGamma) {
{listCaps.Items.Add("The driver supports dynamic "
+
"gamma ramp adjustment in full-screen mode. ");
}
}
Each if statement in this kind of function tests a specific Boolean value inside the composed flag. In this sample, DriverCaps is a structure with many composed flags, each one being a Boolean value associated with a specific driver feature.
You create similar functions to list the flags for the TextureCaps, RasterCaps, DeviceCaps, and TextureCaps members. Because they present the same structure, and the information they add to the list box is basically the one found in SDK help, we won’t reproduce them here; for those interested, they can be found in the downloadable source code.
You can create a special function now that will retrieve the Caps structure for the current device and call the functions created as mentioned previously:
private void ListDeviceCaps(int adapter, DeviceType
deviceType) {
DeviceCapsListBox.Items.Clear();
Caps caps = Manager.GetDeviceCaps(adapter, deviceType);
// List some general driver capabilities.
DirectXLists.ListGeneralCaps(caps, DeviceCapsListBox);
// List the device capabilities.
DirectXLists.ListDevCaps(caps.DeviceCaps,
DeviceCapsListBox);
// List specific driver capabilities.
DirectXLists.ListDriverCaps(caps.DriverCaps,
DeviceCapsListBox);
// List rasterizer capabilities.
DirectXLists.ListRasterCaps(caps.RasterCaps,
DeviceCapsListBox);
// List texture capabilities.
DirectXLists.ListTextureCaps(caps.TextureCaps,
DeviceCapsListBox);
}
You must include a call to this function in the SelectedItemChanged event handler for the Devices list box, so the list gets updated for every new device chosen in the list. Figure 3-23 presents the finished main window of this chapter sample.

Figure 3-23. The finished main window
This chapter is from Beginning .NET Game Programming in C# by David Weller et. al.(Apress, 2004, ISBN: 1590593197). Check it out at your favorite bookstore today. Buy this book now.
|
Next: Second Step: Coding Your First Windowed Test >>
More ASP.NET Articles
More By Apress Publishing