Dynamic vertices attribute update

Howdy,

I’ve been trying to learn the Optix7 by building an interactive viewer, mostly based on the Optix7 SDK optixMeshViewer sample.

Now,I have some questions about updating vertices attribute. For example, I have new vertices positions of original mesh in same position number and face index, how can i update it.

1.Which API function can i use?

2.Do i need update mesh acceleration or instance acceleration even SBT?

Hi wangyulin1029, welcome!

For question #1, if you’re using built-in triangles, then you will have a buffer for vertices that you used to build your first GAS (Geometry Acceleration Structure). In order to upload new vertices, you can use cudaMemcpy to re-upload new data into the vertex buffer you already have, and then you can either re-build or re-fit your GAS, by calling optixAccelBuild() again. Use OPTIX_BUILD_OPERATION_UPDATE if you want to re-fit instead of re-build.

For question #2, hopefully the answer above made clear that yes, you do need to update your mesh acceleration. And yes, you will also need to update your IAS (Instance Acceleration Structure). You do not need to update your SBT unless you are changing which shaders are attached to your mesh. If the only change is vertices moving, then you only need to update the vertex buffer and your acceleration structures.


David.

Thank you very much, dhart!
I will try it.

Hi dhart,
I have update new vertices and rebuild GAS, but it seems not work! Do i do it right?

Below is my update code.

void Scene::updateMeshAccels()
	{
		uint32_t triangle_input_flags = OPTIX_GEOMETRY_FLAG_DISABLE_ANYHIT;

		OptixAccelBuildOptions accel_options = {};
		accel_options.buildFlags = OPTIX_BUILD_FLAG_ALLOW_UPDATE;
		accel_options.operation = OPTIX_BUILD_OPERATION_UPDATE;

		for (size_t i = 0; i < m_meshes.size(); ++i)
		{
			auto& mesh = m_meshes[i];

			// Set New position data
			CUDA_CHECK(cudaMemcpy(reinterpret_cast<void*>(mesh->positions_data), new_position_data, vertices_size_in_byte, cudaMemcpyHostToDevice));
			
			OptixBuildInput triangle_input;
			memset(&triangle_input, 0, sizeof(OptixBuildInput));
			triangle_input.type = OPTIX_BUILD_INPUT_TYPE_TRIANGLES;
			triangle_input.triangleArray.vertexFormat = OPTIX_VERTEX_FORMAT_FLOAT3;
			triangle_input.triangleArray.vertexStrideInBytes = sizeof(float3);
			triangle_input.triangleArray.numVertices = mesh->positions_size;
			triangle_input.triangleArray.vertexBuffers = &(mesh->positions_data);
			triangle_input.triangleArray.indexFormat = OPTIX_INDICES_FORMAT_UNSIGNED_INT3;
			triangle_input.triangleArray.indexStrideInBytes = sizeof(uint32_t) * 3;
			triangle_input.triangleArray.numIndexTriplets = mesh->v_indices_size/* / 3*/;
			triangle_input.triangleArray.indexBuffer = mesh->v_indices_data;
			triangle_input.triangleArray.flags = &triangle_input_flags;
			triangle_input.triangleArray.numSbtRecords = 1;


			OptixAccelBufferSizes gas_buffer_sizes;
			OPTIX_CHECK(optixAccelComputeMemoryUsage(
				m_context,
				&accel_options,
				&triangle_input,
				1,  // num_build_inputs
				&gas_buffer_sizes
			));

			CUdeviceptr d_output;
			CUdeviceptr d_temp;

			CUDA_CHECK(cudaMalloc(reinterpret_cast<void**>(&d_temp), gas_buffer_sizes.tempSizeInBytes));
			CUDA_CHECK(cudaMalloc(reinterpret_cast<void**>(&d_output), gas_buffer_sizes.outputSizeInBytes));

			OPTIX_CHECK(optixAccelBuild(
				m_context,
				0,                                  // CUDA stream
				&accel_options,
				&triangle_input,
				1,                                  // num build inputs
				d_temp,
				gas_buffer_sizes.tempSizeInBytes,
				d_output,
				gas_buffer_sizes.outputSizeInBytes,
				&mesh->gas_handle,
				nullptr,                      // emitted property list
				0                                   // num emitted properties
			));

			CUDA_CHECK(cudaFree(reinterpret_cast<void*>(d_output)));
			CUDA_CHECK(cudaFree(reinterpret_cast<void*>(d_temp)));
		}
	}

As you say, i will also need to update my IAS, but i don’t konw how to do it. Does it has any different with first build IAS?

I will also update my IAS like below.But it still not working.

void Scene::updateInstanceAccel(int rayTypeCount /*= whitted::RAY_TYPE_COUNT*/)
	{
		const size_t num_instances = m_meshes.size();

		m_instance_input.type = OPTIX_BUILD_INPUT_TYPE_INSTANCES;
		m_instance_input.instanceArray.instances = m_d_instances.get();

		m_accel_options.operation = OPTIX_BUILD_OPERATION_UPDATE;

		OptixAccelBufferSizes ias_buffer_sizes;
		OPTIX_CHECK(optixAccelComputeMemoryUsage(
			m_context,
			&m_accel_options,
			&m_instance_input,
			1, // num build inputs
			&ias_buffer_sizes
		));

		CUdeviceptr d_temp_buffer = 0;
		CUDA_CHECK(cudaMalloc(
			reinterpret_cast<void**>(&d_temp_buffer),
			ias_buffer_sizes.tempSizeInBytes
		));
		

		OPTIX_CHECK(optixAccelBuild(
			m_context,
			nullptr,                  // CUDA stream
			&m_accel_options,
			&m_instance_input,
			1,                  // num build inputs
			d_temp_buffer,
			ias_buffer_sizes.tempSizeInBytes,
			m_d_ias_output_buffer,
			ias_buffer_sizes.outputSizeInBytes,
			&m_ias_handle,
			nullptr,            // emitted property list
			0                   // num emitted properties
		));

		CUDA_CHECK(cudaFree(reinterpret_cast<void*>(d_temp_buffer)));
	}

Hi, so what it happening when it doesn’t work? Is the mesh visible but not moving, or is the mesh disappearing, or something else?

I’d guess the problem is calling cudaFree() on the output buffer d_output, when rebuilding your GAS. Keep the output buffer until after you’re done using it.

Just a side note, your update functions also contain memory allocation and deallocation for the temp buffer. That will not break anything, but just so you know, if you aren’t changing the number of instances in your IAS, or the number of verts in your GAS, then you can re-use the temp buffer when update them. cudaMalloc() and cudaFree() can be pretty slow, so try to re-use or consolidate memory allocations when you can. For example, when building your GASes, you could loop through all meshes to compute the GAS buffer sizes, then allocate a single temp buffer using the largest temp buffer size you’ll need, and then loop again over all your meshes to build a GAS for each one and use the same temp buffer every time without allocating or deallocating temp buffers. You’ll still need to allocate a separate output buffer for each mesh.


David.

Hi,dhart. Thank you for replying to my questions.

Now I have changed my code. I keep my GAS’s gas_buffer_size, d_temp buffer and d_gas_output in my mesh struct, and initialize these variables in my first building GAS process. In my updating process, I re-use these variables. I do this because there are only 2 or 3 meshes in my scene, so it doesn’t cost too much memory. And for my IAS, I do the same change as my GAS.

But after doing that, the mesh does not moving anyway.

Below is my code of updateMeshAccels and updateInstanceAccels.

void Scene::updateMeshAccels()
	{
		uint32_t triangle_input_flags = OPTIX_GEOMETRY_FLAG_DISABLE_ANYHIT;

		OptixAccelBuildOptions accel_options = {};
		accel_options.buildFlags = OPTIX_BUILD_FLAG_ALLOW_UPDATE;
		accel_options.operation = OPTIX_BUILD_OPERATION_UPDATE;

		for (size_t i = 0; i < m_meshes.size(); ++i)
		{
			auto& mesh = m_meshes[i];

			OptixBuildInput triangle_input;
			memset(&triangle_input, 0, sizeof(OptixBuildInput));
			triangle_input.type = OPTIX_BUILD_INPUT_TYPE_TRIANGLES;
			triangle_input.triangleArray.vertexFormat = OPTIX_VERTEX_FORMAT_FLOAT3;
			triangle_input.triangleArray.vertexStrideInBytes = sizeof(float3);
			triangle_input.triangleArray.numVertices = mesh->positions_size;
			triangle_input.triangleArray.vertexBuffers = &(mesh->positions_data);
			triangle_input.triangleArray.indexFormat = OPTIX_INDICES_FORMAT_UNSIGNED_INT3;
			triangle_input.triangleArray.indexStrideInBytes = sizeof(uint32_t) * 3;
			triangle_input.triangleArray.numIndexTriplets = mesh->v_indices_size/* / 3*/;
			triangle_input.triangleArray.indexBuffer = mesh->v_indices_data;
			triangle_input.triangleArray.flags = &triangle_input_flags;
			triangle_input.triangleArray.numSbtRecords = 1;

			OPTIX_CHECK(optixAccelBuild(
				m_context,
				0,                                  // CUDA stream
				&accel_options,
				&triangle_input,
				1,                                  // num build inputs
				mesh->d_temp,
				mesh->gas_buffer_size.tempUpdateSizeInBytes,
				mesh->d_gas_output,
				mesh->gas_buffer_size.outputSizeInBytes,
				&mesh->gas_handle,
				nullptr,							// emitted property list
				0                                   // num emitted properties
			));

		}
	}
void Scene::updateInstanceAccel(int rayTypeCount /*= whitted::RAY_TYPE_COUNT*/)
	{
		const size_t num_instances = m_meshes.size();

		m_instance_input.type = OPTIX_BUILD_INPUT_TYPE_INSTANCES;
		m_instance_input.instanceArray.instances = m_d_instances.get();

		m_accel_options.operation = OPTIX_BUILD_OPERATION_UPDATE;

		OPTIX_CHECK(optixAccelBuild(
			m_context,
			nullptr,                  // CUDA stream
			&m_accel_options,
			&m_instance_input,
			1,                  // num build inputs
			m_ias_d_temp_buffer,
			m_ias_buffer_sizes.tempSizeInBytes,
			m_d_ias_output_buffer,
			m_ias_buffer_sizes.outputSizeInBytes,
			&m_ias_handle,
			nullptr,            // emitted property list
			0                   // num emitted properties
		));

	}

Your update code looks okay to me. So everything runs, but you don’t see any motion?

The first thing I would recommend is to verify that the data on the GPU has been updated. For example, you could print out a few of the vertices in mesh->positions_data from your raygen program.

In your first code example, you had a cudaMemcpy() to copy new position data, is that still happening in your new code?


David.