Hi, I am a student trying to get started with OptiX 8.0.0 for a project that I am working on. I am basically trying to make my own program that should produce a 2D plane to get started. So far I am basically just following instructions on the internet and using example code, which probably makes my program very difficult to understand. Anyways, I thought that I’de post my problem(s) and see if anyone could help me out.
So far I have put together the code below and it builds fine. However, I get this error when I try to run it:
Thank you for any suggestions or tips on how I should to forward. :)
[ACCEL] Invalid value (0) for “buildInputs[0].triangleArray.flags”
[ERROR] outputBuffer is 0
[COMPILER]
[PIPELINE] params variable “params” not found in any module. It might have been optimized away.
[COMPILER] Info: Pipeline statistics
module(s) : 3
entry function(s) : 3
trace call(s) : 0
continuation callable call(s) : 0
direct callable call(s) : 0
basic block(s) in entry functions : 3
instruction(s) in entry functions : 48
non-entry function(s) : 0
basic block(s) in non-entry functions: 0
instruction(s) in non-entry functions: 0
debug information : no
[DISKCACHE] Closed database: “C:\Users\PC\AppData\Local\NVIDIA\OptixCache\optix7cache.db”
[DISKCACHE] Cache data size: “33.2 MiB”
C:\Users\PC\Desktop\new_example_4\build\bin\Debug\new_example_4.exe (process 22060) exited with code 0.
To automatically close the console when debugging stops, enable Tools->Options->Debugging->Automatically close the console when debugging stops.
src code:
include <optix.h>
include <cuda_runtime.h>
include <optix_stubs.h>
include <optix_function_table_definition.h>
include
include <…/inc/exception.h>
include <optix_types.h>
include <sutil/sutil.h>
// Initialize OptiX and create context
void initOptix(CUcontext& cuContext, CUstream& stream) {
// Initialize CUDA
CUdevice cuDevice;
cuInit(0);
cuDeviceGet(&cuDevice, 0); // Assumes device 0 is suitable
cuCtxCreate(&cuContext, 0, cuDevice);
// Create a CUDA stream
cuStreamCreate(&stream, CU_STREAM_DEFAULT);
// Initialize OptiX
optixInit();
}
// Vertex structure for a simple 2D plane
struct Vertex {
float x, y, z; // Position
};
// Define vertices of a square plane centered at the origin
Vertex vertices = {
{-0.5f, -0.5f, 0.0f}, // Bottom left
{0.5f, -0.5f, 0.0f}, // Bottom right
{0.5f, 0.5f, 0.0f}, // Top right
{-0.5f, 0.5f, 0.0f} // Top left
};
// Define indices for two triangles that make up the square
unsigned int indices = {
0, 1, 2, // First triangle
2, 3, 0 // Second triangle
};
// Load PTX files path
const char* ptxPathRayGen = “C:/Users/PC/Desktop/new_example_4/cuda/cuda_output/rayGen.ptx”;
const char* ptxPathhit = “C:/Users/PC/Desktop/new_example_4/cuda/cuda_output/hit.ptx”;
const char* ptxPathMiss = “C:/Users/PC/Desktop/new_example_4/cuda/cuda_output/miss.ptx”;
OptixTraversableHandle createAccelerationStructure(OptixDeviceContext context, CUdeviceptr& d_vertices, CUdeviceptr& d_indices, CUdeviceptr& d_tempBufferGas, CUdeviceptr& d_gasOutputBuffer) {
// Assume ‘vertices’ and ‘indices’ are already defined as shown above
size_t vertices_size = sizeof(vertices);
size_t indices_size = sizeof(indices);
// Allocate device memory for vertices and indices
cuMemAlloc(&d_vertices, vertices_size);
cuMemAlloc(&d_indices, indices_size);
// Copy vertices and indices to device memory
cuMemcpyHtoD(d_vertices, vertices, vertices_size);
cuMemcpyHtoD(d_indices, indices, indices_size);
// Define build input
OptixBuildInput buildInput = {};
buildInput.type = OPTIX_BUILD_INPUT_TYPE_TRIANGLES;
// Setup vertex buffer
OptixBuildInputTriangleArray triangleArray = {};
triangleArray.vertexFormat = OPTIX_VERTEX_FORMAT_FLOAT3;
triangleArray.vertexStrideInBytes = sizeof(Vertex);
triangleArray.numVertices = 4;
triangleArray.vertexBuffers = &d_vertices;
// Setup index buffer
triangleArray.indexFormat = OPTIX_INDICES_FORMAT_UNSIGNED_INT3;
triangleArray.indexStrideInBytes = sizeof(unsigned int) * 3;
triangleArray.numIndexTriplets = 2;
triangleArray.indexBuffer = d_indices;
// Set the number of SBT records
triangleArray.numSbtRecords = 1; //<---- MAY BE WRONG!
buildInput.triangleArray = triangleArray;
// Specify build options
OptixAccelBuildOptions accelOptions = {};
accelOptions.buildFlags = OPTIX_BUILD_FLAG_NONE;
accelOptions.operation = OPTIX_BUILD_OPERATION_BUILD;
// Output buffers for acceleration structure
OptixAccelBufferSizes gasBufferSizes;
optixAccelComputeMemoryUsage(context, &accelOptions, &buildInput, 1, &gasBufferSizes);
// Allocate memory for acceleration structure
cuMemAlloc(&d_tempBufferGas, gasBufferSizes.tempSizeInBytes);
cuMemAlloc(&d_gasOutputBuffer, gasBufferSizes.outputSizeInBytes);
// Build acceleration structure
OptixTraversableHandle gasHandle = 0;
optixAccelBuild(context, 0, &accelOptions, &buildInput, 1,
d_tempBufferGas, gasBufferSizes.tempSizeInBytes,
d_gasOutputBuffer, gasBufferSizes.outputSizeInBytes,
&gasHandle, nullptr, 0);
// Return the handle to the created acceleration structure
return gasHandle;
// Do not forget to free the allocated buffers after they are no longer needed
}
// Function to load the contents of a PTX file into a string
std::string loadPtx(const std::string& filepath) {
std::ifstream file(filepath.c_str());
if (!file) {
std::cerr << "Failed to open PTX file: " << filepath << std::endl;
return “”;
}
std::stringstream buffer;
buffer << file.rdbuf();
return buffer.str();
}
int main() {
// Step 1: Setup OptiX context (for OptiX 7+, prepare to use CUDA directly)
CUcontext cuContext;
CUstream stream;
OptixDeviceContext optixContext = nullptr;
// Initialize OptiX and CUDA context
initOptix(cuContext, stream);
CUdeviceptr d_vertices = 0;
CUdeviceptr d_indices = 0;
CUdeviceptr d_tempBufferGas = 0;
CUdeviceptr d_gasOutputBuffer = 0;
// Create an OptiX device context using the CUDA context
OPTIX_CHECK(optixDeviceContextCreate(cuContext, nullptr, &optixContext));
OPTIX_CHECK(optixDeviceContextSetLogCallback(optixContext, [](unsigned int level, const char* tag, const char* message, void* cbdata) {
std::cerr << "[" << tag << "] " << message << "\n";
}, nullptr, 4)); // LogLevel=4 for verbosity
// Step 2: Define geometry and create acceleration structures
OptixTraversableHandle gasHandle = createAccelerationStructure(optixContext, d_vertices, d_indices, d_tempBufferGas, d_gasOutputBuffer);
// Module and Program Group Compile Options
OptixModuleCompileOptions moduleCompileOptions = {};
moduleCompileOptions.maxRegisterCount = OPTIX_COMPILE_DEFAULT_MAX_REGISTER_COUNT;
moduleCompileOptions.optLevel = OPTIX_COMPILE_OPTIMIZATION_DEFAULT;
moduleCompileOptions.debugLevel = OPTIX_COMPILE_DEBUG_LEVEL_NONE;
OptixPipelineCompileOptions pipelineCompileOptions = {};
pipelineCompileOptions.usesMotionBlur = false;
pipelineCompileOptions.traversableGraphFlags = OPTIX_TRAVERSABLE_GRAPH_FLAG_ALLOW_SINGLE_LEVEL_INSTANCING;
pipelineCompileOptions.numPayloadValues = 2; // Adjust based on your needs
pipelineCompileOptions.numAttributeValues = 2; // Adjust based on your needs
pipelineCompileOptions.exceptionFlags = OPTIX_EXCEPTION_FLAG_NONE; // or other flags as necessary
pipelineCompileOptions.pipelineLaunchParamsVariableName = "params";
// Step 3: Create shaders (ray generation, hit, miss)
char log[2048]; // For logging
size_t logSize = sizeof(log);
// Load the PTX source code
std::string rayGenPTX = loadPtx(ptxPathRayGen);
OptixModule rayGenModule;
OPTIX_CHECK(optixModuleCreate(
optixContext,
&moduleCompileOptions,
&pipelineCompileOptions,
rayGenPTX.c_str(), rayGenPTX.size(),
log, &logSize,
&rayGenModule
));
std::string hitPTX = loadPtx(ptxPathhit);
OptixModule hitModule;
OPTIX_CHECK(optixModuleCreate(
optixContext,
&moduleCompileOptions,
&pipelineCompileOptions,
hitPTX.c_str(), hitPTX.size(),
log, &logSize,
&hitModule
));
std::string missPTX = loadPtx(ptxPathMiss);
OptixModule missModule;
OPTIX_CHECK(optixModuleCreate(
optixContext,
&moduleCompileOptions,
&pipelineCompileOptions,
missPTX.c_str(), missPTX.size(),
log, &logSize,
&missModule
));
// Define program group options - typically, this is left as default for basic usage
OptixProgramGroupOptions programGroupOptions = {};
// Create program group for ray generation shader
OptixProgramGroupDesc raygenPGDesc = {};
raygenPGDesc.kind = OPTIX_PROGRAM_GROUP_KIND_RAYGEN;
raygenPGDesc.raygen.module = rayGenModule; // Ensure rayGenModule is correctly initialized
raygenPGDesc.raygen.entryFunctionName = "__raygen__rg";
OptixProgramGroup raygenProgramGroup;
OPTIX_CHECK(optixProgramGroupCreate(
optixContext,
&raygenPGDesc,
1, // Number of program group descriptions
&programGroupOptions, // Corrected to use program group options
log, &logSize, // Log buffer and its size
&raygenProgramGroup // The created program group
));
// Create program group for closest hit shader
OptixProgramGroupDesc hitPGDesc = {};
hitPGDesc.kind = OPTIX_PROGRAM_GROUP_KIND_HITGROUP;
hitPGDesc.hitgroup.moduleCH = hitModule; // Use your hit module here
hitPGDesc.hitgroup.entryFunctionNameCH = "__closesthit__ch"; // Entry point for your hit shader
OptixProgramGroup hitProgramGroup;
OPTIX_CHECK(optixProgramGroupCreate(
optixContext,
&hitPGDesc, // Use the hit program group descriptor
1, // One program group description
&programGroupOptions, // Assuming programGroupOptions is already defined
log, &logSize, // Log buffer and size
&hitProgramGroup // The created program group
));
// Create program group for miss shader
OptixProgramGroupDesc missPGDesc = {};
missPGDesc.kind = OPTIX_PROGRAM_GROUP_KIND_MISS;
missPGDesc.miss.module = missModule; // Ensure missModule is correctly initialized
missPGDesc.miss.entryFunctionName = "__miss__ms";
OptixProgramGroup missProgramGroup;
OPTIX_CHECK(optixProgramGroupCreate(
optixContext,
&missPGDesc,
1, // Number of program group descriptions
&programGroupOptions, // Program group options
log, &logSize, // Log buffer and its size
&missProgramGroup // The created program group
));
// Step 4: Setup ray tracing pipeline
OptixPipeline pipeline;
OptixPipelineLinkOptions pipelineLinkOptions = {};
pipelineLinkOptions.maxTraceDepth = 2;
OptixProgramGroup programGroups[] = { raygenProgramGroup, hitProgramGroup, missProgramGroup };
OPTIX_CHECK(optixPipelineCreate(
optixContext,
&pipelineCompileOptions,
&pipelineLinkOptions,
programGroups,
sizeof(programGroups) / sizeof(programGroups[0]), // Number of program groups
log, &logSize,
&pipeline
));
// Set stack sizes
uint32_t directCallableStackSizeFromTraversal = 64; // Minimal if not using direct callables
uint32_t directCallableStackSizeFromState = 64; // Minimal if not using direct callables
uint32_t continuationStackSize = 1024; // Estimate based on the complexity of your shaders
// The maximum depth of the traversable graph for ray tracing
uint32_t maxTraversableGraphDepth = 2; // Assuming a simple scene
OPTIX_CHECK(optixPipelineSetStackSize(
pipeline,
directCallableStackSizeFromTraversal,
directCallableStackSizeFromState,
continuationStackSize,
maxTraversableGraphDepth // Maximum depth of traversal
));
// Step 5: Render the scene
struct __align__(OPTIX_SBT_RECORD_ALIGNMENT) RayGenSbtRecord {
__align__(OPTIX_SBT_RECORD_HEADER_SIZE) char header[OPTIX_SBT_RECORD_HEADER_SIZE];
// Add ray generation data here
//OptixProgramGroup raygenProgram;
};
struct __align__(OPTIX_SBT_RECORD_ALIGNMENT) MissSbtRecord {
__align__(OPTIX_SBT_RECORD_HEADER_SIZE) char header[OPTIX_SBT_RECORD_HEADER_SIZE];
// Add miss data here
//OptixProgramGroup missProgram;
};
struct __align__(OPTIX_SBT_RECORD_ALIGNMENT) HitGroupSbtRecord {
__align__(OPTIX_SBT_RECORD_HEADER_SIZE) char header[OPTIX_SBT_RECORD_HEADER_SIZE];
// Add hit group data here
//OptixProgramGroup closestHitProgram;
};
RayGenSbtRecord rgSbt;
MissSbtRecord msSbt;
HitGroupSbtRecord hgSbt;
optixSbtRecordPackHeader(raygenProgramGroup, &rgSbt);
optixSbtRecordPackHeader(missProgramGroup, &msSbt);
optixSbtRecordPackHeader(hitProgramGroup, &hgSbt);
// Allocate and copy SBT records to device memory
CUdeviceptr d_raygenRecords, d_missRecords, d_hitgroupRecords;
size_t sbtSize = sizeof(RayGenSbtRecord); // Same for Miss and HitGroup if they don't have additional data
cudaMalloc(reinterpret_cast<void**>(&d_raygenRecords), sbtSize);
cudaMalloc(reinterpret_cast<void**>(&d_missRecords), sbtSize);
cudaMalloc(reinterpret_cast<void**>(&d_hitgroupRecords), sbtSize);
cudaMemcpy(reinterpret_cast<void*>(d_raygenRecords), &rgSbt, sbtSize, cudaMemcpyHostToDevice);
cudaMemcpy(reinterpret_cast<void*>(d_missRecords), &msSbt, sbtSize, cudaMemcpyHostToDevice);
cudaMemcpy(reinterpret_cast<void*>(d_hitgroupRecords), &hgSbt, sbtSize, cudaMemcpyHostToDevice);
OptixShaderBindingTable sbt = {};
sbt.raygenRecord = d_raygenRecords;
sbt.missRecordBase = d_missRecords;
sbt.missRecordStrideInBytes = sizeof(MissSbtRecord);
sbt.missRecordCount = 1;
sbt.hitgroupRecordBase = d_hitgroupRecords;
sbt.hitgroupRecordStrideInBytes = sizeof(HitGroupSbtRecord);
sbt.hitgroupRecordCount = 1;
struct LaunchParams {
int width;
int height;
CUdeviceptr outputBuffer;
// Add other parameters as needed
};
LaunchParams params = { 800, 600 }; // Example dimensions
CUdeviceptr d_params;
cudaMalloc(reinterpret_cast<void**>(&d_params), sizeof(LaunchParams));
cudaMemcpy(reinterpret_cast<void*>(d_params), ¶ms, sizeof(LaunchParams), cudaMemcpyHostToDevice);
CUdeviceptr d_outputBuffer;
cudaMalloc(reinterpret_cast<void**>(&d_outputBuffer), params.width* params.height * sizeof(float4)); // Assuming float4 per pixel
params.outputBuffer = d_outputBuffer;
// Update params on the device
cudaMemcpy(reinterpret_cast<void*>(d_params), ¶ms, sizeof(LaunchParams), cudaMemcpyHostToDevice);
optixLaunch(
pipeline,
stream,
d_params,
sizeof(LaunchParams),
&sbt,
params.width, // launch width
params.height, // launch height
1 // launch depth
);
cudaStreamSynchronize(stream); // Wait for completion
float4* h_outputBuffer = new float4[params.width * params.height];
cudaMemcpy(h_outputBuffer, reinterpret_cast<void*>(d_outputBuffer), params.width* params.height * sizeof(float4), cudaMemcpyDeviceToHost);
// Now, `h_outputBuffer` contains your rendered image. You can save it to a file or display it.
// Step 6: Cleanup and free resources
cudaFree(reinterpret_cast<void*>(d_raygenRecords));
cudaFree(reinterpret_cast<void*>(d_missRecords));
cudaFree(reinterpret_cast<void*>(d_hitgroupRecords));
cudaFree(reinterpret_cast<void*>(d_outputBuffer));
cudaFree(reinterpret_cast<void*>(d_params));
OPTIX_CHECK(optixProgramGroupDestroy(raygenProgramGroup));
OPTIX_CHECK(optixProgramGroupDestroy(hitProgramGroup));
OPTIX_CHECK(optixProgramGroupDestroy(missProgramGroup));
OPTIX_CHECK(optixModuleDestroy(rayGenModule));
OPTIX_CHECK(optixModuleDestroy(hitModule));
OPTIX_CHECK(optixModuleDestroy(missModule));
cuMemFree(d_vertices);
cuMemFree(d_indices);
cuMemFree(d_tempBufferGas);
cuMemFree(d_gasOutputBuffer);
OPTIX_CHECK(optixDeviceContextDestroy(optixContext));
cuStreamDestroy(stream);
cuCtxDestroy(cuContext);
return 0;
}

