I have been working on adapting the texture mesh viewer example from the Optix SIGGRAPH tutorial to allow for continuous update of the mesh position. Optix is returning a build error when I attempt to update the acceleration structure after the initial construction. The example originally loads a .obj file using the tinyobjloader, and primitives are built into an acceleration structure followed by compaction:
OptixTraversableHandle SampleRenderer::buildMeshAccels()
{
const int numMeshes = (int)model->meshes.size();
vertexBuffer.resize(numMeshes);
normalBuffer.resize(numMeshes);
texcoordBuffer.resize(numMeshes);
indexBuffer.resize(numMeshes);
OptixTraversableHandle asHandle { 0 };
// ==================================================================
// triangle inputs
// ==================================================================
std::vector<OptixBuildInput> triangleInput(numMeshes);
std::vector<CUdeviceptr> d_vertices(numMeshes);
std::vector<CUdeviceptr> d_indices(numMeshes);
std::vector<uint32_t> triangleInputFlags(numMeshes);
for (int meshID=0;meshID<numMeshes;meshID++) {
// upload the model to the device: the builder
TriangleMesh &mesh = *model->meshes[meshID];
vertexBuffer[meshID].alloc_and_upload(mesh.vertex);
indexBuffer[meshID].alloc_and_upload(mesh.index);
if (!mesh.normal.empty())
normalBuffer[meshID].alloc_and_upload(mesh.normal);
if (!mesh.texcoord.empty())
texcoordBuffer[meshID].alloc_and_upload(mesh.texcoord);
triangleInput[meshID] = {};
triangleInput[meshID].type
= OPTIX_BUILD_INPUT_TYPE_TRIANGLES;
// create local variables, because we need a *pointer* to the
// device pointers
d_vertices[meshID] = vertexBuffer[meshID].d_pointer();
d_indices[meshID] = indexBuffer[meshID].d_pointer();
triangleInput[meshID].triangleArray.vertexFormat = OPTIX_VERTEX_FORMAT_FLOAT3;
triangleInput[meshID].triangleArray.vertexStrideInBytes = sizeof(vec3f);
triangleInput[meshID].triangleArray.numVertices = (int)mesh.vertex.size();
triangleInput[meshID].triangleArray.vertexBuffers = &d_vertices[meshID];
triangleInput[meshID].triangleArray.indexFormat = OPTIX_INDICES_FORMAT_UNSIGNED_INT3;
triangleInput[meshID].triangleArray.indexStrideInBytes = sizeof(vec3i);
triangleInput[meshID].triangleArray.numIndexTriplets = (int)mesh.index.size();
triangleInput[meshID].triangleArray.indexBuffer = d_indices[meshID];
triangleInputFlags[meshID] = 0;
// in this example we have one SBT entry, and no per-primitive
// materials:
triangleInput[meshID].triangleArray.flags = &triangleInputFlags[meshID];
triangleInput[meshID].triangleArray.numSbtRecords = 1;
triangleInput[meshID].triangleArray.sbtIndexOffsetBuffer = 0;
triangleInput[meshID].triangleArray.sbtIndexOffsetSizeInBytes = 0;
triangleInput[meshID].triangleArray.sbtIndexOffsetStrideInBytes = 0;
}
// ==================================================================
// BLAS setup
// ==================================================================
OptixAccelBuildOptions accelOptions = {};
accelOptions.buildFlags = OPTIX_BUILD_FLAG_ALLOW_UPDATE
| OPTIX_BUILD_FLAG_ALLOW_COMPACTION
;
accelOptions.motionOptions.numKeys = 0;
accelOptions.operation = OPTIX_BUILD_OPERATION_BUILD;
OptixAccelBufferSizes blasBufferSizes;
OPTIX_CHECK(optixAccelComputeMemoryUsage
(optixContext,
&accelOptions,
triangleInput.data(),
(int)numMeshes, // num_build_inputs
&blasBufferSizes
));
// ==================================================================
// prepare compaction
// ==================================================================
CUDABuffer compactedSizeBuffer;
compactedSizeBuffer.alloc(sizeof(uint64_t));
OptixAccelEmitDesc emitDesc;
emitDesc.type = OPTIX_PROPERTY_TYPE_COMPACTED_SIZE;
emitDesc.result = compactedSizeBuffer.d_pointer();
// ==================================================================
// execute build (main stage)
// ==================================================================
CUDABuffer tempBuffer;
tempBuffer.alloc(blasBufferSizes.tempSizeInBytes);
CUDABuffer outputBuffer;
outputBuffer.alloc(blasBufferSizes.outputSizeInBytes);
OPTIX_CHECK(optixAccelBuild(optixContext,
/* stream */0,
&accelOptions,
triangleInput.data(),
(int)numMeshes,
tempBuffer.d_pointer(),
tempBuffer.sizeInBytes,
outputBuffer.d_pointer(),
outputBuffer.sizeInBytes,
&asHandle,
&emitDesc,
1
));
CUDA_SYNC_CHECK();
// ==================================================================
// perform compaction
// ==================================================================
uint64_t compactedSize;
compactedSizeBuffer.download(&compactedSize,1);
gasBuffer.alloc(compactedSize);
OPTIX_CHECK(optixAccelCompact(optixContext,
/*stream:*/0,
gasHandle,
gasBuffer.d_pointer(),
gasBuffer.sizeInBytes,
&gasHandle));
CUDA_SYNC_CHECK();
// ==================================================================
// aaaaaand .... clean up
// ==================================================================
outputBuffer.free(); // << the UNcompacted, temporary output buffer
tempBuffer.free();
compactedSizeBuffer.free();
return gasHandle;
}
My understanding is that in order to apply a transform to this GAS after it has been built, it needs to be built into a parent IAS which has a transform attribute that can be modified. I built an IAS with the GAS transversable handle assigned to the instance, and the optic program manages to run with no problems:
OptixTraversableHandle SampleRenderer::buildInstanceAccel()
{
OptixTraversableHandle iasHandle{ 0 };
OptixInstance instance;
memcpy(instance.transform, launchParams.meshTransform, sizeof(float)*12);
instance.instanceId = 0;
instance.visibilityMask = 255;
instance.sbtOffset = 0;
instance.flags = OPTIX_INSTANCE_FLAG_NONE;
instance.traversableHandle = gasHandle;
CUdeviceptr d_instance;
CUDA_CHECK(Malloc( reinterpret_cast<void**>( &d_instance ), sizeof(OptixInstance) ) );
CUDA_CHECK(Memcpy( reinterpret_cast<void*>( d_instance ), &instance, sizeof(OptixInstance), cudaMemcpyHostToDevice) );
OptixBuildInput instance_input;
instance_input.type = OPTIX_BUILD_INPUT_TYPE_INSTANCES;
instance_input.instanceArray.instances = d_instance;
instance_input.instanceArray.numInstances = 1;
OptixAccelBuildOptions accel_options = {};
accel_options.buildFlags = OPTIX_BUILD_FLAG_ALLOW_UPDATE;
accel_options.operation = OPTIX_BUILD_OPERATION_BUILD;
OptixAccelBufferSizes buffer_size;
OPTIX_CHECK( optixAccelComputeMemoryUsage(optixContext, &accel_options, &instance_input, 1, &buffer_size) );
CUDABuffer tempBuffer;
tempBuffer.alloc(buffer_size.tempSizeInBytes);
iasBuffer.alloc(buffer_size.outputSizeInBytes);
OPTIX_CHECK(optixAccelBuild(
optixContext,
nullptr, // CUDA stream
&accel_options,
&instance_input,
1, // num build inputs
tempBuffer.d_pointer(),
buffer_size.tempSizeInBytes,
iasBuffer.d_pointer(),
buffer_size.outputSizeInBytes,
&iasHandle,
nullptr, // emitted property list
0 // num emitted properties
));
CUDA_SYNC_CHECK();
tempBuffer.free();
return iasHandle;
}
I then attempt to update the existing instance launch before the start of each render, but Optix returns an error stating that the “numAabbs” in “buildInputs[0].instanceArray” is not equal to the “numInstances(1)”:
void SampleRenderer::updateInstanceAccel()
{
OptixInstance instance;
memcpy(instance.transform, launchParams.meshTransform, sizeof(float)*12);
instance.instanceId = 0;
instance.visibilityMask = 255;
instance.sbtOffset = 0;
instance.flags = OPTIX_INSTANCE_FLAG_NONE;
instance.traversableHandle = meshTraversable;
CUdeviceptr d_instance;
CUDA_CHECK(Malloc( reinterpret_cast<void**>( &d_instance ), sizeof(OptixInstance) ) );
CUDA_CHECK(Memcpy( reinterpret_cast<void*>( d_instance ), &instance, sizeof(OptixInstance), cudaMemcpyHostToDevice) );
CUDA_SYNC_CHECK();
OptixBuildInput instance_input;
instance_input.type = OPTIX_BUILD_INPUT_TYPE_INSTANCES;
instance_input.instanceArray.instances = d_instance;
instance_input.instanceArray.numInstances = 1;
OptixAccelBuildOptions accel_options = {};
accel_options.buildFlags = OPTIX_BUILD_FLAG_ALLOW_UPDATE;
accel_options.operation = OPTIX_BUILD_OPERATION_UPDATE;
OptixAccelBufferSizes buffer_size;
OPTIX_CHECK( optixAccelComputeMemoryUsage(optixContext, &accel_options, &instance_input, 1, &buffer_size) );
CUDABuffer tempBuffer;
tempBuffer.alloc(buffer_size.tempSizeInBytes);
OPTIX_CHECK(optixAccelBuild(
optixContext,
nullptr, // CUDA stream
&accel_options,
&instance_input,
1, // num build inputs
tempBuffer.d_pointer(),
buffer_size.tempSizeInBytes,
iasBuffer.d_pointer(),
buffer_size.outputSizeInBytes,
&iasHandle,
nullptr, // emitted property list
0 // num emitted properties
));
CUDA_SYNC_CHECK();
tempBuffer.free();
}
Does anyone have any suggestions on what may be causing this error in the transform update? I appreciate any assistance you can provide.