SAMPLE: Rendering a Triangle Using an Execute Buffer

Last reviewed: September 5, 1997
Article ID: Q169769
The information in this article applies to:
  • Microsoft DirectX 3 Software Development Kit, version 3.0

SUMMARY

EBTri is a sample that demonstrates Direct3D Immediate Mode to render a triangle using an execute buffer. To close the application, press the Esc key or Alt+F4 keys.

The following file is available for download from the Microsoft Software Library:

 ~ D3debtri.exe (size: 55902 bytes) 

For more information about downloading files from the Microsoft Software Library, please see the following article in the Microsoft Knowledge Base:

   ARTICLE-ID: Q119591
   TITLE     : How to Obtain Microsoft Support Files from Online Services

MORE INFORMATION

The following files are created when you run the self-executable file:

  • D3d.ico (2Kb)
  • Dxerrors.c (13Kb)
  • Dxerrors.h (1Kb)
  • Ebtri.c (51Kb)
  • Ebtri.def (1Kb)
  • Ebtri.exe (45Kb)
  • Ebtri.mak (8Kb)
  • Ebtri.rc (2Kb)
  • Readme.txt-you are reading this now.
  • Resource.h (1Kb)

How the Sample Works

The following discussion is "walk you through" the code. The entry point of the sample is the same as any other Windows application, WinMain(). This is where all initialization takes place for the sample. The first initialization step is registering the window class and creating the window. After the window is created successfully, then an infinite message loop is entered until the application is closed. DirectX applications (particularly games) should only "run" when the application has the activation. If the application loses activation, or is paused, the application should just process Window's messages instead of running the game loop and trying to render.

There aren't any differences when you register the window class for a DirectX application. The sample registers the class with an icon. This icon is used by Windows to visibly represent the application on the task bar when the application loses activation with a task switch. No menu is used since the sample is a full-screen exclusive mode application. After the standard CreateWindow, ShowWindow, and UpdateWindow calls, the sample initializes DirectDraw and Direct3D in InitDDrawAndD3D().

The window callback procedure processes window messages. WM_ACTIVATEAPP must be handled for a "well-behaved" application. When the application loses activation, set a flag and do not enter the game loop. Let the other applications use the CPU for a while. When the application gains activation, make sure to restore the application's internal status including DirectX. Hide Window's cursor in the WM_SETCURSOR case as described. All DirectDraw applications should render their own cursor. Do not let Windows handle cursor rendering unless a flickering cursor is a feature of the application. Determine the cursor position with GetCursorPos then render your own software cursor. All the other messages are self- explanatory.

Initializing DirectDraw and Direct3D can be complex. The sample uses a user- defined data structure to hold some global info. The key words for initialization are "never assume anything about the hardware". Following are the steps needed to initialize DirectDraw and Direct3D:

  1. Enumerate all DDraw supported video devices on the system. A system can have more than one video card. Use DirectDrawEnumerate to determine which one is the best video card. In the enumerating callback procedure, check which device supports 3D and then save that information. Make sure to enumerate the display mode that the application requires with EnumDisplayModes. Notice that this sample does not include this code. Do not assume that the particular mode that the application requires is supported on all devices. If a device does not support that mode, have an alternate plan. Try to fallback on a different mode.

  2. The sample tries to use the RGB color model. If you use 3D hardware, the RGB color model is essential. In the case of software HEL, RGB is chosen if the processor supports MMX instructions. The fallback is the MONO color model when no acceleration is available.

  3. Once an IDirect3D interface is queried off of an IDirectDraw interface, enumerate the devices supported. This is where you need to check for HW support for particular features if you are going to use HW. If the application requires HW Z-buffer at 16bpp, fog, perspective corrected texturing, point lights, etc., then check and make sure that you have everything you need.

    One point that this sample does not delve into is the idea of enumerating all available devices and letting the user choose. Applications should profile among available devices, display the available options to the user, and then have a default pre-selected.

  4. After you enumerate all of the DirectDraw and Direct3D devices, then you create some surfaces for the rendering engine. You need a primary surface with one back buffer and a Z-buffer. Whether the device is hardware or software determines where the Z-buffer should be allocated. If you use hardware, then the Z-buffer is allocated to video memory if the hardware supports Z-buffers. If you use software, then create the surface in system memory.

  5. The last initialization the code performs is querying for the D3DDEVICE off the back buffer.

The sample is based on the RenderTriangle function. An array of vertices is defined for one triangle with vertices at (0, 1, 0), (1, 0, 0), (-1, 0, 0). The normal of each vertex is -1 z, which points away from the monitor.

To set up the matrices, you need to define the world, view, and projection matrices, and then associate them with the D3DDEVICE with CreateMatrix and SetMatrix.

In SetupLightAndViewport function, a directional light is created and a viewport is setup to map the 3D world to the visible 2D window.

To give the triangle and background some visual appeal, it needs to have colors to define each. D3D materials are used for this purpose. If you use 3D hardware acceleration, the color of the triangle will be green. If you use software, the color will be red. On DX5, if MMX is detected, the triangle will be yellow. The background will always be blue.

To create an execute buffer, applications should first query the device's HAL to determine the maximum execute buffer size. In the case of the hardware device, the value is either zero or greater than zero. If the value is zero, the device will support any size. "Well-behaved" applications should always calculate the exact size needed by counting everything that will be put in the execute buffer (as shown in the sample code). In the case of a software device, calculate the execute buffer size. Create the execute buffer, and then lock the buffer down for use.

To determine where in the execute buffer the vertex list ends and the instruction stream begins, save the pointer to where the execute buffer starts to perform some calculation at the end. Use another pointer to keep track of where the next data stream should be copied to the execute buffer.

Start filling the execute buffer by copying the vertex list. The sample renders only one triangle so copy 3 D3DVERTEX to the execute buffer. Note that the sample depends on D3D to perform transformation and light the vertices. After the vertex list is copied to the execute buffer, cache the location of where the vertex list ends and instruction stream begins.

The first instruction that is stuffed in the execute buffer sets the lighting module, D3DOP_STATELIGHT. Each instruction is followed by one or more data units (D3DSTATE). For the lighting module, a D3DINSTRUCTION is followed by a D3DSTATE to specify the light state type.

You specify the world, view, and projection transforms with D3DOP_STATETRANSFORM. The data follows the instruction, which are 3 D3DSTATE that contain the handles to the world, view, and projection matrices created earlier.

You turn on the Gouraud shade by specifying D3DOP_STATERENDER instruction with D3DRENDERSTATE_SHADEMODE data.

Specify that the vertices need to be transformed and lit by D3D with D3DOP_PROCESSVERTICES and D3DPROCESSVERTICES_TRANSFORMLIGHT data.

Send out the triangle with D3DOP_TRIANGLE and specify an edge-enabled triangle with D3DTRIFLAG_EDGEENABLETRIANGLE.

Terminate the execute buffer with D3DOP_EXIT, and then unlock the execute buffer.

Before the execute buffer can be executed, the contents (how many vertices, where does the instruction code begin, how long is the instruction buffer) of the execute buffer needs to be described with SetExecuteData.

After the execute buffer is created, then you can render the triangle. First, clear the viewport and Z-buffer with Clear. Then render the triangle with BeginScene, Execute, and EndScene. Those four APIs are usually called together in the sequence shown.

With each frame, the triangle is animated by modifying the world matrix to apply a rotation around the Y axis, and then the following four rendering calls:

   IDirect3DViewport::Clear
   IDirect3DDevice::BeginScene
   IDirect3DDevice::Execute
   IDirect3DDevice::EndScene

The image is made visible with a call to IDirectDrawSurface::Flip.

During execution the application may be deactivated and switched away. Make sure to pause all processing as mentioned earlier. When the application is re-activated, restore all surfaces and reload any art work onto respective surfaces. The sample doesn't use any textures so IDDrawSurface::Restore is sufficient.

When you clean up, release all objects in the reverse order of creation. Notice that only the front buffer is released, not the back nor the Z- buffer. If you release a surface with attached surfaces, all attached surfaces will be released.

Keywords          : GdiDirect3D GdiDirectDraw kbsample
Technology        : kbDirectXSDK
Version           : 3.0
Platform          : WINDOWS
Solution Type     : kbfile


================================================================================


THE INFORMATION PROVIDED IN THE MICROSOFT KNOWLEDGE BASE IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND. MICROSOFT DISCLAIMS ALL WARRANTIES, EITHER EXPRESS OR IMPLIED, INCLUDING THE WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL MICROSOFT CORPORATION OR ITS SUPPLIERS BE LIABLE FOR ANY DAMAGES WHATSOEVER INCLUDING DIRECT, INDIRECT, INCIDENTAL, CONSEQUENTIAL, LOSS OF BUSINESS PROFITS OR SPECIAL DAMAGES, EVEN IF MICROSOFT CORPORATION OR ITS SUPPLIERS HAVE BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. SOME STATES DO NOT ALLOW THE EXCLUSION OR LIMITATION OF LIABILITY FOR CONSEQUENTIAL OR INCIDENTAL DAMAGES SO THE FOREGOING LIMITATION MAY NOT APPLY.

Last reviewed: September 5, 1997
© 1998 Microsoft Corporation. All rights reserved. Terms of Use.