Cg programs supply programs for GPUs, but they need the support of an application to render images. To interface Cg programs with an application, you must do two things:
You can choose when you want to perform these operations. You can perform them at compile time, when the application program is compiled into an executable, or you can perform them at runtime, when the application is actually executed. The Cg runtime is a set of application programming interfaces (APIs) that allows an application to compile and link Cg programs at runtime.
Most applications need to run on a variety of GPUs with various levels of functionality, so these applications need to run on a variety of profiles. If an application precompiles its Cg programs (at compile time), it must store a precompiled version of each program for each profile. Although possible, the precompiled approach is cumbersome for an application that uses many Cg programs. What's worse, the Cg programs become frozen in time. By precompiling Cg programs, an application sacrifices the optimizations that future compilers could offer.
In contrast, Cg programs compiled by applications at runtime benefit from future compiler optimizations for existing profiles. And these programs can run on future profiles corresponding to new hardware and 3D API functionality that did not exist when the application's Cg programs were written.
If you link a compiled Cg program to an application, the application becomes tied to the result of the compilation, particularly with respect to how the compiler allocates parameters. The application program would have to refer to the Cg program input parameters by using the hardware register names that the Cg compiler outputs. This approach causes two significant problems:
In contrast, linking a Cg program to the application program at runtime removes the dependency on the Cg compiler. With the Cg runtime, you only need to alter the application code when you add, delete, or rename Cg input parameters.
The Cg runtime also offers facilities to manage the input parameters of the Cg program. In particular, it makes data types such as arrays and matrices easier to deal with.
These additional functions also encompass the necessary 3D API calls to minimize code length and reduce programmer errors.
Figure B-1 shows the three libraries that make up the Cg runtime API.
Figure B-1 The Parts of the Cg Runtime API
To make it easier for application writers, the OpenGL and Direct3D libraries each adopt the philosophy and data structure style of their respective APIs. You need only link with the 3D API–specific Cg runtime library for the 3D API your application uses. Therefore, most applications use either the OpenGL or Direct3D Cg runtime library.
The rest of this appendix provides code fragments, written in C, for using the Cg runtime in the framework of an application. Each step includes source code for OpenGL and Direct3D programming.
Functions that involve only pure Cg resource management belong to the core runtime and have a cg prefix. In these cases, the same code is used for OpenGL and Direct3D.
When functions from the OpenGL or Direct3D Cg runtime libraries are used, notice that the API name is indicated by the function name. Functions belonging to the OpenGL Cg runtime library have a cgGL prefix, and functions in the Direct3D Cg runtime library have a cgD3D8 or cgD3D9 prefix, for DirectX 8 and DirectX 9, respectively. In the examples that follow, we show the DirectX 9 versions of the examples. Replacing " D3D9 " with " D3D8 " will produce the DirectX 8 versions of the same examples. Note that the functions we list here take the same parameters in DirectX 8 and DirectX 9. In general, this is not always the case.
Here's how to include the core Cg runtime API into your C or C++ program:
#include <Cg/cg.h>
Here's how to include the OpenGL-specific Cg runtime API:
#include <Cg/cgGL.h>
Here's how to include the DirectX 8–specific Cg runtime API:
#include <Cg/cgD3D8.h>
Here's how to include the DirectX 9–specific Cg runtime API:
#include <Cg/cgD3D9.h>
A context is a container for Cg programs. It holds the Cg programs you load, as well as their shared data.
Here's how to create a context:
CGcontext context = cgCreateContext();
Compile a Cg program by adding it to a context, using the cgCreateProgram function:
CGprogram program = cgCreateProgram(context, // from cgCreateContext CG_SOURCE, // type: source or object programString, // program text/data profile, // profile "main", // entry function name args); // compiler options
The CG_SOURCE parameter indicates that the following string argument, programString , is an array of bytes containing Cg source code, not precompiled code. The Cg runtime does let you create a program from compiled code (called object code) by using the CG_OBJECT rather than CG_SOURCE parameter, if you want to.
profile specifies the profile for which the program will be compiled—for example, CG_PROFILE_ARBVP1 for OpenGL applications, or CG_PROFILE_VS_2_0 for Direct3D applications. The main string parameter gives the name of the function to use as the entry function for your program. Finally, args is a list of strings that supplies options to the compiler.
After you compile a program, you need to pass the resulting object code to the 3D API that you are using. For this, you need to invoke the Cg runtime's 3D API–specific functions.
In OpenGL, you load a program like this:
cgGLLoadProgram(program);
The Direct3D-specific functions require the Direct3D device structure in order to make the necessary Direct3D calls. The application passes it to the runtime using the following call:
cgD3D9SetDevice(device);
You must do this every time a new Direct3D device is created, typically only at the beginning of the application.
You can then load a Cg program this way in Direct3D 9:
cgD3D9LoadProgram(program, // CGprogram false, // Parameter shadowing 0); // Assembly flags
or this way in Direct3D 8:
cgD3D8LoadProgram(program, // CGprogram false, // Parameter shadowing 0, // Assembly flags 0, // Vertex shader usage vertexDeclaration); // Vertex declaration
vertexDeclaration is the Direct3D vertex declaration array that describes where to find the necessary vertex attributes in the vertex streams.
The runtime lets you modify the values of your program parameters. The first step is to get a handle to the parameter:
CGparameter myParameter = cgGetNamedParameter(program, "myParameter");
myParameter is the name of the parameter as it appears in the program source code.
The second step is to set the parameter value. The function used depends on the parameter type.
Here is an example in OpenGL:
cgGLSetParameter4fv(myParameter, value);
Here is the same example in Direct3D:
cgD3D9SetUniform(myParameter, value);
These function calls assign the four floating-point values contained in the array value to the parameter myParameter (assumed to be of type float4 ).
In both APIs, there are variants of these calls to set matrices, arrays, textures, and texture states.
Before you can execute a program in OpenGL, you must enable its corresponding profile. For example:
cgGLEnableProfile(CG_PROFILE_ARBVP1);
In Direct3D, nothing special needs to be done to enable a specific profile.
Next, you bind the program to the current 3D API state. This means that it will execute, in the subsequent drawing calls, for every vertex (in the case of a vertex program) and for every fragment (in the case of a fragment program).
Here's how to bind a program in OpenGL:
cgGLBindProgram(program);
Here's how to bind a program in Direct3D:
cgD3D9BindProgram(program);
You can bind only one vertex and one fragment program at a time for a particular profile. Therefore, the same vertex program is executed as long as no other vertex program is bound. Similarly, the same fragment program is executed as long as no other fragment program is bound.
In OpenGL, disable profiles with the following call:
cgGLDisableProfile(CG_PROFILE_ARBVP1);
Disabling a profile issues commands, based on the profile, to return OpenGL to its fixed-function mode.
If your application no longer needs a Cg program, it is good programming practice to free the resources maintained for the program by the Cg runtime. Because the Direct3D runtime keeps an internal reference to the Direct3D device, you must tell it to release this reference when you finish using the Direct3D runtime. This is done with the following call:
cgD3D9SetDevice(0);
To free resources allocated for a single program, use this function call:
cgDestroyProgram(program);
To free all the resources allocated for a context, use this function call:
cgDestroyContext(context);
Note that destroying a context destroys all the programs it contains as well.
The core Cg runtime reports an error by setting a global variable that contains the error code. You can query it, as well as the corresponding error string, in the following way:
CGerror error = cgGetError(); const char* errorString = cgGetErrorString(error);
Each time an error occurs, the core library also calls a callback function optionally provided by the application. This callback function would usually call cgGetError :
void MyErrorCallback(void) { const char* errorString = cgGetErrorString(cgGetError()); printf(logfile, "Cg error: %s", errorString); } cgSetErrorCallback(MyErrorCallback);
Calls to 3D API–specific Cg runtime functions can also generate API-specific errors. For the OpenGL Cg runtime library, they are checked using glGetError . Most of the Direct3D Cg runtime library functions also return a Direct3D error code ( HRESULT ). Similar to the Direct3D runtime, the Direct3D Cg runtime library can be run in a debug mode, provided you use the debug version of the Direct3D Cg DLL. This mode is enabled by the following call:
cgD3D9EnableDebugTracing(true);
In this mode, many helpful messages and traces will be output to the debug output console.
The latest information and documentation about the Cg runtime is available at the NVIDIA Cg Web site:
http://developer.nvidia.com/Cg
Many of the designations used by manufacturers and sellers to distinguish their products are claimed as trademarks. Where those designations appear in this book, and Addison-Wesley was aware of a trademark claim, the designations have been printed with initial capital letters or in all capitals.
The authors and publisher have taken care in the preparation of this book, but make no expressed or implied warranty of any kind and assume no responsibility for errors or omissions. No liability is assumed for incidental or consequential damages in connection with or arising out of the use of the information or programs contained herein.
The publisher offers discounts on this book when ordered in quantity for bulk purchases and special sales. For more information, please contact:
U.S. Corporate and Government Sales
(800) 382-3419
corpsales@pearsontechgroup.com
For sales outside of the U.S., please contact:
International Sales
international@pearsontechgroup.com
Visit Addison-Wesley on the Web: www.awprofessional.com
Library of Congress Control Number: 2002117794
Copyright © 2003 by NVIDIA Corporation
Cover image © 2003 by NVIDIA Corporation
All rights reserved. No part of this publication may be reproduced, stored in a retrieval system, or transmitted, in any form, or by any means, electronic, mechanical, photocopying, recording, or otherwise, without the prior consent of the publisher. Printed in the United States of America. Published simultaneously in Canada.
For information on obtaining permission for use of material from this work, please submit a written request to:
Pearson Education, Inc.
Rights and Contracts Department
75 Arlington Street, Suite 300
Boston, MA 02116
Fax: (617) 848-7047
Text printed on recycled paper at RR Donnelley Crawfordsville in Crawfordsville, Indiana.
8 9 10111213 DOC 09 08 07
8th Printing, November 2007