19#include <optix_function_table_definition.h>
33OptiX8Backend::OptiX8Backend() =
default;
38 cudaError_t rc = cudaGetDeviceCount(&device_count);
39 if (rc != cudaSuccess || device_count == 0) {
42 OptixResult optix_rc = optixInit();
43 return (optix_rc == OPTIX_SUCCESS);
49OptiX8Backend::~OptiX8Backend() {
61 CUDA_CHECK(cudaFree(
nullptr));
64 CUDA_CHECK(cudaStreamCreate(&cuda_stream));
67 OPTIX_CHECK(optixInit());
70 CUcontext cuda_context =
nullptr;
71 OptixDeviceContextOptions ctx_options = {};
72 ctx_options.logCallbackFunction = [](
unsigned int level,
const char *tag,
const char *message,
void *) {
74 std::cerr <<
"[OptiX][" << tag <<
"] " << message <<
"\n";
77 ctx_options.logCallbackLevel = 2;
78 OPTIX_CHECK(optixDeviceContextCreate(cuda_context, &ctx_options, &optix_context));
81 const std::string device_code_path = findDeviceCodeFile();
82 std::ifstream file(device_code_path, std::ios::binary | std::ios::ate);
83 if (!file.is_open()) {
84 helios_runtime_error(
"ERROR (OptiX8Backend::initialize): Could not open device code file: " + device_code_path);
86 const std::streamsize file_size = file.tellg();
87 file.seekg(0, std::ios::beg);
88 std::vector<char> device_code(file_size);
89 if (!file.read(device_code.data(), file_size)) {
90 helios_runtime_error(
"ERROR (OptiX8Backend::initialize): Could not read device code file: " + device_code_path);
93 OptixModuleCompileOptions module_options = {};
94 module_options.maxRegisterCount = OPTIX_COMPILE_DEFAULT_MAX_REGISTER_COUNT;
96 module_options.optLevel = OPTIX_COMPILE_OPTIMIZATION_LEVEL_0;
97 module_options.debugLevel = OPTIX_COMPILE_DEBUG_LEVEL_FULL;
99 module_options.optLevel = OPTIX_COMPILE_OPTIMIZATION_LEVEL_3;
100 module_options.debugLevel = OPTIX_COMPILE_DEBUG_LEVEL_NONE;
103 OptixPipelineCompileOptions pipeline_options = {};
104 pipeline_options.usesMotionBlur = 0;
105 pipeline_options.traversableGraphFlags = OPTIX_TRAVERSABLE_GRAPH_FLAG_ALLOW_SINGLE_GAS;
106 pipeline_options.numPayloadValues = 2;
107 pipeline_options.numAttributeValues = 2;
108 pipeline_options.exceptionFlags = OPTIX_EXCEPTION_FLAG_NONE;
109 pipeline_options.pipelineLaunchParamsVariableName =
"params";
110 pipeline_options.usesPrimitiveTypeFlags = OPTIX_PRIMITIVE_TYPE_FLAGS_CUSTOM;
113 size_t log_size =
sizeof(log);
115 OPTIX_CHECK(optixModuleCreate(
120 static_cast<size_t>(file_size),
125 OptixProgramGroupOptions pg_options = {};
128 auto createRaygen = [&](
const char *entry, OptixProgramGroup &pg) {
129 OptixProgramGroupDesc desc = {};
130 desc.kind = OPTIX_PROGRAM_GROUP_KIND_RAYGEN;
131 desc.raygen.module = optix_module;
132 desc.raygen.entryFunctionName = entry;
133 log_size =
sizeof(log);
134 OPTIX_CHECK(optixProgramGroupCreate(optix_context, &desc, 1, &pg_options, log, &log_size, &pg));
138 createRaygen(
"__raygen__direct", pg_raygen_direct);
139 createRaygen(
"__raygen__diffuse", pg_raygen_diffuse);
140 createRaygen(
"__raygen__camera", pg_raygen_camera);
141 createRaygen(
"__raygen__pixel_label", pg_raygen_pixel_label);
144 auto createMiss = [&](
const char *entry, OptixProgramGroup &pg) {
145 OptixProgramGroupDesc desc = {};
146 desc.kind = OPTIX_PROGRAM_GROUP_KIND_MISS;
147 desc.miss.module = optix_module;
148 desc.miss.entryFunctionName = entry;
149 log_size =
sizeof(log);
150 OPTIX_CHECK(optixProgramGroupCreate(optix_context, &desc, 1, &pg_options, log, &log_size, &pg));
154 createMiss(
"__miss__direct", pg_miss_direct);
155 createMiss(
"__miss__diffuse", pg_miss_diffuse);
156 createMiss(
"__miss__camera", pg_miss_camera);
157 createMiss(
"__miss__pixel_label", pg_miss_pixel_label);
165 auto createHitGroup = [&](
const char *ch_entry,
const char *is_entry,
const char *ah_entry, OptixProgramGroup &pg) {
166 OptixProgramGroupDesc desc = {};
167 desc.kind = OPTIX_PROGRAM_GROUP_KIND_HITGROUP;
168 desc.hitgroup.moduleCH = optix_module;
169 desc.hitgroup.entryFunctionNameCH = ch_entry;
170 desc.hitgroup.moduleIS = optix_module;
171 desc.hitgroup.entryFunctionNameIS = is_entry;
172 desc.hitgroup.moduleAH = ah_entry ? optix_module :
nullptr;
173 desc.hitgroup.entryFunctionNameAH = ah_entry;
174 log_size =
sizeof(log);
175 OPTIX_CHECK(optixProgramGroupCreate(optix_context, &desc, 1, &pg_options, log, &log_size, &pg));
179 createHitGroup(
"__closesthit__direct",
"__intersection__patch",
"__anyhit__direct", pg_hit_direct);
180 createHitGroup(
"__closesthit__diffuse",
"__intersection__patch",
"__anyhit__diffuse", pg_hit_diffuse);
181 createHitGroup(
"__closesthit__camera",
"__intersection__patch",
nullptr, pg_hit_camera);
182 createHitGroup(
"__closesthit__pixel_label",
"__intersection__patch",
nullptr, pg_hit_pixel_label);
185 OptixProgramGroup all_groups[] = {
186 pg_raygen_direct, pg_raygen_diffuse, pg_raygen_camera, pg_raygen_pixel_label,
187 pg_miss_direct, pg_miss_diffuse, pg_miss_camera, pg_miss_pixel_label,
188 pg_hit_direct, pg_hit_diffuse, pg_hit_camera, pg_hit_pixel_label
191 OptixPipelineLinkOptions link_options = {};
192 link_options.maxTraceDepth = 1;
194 log_size =
sizeof(log);
195 OPTIX_CHECK(optixPipelineCreate(
200 sizeof(all_groups) /
sizeof(all_groups[0]),
205 OptixStackSizes stack_sizes = {};
206 for (
auto &pg : all_groups) {
207 OPTIX_CHECK(optixUtilAccumulateStackSizes(pg, &stack_sizes, optix_pipeline));
209 uint32_t max_trace_depth = 1;
210 uint32_t direct_callable_stack_size_from_traversal = 0;
211 uint32_t direct_callable_stack_size_from_state = 0;
212 uint32_t continuation_stack_size = 0;
213 OPTIX_CHECK(optixUtilComputeStackSizes(
218 &direct_callable_stack_size_from_traversal,
219 &direct_callable_stack_size_from_state,
220 &continuation_stack_size));
221 OPTIX_CHECK(optixPipelineSetStackSize(
223 direct_callable_stack_size_from_traversal,
224 direct_callable_stack_size_from_state,
225 continuation_stack_size,
229 CUDA_CHECK(cudaMalloc(
reinterpret_cast<void **
>(&d_params),
sizeof(
OptiX8LaunchParams)));
230 memset(&h_params, 0,
sizeof(h_params));
232 is_initialized =
true;
236 if (!is_initialized) {
242 cudaStreamSynchronize(cuda_stream);
246 freeGeometryBuffers();
247 freeMaterialBuffers();
249 auto freePtr = [](CUdeviceptr &ptr) {
250 if (ptr) { cudaFree(
reinterpret_cast<void *
>(ptr)); ptr = 0; }
253 freePtr(d_radiation_in);
254 freePtr(d_radiation_out_top);
255 freePtr(d_radiation_out_bottom);
256 freePtr(d_scatter_buff_top);
257 freePtr(d_scatter_buff_bottom);
258 freePtr(d_radiation_in_camera);
259 freePtr(d_scatter_buff_top_cam);
260 freePtr(d_scatter_buff_bottom_cam);
261 freePtr(d_radiation_specular);
263 freePtr(d_camera_pixel_label);
264 freePtr(d_camera_pixel_depth);
265 freePtr(d_source_positions);
266 freePtr(d_source_rotations);
267 freePtr(d_source_widths);
268 freePtr(d_source_types);
269 freePtr(d_source_fluxes);
270 freePtr(d_source_fluxes_cam);
271 freePtr(d_diffuse_flux);
272 freePtr(d_diffuse_extinction);
273 freePtr(d_diffuse_peak_dir);
274 freePtr(d_diffuse_dist_norm);
275 freePtr(d_sky_radiance_params);
276 freePtr(d_camera_sky_radiance);
277 freePtr(d_solar_disk_radiance);
278 freePtr(d_camera_diffuse_flux);
279 freePtr(d_band_emission_flag);
280 freePtr(d_band_launch_flag);
281 freePtr(d_mask_data);
282 freePtr(d_mask_sizes);
289 if (d_raygen_records) { cudaFree(
reinterpret_cast<void *
>(d_raygen_records)); d_raygen_records = 0; }
290 if (d_miss_records) { cudaFree(
reinterpret_cast<void *
>(d_miss_records)); d_miss_records = 0; }
291 if (d_hitgroup_records) { cudaFree(
reinterpret_cast<void *
>(d_hitgroup_records)); d_hitgroup_records = 0; }
294 if (d_gas_output) { cudaFree(
reinterpret_cast<void *
>(d_gas_output)); d_gas_output = 0; }
297 auto destroyPG = [](OptixProgramGroup &pg) {
298 if (pg) { optixProgramGroupDestroy(pg); pg =
nullptr; }
300 destroyPG(pg_raygen_direct); destroyPG(pg_raygen_diffuse);
301 destroyPG(pg_raygen_camera); destroyPG(pg_raygen_pixel_label);
302 destroyPG(pg_miss_direct); destroyPG(pg_miss_diffuse);
303 destroyPG(pg_miss_camera); destroyPG(pg_miss_pixel_label);
304 destroyPG(pg_hit_direct); destroyPG(pg_hit_diffuse);
305 destroyPG(pg_hit_camera); destroyPG(pg_hit_pixel_label);
307 if (optix_pipeline) { optixPipelineDestroy(optix_pipeline); optix_pipeline =
nullptr; }
308 if (optix_module) { optixModuleDestroy(optix_module); optix_module =
nullptr; }
309 if (optix_context) { optixDeviceContextDestroy(optix_context); optix_context =
nullptr; }
310 if (cuda_stream) { cudaStreamDestroy(cuda_stream); cuda_stream =
nullptr; }
312 is_initialized =
false;
320 validateGeometryBeforeUpload(geometry);
325 if (geometry.disk_count > 0) {
327 "which are not yet supported by the OptiX 8.1 backend.");
329 if (geometry.voxel_count > 0) {
331 "which are not yet supported by the OptiX 8.1 backend.");
334 freeGeometryBuffers();
336 auto upload = [
this](CUdeviceptr &d_ptr,
const void *src,
size_t bytes) {
337 if (bytes > 0 && src) {
338 CUDA_CHECK(cudaMalloc(
reinterpret_cast<void **
>(&d_ptr), bytes));
339 CUDA_CHECK(cudaMemcpy(
reinterpret_cast<void *
>(d_ptr), src, bytes, cudaMemcpyHostToDevice));
343 upload(d_transform_matrix, geometry.transform_matrices.data(), geometry.transform_matrices.size() *
sizeof(
float));
344 upload(d_primitive_type, geometry.primitive_types.data(), geometry.primitive_types.size() *
sizeof(uint32_t));
345 upload(d_primitive_positions, geometry.primitive_positions.data(), geometry.primitive_positions.size() *
sizeof(uint32_t));
346 upload(d_primitive_uuid_arr, geometry.primitive_UUIDs.data(), geometry.primitive_UUIDs.size() *
sizeof(uint32_t));
347 upload(d_primitiveID, geometry.primitive_IDs.data(), geometry.primitive_IDs.size() *
sizeof(uint32_t));
348 upload(d_objectID, geometry.object_IDs.data(), geometry.object_IDs.size() *
sizeof(uint32_t));
349 upload(d_twosided_flag, geometry.twosided_flags.data(), geometry.twosided_flags.size() *
sizeof(
char));
350 upload(d_primitive_solid_fraction, geometry.solid_fractions.data(), geometry.solid_fractions.size() *
sizeof(
float));
353 if (!geometry.object_subdivisions.empty()) {
354 const size_t bytes = geometry.object_subdivisions.size() *
sizeof(
helios::int2);
355 CUDA_CHECK(cudaMalloc(
reinterpret_cast<void **
>(&d_object_subdivisions), bytes));
356 CUDA_CHECK(cudaMemcpy(
reinterpret_cast<void *
>(d_object_subdivisions),
357 geometry.object_subdivisions.data(), bytes, cudaMemcpyHostToDevice));
361 if (geometry.patch_count > 0) {
362 upload(d_patch_vertices, geometry.patches.vertices.data(),
363 geometry.patches.vertices.size() *
sizeof(
helios::vec3));
364 upload(d_patch_UUIDs, geometry.patches.UUIDs.data(),
365 geometry.patches.UUIDs.size() *
sizeof(uint32_t));
367 if (geometry.triangles.count > 0) {
368 upload(d_triangle_vertices, geometry.triangles.vertices.data(),
369 geometry.triangles.vertices.size() *
sizeof(
helios::vec3));
370 upload(d_triangle_UUIDs, geometry.triangles.UUIDs.data(),
371 geometry.triangles.UUIDs.size() *
sizeof(uint32_t));
373 if (geometry.disk_count > 0) {
374 upload(d_disk_centers, geometry.disk_centers.data(), geometry.disk_centers.size() *
sizeof(
helios::vec3));
375 upload(d_disk_radii, geometry.disk_radii.data(), geometry.disk_radii.size() *
sizeof(
float));
376 upload(d_disk_normals, geometry.disk_normals.data(), geometry.disk_normals.size() *
sizeof(
helios::vec3));
377 upload(d_disk_UUIDs, geometry.disk_UUIDs.data(), geometry.disk_UUIDs.size() *
sizeof(uint32_t));
379 if (geometry.tiles.count > 0) {
380 upload(d_tile_vertices, geometry.tiles.vertices.data(),
382 upload(d_tile_UUIDs, geometry.tiles.UUIDs.data(),
383 geometry.tiles.UUIDs.size() *
sizeof(uint32_t));
385 if (geometry.voxels.count > 0) {
386 upload(d_voxel_vertices, geometry.voxels.vertices.data(),
387 geometry.voxels.vertices.size() *
sizeof(
helios::vec3));
388 upload(d_voxel_UUIDs, geometry.voxels.UUIDs.data(),
389 geometry.voxels.UUIDs.size() *
sizeof(uint32_t));
391 if (geometry.bbox_count > 0) {
392 upload(d_bbox_vertices, geometry.bboxes.vertices.data(),
393 geometry.bboxes.vertices.size() *
sizeof(
helios::vec3));
394 upload(d_bbox_UUIDs, geometry.bboxes.UUIDs.data(),
395 geometry.bboxes.UUIDs.size() *
sizeof(uint32_t));
403 if (geometry.bbox_count > 0) {
404 const size_t Nprims = geometry.primitive_count;
405 const size_t Nbboxes = geometry.bbox_count;
407 freeCUdeviceptr(d_primitive_type);
408 std::vector<uint32_t> ext_types(geometry.primitive_types);
409 ext_types.resize(Nprims + Nbboxes, 5u);
410 upload(d_primitive_type, ext_types.data(), ext_types.size() *
sizeof(uint32_t));
412 freeCUdeviceptr(d_primitive_uuid_arr);
413 std::vector<uint32_t> ext_uuids(geometry.primitive_UUIDs);
414 ext_uuids.insert(ext_uuids.end(),
415 geometry.bboxes.UUIDs.begin(), geometry.bboxes.UUIDs.end());
416 upload(d_primitive_uuid_arr, ext_uuids.data(), ext_uuids.size() *
sizeof(uint32_t));
422 std::vector<uint32_t> mask_offsets;
423 uint32_t cumulative = 0;
424 for (
const auto &sz : geometry.mask_sizes) {
425 mask_offsets.push_back(cumulative);
426 cumulative +=
static_cast<uint32_t
>(sz.x) *
static_cast<uint32_t
>(sz.y);
430 std::vector<uint8_t> mask_data_u8(cumulative);
431 for (uint32_t i = 0; i < cumulative; ++i) {
432 mask_data_u8[i] = geometry.mask_data[i] ? 1u : 0u;
438 const size_t Np = geometry.primitive_count;
441 for (
size_t p = 0; p < Np; ++p) {
442 if (!geometry.uv_IDs.empty() && geometry.uv_IDs[p] >= 0) {
443 for (
int v = 0; v < 4 && uv_read < geometry.uv_data.size(); ++v) {
444 uv_flat[p * 4 + v] = geometry.uv_data[uv_read++];
449 freeCUdeviceptr(d_mask_data);
450 freeCUdeviceptr(d_mask_offsets);
451 freeCUdeviceptr(d_mask_sizes);
452 freeCUdeviceptr(d_mask_IDs);
453 freeCUdeviceptr(d_uv_data);
454 freeCUdeviceptr(d_uv_IDs);
456 upload(d_mask_data, mask_data_u8.data(), mask_data_u8.size() *
sizeof(uint8_t));
457 upload(d_mask_offsets, mask_offsets.data(), mask_offsets.size() *
sizeof(uint32_t));
458 upload(d_mask_sizes, geometry.mask_sizes.data(), geometry.mask_sizes.size() *
sizeof(
helios::int2));
459 upload(d_mask_IDs, geometry.mask_IDs.data(), geometry.mask_IDs.size() *
sizeof(int32_t));
460 upload(d_uv_data, uv_flat.data(), uv_flat.size() *
sizeof(
helios::vec2));
461 upload(d_uv_IDs, geometry.uv_IDs.data(), geometry.uv_IDs.size() *
sizeof(int32_t));
465 const uint32_t Nprims =
static_cast<uint32_t
>(geometry.primitive_count);
467 h_params.
primitive_type =
reinterpret_cast<uint32_t *
>(d_primitive_type);
469 h_params.
primitive_uuid =
reinterpret_cast<uint32_t *
>(d_primitive_uuid_arr);
470 h_params.
primitiveID =
reinterpret_cast<uint32_t *
>(d_primitiveID);
471 h_params.
objectID =
reinterpret_cast<uint32_t *
>(d_objectID);
473 h_params.
twosided_flag =
reinterpret_cast<int8_t *
>(d_twosided_flag);
474 h_params.primitive_solid_fraction =
reinterpret_cast<float *
>(d_primitive_solid_fraction);
475 h_params.
patch_vertices =
reinterpret_cast<float3 *
>(d_patch_vertices);
476 h_params.patch_UUIDs =
reinterpret_cast<uint32_t *
>(d_patch_UUIDs);
478 h_params.triangle_UUIDs =
reinterpret_cast<uint32_t *
>(d_triangle_UUIDs);
479 h_params.disk_centers =
reinterpret_cast<float3 *
>(d_disk_centers);
480 h_params.disk_radii =
reinterpret_cast<float *
>(d_disk_radii);
481 h_params.disk_normals =
reinterpret_cast<float3 *
>(d_disk_normals);
482 h_params.disk_UUIDs =
reinterpret_cast<uint32_t *
>(d_disk_UUIDs);
483 h_params.
tile_vertices =
reinterpret_cast<float3 *
>(d_tile_vertices);
484 h_params.tile_UUIDs =
reinterpret_cast<uint32_t *
>(d_tile_UUIDs);
485 h_params.
voxel_vertices =
reinterpret_cast<float3 *
>(d_voxel_vertices);
486 h_params.voxel_UUIDs =
reinterpret_cast<uint32_t *
>(d_voxel_UUIDs);
487 h_params.
bbox_vertices =
reinterpret_cast<float3 *
>(d_bbox_vertices);
488 h_params.bbox_UUIDs =
reinterpret_cast<uint32_t *
>(d_bbox_UUIDs);
489 h_params.Nprimitives = Nprims;
491 h_params.
periodic_flag = make_float2(geometry.periodic_flag.x, geometry.periodic_flag.y);
492 h_params.
mask_data =
reinterpret_cast<uint8_t *
>(d_mask_data);
493 h_params.
mask_offsets =
reinterpret_cast<uint32_t *
>(d_mask_offsets);
494 h_params.
mask_sizes =
reinterpret_cast<int32_t *
>(d_mask_sizes);
495 h_params.
mask_IDs =
reinterpret_cast<int32_t *
>(d_mask_IDs);
496 h_params.
uv_data =
reinterpret_cast<float2 *
>(d_uv_data);
497 h_params.
uv_IDs =
reinterpret_cast<int32_t *
>(d_uv_IDs);
500 current_primitive_count = geometry.primitive_count;
501 current_patch_count = geometry.patch_count;
502 current_triangle_count = geometry.triangle_count;
503 current_disk_count = geometry.disk_count;
504 current_tile_count = geometry.tile_count;
505 current_voxel_count = geometry.voxel_count;
506 current_bbox_count = geometry.bbox_count;
508 buildAABBs(geometry);
512 if (current_primitive_count == 0) {
513 helios_runtime_error(
"ERROR (OptiX8Backend::buildAccelerationStructure): No geometry uploaded. Call updateGeometry() first.");
515 buildGAS(
static_cast<uint32_t
>(current_primitive_count + current_bbox_count));
524 freeMaterialBuffers();
526 auto upload = [
this](CUdeviceptr &d_ptr,
const void *src,
size_t bytes) {
527 if (bytes > 0 && src) {
528 CUDA_CHECK(cudaMalloc(
reinterpret_cast<void **
>(&d_ptr), bytes));
529 CUDA_CHECK(cudaMemcpy(
reinterpret_cast<void *
>(d_ptr), src, bytes, cudaMemcpyHostToDevice));
536 const size_t Nprims = current_primitive_count;
537 const size_t Nbands = materials.
num_bands;
538 const size_t alloc = std::max(materials.
reflectivity.size(), std::max(Nprims * Nbands, (
size_t)1));
539 CUDA_CHECK(cudaMalloc(
reinterpret_cast<void **
>(&d_rho), alloc *
sizeof(
float)));
540 CUDA_CHECK(cudaMemset(
reinterpret_cast<void *
>(d_rho), 0, alloc *
sizeof(
float)));
542 CUDA_CHECK(cudaMemcpy(
reinterpret_cast<void *
>(d_rho), materials.
reflectivity.data(),
543 materials.
reflectivity.size() *
sizeof(
float), cudaMemcpyHostToDevice));
547 const size_t Nprims = current_primitive_count;
548 const size_t Nbands = materials.
num_bands;
549 const size_t alloc = std::max(materials.
transmissivity.size(), std::max(Nprims * Nbands, (
size_t)1));
550 CUDA_CHECK(cudaMalloc(
reinterpret_cast<void **
>(&d_tau), alloc *
sizeof(
float)));
551 CUDA_CHECK(cudaMemset(
reinterpret_cast<void *
>(d_tau), 0, alloc *
sizeof(
float)));
553 CUDA_CHECK(cudaMemcpy(
reinterpret_cast<void *
>(d_tau), materials.
transmissivity.data(),
554 materials.
transmissivity.size() *
sizeof(
float), cudaMemcpyHostToDevice));
568 bool any_glass =
false;
570 if (g != 0) { any_glass =
true;
break; }
575 if (materials.
num_bands > (
size_t)HELIOS_MAX_RADIATION_BANDS) {
577 " radiation bands, which exceeds the compile-time maximum of " + std::to_string(HELIOS_MAX_RADIATION_BANDS) +
578 " (HELIOS_MAX_RADIATION_BANDS). Reduce the number of bands or increase the cap.");
580 upload(d_glass_n, materials.
glass_n.data(), materials.
glass_n.size() *
sizeof(
float));
581 upload(d_glass_KL, materials.
glass_KL.data(), materials.
glass_KL.size() *
sizeof(
float));
582 upload(d_is_glass, materials.
is_glass.data(), materials.
is_glass.size() *
sizeof(
char));
586 const size_t Nprims = current_primitive_count;
587 const size_t Nbands = materials.
num_bands;
588 const size_t rad_bytes = Nprims * Nbands *
sizeof(float);
590 reallocDevice(d_radiation_in, rad_bytes);
591 reallocDevice(d_radiation_out_top, rad_bytes);
592 reallocDevice(d_radiation_out_bottom, rad_bytes);
593 reallocDevice(d_scatter_buff_top, rad_bytes);
594 reallocDevice(d_scatter_buff_bottom, rad_bytes);
596 current_band_count = Nbands;
601 h_params.
rho =
reinterpret_cast<float *
>(d_rho);
602 h_params.
tau =
reinterpret_cast<float *
>(d_tau);
603 h_params.
rho_cam =
reinterpret_cast<float *
>(d_rho_cam);
604 h_params.
tau_cam =
reinterpret_cast<float *
>(d_tau_cam);
605 h_params.specular_exponent =
reinterpret_cast<float *
>(d_specular_exponent);
606 h_params.specular_scale =
reinterpret_cast<float *
>(d_specular_scale);
607 h_params.
glass_n =
reinterpret_cast<float *
>(d_glass_n);
608 h_params.
glass_KL =
reinterpret_cast<float *
>(d_glass_KL);
609 h_params.
is_glass =
reinterpret_cast<int8_t *
>(d_is_glass);
610 h_params.
radiation_in =
reinterpret_cast<float *
>(d_radiation_in);
615 h_params.Nsources =
static_cast<uint32_t
>(materials.
num_sources);
616 h_params.Ncameras =
static_cast<uint32_t
>(materials.
num_cameras);
617 h_params.Nbands_global =
static_cast<uint32_t
>(Nbands);
625 auto freePtr = [
this](CUdeviceptr &ptr) { freeCUdeviceptr(ptr); };
626 freePtr(d_source_positions);
627 freePtr(d_source_rotations);
628 freePtr(d_source_widths);
629 freePtr(d_source_types);
632 const size_t Nsources = sources.size();
634 current_source_count = 0;
635 h_params.Nsources = 0;
639 std::vector<float3> positions(Nsources);
640 std::vector<float3> rotations(Nsources);
641 std::vector<float2> widths(Nsources);
642 std::vector<uint32_t> types(Nsources);
644 for (
size_t i = 0; i < Nsources; i++) {
645 positions[i] = make_float3(sources[i].position.x, sources[i].position.y, sources[i].position.z);
646 rotations[i] = make_float3(sources[i].rotation.x, sources[i].rotation.y, sources[i].rotation.z);
647 widths[i] = make_float2(sources[i].width.x, sources[i].width.y);
648 types[i] = sources[i].type;
651 auto upload = [
this](CUdeviceptr &d_ptr,
const void *src,
size_t bytes) {
652 CUDA_CHECK(cudaMalloc(
reinterpret_cast<void **
>(&d_ptr), bytes));
653 CUDA_CHECK(cudaMemcpy(
reinterpret_cast<void *
>(d_ptr), src, bytes, cudaMemcpyHostToDevice));
656 upload(d_source_positions, positions.data(), Nsources *
sizeof(float3));
657 upload(d_source_rotations, rotations.data(), Nsources *
sizeof(float3));
658 upload(d_source_widths, widths.data(), Nsources *
sizeof(float2));
659 upload(d_source_types, types.data(), Nsources *
sizeof(uint32_t));
664 freeCUdeviceptr(d_source_fluxes_cam);
665 std::vector<float> fluxes_cam;
666 for (
size_t i = 0; i < Nsources; i++) {
667 for (
float f : sources[i].fluxes_cam) {
668 fluxes_cam.push_back(f);
671 if (!fluxes_cam.empty()) {
672 upload(d_source_fluxes_cam, fluxes_cam.data(), fluxes_cam.size() *
sizeof(
float));
676 current_source_count = Nsources;
678 h_params.Nsources =
static_cast<uint32_t
>(Nsources);
679 h_params.source_positions =
reinterpret_cast<float3 *
>(d_source_positions);
680 h_params.source_rotations =
reinterpret_cast<float3 *
>(d_source_rotations);
681 h_params.source_widths =
reinterpret_cast<float2 *
>(d_source_widths);
682 h_params.source_types =
reinterpret_cast<uint32_t *
>(d_source_types);
690 const std::vector<helios::vec3> &peak_dir,
691 const std::vector<float> &dist_norm,
692 const std::vector<float> &sky_energy) {
693 freeCUdeviceptr(d_diffuse_flux);
694 freeCUdeviceptr(d_diffuse_extinction);
695 freeCUdeviceptr(d_diffuse_peak_dir);
696 freeCUdeviceptr(d_diffuse_dist_norm);
697 freeCUdeviceptr(d_Rsky);
699 auto upload_f = [
this](CUdeviceptr &ptr,
const std::vector<float> &v) {
701 CUDA_CHECK(cudaMalloc(
reinterpret_cast<void **
>(&ptr), v.size() *
sizeof(
float)));
702 CUDA_CHECK(cudaMemcpy(
reinterpret_cast<void *
>(ptr), v.data(),
703 v.size() *
sizeof(
float), cudaMemcpyHostToDevice));
706 upload_f(d_diffuse_flux, flux);
707 upload_f(d_diffuse_extinction, extinction);
708 upload_f(d_diffuse_dist_norm, dist_norm);
709 upload_f(d_Rsky, sky_energy);
711 if (!peak_dir.empty()) {
712 std::vector<float3> pd_f3(peak_dir.size());
713 for (
size_t i = 0; i < peak_dir.size(); i++) {
714 pd_f3[i] = make_float3(peak_dir[i].x, peak_dir[i].y, peak_dir[i].z);
716 CUDA_CHECK(cudaMalloc(
reinterpret_cast<void **
>(&d_diffuse_peak_dir),
717 pd_f3.size() *
sizeof(float3)));
718 CUDA_CHECK(cudaMemcpy(
reinterpret_cast<void *
>(d_diffuse_peak_dir), pd_f3.data(),
719 pd_f3.size() *
sizeof(float3), cudaMemcpyHostToDevice));
722 h_params.
diffuse_flux =
reinterpret_cast<float *
>(d_diffuse_flux);
726 h_params.
Rsky =
reinterpret_cast<float *
>(d_Rsky);
730 const std::vector<float> &camera_sky_radiance,
732 const std::vector<float> &solar_disk_radiance,
733 float solar_disk_cos_angle,
734 const std::vector<float> &camera_diffuse_flux,
735 const std::vector<uint32_t> &band_emission_flag) {
737 freeCUdeviceptr(d_sky_radiance_params);
738 if (!sky_radiance_params.empty()) {
739 std::vector<float4> f4(sky_radiance_params.size());
740 for (
size_t i = 0; i < sky_radiance_params.size(); i++) {
741 f4[i] = make_float4(sky_radiance_params[i].x, sky_radiance_params[i].y,
742 sky_radiance_params[i].z, sky_radiance_params[i].w);
744 CUDA_CHECK(cudaMalloc(
reinterpret_cast<void **
>(&d_sky_radiance_params), f4.size() *
sizeof(float4)));
745 CUDA_CHECK(cudaMemcpy(
reinterpret_cast<void *
>(d_sky_radiance_params), f4.data(),
746 f4.size() *
sizeof(float4), cudaMemcpyHostToDevice));
749 freeCUdeviceptr(d_camera_sky_radiance);
750 if (!camera_sky_radiance.empty()) {
751 CUDA_CHECK(cudaMalloc(
reinterpret_cast<void **
>(&d_camera_sky_radiance),
752 camera_sky_radiance.size() *
sizeof(
float)));
753 CUDA_CHECK(cudaMemcpy(
reinterpret_cast<void *
>(d_camera_sky_radiance), camera_sky_radiance.data(),
754 camera_sky_radiance.size() *
sizeof(
float), cudaMemcpyHostToDevice));
757 freeCUdeviceptr(d_solar_disk_radiance);
758 if (!solar_disk_radiance.empty()) {
759 CUDA_CHECK(cudaMalloc(
reinterpret_cast<void **
>(&d_solar_disk_radiance),
760 solar_disk_radiance.size() *
sizeof(
float)));
761 CUDA_CHECK(cudaMemcpy(
reinterpret_cast<void *
>(d_solar_disk_radiance), solar_disk_radiance.data(),
762 solar_disk_radiance.size() *
sizeof(
float), cudaMemcpyHostToDevice));
769 freeCUdeviceptr(d_camera_diffuse_flux);
770 if (!camera_diffuse_flux.empty()) {
771 CUDA_CHECK(cudaMalloc(
reinterpret_cast<void **
>(&d_camera_diffuse_flux),
772 camera_diffuse_flux.size() *
sizeof(
float)));
773 CUDA_CHECK(cudaMemcpy(
reinterpret_cast<void *
>(d_camera_diffuse_flux), camera_diffuse_flux.data(),
774 camera_diffuse_flux.size() *
sizeof(
float), cudaMemcpyHostToDevice));
777 freeCUdeviceptr(d_band_emission_flag);
778 if (!band_emission_flag.empty()) {
779 CUDA_CHECK(cudaMalloc(
reinterpret_cast<void **
>(&d_band_emission_flag),
780 band_emission_flag.size() *
sizeof(uint32_t)));
781 CUDA_CHECK(cudaMemcpy(
reinterpret_cast<void *
>(d_band_emission_flag), band_emission_flag.data(),
782 band_emission_flag.size() *
sizeof(uint32_t), cudaMemcpyHostToDevice));
790 h_params.sun_direction = make_float3(sun_direction.
x, sun_direction.
y, sun_direction.
z);
791 h_params.solar_disk_cos_angle = solar_disk_cos_angle;
799 if (!is_initialized) {
802 if (gas_handle == 0) {
803 helios_runtime_error(
"ERROR (OptiX8Backend::launchDirectRays): No acceleration structure. Call buildAccelerationStructure() first.");
806 applyLaunchParams(launch_params);
809 if (d_band_launch_flag) { freeCUdeviceptr(d_band_launch_flag); }
812 std::vector<uint8_t> flags_u8(Nbands_g);
813 for (
size_t i = 0; i < Nbands_g; i++) {
816 CUDA_CHECK(cudaMalloc(
reinterpret_cast<void **
>(&d_band_launch_flag), Nbands_g *
sizeof(uint8_t)));
817 CUDA_CHECK(cudaMemcpy(
reinterpret_cast<void *
>(d_band_launch_flag), flags_u8.data(),
818 Nbands_g *
sizeof(uint8_t), cudaMemcpyHostToDevice));
821 h_params.traversable = gas_handle;
825 const uint32_t launch_count = launch_params.
launch_count;
827 const uint32_t dim_x =
static_cast<uint32_t
>(sqrtf(
static_cast<float>(rpp)));
828 const uint32_t dim_y = (dim_x > 0) ? (rpp / dim_x) : 1u;
829 h_params.launch_dim_x = dim_x;
830 h_params.launch_dim_y = dim_y;
833 h_params.prd_pool =
nullptr;
836 OptixShaderBindingTable direct_sbt = sbt;
837 direct_sbt.raygenRecord = d_raygen_records;
840 const uint32_t MAX_DEPTH = 65535u;
842 uint32_t remaining = launch_count;
843 while (remaining > 0) {
844 const uint32_t batch = std::min(remaining, MAX_DEPTH);
847 uploadLaunchParams();
848 OPTIX_CHECK(optixLaunch(optix_pipeline, cuda_stream, d_params,
850 dim_x, dim_y, batch));
851 CUDA_CHECK(cudaStreamSynchronize(cuda_stream));
858 if (!is_initialized) {
861 if (gas_handle == 0) {
863 "Call buildAccelerationStructure() first.");
866 applyLaunchParams(launch_params);
873 CUDA_CHECK(cudaMemcpy(
reinterpret_cast<void *
>(d_radiation_out_top),
876 cudaMemcpyHostToDevice));
879 CUDA_CHECK(cudaMemcpy(
reinterpret_cast<void *
>(d_radiation_out_bottom),
882 cudaMemcpyHostToDevice));
886 if (d_band_launch_flag) { freeCUdeviceptr(d_band_launch_flag); }
889 std::vector<uint8_t> flags_u8(Nbands_g);
890 for (
size_t i = 0; i < Nbands_g; i++) {
893 CUDA_CHECK(cudaMalloc(
reinterpret_cast<void **
>(&d_band_launch_flag),
894 Nbands_g *
sizeof(uint8_t)));
895 CUDA_CHECK(cudaMemcpy(
reinterpret_cast<void *
>(d_band_launch_flag), flags_u8.data(),
896 Nbands_g *
sizeof(uint8_t), cudaMemcpyHostToDevice));
899 h_params.traversable = gas_handle;
902 freeCUdeviceptr(d_diffuse_flux);
903 freeCUdeviceptr(d_diffuse_extinction);
904 freeCUdeviceptr(d_diffuse_peak_dir);
905 freeCUdeviceptr(d_diffuse_dist_norm);
906 freeCUdeviceptr(d_sky_radiance_params);
908 auto upload_f = [
this](CUdeviceptr &ptr,
const std::vector<float> &v) {
910 CUDA_CHECK(cudaMalloc(
reinterpret_cast<void **
>(&ptr), v.size() *
sizeof(
float)));
911 CUDA_CHECK(cudaMemcpy(
reinterpret_cast<void *
>(ptr), v.data(),
912 v.size() *
sizeof(
float), cudaMemcpyHostToDevice));
926 CUDA_CHECK(cudaMalloc(
reinterpret_cast<void **
>(&d_diffuse_peak_dir),
927 pd_f3.size() *
sizeof(float3)));
928 CUDA_CHECK(cudaMemcpy(
reinterpret_cast<void *
>(d_diffuse_peak_dir), pd_f3.data(),
929 pd_f3.size() *
sizeof(float3), cudaMemcpyHostToDevice));
940 CUDA_CHECK(cudaMalloc(
reinterpret_cast<void **
>(&d_sky_radiance_params),
941 sky_f4.size() *
sizeof(float4)));
942 CUDA_CHECK(cudaMemcpy(
reinterpret_cast<void *
>(d_sky_radiance_params), sky_f4.data(),
943 sky_f4.size() *
sizeof(float4), cudaMemcpyHostToDevice));
946 h_params.
diffuse_flux =
reinterpret_cast<float *
>(d_diffuse_flux);
960 const uint32_t dim_x =
static_cast<uint32_t
>(sqrtf(
static_cast<float>(rpp)));
961 const uint32_t dim_y = (dim_x > 0) ? (rpp / dim_x) : 1u;
962 h_params.launch_dim_x = dim_x;
963 h_params.launch_dim_y = dim_y;
964 h_params.prd_pool =
nullptr;
967 OptixShaderBindingTable diffuse_sbt = sbt;
968 diffuse_sbt.raygenRecord = d_raygen_record_diffuse;
971 const uint32_t MAX_DEPTH = 65535u;
972 const uint32_t launch_count = launch_params.
launch_count;
974 uint32_t remaining = launch_count;
975 while (remaining > 0) {
976 const uint32_t batch = std::min(remaining, MAX_DEPTH);
979 uploadLaunchParams();
980 OPTIX_CHECK(optixLaunch(optix_pipeline, cuda_stream, d_params,
982 dim_x, dim_y, batch));
983 CUDA_CHECK(cudaStreamSynchronize(cuda_stream));
990 if (!is_initialized) {
993 if (gas_handle == 0) {
995 "Call buildAccelerationStructure() first.");
998 applyLaunchParams(launch_params);
1006 const uint32_t cam_id = launch_params.
camera_id;
1010 const size_t Npixels = (size_t)full_w * full_h;
1011 const size_t cam_bytes = Npixels * Nbands_l *
sizeof(float);
1012 if (cam_id != current_camera_launch_id || current_launch_band_count != Nbands_l) {
1013 reallocDevice(d_radiation_in_camera, cam_bytes);
1014 CUDA_CHECK(cudaMemset(
reinterpret_cast<void *
>(d_radiation_in_camera), 0, cam_bytes));
1017 freeCUdeviceptr(d_camera_pixel_label);
1018 freeCUdeviceptr(d_camera_pixel_depth);
1019 h_params.camera_pixel_label =
nullptr;
1020 h_params.camera_pixel_depth =
nullptr;
1021 current_camera_launch_id = cam_id;
1022 current_launch_band_count = Nbands_l;
1027 if (d_band_launch_flag) { freeCUdeviceptr(d_band_launch_flag); }
1030 std::vector<uint8_t> flags_u8(Nbands_g);
1031 for (
size_t i = 0; i < Nbands_g; i++) {
1034 CUDA_CHECK(cudaMalloc(
reinterpret_cast<void **
>(&d_band_launch_flag), Nbands_g *
sizeof(uint8_t)));
1035 CUDA_CHECK(cudaMemcpy(
reinterpret_cast<void *
>(d_band_launch_flag), flags_u8.data(),
1036 Nbands_g *
sizeof(uint8_t), cudaMemcpyHostToDevice));
1039 h_params.traversable = gas_handle;
1042 h_params.launch_dim_x = anti_samples;
1043 h_params.launch_dim_y = tile_w;
1045 uploadLaunchParams();
1047 OptixShaderBindingTable camera_sbt = sbt;
1048 camera_sbt.raygenRecord = d_raygen_record_camera;
1050 OPTIX_CHECK(optixLaunch(optix_pipeline, cuda_stream, d_params,
1052 anti_samples, tile_w, tile_h));
1053 CUDA_CHECK(cudaStreamSynchronize(cuda_stream));
1057 if (!is_initialized) {
1060 if (gas_handle == 0) {
1061 helios_runtime_error(
"ERROR (OptiX8Backend::launchPixelLabelRays): No acceleration structure. "
1062 "Call buildAccelerationStructure() first.");
1065 applyLaunchParams(launch_params);
1071 if (d_band_launch_flag) { freeCUdeviceptr(d_band_launch_flag); }
1074 std::vector<uint8_t> flags_u8(Nbands_g);
1075 for (
size_t i = 0; i < Nbands_g; i++) {
1078 CUDA_CHECK(cudaMalloc(
reinterpret_cast<void **
>(&d_band_launch_flag), Nbands_g *
sizeof(uint8_t)));
1079 CUDA_CHECK(cudaMemcpy(
reinterpret_cast<void *
>(d_band_launch_flag), flags_u8.data(),
1080 Nbands_g *
sizeof(uint8_t), cudaMemcpyHostToDevice));
1083 h_params.traversable = gas_handle;
1086 h_params.launch_dim_x = 1u;
1087 h_params.launch_dim_y = tile_w;
1089 uploadLaunchParams();
1091 OptixShaderBindingTable pixel_label_sbt = sbt;
1092 pixel_label_sbt.raygenRecord = d_raygen_record_pixel_label;
1094 OPTIX_CHECK(optixLaunch(optix_pipeline, cuda_stream, d_params,
1096 1u, tile_w, tile_h));
1097 CUDA_CHECK(cudaStreamSynchronize(cuda_stream));
1105 const size_t Nprims = current_primitive_count;
1106 const size_t Nbands = current_band_count;
1107 const size_t total = Nprims * Nbands;
1115 if (d_radiation_in) results.
radiation_in = downloadFloat(d_radiation_in, total);
1116 if (d_radiation_out_top) results.
radiation_out_top = downloadFloat(d_radiation_out_top, total);
1117 if (d_radiation_out_bottom) results.
radiation_out_bottom = downloadFloat(d_radiation_out_bottom, total);
1118 if (d_scatter_buff_top) results.
scatter_buff_top = downloadFloat(d_scatter_buff_top, total);
1119 if (d_scatter_buff_bottom) results.
scatter_buff_bottom = downloadFloat(d_scatter_buff_bottom, total);
1123 if (Nprims > 0 && current_launch_band_count > 0) {
1124 const size_t cam_total = Nprims * current_launch_band_count;
1125 if (d_scatter_buff_top_cam) results.
scatter_buff_top_cam = downloadFloat(d_scatter_buff_top_cam, cam_total);
1126 if (d_scatter_buff_bottom_cam) results.
scatter_buff_bottom_cam = downloadFloat(d_scatter_buff_bottom_cam, cam_total);
1131 std::vector<float> &pixel_depths,
uint camera_id,
1133 const size_t Npixels = (size_t)resolution.x * resolution.y;
1135 if (d_radiation_in_camera && Npixels > 0 && current_launch_band_count > 0) {
1136 pixel_data = downloadFloat(d_radiation_in_camera, Npixels * current_launch_band_count);
1139 if (d_camera_pixel_label && Npixels > 0) {
1140 auto labels_u32 = downloadUInt32(d_camera_pixel_label, Npixels);
1141 pixel_labels.assign(labels_u32.begin(), labels_u32.end());
1144 if (d_camera_pixel_depth && Npixels > 0) {
1145 pixel_depths = downloadFloat(d_camera_pixel_depth, Npixels);
1154 const size_t Nprims = current_primitive_count;
1155 if (Nprims == 0 || launch_band_count == 0)
return;
1157 if (launch_band_count > current_band_count) {
1159 std::to_string(launch_band_count) +
") exceeds current_band_count (" +
1160 std::to_string(current_band_count) +
"). Call updateMaterials() first.");
1164 current_camera_launch_id = 0xFFFFFFFFu;
1166 const size_t bytes = Nprims * launch_band_count *
sizeof(float);
1168 if (d_radiation_in) CUDA_CHECK(cudaMemset(
reinterpret_cast<void *
>(d_radiation_in), 0, bytes));
1169 if (d_radiation_out_top) CUDA_CHECK(cudaMemset(
reinterpret_cast<void *
>(d_radiation_out_top), 0, bytes));
1170 if (d_radiation_out_bottom) CUDA_CHECK(cudaMemset(
reinterpret_cast<void *
>(d_radiation_out_bottom), 0, bytes));
1171 if (d_scatter_buff_top) CUDA_CHECK(cudaMemset(
reinterpret_cast<void *
>(d_scatter_buff_top), 0, bytes));
1172 if (d_scatter_buff_bottom) CUDA_CHECK(cudaMemset(
reinterpret_cast<void *
>(d_scatter_buff_bottom), 0, bytes));
1175 const size_t specular_size = current_source_count * current_camera_count * Nprims * launch_band_count;
1176 if (specular_size > 0) {
1177 const size_t specular_bytes = specular_size *
sizeof(float);
1178 reallocDevice(d_radiation_specular, specular_bytes);
1179 CUDA_CHECK(cudaMemset(
reinterpret_cast<void *
>(d_radiation_specular), 0, specular_bytes));
1185 const size_t total_bytes = current_primitive_count * current_band_count *
sizeof(float);
1186 if (total_bytes == 0)
return;
1188 if (d_scatter_buff_top) CUDA_CHECK(cudaMemset(
reinterpret_cast<void *
>(d_scatter_buff_top), 0, total_bytes));
1189 if (d_scatter_buff_bottom) CUDA_CHECK(cudaMemset(
reinterpret_cast<void *
>(d_scatter_buff_bottom), 0, total_bytes));
1193 const size_t Npixels = (size_t)resolution.x * resolution.y;
1194 if (Npixels == 0)
return;
1196 reallocDevice(d_camera_pixel_label, Npixels *
sizeof(uint32_t));
1197 reallocDevice(d_camera_pixel_depth, Npixels *
sizeof(
float));
1198 CUDA_CHECK(cudaMemset(
reinterpret_cast<void *
>(d_camera_pixel_label), 0, Npixels *
sizeof(uint32_t)));
1199 CUDA_CHECK(cudaMemset(
reinterpret_cast<void *
>(d_camera_pixel_depth), 0, Npixels *
sizeof(
float)));
1201 h_params.camera_pixel_label =
reinterpret_cast<uint32_t *
>(d_camera_pixel_label);
1202 h_params.camera_pixel_depth =
reinterpret_cast<float *
>(d_camera_pixel_depth);
1206 const size_t total = current_primitive_count * current_band_count *
sizeof(float);
1207 if (total == 0)
return;
1208 if (d_scatter_buff_top && d_radiation_out_top) {
1209 CUDA_CHECK(cudaMemcpy(
reinterpret_cast<void *
>(d_radiation_out_top),
1210 reinterpret_cast<const void *
>(d_scatter_buff_top),
1211 total, cudaMemcpyDeviceToDevice));
1213 if (d_scatter_buff_bottom && d_radiation_out_bottom) {
1214 CUDA_CHECK(cudaMemcpy(
reinterpret_cast<void *
>(d_radiation_out_bottom),
1215 reinterpret_cast<const void *
>(d_scatter_buff_bottom),
1216 total, cudaMemcpyDeviceToDevice));
1221 const std::vector<float> &radiation_out_bottom) {
1222 if (!radiation_out_top.empty() && d_radiation_out_top) {
1223 CUDA_CHECK(cudaMemcpy(
reinterpret_cast<void *
>(d_radiation_out_top),
1224 radiation_out_top.data(),
1225 radiation_out_top.size() *
sizeof(
float),
1226 cudaMemcpyHostToDevice));
1228 if (!radiation_out_bottom.empty() && d_radiation_out_bottom) {
1229 CUDA_CHECK(cudaMemcpy(
reinterpret_cast<void *
>(d_radiation_out_bottom),
1230 radiation_out_bottom.data(),
1231 radiation_out_bottom.size() *
sizeof(
float),
1232 cudaMemcpyHostToDevice));
1237 const std::vector<float> &scatter_bottom_cam) {
1238 if (!scatter_top_cam.empty() && d_scatter_buff_top_cam) {
1239 CUDA_CHECK(cudaMemcpy(
reinterpret_cast<void *
>(d_scatter_buff_top_cam),
1240 scatter_top_cam.data(),
1241 scatter_top_cam.size() *
sizeof(
float),
1242 cudaMemcpyHostToDevice));
1244 if (!scatter_bottom_cam.empty() && d_scatter_buff_bottom_cam) {
1245 CUDA_CHECK(cudaMemcpy(
reinterpret_cast<void *
>(d_scatter_buff_bottom_cam),
1246 scatter_bottom_cam.data(),
1247 scatter_bottom_cam.size() *
sizeof(
float),
1248 cudaMemcpyHostToDevice));
1253 const size_t Nprims = current_primitive_count;
1254 if (Nprims == 0 || launch_band_count == 0)
return;
1256 const size_t bytes = Nprims * launch_band_count *
sizeof(float);
1257 reallocDevice(d_scatter_buff_top_cam, bytes);
1258 reallocDevice(d_scatter_buff_bottom_cam, bytes);
1259 CUDA_CHECK(cudaMemset(
reinterpret_cast<void *
>(d_scatter_buff_top_cam), 0, bytes));
1260 CUDA_CHECK(cudaMemset(
reinterpret_cast<void *
>(d_scatter_buff_bottom_cam), 0, bytes));
1263 h_params.scatter_buff_bottom_cam =
reinterpret_cast<float *
>(d_scatter_buff_bottom_cam);
1264 current_launch_band_count = launch_band_count;
1268 freeCUdeviceptr(d_source_fluxes);
1269 if (!fluxes.empty()) {
1270 const size_t bytes = fluxes.size() *
sizeof(float);
1271 CUDA_CHECK(cudaMalloc(
reinterpret_cast<void **
>(&d_source_fluxes), bytes));
1272 CUDA_CHECK(cudaMemcpy(
reinterpret_cast<void *
>(d_source_fluxes), fluxes.data(), bytes, cudaMemcpyHostToDevice));
1276 h_params.
source_fluxes =
reinterpret_cast<float *
>(d_source_fluxes);
1280 freeCUdeviceptr(d_source_fluxes_cam);
1281 if (!fluxes_cam.empty()) {
1282 const size_t bytes = fluxes_cam.size() *
sizeof(float);
1283 CUDA_CHECK(cudaMalloc(
reinterpret_cast<void **
>(&d_source_fluxes_cam), bytes));
1284 CUDA_CHECK(cudaMemcpy(
reinterpret_cast<void *
>(d_source_fluxes_cam), fluxes_cam.data(), bytes,
1285 cudaMemcpyHostToDevice));
1296 size_t free_bytes = 0;
1297 size_t total_bytes = 0;
1298 CUDA_CHECK(cudaMemGetInfo(&free_bytes, &total_bytes));
1299 const float free_mb =
static_cast<float>(free_bytes) / (1024.0f * 1024.0f);
1300 const float total_mb =
static_cast<float>(total_bytes) / (1024.0f * 1024.0f);
1301 std::cout <<
"GPU memory: " << free_mb <<
" MB free / " << total_mb <<
" MB total" << std::endl;
1308void OptiX8Backend::freeCUdeviceptr(CUdeviceptr &ptr) {
1310 cudaFree(
reinterpret_cast<void *
>(ptr));
1315void OptiX8Backend::freeGeometryBuffers() {
1316 freeCUdeviceptr(d_transform_matrix);
1317 freeCUdeviceptr(d_primitive_type);
1318 freeCUdeviceptr(d_primitive_positions);
1319 freeCUdeviceptr(d_primitive_uuid_arr);
1320 freeCUdeviceptr(d_primitiveID);
1321 freeCUdeviceptr(d_objectID);
1322 freeCUdeviceptr(d_object_subdivisions);
1323 freeCUdeviceptr(d_twosided_flag);
1324 freeCUdeviceptr(d_primitive_solid_fraction);
1325 freeCUdeviceptr(d_patch_vertices);
1326 freeCUdeviceptr(d_patch_UUIDs);
1327 freeCUdeviceptr(d_triangle_vertices);
1328 freeCUdeviceptr(d_triangle_UUIDs);
1329 freeCUdeviceptr(d_disk_centers);
1330 freeCUdeviceptr(d_disk_radii);
1331 freeCUdeviceptr(d_disk_normals);
1332 freeCUdeviceptr(d_disk_UUIDs);
1333 freeCUdeviceptr(d_tile_vertices);
1334 freeCUdeviceptr(d_tile_UUIDs);
1335 freeCUdeviceptr(d_voxel_vertices);
1336 freeCUdeviceptr(d_voxel_UUIDs);
1337 freeCUdeviceptr(d_bbox_vertices);
1338 freeCUdeviceptr(d_bbox_UUIDs);
1339 freeCUdeviceptr(d_mask_data);
1340 freeCUdeviceptr(d_mask_offsets);
1341 freeCUdeviceptr(d_mask_sizes);
1342 freeCUdeviceptr(d_mask_IDs);
1343 freeCUdeviceptr(d_uv_data);
1344 freeCUdeviceptr(d_uv_IDs);
1345 freeCUdeviceptr(d_aabbs);
1346 freeCUdeviceptr(d_gas_output);
1350void OptiX8Backend::freeMaterialBuffers() {
1351 freeCUdeviceptr(d_rho);
1352 freeCUdeviceptr(d_tau);
1353 freeCUdeviceptr(d_rho_cam);
1354 freeCUdeviceptr(d_tau_cam);
1355 freeCUdeviceptr(d_specular_exponent);
1356 freeCUdeviceptr(d_specular_scale);
1357 freeCUdeviceptr(d_glass_n);
1358 freeCUdeviceptr(d_glass_KL);
1359 freeCUdeviceptr(d_is_glass);
1362void OptiX8Backend::reallocDevice(CUdeviceptr &ptr,
size_t bytes) {
1363 freeCUdeviceptr(ptr);
1365 CUDA_CHECK(cudaMalloc(
reinterpret_cast<void **
>(&ptr), bytes));
1369std::vector<float> OptiX8Backend::downloadFloat(CUdeviceptr ptr,
size_t count)
const {
1370 std::vector<float> result(count);
1371 CUDA_CHECK(cudaMemcpy(result.data(),
reinterpret_cast<const void *
>(ptr),
1372 count *
sizeof(
float), cudaMemcpyDeviceToHost));
1376std::vector<uint32_t> OptiX8Backend::downloadUInt32(CUdeviceptr ptr,
size_t count)
const {
1377 std::vector<uint32_t> result(count);
1378 CUDA_CHECK(cudaMemcpy(result.data(),
reinterpret_cast<const void *
>(ptr),
1379 count *
sizeof(uint32_t), cudaMemcpyDeviceToHost));
1383void OptiX8Backend::buildAABBs(
const RayTracingGeometry &geometry) {
1384 const uint32_t Nprims =
static_cast<uint32_t
>(geometry.primitive_count);
1385 const uint32_t Nbboxes =
static_cast<uint32_t
>(geometry.bboxes.UUIDs.size());
1386 const uint32_t Ntotal = Nprims + Nbboxes;
1387 if (Ntotal == 0)
return;
1389 std::vector<OptixAabb> aabbs(Ntotal);
1390 const float eps = 1e-5f;
1392 auto store_aabb = [&](uint32_t pos,
float mn_x,
float mn_y,
float mn_z,
1393 float mx_x,
float mx_y,
float mx_z) {
1394 if (mx_x - mn_x < eps) { mn_x -= eps; mx_x += eps; }
1395 if (mx_y - mn_y < eps) { mn_y -= eps; mx_y += eps; }
1396 if (mx_z - mn_z < eps) { mn_z -= eps; mx_z += eps; }
1397 aabbs[pos] = {mn_x, mn_y, mn_z, mx_x, mx_y, mx_z};
1401 for (uint32_t pos = 0; pos < Nprims; pos++) {
1402 const float *T = &geometry.transform_matrices[pos * 16];
1403 const uint32_t pt = geometry.primitive_types[pos];
1405 float mn_x = FLT_MAX, mn_y = FLT_MAX, mn_z = FLT_MAX;
1406 float mx_x = -FLT_MAX, mx_y = -FLT_MAX, mx_z = -FLT_MAX;
1408 auto expand = [&](
float x,
float y,
float z) {
1409 const float wx = T[0]*x + T[1]*y + T[2]*z + T[3];
1410 const float wy = T[4]*x + T[5]*y + T[6]*z + T[7];
1411 const float wz = T[8]*x + T[9]*y + T[10]*z + T[11];
1412 if (wx < mn_x) mn_x = wx;
if (wx > mx_x) mx_x = wx;
1413 if (wy < mn_y) mn_y = wy;
if (wy > mx_y) mx_y = wy;
1414 if (wz < mn_z) mn_z = wz;
if (wz > mx_z) mx_z = wz;
1417 if (pt == 0 || pt == 3) {
1418 expand(-0.5f, -0.5f, 0.f); expand( 0.5f, -0.5f, 0.f);
1419 expand(-0.5f, 0.5f, 0.f); expand( 0.5f, 0.5f, 0.f);
1420 }
else if (pt == 1) {
1421 expand(0.f, 0.f, 0.f); expand(0.f, 1.f, 0.f); expand(1.f, 1.f, 0.f);
1422 }
else if (pt == 2) {
1423 expand(-0.5f, -0.5f, 0.f); expand( 0.5f, -0.5f, 0.f);
1424 expand(-0.5f, 0.5f, 0.f); expand( 0.5f, 0.5f, 0.f);
1425 }
else if (pt == 4) {
1426 for (
float fx : {0.f, 1.f})
1427 for (
float fy : {0.f, 1.f})
1428 for (
float fz : {0.f, 1.f})
1431 expand(-0.5f, -0.5f, -0.5f); expand( 0.5f, 0.5f, 0.5f);
1434 store_aabb(pos, mn_x, mn_y, mn_z, mx_x, mx_y, mx_z);
1438 for (uint32_t b = 0; b < Nbboxes; b++) {
1439 float mn_x = FLT_MAX, mn_y = FLT_MAX, mn_z = FLT_MAX;
1440 float mx_x = -FLT_MAX, mx_y = -FLT_MAX, mx_z = -FLT_MAX;
1441 for (
int v = 0; v < 4; v++) {
1442 const helios::vec3 &vtx = geometry.bboxes.vertices[b * 4 + v];
1443 mn_x = std::min(mn_x, vtx.
x); mx_x = std::max(mx_x, vtx.
x);
1444 mn_y = std::min(mn_y, vtx.
y); mx_y = std::max(mx_y, vtx.
y);
1445 mn_z = std::min(mn_z, vtx.
z); mx_z = std::max(mx_z, vtx.
z);
1447 store_aabb(Nprims + b, mn_x, mn_y, mn_z, mx_x, mx_y, mx_z);
1450 const size_t aabb_bytes = Ntotal *
sizeof(OptixAabb);
1451 reallocDevice(d_aabbs, aabb_bytes);
1452 CUDA_CHECK(cudaMemcpy(
reinterpret_cast<void *
>(d_aabbs), aabbs.data(), aabb_bytes, cudaMemcpyHostToDevice));
1455void OptiX8Backend::buildGAS(uint32_t Nprimitives) {
1456 if (d_gas_output) { cudaFree(
reinterpret_cast<void *
>(d_gas_output)); d_gas_output = 0; }
1459 if (Nprimitives == 0 || !d_aabbs)
return;
1461 const unsigned int build_flags[] = {OPTIX_GEOMETRY_FLAG_NONE};
1462 OptixBuildInputCustomPrimitiveArray ca = {};
1463 ca.aabbBuffers = &d_aabbs;
1464 ca.numPrimitives = Nprimitives;
1465 ca.strideInBytes =
sizeof(OptixAabb);
1466 ca.flags = build_flags;
1467 ca.numSbtRecords = 1;
1469 OptixBuildInput bi = {};
1470 bi.type = OPTIX_BUILD_INPUT_TYPE_CUSTOM_PRIMITIVES;
1471 bi.customPrimitiveArray = ca;
1473 OptixAccelBuildOptions opts = {};
1474 opts.buildFlags = OPTIX_BUILD_FLAG_ALLOW_COMPACTION | OPTIX_BUILD_FLAG_PREFER_FAST_TRACE;
1475 opts.operation = OPTIX_BUILD_OPERATION_BUILD;
1477 OptixAccelBufferSizes sizes = {};
1478 OPTIX_CHECK(optixAccelComputeMemoryUsage(optix_context, &opts, &bi, 1, &sizes));
1480 CUdeviceptr d_temp = 0, d_pre_compact = 0, d_compact_size = 0;
1481 CUDA_CHECK(cudaMalloc(
reinterpret_cast<void **
>(&d_temp), sizes.tempSizeInBytes));
1482 CUDA_CHECK(cudaMalloc(
reinterpret_cast<void **
>(&d_pre_compact), sizes.outputSizeInBytes));
1483 CUDA_CHECK(cudaMalloc(
reinterpret_cast<void **
>(&d_compact_size),
sizeof(uint64_t)));
1485 OptixAccelEmitDesc emit = {};
1486 emit.type = OPTIX_PROPERTY_TYPE_COMPACTED_SIZE;
1487 emit.result = d_compact_size;
1489 OPTIX_CHECK(optixAccelBuild(optix_context, cuda_stream, &opts, &bi, 1,
1490 d_temp, sizes.tempSizeInBytes,
1491 d_pre_compact, sizes.outputSizeInBytes,
1492 &gas_handle, &emit, 1));
1493 CUDA_CHECK(cudaStreamSynchronize(cuda_stream));
1495 uint64_t compact_size = 0;
1496 CUDA_CHECK(cudaMemcpy(&compact_size,
reinterpret_cast<const void *
>(d_compact_size),
1497 sizeof(uint64_t), cudaMemcpyDeviceToHost));
1499 CUDA_CHECK(cudaMalloc(
reinterpret_cast<void **
>(&d_gas_output), compact_size));
1500 OPTIX_CHECK(optixAccelCompact(optix_context, cuda_stream, gas_handle, d_gas_output, compact_size, &gas_handle));
1501 CUDA_CHECK(cudaStreamSynchronize(cuda_stream));
1503 CUDA_CHECK(cudaFree(
reinterpret_cast<void *
>(d_temp)));
1504 CUDA_CHECK(cudaFree(
reinterpret_cast<void *
>(d_pre_compact)));
1505 CUDA_CHECK(cudaFree(
reinterpret_cast<void *
>(d_compact_size)));
1507 h_params.traversable = gas_handle;
1510void OptiX8Backend::buildSBT() {
1511 if (d_raygen_records) { cudaFree(
reinterpret_cast<void *
>(d_raygen_records)); d_raygen_records = 0; }
1512 if (d_miss_records) { cudaFree(
reinterpret_cast<void *
>(d_miss_records)); d_miss_records = 0; }
1513 if (d_hitgroup_records) { cudaFree(
reinterpret_cast<void *
>(d_hitgroup_records)); d_hitgroup_records = 0; }
1516 struct alignas(OPTIX_SBT_RECORD_ALIGNMENT) RaygenRecord {
1517 char header[OPTIX_SBT_RECORD_HEADER_SIZE];
1519 constexpr int N_rg = 4;
1520 std::vector<RaygenRecord> rg_recs(N_rg);
1521 OPTIX_CHECK(optixSbtRecordPackHeader(pg_raygen_direct, &rg_recs[0]));
1522 OPTIX_CHECK(optixSbtRecordPackHeader(pg_raygen_diffuse, &rg_recs[1]));
1523 OPTIX_CHECK(optixSbtRecordPackHeader(pg_raygen_camera, &rg_recs[2]));
1524 OPTIX_CHECK(optixSbtRecordPackHeader(pg_raygen_pixel_label, &rg_recs[3]));
1525 CUDA_CHECK(cudaMalloc(
reinterpret_cast<void **
>(&d_raygen_records), N_rg *
sizeof(RaygenRecord)));
1526 CUDA_CHECK(cudaMemcpy(
reinterpret_cast<void *
>(d_raygen_records), rg_recs.data(),
1527 N_rg *
sizeof(RaygenRecord), cudaMemcpyHostToDevice));
1530 d_raygen_record_direct = d_raygen_records;
1531 d_raygen_record_diffuse = d_raygen_records + 1 *
sizeof(RaygenRecord);
1532 d_raygen_record_camera = d_raygen_records + 2 *
sizeof(RaygenRecord);
1533 d_raygen_record_pixel_label = d_raygen_records + 3 *
sizeof(RaygenRecord);
1536 struct alignas(OPTIX_SBT_RECORD_ALIGNMENT) MissRecord {
1537 char header[OPTIX_SBT_RECORD_HEADER_SIZE];
1539 constexpr int N_ms = 4;
1540 std::vector<MissRecord> ms_recs(N_ms);
1541 OPTIX_CHECK(optixSbtRecordPackHeader(pg_miss_direct, &ms_recs[0]));
1542 OPTIX_CHECK(optixSbtRecordPackHeader(pg_miss_diffuse, &ms_recs[1]));
1543 OPTIX_CHECK(optixSbtRecordPackHeader(pg_miss_camera, &ms_recs[2]));
1544 OPTIX_CHECK(optixSbtRecordPackHeader(pg_miss_pixel_label, &ms_recs[3]));
1545 CUDA_CHECK(cudaMalloc(
reinterpret_cast<void **
>(&d_miss_records), N_ms *
sizeof(MissRecord)));
1546 CUDA_CHECK(cudaMemcpy(
reinterpret_cast<void *
>(d_miss_records), ms_recs.data(),
1547 N_ms *
sizeof(MissRecord), cudaMemcpyHostToDevice));
1550 struct alignas(OPTIX_SBT_RECORD_ALIGNMENT) HitRecord {
1551 char header[OPTIX_SBT_RECORD_HEADER_SIZE];
1554 constexpr int N_hg = 4;
1555 std::vector<HitRecord> hg_recs(N_hg);
1556 for (
auto &r : hg_recs) {
1557 r.data.vertices =
reinterpret_cast<float3 *
>(d_patch_vertices);
1558 r.data.UUIDs =
reinterpret_cast<uint32_t *
>(d_patch_UUIDs);
1559 r.data.prim_type = 0;
1561 OPTIX_CHECK(optixSbtRecordPackHeader(pg_hit_direct, &hg_recs[0]));
1562 OPTIX_CHECK(optixSbtRecordPackHeader(pg_hit_diffuse, &hg_recs[1]));
1563 OPTIX_CHECK(optixSbtRecordPackHeader(pg_hit_camera, &hg_recs[2]));
1564 OPTIX_CHECK(optixSbtRecordPackHeader(pg_hit_pixel_label, &hg_recs[3]));
1565 CUDA_CHECK(cudaMalloc(
reinterpret_cast<void **
>(&d_hitgroup_records), N_hg *
sizeof(HitRecord)));
1566 CUDA_CHECK(cudaMemcpy(
reinterpret_cast<void *
>(d_hitgroup_records), hg_recs.data(),
1567 N_hg *
sizeof(HitRecord), cudaMemcpyHostToDevice));
1570 sbt.raygenRecord = d_raygen_records;
1571 sbt.missRecordBase = d_miss_records;
1572 sbt.missRecordStrideInBytes =
static_cast<uint32_t
>(
sizeof(MissRecord));
1573 sbt.missRecordCount = N_ms;
1574 sbt.hitgroupRecordBase = d_hitgroup_records;
1575 sbt.hitgroupRecordStrideInBytes =
static_cast<uint32_t
>(
sizeof(HitRecord));
1576 sbt.hitgroupRecordCount = N_hg;
1579void OptiX8Backend::uploadLaunchParams() {
1580 CUDA_CHECK(cudaMemcpyAsync(
reinterpret_cast<void *
>(d_params), &h_params,
1584void OptiX8Backend::applyLaunchParams(
const RayTracingLaunchParams ¶ms) {
1587 h_params.rays_per_primitive = params.rays_per_primitive;
1588 h_params.random_seed = params.random_seed;
1589 h_params.Nbands_global = params.num_bands_global;
1590 h_params.Nbands_launch = params.num_bands_launch;
1592 h_params.scattering_iteration = params.scattering_iteration;
1593 h_params.specular_reflection_enabled = params.specular_reflection_enabled;
1594 h_params.camera_ID = params.camera_id;
1595 h_params.camera_position = make_float3(params.camera_position.x, params.camera_position.y, params.camera_position.z);
1597 h_params.camera_focal_length = params.camera_focal_length;
1598 h_params.camera_lens_diameter = params.camera_lens_diameter;
1599 h_params.FOV_aspect_ratio = params.camera_fov_aspect;
1600 h_params.camera_HFOV = params.camera_HFOV;
1601 h_params.camera_resolution.x = params.camera_resolution.x;
1602 h_params.camera_resolution.y = params.camera_resolution.y;
1603 h_params.camera_viewplane_length= params.camera_viewplane_length;
1604 h_params.camera_pixel_solid_angle = params.camera_pixel_solid_angle;
1605 h_params.camera_pixel_offset.x = params.camera_pixel_offset.x;
1606 h_params.camera_pixel_offset.y = params.camera_pixel_offset.y;
1607 h_params.camera_resolution_full.x = params.camera_resolution_full.x;
1608 h_params.camera_resolution_full.y = params.camera_resolution_full.y;
1611std::string OptiX8Backend::findDeviceCodeFile()
const {
1614 const std::vector<std::string> candidate_names = {
1615 "OptiX8DeviceCode.optixir",
1616 "OptiX8DeviceCode.ptx",
1618 for (
const auto &name : candidate_names) {
1620 if (!path.empty()) {
1621 return path.string();
1625 "ERROR (OptiX8Backend::findDeviceCodeFile): Could not find OptiX8DeviceCode.optixir or "
1626 "OptiX8DeviceCode.ptx in the radiation plugin asset directory. "
1627 "Ensure the radiation plugin was built with OptiX 8 support (HELIOS_HAVE_OPTIX8).");