Upcoming Events
Unite 2010
11/10 - 11/12 @ Montréal, Canada

GDC China
12/5 - 12/7 @ Shanghai, China

Asia Game Show 2010
12/24 - 12/27  

GDC 2011
2/28 - 3/4 @ San Francisco, CA

More events...
Quick Stats
64 people currently visiting GDNet.
2406 articles in the reference section.

Help us fight cancer!
Join SETI Team GDNet!
Link to us Events 4 Gamers
Intel sponsors gamedev.net search:

RacorX2

The main difference between RacorX and RacorX2 is the compilation of the vertex shader with NVASM. Whereas the first example compiles the vertex shader with D3DXAssembleShader() while the application starts up, RacorX2 uses a pre-compiled vertex shader.

To add the NVIDIA vertex and pixel shader assembler, you have to do the following steps:

  • Create a directory, for example <C:\NVASM>: and unzip nvasm.exe and the documentation into it
  • Show your Visual C++ IDE the path to this exe with
    <Tools->Options->Directories>
    and choose from the drop down menu <Show directories for:>
    <Executable files>
    Add the path to NVASM
    <C:\NVASM>
    Now the dialog box should look like this:


    Figure 7 - Integrating NVASM into Visual C/C++

  • Additionally you have to tell every vertex shader file in the IDE, that it has to be compiled with NVASM. The easiest way to do that is looking into the example RacorX2. Just fire up your Visual C++ IDE by clicking on the <RacorX.dsp> in the RacorX2 directory. Click on the <FileView> tab of the Workspace dialog and there on <Shaders> to view the available shader files. A right-click on the file <basic.vsh> should show you a popup. Click on <Settings...>. The project settings of your project might look like:


    Figure 8 - Custom Build Options Vertex Shader Files

  • The entry in the entry field called "Commands" is:

    nvasm.exe $(InputPath) shaders\$(InputName).vso

    For the entry field named "Outputs", I use the input name as the name for the output file with an *.vso extension. The output directory should be the shaders directory. ShaderX author Kenneth Hurley is the author of NVASM. Read more in his paper [Hurley].

The output of NVASM in the build window of your Visual C/C++ IDE should look like this:


Figure 9 - NVASM Output

The vertex shader is provided in its own ASCII file called basic.vsh. After compilation a binary object file with the name basic.vso is created in the directory <shaders>:


Figure 10 - Directory Content RacorX2

Creating a Vertex Shader

Because of the already compiled vertex shader, the creation of the vertex shader has to be done in a different way than in the previous example. This happens in the InitDeviceObjects() function:

//shader decl
DWORD dwDecl[] =
{
  D3DVSD_STREAM(0),
  D3DVSD_REG(0, D3DVSDT_FLOAT3 ), // input register 0
  D3DVSD_END()
};

// loads a *.vso binary file, already compiled with NVASM and
// creates a vertex shader
if (FAILED(CreateVSFromCompiledFile (m_pd3dDevice, dwDecl,"shaders/basic.vso",
                                     &m_dwVertexShader)))
   return E_FAIL;

CreateVSFromCompiledFile() opens and reads in the binary vertex shader file and creates a vertex shader. The source of this function can be found at the end of the file racorx.cpp in the RacorX2 directory:

//----------------------------------------------------------------------------
// Name: CreateVSFromBinFile
// Desc: loads a binary *.vso file that was compiled by NVASM
// and creates a vertex shader
//----------------------------------------------------------------------------
HRESULT CMyD3DApplication::CreateVSFromCompiledFile (IDirect3DDevice8* m_pd3dDevice,
                                                     DWORD* dwDeclaration,
                                                     TCHAR* strVSPath,
                                                     DWORD* m_dwVS)
{
  char szBuffer[128]; // debug output
  DWORD* dwpVS; // pointer to address space of the calling process
  HANDLE hFile, hMap; // handle file and handle mapped file
  TCHAR tempVSPath[512]; // temporary file path
  HRESULT hr; // error

if( FAILED( hr = DXUtil_FindMediaFile( tempVSPath, strVSPath ) ) )
  return D3DAPPERR_MEDIANOTFOUND;

hFile = CreateFile(tempVSPath, GENERIC_READ,0,0,OPEN_EXISTING,
                   FILE_ATTRIBUTE_NORMAL,0);

if(hFile != INVALID_HANDLE_VALUE)
{
  if(GetFileSize(hFile,0) > 0)
  hMap = CreateFileMapping(hFile,0,PAGE_READONLY,0,0,0);
  else
  {
    CloseHandle(hFile);
  return E_FAIL;
  }
}
else
  return E_FAIL;

// maps a view of a file into the address space of the calling process
dwpVS = (DWORD *)MapViewOfFile(hMap, FILE_MAP_READ, 0, 0, 0);

// Create the vertex shader
hr = m_pd3dDevice->CreateVertexShader( dwDeclaration, dwpVS, m_dwVS, 0 );
if ( FAILED(hr) )
{
  OutputDebugString( "Failed to create Vertex Shader, errors:\n" );
  D3DXGetErrorStringA(hr,szBuffer,sizeof(szBuffer));
  OutputDebugString( szBuffer );
  OutputDebugString( "\n" );
return hr;
}

  UnmapViewOfFile(dwpVS);
  CloseHandle(hMap);
  CloseHandle(hFile);

  return S_OK;
}

DXUtil_FindMediaFile(), a helper function located in the framework file dxutil.cpp, returns the path to the already compiled vertex shader file. CreateFile() opens and reads the existing file:

HANDLE CreateFile(
  LPCTSTR lpFileName, // file name
  DWORD dwDesiredAccess, // access mode
  DWORD dwShareMode, // share mode
  LPSECURITY_ATTRIBUTES lpSecurityAttributes, // SD
  DWORD dwCreationDisposition, // how to create
  DWORD dwFlagsAndAttributes, // file attributes
  HANDLE hTemplateFile // handle to template file
);

Its first parameter is the path to the file. The flag GENERIC_READ specifies read access to the file in the second parameter. The following two parameters are not used, because file sharing should not happen and the fourth parameter is not used, because the file should not be inherited by a child process. The fifth parameter is set to OPEN_EXISTING. This way, the function fails if the file does not exist. Setting the sixt parameter to FILE_ATTRIBUTE_NORMAL indicates, that the file has no other attributes. A template file is not used here, so the last parameter is set to 0. Please consult the Platform SDK help file for more information.

CreateFileMapping() creates or opens a named or unnamed file-mapping object for the specified file:

HANDLE CreateFileMapping(
  HANDLE hFile, // handle to file
  LPSECURITY_ATTRIBUTES lpAttributes, // security
  DWORD flProtect, // protection
  DWORD dwMaximumSizeHigh, // high-order DWORD of size
  DWORD dwMaximumSizeLow, // low-order DWORD of size
  LPCTSTR lpName // object name
);

The first parameter is a handle to the file from which to create a mapping object. The file must be opened with an access mode compatible with the protection flags specified by the flProtect parameter. We have opened the file in CreateFile() with GENERIC_READ, therefore we use PAGE_READONLY here. Other features of CreateFileMapping() are not needed, therefore we set the rest of the parameters to 0.

MapViewOfFile() function maps a view of a file into the address space of the calling process:

LPVOID MapViewOfFile(
  HANDLE hFileMappingObject, // handle to file-mapping object  
  DWORD dwDesiredAccess, // access mode
  DWORD dwFileOffsetHigh, // high-order DWORD of offset
  DWORD dwFileOffsetLow, // low-order DWORD of offset
  SIZE_T dwNumberOfBytesToMap // number of bytes to map
);

This function only gets the handle to the file-mapping object from CreateFileMapping() and in the second parameter the access mode FILE_MAP_READ. The access mode parameter specifies the type of access to the file view and, therefore, the protection of the pages mapped by the file. More features are not needed, therefore the rest of the parameters are set to 0.

CreateVertexShader() is used to create and validate a vertex shader. It takes the vertex shader declaration (which maps vertex buffer streams to different vertex input registers) in its first parameter as a pointer and returns the shader handle in the third parameter. The second parameter gets the vertex shader instructions of the binary code pre-compiled by a vertex shader assembler. With the fourth parameter you can force software vertex processing with D3DUSAGE_SOFTWAREPROCESSING.

As in the previous example OutputDebugString() shows the complete error message in the output debug window of the Visual C/C++ IDE and D3DXGetErrorStringA() interprets all Direct3D and Direct3DX HRESULTS and returns an error message in szBuffer.

Summarize

This example showed the integration of NVASM to pre-compile a vertex shader and how to open and read a binary vertex shader file.





RacorX3

Contents
  RacorX
  RacorX2
  RacorX3
  RacorX4
  RacorX5

  Printable version
  Discuss this article

The Series
  Fundamentals of Vertex Shaders
  Programming Vertex Shaders
  Fundamentals of Pixel Shaders
  Programming Pixel Shaders
  Diffuse & Specular Lighting with Pixel Shaders