1.3.77
 
Loading...
Searching...
No Matches
OptiX6Backend.cpp
Go to the documentation of this file.
1
16#include "OptiX6Backend.h"
17#include "Context.h"
18#include <chrono>
19
20using namespace helios;
21
22// Forward declaration of error handling functions
23static void sutilReportError(const char *message);
24static void sutilHandleError(RTcontext context, RTresult code, const char *file, int line);
25
26OptiX6Backend::OptiX6Backend() {
27 // Constructor - initialization happens in initialize()
28}
29
30bool OptiX6Backend::probe() noexcept {
31 try {
32 RTcontext ctx = nullptr;
33 RTresult rc = rtContextCreate(&ctx);
34 if (rc != RT_SUCCESS) {
35 return false;
36 }
37 // RAII guard ensures context is destroyed even if an exception occurs
38 struct ContextGuard {
39 RTcontext c;
40 ~ContextGuard() { if (c) rtContextDestroy(c); }
41 } guard{ctx};
42 return true;
43 } catch (...) {
44 return false;
45 }
46}
47
48OptiX6Backend::~OptiX6Backend() {
49 if (is_initialized) {
50 shutdown();
51 }
52}
53
55
56 if (is_initialized) {
57 helios_runtime_error("ERROR (OptiX6Backend::initialize): Backend already initialized.");
58 }
59
60 /* Create OptiX Context */
61 RT_CHECK_ERROR(rtContextCreate(&OptiX_Context));
62 RT_CHECK_ERROR(rtContextSetPrintEnabled(OptiX_Context, 1));
63
64 /* Set ray type and entry point counts */
65 RT_CHECK_ERROR(rtContextSetRayTypeCount(OptiX_Context, 4));
66 // ray types: 0=direct, 1=diffuse, 2=camera, 3=pixel_label
67
68 RT_CHECK_ERROR(rtContextSetEntryPointCount(OptiX_Context, 4));
69 // entry points: 0=direct_raygen, 1=diffuse_raygen, 2=camera_raygen, 3=pixel_label_raygen
70
71 /* Declare ray type variables */
72 RT_CHECK_ERROR(rtContextDeclareVariable(OptiX_Context, "direct_ray_type", &direct_ray_type_RTvariable));
73 RT_CHECK_ERROR(rtVariableSet1ui(direct_ray_type_RTvariable, RAYTYPE_DIRECT));
74 RT_CHECK_ERROR(rtContextDeclareVariable(OptiX_Context, "diffuse_ray_type", &diffuse_ray_type_RTvariable));
75 RT_CHECK_ERROR(rtVariableSet1ui(diffuse_ray_type_RTvariable, RAYTYPE_DIFFUSE));
76 RT_CHECK_ERROR(rtContextDeclareVariable(OptiX_Context, "camera_ray_type", &camera_ray_type_RTvariable));
77 RT_CHECK_ERROR(rtVariableSet1ui(camera_ray_type_RTvariable, RAYTYPE_CAMERA));
78 RT_CHECK_ERROR(rtContextDeclareVariable(OptiX_Context, "pixel_label_ray_type", &pixel_label_ray_type_RTvariable));
79 RT_CHECK_ERROR(rtVariableSet1ui(pixel_label_ray_type_RTvariable, RAYTYPE_PIXEL_LABEL));
80
81 /* Load ray generation programs from PTX */
82 std::string ptx_path = helios::resolvePluginAsset("radiation", "cuda_compile_ptx_generated_rayGeneration.cu.ptx").string();
83 RT_CHECK_ERROR(rtProgramCreateFromPTXFile(OptiX_Context, ptx_path.c_str(), "direct_raygen", &direct_raygen));
84 RT_CHECK_ERROR(rtContextSetRayGenerationProgram(OptiX_Context, RAYTYPE_DIRECT, direct_raygen));
85 RT_CHECK_ERROR(rtProgramCreateFromPTXFile(OptiX_Context, ptx_path.c_str(), "diffuse_raygen", &diffuse_raygen));
86 RT_CHECK_ERROR(rtContextSetRayGenerationProgram(OptiX_Context, RAYTYPE_DIFFUSE, diffuse_raygen));
87 RT_CHECK_ERROR(rtProgramCreateFromPTXFile(OptiX_Context, ptx_path.c_str(), "camera_raygen", &camera_raygen));
88 RT_CHECK_ERROR(rtContextSetRayGenerationProgram(OptiX_Context, RAYTYPE_CAMERA, camera_raygen));
89 RT_CHECK_ERROR(rtProgramCreateFromPTXFile(OptiX_Context, ptx_path.c_str(), "pixel_label_raygen", &pixel_label_raygen));
90 RT_CHECK_ERROR(rtContextSetRayGenerationProgram(OptiX_Context, RAYTYPE_PIXEL_LABEL, pixel_label_raygen));
91
92 /* Load hit programs from PTX */
93 std::string hit_ptx_path = helios::resolvePluginAsset("radiation", "cuda_compile_ptx_generated_rayHit.cu.ptx").string();
94 RT_CHECK_ERROR(rtProgramCreateFromPTXFile(OptiX_Context, hit_ptx_path.c_str(), "closest_hit_direct", &closest_hit_direct));
95 RT_CHECK_ERROR(rtProgramCreateFromPTXFile(OptiX_Context, hit_ptx_path.c_str(), "closest_hit_diffuse", &closest_hit_diffuse));
96 RT_CHECK_ERROR(rtProgramCreateFromPTXFile(OptiX_Context, hit_ptx_path.c_str(), "closest_hit_camera", &closest_hit_camera));
97 RT_CHECK_ERROR(rtProgramCreateFromPTXFile(OptiX_Context, hit_ptx_path.c_str(), "closest_hit_pixel_label", &closest_hit_pixel_label));
98 RT_CHECK_ERROR(rtProgramCreateFromPTXFile(OptiX_Context, hit_ptx_path.c_str(), "miss_direct", &miss_direct));
99 RT_CHECK_ERROR(rtProgramCreateFromPTXFile(OptiX_Context, hit_ptx_path.c_str(), "miss_diffuse", &miss_diffuse));
100 RT_CHECK_ERROR(rtProgramCreateFromPTXFile(OptiX_Context, hit_ptx_path.c_str(), "miss_camera", &miss_camera));
101 // Translucent cover (glass/plastic) any-hit programs: let direct/diffuse rays pass through covers.
102 RT_CHECK_ERROR(rtProgramCreateFromPTXFile(OptiX_Context, hit_ptx_path.c_str(), "any_hit_direct", &any_hit_direct));
103 RT_CHECK_ERROR(rtProgramCreateFromPTXFile(OptiX_Context, hit_ptx_path.c_str(), "any_hit_diffuse", &any_hit_diffuse));
104
105 /* Set miss programs */
106 RT_CHECK_ERROR(rtContextSetMissProgram(OptiX_Context, RAYTYPE_DIRECT, miss_direct));
107 RT_CHECK_ERROR(rtContextSetMissProgram(OptiX_Context, RAYTYPE_DIFFUSE, miss_diffuse));
108 RT_CHECK_ERROR(rtContextSetMissProgram(OptiX_Context, RAYTYPE_CAMERA, miss_camera));
109
110 /* Load intersection programs from PTX */
111 std::string intersect_ptx_path = helios::resolvePluginAsset("radiation", "cuda_compile_ptx_generated_primitiveIntersection.cu.ptx").string();
112
113 // Patch (rectangle) programs
114 RT_CHECK_ERROR(rtProgramCreateFromPTXFile(OptiX_Context, intersect_ptx_path.c_str(), "rectangle_intersect", &rectangle_intersect));
115 RT_CHECK_ERROR(rtProgramCreateFromPTXFile(OptiX_Context, intersect_ptx_path.c_str(), "rectangle_bounds", &rectangle_bounds));
116
117 // Triangle programs
118 RT_CHECK_ERROR(rtProgramCreateFromPTXFile(OptiX_Context, intersect_ptx_path.c_str(), "triangle_intersect", &triangle_intersect));
119 RT_CHECK_ERROR(rtProgramCreateFromPTXFile(OptiX_Context, intersect_ptx_path.c_str(), "triangle_bounds", &triangle_bounds));
120
121 // Disk programs
122 RT_CHECK_ERROR(rtProgramCreateFromPTXFile(OptiX_Context, intersect_ptx_path.c_str(), "disk_intersect", &disk_intersect));
123 RT_CHECK_ERROR(rtProgramCreateFromPTXFile(OptiX_Context, intersect_ptx_path.c_str(), "disk_bounds", &disk_bounds));
124
125 // Tile programs
126 RT_CHECK_ERROR(rtProgramCreateFromPTXFile(OptiX_Context, intersect_ptx_path.c_str(), "tile_intersect", &tile_intersect));
127 RT_CHECK_ERROR(rtProgramCreateFromPTXFile(OptiX_Context, intersect_ptx_path.c_str(), "tile_bounds", &tile_bounds));
128
129 // Voxel programs
130 RT_CHECK_ERROR(rtProgramCreateFromPTXFile(OptiX_Context, intersect_ptx_path.c_str(), "voxel_intersect", &voxel_intersect));
131 RT_CHECK_ERROR(rtProgramCreateFromPTXFile(OptiX_Context, intersect_ptx_path.c_str(), "voxel_bounds", &voxel_bounds));
132
133 RT_CHECK_ERROR(rtProgramCreateFromPTXFile(OptiX_Context, intersect_ptx_path.c_str(), "bbox_intersect", &bbox_intersect));
134 RT_CHECK_ERROR(rtProgramCreateFromPTXFile(OptiX_Context, intersect_ptx_path.c_str(), "bbox_bounds", &bbox_bounds));
135
136 /* Create geometry objects for each primitive type */
137
138 // Patch geometry
139 RT_CHECK_ERROR(rtGeometryCreate(OptiX_Context, &patch_geometry));
140 RT_CHECK_ERROR(rtGeometrySetBoundingBoxProgram(patch_geometry, rectangle_bounds));
141 RT_CHECK_ERROR(rtGeometrySetIntersectionProgram(patch_geometry, rectangle_intersect));
142
143 // Triangle geometry
144 RT_CHECK_ERROR(rtGeometryCreate(OptiX_Context, &triangle_geometry));
145 RT_CHECK_ERROR(rtGeometrySetBoundingBoxProgram(triangle_geometry, triangle_bounds));
146 RT_CHECK_ERROR(rtGeometrySetIntersectionProgram(triangle_geometry, triangle_intersect));
147
148 // Disk geometry
149 RT_CHECK_ERROR(rtGeometryCreate(OptiX_Context, &disk_geometry));
150 RT_CHECK_ERROR(rtGeometrySetBoundingBoxProgram(disk_geometry, disk_bounds));
151 RT_CHECK_ERROR(rtGeometrySetIntersectionProgram(disk_geometry, disk_intersect));
152
153 // Tile geometry
154 RT_CHECK_ERROR(rtGeometryCreate(OptiX_Context, &tile_geometry));
155 RT_CHECK_ERROR(rtGeometrySetBoundingBoxProgram(tile_geometry, tile_bounds));
156 RT_CHECK_ERROR(rtGeometrySetIntersectionProgram(tile_geometry, tile_intersect));
157
158 // Voxel geometry
159 RT_CHECK_ERROR(rtGeometryCreate(OptiX_Context, &voxel_geometry));
160 RT_CHECK_ERROR(rtGeometrySetBoundingBoxProgram(voxel_geometry, voxel_bounds));
161 RT_CHECK_ERROR(rtGeometrySetIntersectionProgram(voxel_geometry, voxel_intersect));
162
163 // Bbox geometry
164 RT_CHECK_ERROR(rtGeometryCreate(OptiX_Context, &bbox_geometry));
165 RT_CHECK_ERROR(rtGeometrySetBoundingBoxProgram(bbox_geometry, bbox_bounds));
166 RT_CHECK_ERROR(rtGeometrySetIntersectionProgram(bbox_geometry, bbox_intersect));
167
168 /* Create materials for each primitive type */
169
170 // Patch material
171 RT_CHECK_ERROR(rtMaterialCreate(OptiX_Context, &patch_material));
172 RT_CHECK_ERROR(rtMaterialSetClosestHitProgram(patch_material, RAYTYPE_DIRECT, closest_hit_direct));
173 RT_CHECK_ERROR(rtMaterialSetClosestHitProgram(patch_material, RAYTYPE_DIFFUSE, closest_hit_diffuse));
174 RT_CHECK_ERROR(rtMaterialSetClosestHitProgram(patch_material, RAYTYPE_CAMERA, closest_hit_camera));
175 RT_CHECK_ERROR(rtMaterialSetClosestHitProgram(patch_material, RAYTYPE_PIXEL_LABEL, closest_hit_pixel_label));
176 // Translucent-cover pass-through (glass/plastic) for direct/diffuse rays only.
177 RT_CHECK_ERROR(rtMaterialSetAnyHitProgram(patch_material, RAYTYPE_DIRECT, any_hit_direct));
178 RT_CHECK_ERROR(rtMaterialSetAnyHitProgram(patch_material, RAYTYPE_DIFFUSE, any_hit_diffuse));
179
180 // Triangle material
181 RT_CHECK_ERROR(rtMaterialCreate(OptiX_Context, &triangle_material));
182 RT_CHECK_ERROR(rtMaterialSetClosestHitProgram(triangle_material, RAYTYPE_DIRECT, closest_hit_direct));
183 RT_CHECK_ERROR(rtMaterialSetClosestHitProgram(triangle_material, RAYTYPE_DIFFUSE, closest_hit_diffuse));
184 RT_CHECK_ERROR(rtMaterialSetClosestHitProgram(triangle_material, RAYTYPE_CAMERA, closest_hit_camera));
185 RT_CHECK_ERROR(rtMaterialSetClosestHitProgram(triangle_material, RAYTYPE_PIXEL_LABEL, closest_hit_pixel_label));
186 RT_CHECK_ERROR(rtMaterialSetAnyHitProgram(triangle_material, RAYTYPE_DIRECT, any_hit_direct));
187 RT_CHECK_ERROR(rtMaterialSetAnyHitProgram(triangle_material, RAYTYPE_DIFFUSE, any_hit_diffuse));
188
189 // Disk material
190 RT_CHECK_ERROR(rtMaterialCreate(OptiX_Context, &disk_material));
191 RT_CHECK_ERROR(rtMaterialSetClosestHitProgram(disk_material, RAYTYPE_DIRECT, closest_hit_direct));
192 RT_CHECK_ERROR(rtMaterialSetClosestHitProgram(disk_material, RAYTYPE_DIFFUSE, closest_hit_diffuse));
193 RT_CHECK_ERROR(rtMaterialSetClosestHitProgram(disk_material, RAYTYPE_CAMERA, closest_hit_camera));
194 RT_CHECK_ERROR(rtMaterialSetClosestHitProgram(disk_material, RAYTYPE_PIXEL_LABEL, closest_hit_pixel_label));
195 RT_CHECK_ERROR(rtMaterialSetAnyHitProgram(disk_material, RAYTYPE_DIRECT, any_hit_direct));
196 RT_CHECK_ERROR(rtMaterialSetAnyHitProgram(disk_material, RAYTYPE_DIFFUSE, any_hit_diffuse));
197
198 // Tile material
199 RT_CHECK_ERROR(rtMaterialCreate(OptiX_Context, &tile_material));
200 RT_CHECK_ERROR(rtMaterialSetClosestHitProgram(tile_material, RAYTYPE_DIRECT, closest_hit_direct));
201 RT_CHECK_ERROR(rtMaterialSetClosestHitProgram(tile_material, RAYTYPE_DIFFUSE, closest_hit_diffuse));
202 RT_CHECK_ERROR(rtMaterialSetClosestHitProgram(tile_material, RAYTYPE_CAMERA, closest_hit_camera));
203 RT_CHECK_ERROR(rtMaterialSetClosestHitProgram(tile_material, RAYTYPE_PIXEL_LABEL, closest_hit_pixel_label));
204 RT_CHECK_ERROR(rtMaterialSetAnyHitProgram(tile_material, RAYTYPE_DIRECT, any_hit_direct));
205 RT_CHECK_ERROR(rtMaterialSetAnyHitProgram(tile_material, RAYTYPE_DIFFUSE, any_hit_diffuse));
206
207 // Voxel material
208 RT_CHECK_ERROR(rtMaterialCreate(OptiX_Context, &voxel_material));
209 RT_CHECK_ERROR(rtMaterialSetClosestHitProgram(voxel_material, RAYTYPE_DIRECT, closest_hit_direct));
210 RT_CHECK_ERROR(rtMaterialSetClosestHitProgram(voxel_material, RAYTYPE_DIFFUSE, closest_hit_diffuse));
211 RT_CHECK_ERROR(rtMaterialSetClosestHitProgram(voxel_material, RAYTYPE_CAMERA, closest_hit_camera));
212 RT_CHECK_ERROR(rtMaterialSetClosestHitProgram(voxel_material, RAYTYPE_PIXEL_LABEL, closest_hit_pixel_label));
213
214 // Bbox material
215 RT_CHECK_ERROR(rtMaterialCreate(OptiX_Context, &bbox_material));
216 RT_CHECK_ERROR(rtMaterialSetClosestHitProgram(bbox_material, RAYTYPE_DIRECT, closest_hit_direct));
217 RT_CHECK_ERROR(rtMaterialSetClosestHitProgram(bbox_material, RAYTYPE_DIFFUSE, closest_hit_diffuse));
218 RT_CHECK_ERROR(rtMaterialSetClosestHitProgram(bbox_material, RAYTYPE_CAMERA, closest_hit_camera));
219 RT_CHECK_ERROR(rtMaterialSetClosestHitProgram(bbox_material, RAYTYPE_PIXEL_LABEL, closest_hit_pixel_label));
220
221 /* Create OptiX scene graph structure */
222
223 // Create top level group
224 RT_CHECK_ERROR(rtGroupCreate(OptiX_Context, &top_level_group));
225 RT_CHECK_ERROR(rtGroupSetChildCount(top_level_group, 1));
226
227 // Create top level acceleration (NoAccel for minimal overhead)
228 RT_CHECK_ERROR(rtAccelerationCreate(OptiX_Context, &top_level_acceleration));
229 RT_CHECK_ERROR(rtAccelerationSetBuilder(top_level_acceleration, "NoAccel"));
230 RT_CHECK_ERROR(rtAccelerationSetTraverser(top_level_acceleration, "NoAccel"));
231 RT_CHECK_ERROR(rtGroupSetAcceleration(top_level_group, top_level_acceleration));
232 RT_CHECK_ERROR(rtAccelerationMarkDirty(top_level_acceleration));
233
234 // Create transform node (identity matrix)
235 RT_CHECK_ERROR(rtTransformCreate(OptiX_Context, &transform));
236 float identity[16] = {1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1};
237 RT_CHECK_ERROR(rtTransformSetMatrix(transform, 0, identity, nullptr));
238 RT_CHECK_ERROR(rtGroupSetChild(top_level_group, 0, transform));
239
240 // Create geometry group
241 RT_CHECK_ERROR(rtGeometryGroupCreate(OptiX_Context, &base_geometry_group));
242 RT_CHECK_ERROR(rtGeometryGroupSetChildCount(base_geometry_group, 6)); // 6 primitive types
243 RT_CHECK_ERROR(rtTransformSetChild(transform, base_geometry_group));
244
245 // Create geometry acceleration (Trbvh for fast BVH building)
246 RT_CHECK_ERROR(rtAccelerationCreate(OptiX_Context, &base_acceleration));
247 RT_CHECK_ERROR(rtAccelerationSetBuilder(base_acceleration, "Trbvh"));
248 RT_CHECK_ERROR(rtAccelerationSetTraverser(base_acceleration, "Bvh"));
249 RT_CHECK_ERROR(rtGeometryGroupSetAcceleration(base_geometry_group, base_acceleration));
250 RT_CHECK_ERROR(rtAccelerationMarkDirty(base_acceleration));
251
252 // Create geometry instances
253 RT_CHECK_ERROR(rtGeometryInstanceCreate(OptiX_Context, &patch_geometryinstance));
254 RT_CHECK_ERROR(rtGeometryInstanceSetGeometry(patch_geometryinstance, patch_geometry));
255 RT_CHECK_ERROR(rtGeometryInstanceSetMaterialCount(patch_geometryinstance, 1));
256 RT_CHECK_ERROR(rtGeometryInstanceSetMaterial(patch_geometryinstance, 0, patch_material));
257 RT_CHECK_ERROR(rtGeometryGroupSetChild(base_geometry_group, 0, patch_geometryinstance));
258
259 RT_CHECK_ERROR(rtGeometryInstanceCreate(OptiX_Context, &triangle_geometryinstance));
260 RT_CHECK_ERROR(rtGeometryInstanceSetGeometry(triangle_geometryinstance, triangle_geometry));
261 RT_CHECK_ERROR(rtGeometryInstanceSetMaterialCount(triangle_geometryinstance, 1));
262 RT_CHECK_ERROR(rtGeometryInstanceSetMaterial(triangle_geometryinstance, 0, triangle_material));
263 RT_CHECK_ERROR(rtGeometryGroupSetChild(base_geometry_group, 1, triangle_geometryinstance));
264
265 RT_CHECK_ERROR(rtGeometryInstanceCreate(OptiX_Context, &disk_geometryinstance));
266 RT_CHECK_ERROR(rtGeometryInstanceSetGeometry(disk_geometryinstance, disk_geometry));
267 RT_CHECK_ERROR(rtGeometryInstanceSetMaterialCount(disk_geometryinstance, 1));
268 RT_CHECK_ERROR(rtGeometryInstanceSetMaterial(disk_geometryinstance, 0, disk_material));
269 RT_CHECK_ERROR(rtGeometryGroupSetChild(base_geometry_group, 2, disk_geometryinstance));
270
271 RT_CHECK_ERROR(rtGeometryInstanceCreate(OptiX_Context, &tile_geometryinstance));
272 RT_CHECK_ERROR(rtGeometryInstanceSetGeometry(tile_geometryinstance, tile_geometry));
273 RT_CHECK_ERROR(rtGeometryInstanceSetMaterialCount(tile_geometryinstance, 1));
274 RT_CHECK_ERROR(rtGeometryInstanceSetMaterial(tile_geometryinstance, 0, tile_material));
275 RT_CHECK_ERROR(rtGeometryGroupSetChild(base_geometry_group, 3, tile_geometryinstance));
276
277 RT_CHECK_ERROR(rtGeometryInstanceCreate(OptiX_Context, &voxel_geometryinstance));
278 RT_CHECK_ERROR(rtGeometryInstanceSetGeometry(voxel_geometryinstance, voxel_geometry));
279 RT_CHECK_ERROR(rtGeometryInstanceSetMaterialCount(voxel_geometryinstance, 1));
280 RT_CHECK_ERROR(rtGeometryInstanceSetMaterial(voxel_geometryinstance, 0, voxel_material));
281 RT_CHECK_ERROR(rtGeometryGroupSetChild(base_geometry_group, 4, voxel_geometryinstance));
282
283 RT_CHECK_ERROR(rtGeometryInstanceCreate(OptiX_Context, &bbox_geometryinstance));
284 RT_CHECK_ERROR(rtGeometryInstanceSetGeometry(bbox_geometryinstance, bbox_geometry));
285 RT_CHECK_ERROR(rtGeometryInstanceSetMaterialCount(bbox_geometryinstance, 1));
286 RT_CHECK_ERROR(rtGeometryInstanceSetMaterial(bbox_geometryinstance, 0, bbox_material));
287 RT_CHECK_ERROR(rtGeometryGroupSetChild(base_geometry_group, 5, bbox_geometryinstance));
288
289 // Set top_object variable
290 RTvariable top_object;
291 RT_CHECK_ERROR(rtContextDeclareVariable(OptiX_Context, "top_object", &top_object));
292 RT_CHECK_ERROR(rtVariableSetObject(top_object, top_level_group));
293
294 /* Create all required buffers and variables */
295
296 // Geometry buffers
297 addBuffer("patch_vertices", patch_vertices_RTbuffer, patch_vertices_RTvariable, RT_BUFFER_INPUT, RT_FORMAT_FLOAT3, 2);
298 addBuffer("triangle_vertices", triangle_vertices_RTbuffer, triangle_vertices_RTvariable, RT_BUFFER_INPUT, RT_FORMAT_FLOAT3, 2);
299 addBuffer("disk_centers", disk_centers_RTbuffer, disk_centers_RTvariable, RT_BUFFER_INPUT, RT_FORMAT_FLOAT3, 1);
300 addBuffer("disk_radii", disk_radii_RTbuffer, disk_radii_RTvariable, RT_BUFFER_INPUT, RT_FORMAT_FLOAT, 1);
301 addBuffer("disk_normals", disk_normals_RTbuffer, disk_normals_RTvariable, RT_BUFFER_INPUT, RT_FORMAT_FLOAT3, 1);
302 addBuffer("tile_vertices", tile_vertices_RTbuffer, tile_vertices_RTvariable, RT_BUFFER_INPUT, RT_FORMAT_FLOAT3, 2);
303 addBuffer("voxel_vertices", voxel_vertices_RTbuffer, voxel_vertices_RTvariable, RT_BUFFER_INPUT, RT_FORMAT_FLOAT3, 2);
304 addBuffer("bbox_vertices", bbox_vertices_RTbuffer, bbox_vertices_RTvariable, RT_BUFFER_INPUT, RT_FORMAT_FLOAT3, 2);
305
306 // Primitive data buffers
307 addBuffer("transform_matrix", transform_matrix_RTbuffer, transform_matrix_RTvariable, RT_BUFFER_INPUT, RT_FORMAT_FLOAT, 2);
308 addBuffer("primitive_type", primitive_type_RTbuffer, primitive_type_RTvariable, RT_BUFFER_INPUT, RT_FORMAT_UNSIGNED_INT, 1);
309 addBuffer("primitive_solid_fraction", primitive_solid_fraction_RTbuffer, primitive_solid_fraction_RTvariable, RT_BUFFER_INPUT, RT_FORMAT_FLOAT, 1);
310 addBuffer("twosided_flag", twosided_flag_RTbuffer, twosided_flag_RTvariable, RT_BUFFER_INPUT, RT_FORMAT_BYTE, 1);
311
312 // UUID buffers
313 addBuffer("patch_UUID", patch_UUID_RTbuffer, patch_UUID_RTvariable, RT_BUFFER_INPUT, RT_FORMAT_UNSIGNED_INT, 1);
314 addBuffer("triangle_UUID", triangle_UUID_RTbuffer, triangle_UUID_RTvariable, RT_BUFFER_INPUT, RT_FORMAT_UNSIGNED_INT, 1);
315 addBuffer("disk_UUID", disk_UUID_RTbuffer, disk_UUID_RTvariable, RT_BUFFER_INPUT, RT_FORMAT_UNSIGNED_INT, 1);
316 addBuffer("tile_UUID", tile_UUID_RTbuffer, tile_UUID_RTvariable, RT_BUFFER_INPUT, RT_FORMAT_UNSIGNED_INT, 1);
317 addBuffer("voxel_UUID", voxel_UUID_RTbuffer, voxel_UUID_RTvariable, RT_BUFFER_INPUT, RT_FORMAT_UNSIGNED_INT, 1);
318 addBuffer("bbox_UUID", bbox_UUID_RTbuffer, bbox_UUID_RTvariable, RT_BUFFER_INPUT, RT_FORMAT_UNSIGNED_INT, 1);
319
320 // Mapping buffers
321 addBuffer("objectID", objectID_RTbuffer, objectID_RTvariable, RT_BUFFER_INPUT, RT_FORMAT_UNSIGNED_INT, 1);
322 addBuffer("primitiveID", primitiveID_RTbuffer, primitiveID_RTvariable, RT_BUFFER_INPUT, RT_FORMAT_UNSIGNED_INT, 1);
323 addBuffer("primitive_positions", primitive_positions_RTbuffer, primitive_positions_RTvariable, RT_BUFFER_INPUT, RT_FORMAT_UNSIGNED_INT, 1);
324 addBuffer("object_subdivisions", object_subdivisions_RTbuffer, object_subdivisions_RTvariable, RT_BUFFER_INPUT, RT_FORMAT_INT2, 1);
325
326 // Material property buffers
327 addBuffer("rho", rho_RTbuffer, rho_RTvariable, RT_BUFFER_INPUT, RT_FORMAT_FLOAT, 1);
328 addBuffer("tau", tau_RTbuffer, tau_RTvariable, RT_BUFFER_INPUT, RT_FORMAT_FLOAT, 1);
329 addBuffer("rho_cam", rho_cam_RTbuffer, rho_cam_RTvariable, RT_BUFFER_INPUT, RT_FORMAT_FLOAT, 1);
330 addBuffer("tau_cam", tau_cam_RTbuffer, tau_cam_RTvariable, RT_BUFFER_INPUT, RT_FORMAT_FLOAT, 1);
331 addBuffer("specular_exponent", specular_exponent_RTbuffer, specular_exponent_RTvariable, RT_BUFFER_INPUT, RT_FORMAT_FLOAT, 1);
332 addBuffer("specular_scale", specular_scale_RTbuffer, specular_scale_RTvariable, RT_BUFFER_INPUT, RT_FORMAT_FLOAT, 1);
333
334 // Translucent cover (glass/plastic) material buffers (same [source][primitive][band] layout as rho/tau).
335 // is_glass is uploaded as float (1.0/0.0) widened from the host char vector.
336 addBuffer("glass_n", glass_n_RTbuffer, glass_n_RTvariable, RT_BUFFER_INPUT, RT_FORMAT_FLOAT, 1);
337 addBuffer("glass_KL", glass_KL_RTbuffer, glass_KL_RTvariable, RT_BUFFER_INPUT, RT_FORMAT_FLOAT, 1);
338 addBuffer("is_glass", is_glass_RTbuffer, is_glass_RTvariable, RT_BUFFER_INPUT, RT_FORMAT_FLOAT, 1);
339 // Cheap gate: 0 = no primitive uses the glass model (any-hit programs early-return).
340 RT_CHECK_ERROR(rtContextDeclareVariable(OptiX_Context, "glass_enabled", &glass_enabled_RTvariable));
341 RT_CHECK_ERROR(rtVariableSet1ui(glass_enabled_RTvariable, 0));
342
343 // Radiation energy buffers
344 addBuffer("radiation_in", radiation_in_RTbuffer, radiation_in_RTvariable, RT_BUFFER_INPUT_OUTPUT, RT_FORMAT_FLOAT, 1);
345 addBuffer("radiation_out_top", radiation_out_top_RTbuffer, radiation_out_top_RTvariable, RT_BUFFER_INPUT_OUTPUT, RT_FORMAT_FLOAT, 1);
346 addBuffer("radiation_out_bottom", radiation_out_bottom_RTbuffer, radiation_out_bottom_RTvariable, RT_BUFFER_INPUT_OUTPUT, RT_FORMAT_FLOAT, 1);
347 addBuffer("radiation_in_camera", radiation_in_camera_RTbuffer, radiation_in_camera_RTvariable, RT_BUFFER_INPUT_OUTPUT, RT_FORMAT_FLOAT, 1);
348 addBuffer("camera_pixel_label", camera_pixel_label_RTbuffer, camera_pixel_label_RTvariable, RT_BUFFER_INPUT_OUTPUT, RT_FORMAT_UNSIGNED_INT, 1);
349 addBuffer("camera_pixel_depth", camera_pixel_depth_RTbuffer, camera_pixel_depth_RTvariable, RT_BUFFER_INPUT_OUTPUT, RT_FORMAT_FLOAT, 1);
350 addBuffer("scatter_buff_top", scatter_buff_top_RTbuffer, scatter_buff_top_RTvariable, RT_BUFFER_INPUT_OUTPUT, RT_FORMAT_FLOAT, 1);
351 addBuffer("scatter_buff_bottom", scatter_buff_bottom_RTbuffer, scatter_buff_bottom_RTvariable, RT_BUFFER_INPUT_OUTPUT, RT_FORMAT_FLOAT, 1);
352 addBuffer("radiation_specular", radiation_specular_RTbuffer, radiation_specular_RTvariable, RT_BUFFER_INPUT_OUTPUT, RT_FORMAT_FLOAT, 1);
353 addBuffer("Rsky", Rsky_RTbuffer, Rsky_RTvariable, RT_BUFFER_INPUT_OUTPUT, RT_FORMAT_FLOAT, 1);
354 addBuffer("scatter_buff_top_cam", scatter_buff_top_cam_RTbuffer, scatter_buff_top_cam_RTvariable, RT_BUFFER_INPUT_OUTPUT, RT_FORMAT_FLOAT, 1);
355 addBuffer("scatter_buff_bottom_cam", scatter_buff_bottom_cam_RTbuffer, scatter_buff_bottom_cam_RTvariable, RT_BUFFER_INPUT_OUTPUT, RT_FORMAT_FLOAT, 1);
356
357 // Source buffers
358 addBuffer("source_positions", source_positions_RTbuffer, source_positions_RTvariable, RT_BUFFER_INPUT, RT_FORMAT_FLOAT3, 1);
359 addBuffer("source_widths", source_widths_RTbuffer, source_widths_RTvariable, RT_BUFFER_INPUT, RT_FORMAT_FLOAT2, 1);
360 addBuffer("source_rotations", source_rotations_RTbuffer, source_rotations_RTvariable, RT_BUFFER_INPUT, RT_FORMAT_FLOAT3, 1);
361 addBuffer("source_types", source_types_RTbuffer, source_types_RTvariable, RT_BUFFER_INPUT, RT_FORMAT_UNSIGNED_INT, 1);
362 addBuffer("source_fluxes", source_fluxes_RTbuffer, source_fluxes_RTvariable, RT_BUFFER_INPUT, RT_FORMAT_FLOAT, 1);
363 addBuffer("source_fluxes_cam", source_fluxes_cam_RTbuffer, source_fluxes_cam_RTvariable, RT_BUFFER_INPUT, RT_FORMAT_FLOAT, 1);
364
365 // Diffuse radiation buffers
366 addBuffer("diffuse_flux", diffuse_flux_RTbuffer, diffuse_flux_RTvariable, RT_BUFFER_INPUT, RT_FORMAT_FLOAT, 1);
367 addBuffer("diffuse_extinction", diffuse_extinction_RTbuffer, diffuse_extinction_RTvariable, RT_BUFFER_INPUT, RT_FORMAT_FLOAT, 1);
368 addBuffer("diffuse_peak_dir", diffuse_peak_dir_RTbuffer, diffuse_peak_dir_RTvariable, RT_BUFFER_INPUT, RT_FORMAT_FLOAT3, 1);
369 addBuffer("diffuse_dist_norm", diffuse_dist_norm_RTbuffer, diffuse_dist_norm_RTvariable, RT_BUFFER_INPUT, RT_FORMAT_FLOAT, 1);
370
371 // Sky model buffers
372 addBuffer("sky_radiance_params", sky_radiance_params_RTbuffer, sky_radiance_params_RTvariable, RT_BUFFER_INPUT, RT_FORMAT_FLOAT4, 1);
373 addBuffer("camera_sky_radiance", camera_sky_radiance_RTbuffer, camera_sky_radiance_RTvariable, RT_BUFFER_INPUT, RT_FORMAT_FLOAT, 1);
374 addBuffer("solar_disk_radiance", solar_disk_radiance_RTbuffer, solar_disk_radiance_RTvariable, RT_BUFFER_INPUT, RT_FORMAT_FLOAT, 1);
375 addBuffer("camera_diffuse_flux", camera_diffuse_flux_RTbuffer, camera_diffuse_flux_RTvariable, RT_BUFFER_INPUT, RT_FORMAT_FLOAT, 1);
376 addBuffer("band_emission_flag", band_emission_flag_RTbuffer, band_emission_flag_RTvariable, RT_BUFFER_INPUT, RT_FORMAT_UNSIGNED_INT, 1);
377
378 // Band control buffers
379 addBuffer("band_launch_flag", band_launch_flag_RTbuffer, band_launch_flag_RTvariable, RT_BUFFER_INPUT, RT_FORMAT_BYTE, 1);
380 addBuffer("max_scatters", max_scatters_RTbuffer, max_scatters_RTvariable, RT_BUFFER_INPUT, RT_FORMAT_UNSIGNED_INT, 1);
381
382 // Texture/masking buffers
383 addBuffer("masksize", masksize_RTbuffer, masksize_RTvariable, RT_BUFFER_INPUT, RT_FORMAT_INT2, 1);
384 addBuffer("maskID", maskID_RTbuffer, maskID_RTvariable, RT_BUFFER_INPUT, RT_FORMAT_INT, 1);
385 addBuffer("uvdata", uvdata_RTbuffer, uvdata_RTvariable, RT_BUFFER_INPUT, RT_FORMAT_FLOAT2, 2);
386 addBuffer("uvID", uvID_RTbuffer, uvID_RTvariable, RT_BUFFER_INPUT, RT_FORMAT_INT, 1);
387
388 // Special handling for 3D mask buffer
389 RT_CHECK_ERROR(rtBufferCreate(OptiX_Context, RT_BUFFER_INPUT, &maskdata_RTbuffer));
390 RT_CHECK_ERROR(rtBufferSetFormat(maskdata_RTbuffer, RT_FORMAT_BYTE));
391 RT_CHECK_ERROR(rtContextDeclareVariable(OptiX_Context, "maskdata", &maskdata_RTvariable));
392 RT_CHECK_ERROR(rtVariableSetObject(maskdata_RTvariable, maskdata_RTbuffer));
393 std::vector<std::vector<std::vector<bool>>> dummydata;
394 initializeBuffer3Dbool(maskdata_RTbuffer, dummydata);
395
396 // Context variables
397 RT_CHECK_ERROR(rtContextDeclareVariable(OptiX_Context, "Nprimitives", &Nprimitives_RTvariable));
398 RT_CHECK_ERROR(rtVariableSet1ui(Nprimitives_RTvariable, 0));
399
400 RT_CHECK_ERROR(rtContextDeclareVariable(OptiX_Context, "bbox_UUID_base", &bbox_UUID_base_RTvariable));
401 RT_CHECK_ERROR(rtVariableSet1ui(bbox_UUID_base_RTvariable, UINT_MAX)); // Initialize to sentinel (no bboxes)
402
403 RT_CHECK_ERROR(rtContextDeclareVariable(OptiX_Context, "Nsources", &Nsources_RTvariable));
404 RT_CHECK_ERROR(rtVariableSet1ui(Nsources_RTvariable, 0));
405
406 RT_CHECK_ERROR(rtContextDeclareVariable(OptiX_Context, "Nbands_global", &Nbands_global_RTvariable));
407 RT_CHECK_ERROR(rtVariableSet1ui(Nbands_global_RTvariable, 0));
408
409 RT_CHECK_ERROR(rtContextDeclareVariable(OptiX_Context, "Nbands_launch", &Nbands_launch_RTvariable));
410 RT_CHECK_ERROR(rtVariableSet1ui(Nbands_launch_RTvariable, 0));
411
412 RT_CHECK_ERROR(rtContextDeclareVariable(OptiX_Context, "Ncameras", &Ncameras_RTvariable));
413 RT_CHECK_ERROR(rtVariableSet1ui(Ncameras_RTvariable, 0));
414
415 RT_CHECK_ERROR(rtContextDeclareVariable(OptiX_Context, "periodic_flag", &periodic_flag_RTvariable));
416 RT_CHECK_ERROR(rtVariableSet2f(periodic_flag_RTvariable, 0.f, 0.f));
417
418 RT_CHECK_ERROR(rtContextDeclareVariable(OptiX_Context, "specular_reflection_enabled", &specular_reflection_enabled_RTvariable));
419 RT_CHECK_ERROR(rtVariableSet1ui(specular_reflection_enabled_RTvariable, 0));
420
421 RT_CHECK_ERROR(rtContextDeclareVariable(OptiX_Context, "scattering_iteration", &scattering_iteration_RTvariable));
422 RT_CHECK_ERROR(rtVariableSet1ui(scattering_iteration_RTvariable, 0));
423
424 // Launch control variables
425 RT_CHECK_ERROR(rtContextDeclareVariable(OptiX_Context, "random_seed", &random_seed_RTvariable));
426 RT_CHECK_ERROR(rtVariableSet1ui(random_seed_RTvariable, std::chrono::system_clock::now().time_since_epoch().count()));
427
428 RT_CHECK_ERROR(rtContextDeclareVariable(OptiX_Context, "launch_offset", &launch_offset_RTvariable));
429 RT_CHECK_ERROR(rtVariableSet1ui(launch_offset_RTvariable, 0));
430
431 RT_CHECK_ERROR(rtContextDeclareVariable(OptiX_Context, "launch_face", &launch_face_RTvariable));
432 RT_CHECK_ERROR(rtVariableSet1ui(launch_face_RTvariable, 0));
433
434 // Camera variables
435 RT_CHECK_ERROR(rtContextDeclareVariable(OptiX_Context, "camera_position", &camera_position_RTvariable));
436 RT_CHECK_ERROR(rtVariableSet3f(camera_position_RTvariable, 0.f, 0.f, 0.f));
437
438 RT_CHECK_ERROR(rtContextDeclareVariable(OptiX_Context, "camera_direction", &camera_direction_RTvariable));
439 RT_CHECK_ERROR(rtVariableSet2f(camera_direction_RTvariable, 0.f, 0.f));
440
441 RT_CHECK_ERROR(rtContextDeclareVariable(OptiX_Context, "camera_lens_diameter", &camera_lens_diameter_RTvariable));
442 RT_CHECK_ERROR(rtVariableSet1f(camera_lens_diameter_RTvariable, 0.f));
443
444 RT_CHECK_ERROR(rtContextDeclareVariable(OptiX_Context, "FOV_aspect_ratio", &FOV_aspect_ratio_RTvariable));
445 RT_CHECK_ERROR(rtVariableSet1f(FOV_aspect_ratio_RTvariable, 1.f));
446
447 RT_CHECK_ERROR(rtContextDeclareVariable(OptiX_Context, "camera_HFOV", &camera_HFOV_RTvariable));
448 RT_CHECK_ERROR(rtVariableSet1f(camera_HFOV_RTvariable, 0.f));
449
450 RT_CHECK_ERROR(rtContextDeclareVariable(OptiX_Context, "camera_focal_length", &camera_focal_length_RTvariable));
451 RT_CHECK_ERROR(rtVariableSet1f(camera_focal_length_RTvariable, 0.f));
452
453 RT_CHECK_ERROR(rtContextDeclareVariable(OptiX_Context, "camera_viewplane_length", &camera_viewplane_length_RTvariable));
454 RT_CHECK_ERROR(rtVariableSet1f(camera_viewplane_length_RTvariable, 0.f));
455
456 RT_CHECK_ERROR(rtContextDeclareVariable(OptiX_Context, "camera_pixel_solid_angle", &camera_pixel_solid_angle_RTvariable));
457 RT_CHECK_ERROR(rtVariableSet1f(camera_pixel_solid_angle_RTvariable, 0.f));
458
459 RT_CHECK_ERROR(rtContextDeclareVariable(OptiX_Context, "camera_pixel_offset_x", &camera_pixel_offset_x_RTvariable));
460 RT_CHECK_ERROR(rtVariableSet1ui(camera_pixel_offset_x_RTvariable, 0));
461
462 RT_CHECK_ERROR(rtContextDeclareVariable(OptiX_Context, "camera_pixel_offset_y", &camera_pixel_offset_y_RTvariable));
463 RT_CHECK_ERROR(rtVariableSet1ui(camera_pixel_offset_y_RTvariable, 0));
464
465 RT_CHECK_ERROR(rtContextDeclareVariable(OptiX_Context, "camera_ID", &camera_ID_RTvariable));
466 RT_CHECK_ERROR(rtVariableSet1ui(camera_ID_RTvariable, 0));
467
468 RT_CHECK_ERROR(rtContextDeclareVariable(OptiX_Context, "camera_resolution_full", &camera_resolution_full_RTvariable));
469 RT_CHECK_ERROR(rtVariableSet2i(camera_resolution_full_RTvariable, 0, 0));
470
471 // Sun direction for sky model
472 RT_CHECK_ERROR(rtContextDeclareVariable(OptiX_Context, "sun_direction", &sun_direction_RTvariable));
473 RT_CHECK_ERROR(rtVariableSet3f(sun_direction_RTvariable, 0.f, 0.f, 1.f));
474
475 RT_CHECK_ERROR(rtContextDeclareVariable(OptiX_Context, "solar_disk_cos_angle", &solar_disk_cos_angle_RTvariable));
476 RT_CHECK_ERROR(rtVariableSet1f(solar_disk_cos_angle_RTvariable, 0.f));
477
478 is_initialized = true;
479}
480
482
483 if (!is_initialized) {
484 return; // Already shut down or never initialized
485 }
486
487 // Destroy OptiX context (this destroys all child objects automatically)
488 if (OptiX_Context) {
489 RT_CHECK_ERROR_NOEXIT(rtContextDestroy(OptiX_Context));
490 OptiX_Context = nullptr;
491 }
492
493 is_initialized = false;
494}
495
497 if (!is_initialized) {
498 helios_runtime_error("ERROR (OptiX6Backend::updateGeometry): Backend not initialized.");
499 }
500
501 // Validate geometry before upload (debug builds only)
502 // Catches buffer sizing errors that cause 90% of backend debugging issues
503 validateGeometryBeforeUpload(geometry);
504
505 // Convert geometry data to OptiX buffers
506 geometryToBuffers(geometry);
507
508 // Update primitive counts
509 current_primitive_count = geometry.primitive_count;
510 current_patch_count = geometry.patch_count;
511 current_triangle_count = geometry.triangle_count;
512 current_disk_count = geometry.disk_count;
513 current_tile_count = geometry.tile_count;
514 current_voxel_count = geometry.voxel_count;
515 current_bbox_count = geometry.bbox_count;
516
517 RT_CHECK_ERROR(rtVariableSet1ui(Nprimitives_RTvariable, geometry.primitive_count));
518 RT_CHECK_ERROR(rtVariableSet1ui(bbox_UUID_base_RTvariable, geometry.bbox_UUID_base));
519
520 // Update periodic boundary flags
521 RT_CHECK_ERROR(rtVariableSet2f(periodic_flag_RTvariable, geometry.periodic_flag.x, geometry.periodic_flag.y));
522
523 // Mark acceleration structure as dirty (needs rebuild)
524 RT_CHECK_ERROR(rtAccelerationMarkDirty(base_acceleration));
525}
526
528 if (!is_initialized) {
529 helios_runtime_error("ERROR (OptiX6Backend::buildAccelerationStructure): Backend not initialized.");
530 }
531
532 // Set primitive counts for each geometry type (use type-specific counts, not total)
533 RT_CHECK_ERROR(rtGeometrySetPrimitiveCount(patch_geometry, current_patch_count));
534 RT_CHECK_ERROR(rtGeometrySetPrimitiveCount(triangle_geometry, current_triangle_count));
535 RT_CHECK_ERROR(rtGeometrySetPrimitiveCount(disk_geometry, current_disk_count));
536 RT_CHECK_ERROR(rtGeometrySetPrimitiveCount(tile_geometry, current_tile_count));
537 RT_CHECK_ERROR(rtGeometrySetPrimitiveCount(voxel_geometry, current_voxel_count));
538 RT_CHECK_ERROR(rtGeometrySetPrimitiveCount(bbox_geometry, current_bbox_count));
539
540 // OptiX will automatically rebuild the acceleration structure on next launch
541}
542
544 if (!is_initialized) {
545 helios_runtime_error("ERROR (OptiX6Backend::updateMaterials): Backend not initialized.");
546 }
547
548 materialsToBuffers(materials);
549
550 // Update material counts
551 current_band_count = materials.num_bands;
552 current_source_count = materials.num_sources;
553 current_camera_count = materials.num_cameras;
554
555 // Update Ncameras variable in backend's OptiX context
556 RT_CHECK_ERROR(rtVariableSet1ui(Ncameras_RTvariable, materials.num_cameras));
557}
558
559void OptiX6Backend::updateSources(const std::vector<RayTracingSource> &sources) {
560 if (!is_initialized) {
561 helios_runtime_error("ERROR (OptiX6Backend::updateSources): Backend not initialized.");
562 }
563
564 sourcesToBuffers(sources);
565
566 // Update source count variable
567 current_source_count = sources.size();
568 RT_CHECK_ERROR(rtVariableSet1ui(Nsources_RTvariable, sources.size()));
569}
570
571void OptiX6Backend::updateDiffuseRadiation(const std::vector<float> &flux, const std::vector<float> &extinction, const std::vector<helios::vec3> &peak_dir, const std::vector<float> &dist_norm, const std::vector<float> &sky_energy) {
572
573 if (!is_initialized) {
574 helios_runtime_error("ERROR (OptiX6Backend::updateDiffuseRadiation): Backend not initialized.");
575 }
576
577 diffuseToBuffers(flux, extinction, peak_dir, dist_norm, sky_energy);
578}
579
580void OptiX6Backend::updateSkyModel(const std::vector<helios::vec4> &sky_radiance_params, const std::vector<float> &camera_sky_radiance, const helios::vec3 &sun_direction, const std::vector<float> &solar_disk_radiance, float solar_disk_cos_angle,
581 const std::vector<float> &camera_diffuse_flux, const std::vector<uint32_t> &band_emission_flag) {
582
583 if (!is_initialized) {
584 helios_runtime_error("ERROR (OptiX6Backend::updateSkyModel): Backend not initialized.");
585 }
586
587 skyModelToBuffers(sky_radiance_params, camera_sky_radiance, sun_direction, solar_disk_radiance, solar_disk_cos_angle, camera_diffuse_flux, band_emission_flag);
588}
589
591 if (!is_initialized) {
592 helios_runtime_error("ERROR (OptiX6Backend::launchDirectRays): Backend not initialized.");
593 }
594
595 // Validate context to ensure acceleration structure is built and buffers are synchronized
596 RT_CHECK_ERROR(rtContextValidate(OptiX_Context));
597
598 // OptiX 6.5 batching: limit total rays per launch to avoid GPU timeout/memory issues
599 // Maximum rays per launch (1 billion = OptiX practical limit)
600 size_t maxRays = 1024 * 1024 * 1024;
601 uint n = std::ceil(std::sqrt(static_cast<double>(params.rays_per_primitive)));
602 size_t rays_per_primitive = n * n;
603
604 // Calculate batching parameters
605 size_t maxPrims = std::floor(static_cast<float>(maxRays) / static_cast<float>(rays_per_primitive));
606 size_t Nlaunches = std::ceil(rays_per_primitive * params.launch_count / static_cast<float>(maxRays));
607 size_t prims_per_launch = std::min(static_cast<size_t>(params.launch_count), maxPrims);
608
609 // Batch launches if primitive count exceeds limit
610 for (size_t launch = 0; launch < Nlaunches; launch++) {
611 size_t prims_this_launch;
612 if ((launch + 1) * prims_per_launch > params.launch_count) {
613 prims_this_launch = params.launch_count - launch * prims_per_launch;
614 } else {
615 prims_this_launch = prims_per_launch;
616 }
617
618 // Set launch offset for this batch
619 uint launch_offset = params.launch_offset + launch * prims_per_launch;
620 RT_CHECK_ERROR(rtVariableSet1ui(launch_offset_RTvariable, launch_offset));
621
622 // Set launch parameters for this batch (excluding launch_offset which is set above)
623 RT_CHECK_ERROR(rtVariableSet1ui(random_seed_RTvariable, params.random_seed));
624 RT_CHECK_ERROR(rtVariableSet1ui(Nbands_global_RTvariable, params.num_bands_global));
625 RT_CHECK_ERROR(rtVariableSet1ui(Nbands_launch_RTvariable, params.num_bands_launch));
626 RT_CHECK_ERROR(rtVariableSet1ui(launch_face_RTvariable, params.launch_face));
627 RT_CHECK_ERROR(rtVariableSet1ui(scattering_iteration_RTvariable, params.scattering_iteration));
628
629 // Band launch flags (same for all batches)
630 if (!params.band_launch_flag.empty()) {
631 initializeBuffer1Dbool(band_launch_flag_RTbuffer, params.band_launch_flag);
632 }
633
634 // Specular reflection flag
635 uint specular_enabled = params.specular_reflection_enabled ? 1 : 0;
636 RT_CHECK_ERROR(rtVariableSet1ui(specular_reflection_enabled_RTvariable, specular_enabled));
637
638 // Launch this batch: dimension = (n, n, primitives_this_batch)
639 RT_CHECK_ERROR(rtContextLaunch3D(OptiX_Context, RAYTYPE_DIRECT, n, n, prims_this_launch));
640 }
641}
642
644 if (!is_initialized) {
645 helios_runtime_error("ERROR (OptiX6Backend::launchDiffuseRays): Backend not initialized.");
646 }
647
648 // Upload emission/outgoing radiation if provided (upload once for all batches)
649 if (!params.radiation_out_top.empty()) {
650 initializeBuffer1Df(radiation_out_top_RTbuffer, params.radiation_out_top);
651 }
652 if (!params.radiation_out_bottom.empty()) {
653 initializeBuffer1Df(radiation_out_bottom_RTbuffer, params.radiation_out_bottom);
654 }
655
656 // Upload diffuse parameters if provided (upload once for all batches)
657 if (!params.diffuse_flux.empty()) {
658 initializeBuffer1Df(diffuse_flux_RTbuffer, params.diffuse_flux);
659 }
660 if (!params.diffuse_extinction.empty()) {
661 initializeBuffer1Df(diffuse_extinction_RTbuffer, params.diffuse_extinction);
662 }
663 if (!params.diffuse_peak_dir.empty()) {
664 initializeBuffer1Dfloat3(diffuse_peak_dir_RTbuffer, params.diffuse_peak_dir);
665 }
666 if (!params.diffuse_dist_norm.empty()) {
667 initializeBuffer1Df(diffuse_dist_norm_RTbuffer, params.diffuse_dist_norm);
668 }
669 if (!params.sky_radiance_params.empty()) {
670 initializeBuffer1Dfloat4(sky_radiance_params_RTbuffer, params.sky_radiance_params);
671 }
672
673 // Validate context to ensure acceleration structure is built
674 RT_CHECK_ERROR(rtContextValidate(OptiX_Context));
675
676 // OptiX 6.5 batching: limit total rays per launch to avoid GPU timeout/memory issues
677 // Maximum rays per launch (1 billion = OptiX practical limit)
678 size_t maxRays = 1024 * 1024 * 1024;
679 uint n = std::ceil(std::sqrt(static_cast<double>(params.rays_per_primitive)));
680 size_t rays_per_primitive = n * n;
681
682 // Calculate batching parameters
683 size_t maxPrims = std::floor(static_cast<float>(maxRays) / static_cast<float>(rays_per_primitive));
684 size_t Nlaunches = std::ceil(rays_per_primitive * params.launch_count / static_cast<float>(maxRays));
685 size_t prims_per_launch = std::min(static_cast<size_t>(params.launch_count), maxPrims);
686
687 // Batch launches if primitive count exceeds limit
688 for (size_t launch = 0; launch < Nlaunches; launch++) {
689 size_t prims_this_launch;
690 if ((launch + 1) * prims_per_launch > params.launch_count) {
691 prims_this_launch = params.launch_count - launch * prims_per_launch;
692 } else {
693 prims_this_launch = prims_per_launch;
694 }
695
696 // Set launch offset for this batch
697 uint launch_offset = params.launch_offset + launch * prims_per_launch;
698 RT_CHECK_ERROR(rtVariableSet1ui(launch_offset_RTvariable, launch_offset));
699
700 // Set launch parameters for this batch (excluding launch_offset which is set above)
701 RT_CHECK_ERROR(rtVariableSet1ui(random_seed_RTvariable, params.random_seed));
702 RT_CHECK_ERROR(rtVariableSet1ui(Nbands_global_RTvariable, params.num_bands_global));
703 RT_CHECK_ERROR(rtVariableSet1ui(Nbands_launch_RTvariable, params.num_bands_launch));
704 RT_CHECK_ERROR(rtVariableSet1ui(launch_face_RTvariable, params.launch_face));
705 RT_CHECK_ERROR(rtVariableSet1ui(scattering_iteration_RTvariable, params.scattering_iteration));
706
707 // Band launch flags (same for all batches)
708 if (!params.band_launch_flag.empty()) {
709 initializeBuffer1Dbool(band_launch_flag_RTbuffer, params.band_launch_flag);
710 }
711
712 // Specular reflection flag
713 uint specular_enabled = params.specular_reflection_enabled ? 1 : 0;
714 RT_CHECK_ERROR(rtVariableSet1ui(specular_reflection_enabled_RTvariable, specular_enabled));
715
716 // Launch this batch: dimension = (n, n, primitives_this_batch)
717 RT_CHECK_ERROR(rtContextLaunch3D(OptiX_Context, RAYTYPE_DIFFUSE, n, n, prims_this_launch));
718 }
719}
720
722 if (!is_initialized) {
723 helios_runtime_error("ERROR (OptiX6Backend::launchCameraRays): Backend not initialized.");
724 }
725
726
727 // Set common launch parameters
728 launchParamsToVariables(params);
729
730 // Set camera-specific parameters
731 RT_CHECK_ERROR(rtVariableSet3f(camera_position_RTvariable, params.camera_position.x, params.camera_position.y, params.camera_position.z));
732
733 RT_CHECK_ERROR(rtVariableSet2f(camera_direction_RTvariable, params.camera_direction.x, params.camera_direction.y));
734
735 RT_CHECK_ERROR(rtVariableSet1f(camera_focal_length_RTvariable, params.camera_focal_length));
736 RT_CHECK_ERROR(rtVariableSet1f(camera_lens_diameter_RTvariable, params.camera_lens_diameter));
737 RT_CHECK_ERROR(rtVariableSet1f(FOV_aspect_ratio_RTvariable, params.camera_fov_aspect));
738
739 // Debug: check camera_HFOV value
740 if (std::isnan(params.camera_HFOV) || std::isinf(params.camera_HFOV)) {
741 }
742
743 RT_CHECK_ERROR(rtVariableSet1f(camera_HFOV_RTvariable, params.camera_HFOV));
744
745 RT_CHECK_ERROR(rtVariableSet1ui(camera_pixel_offset_x_RTvariable, params.camera_pixel_offset.x));
746 RT_CHECK_ERROR(rtVariableSet1ui(camera_pixel_offset_y_RTvariable, params.camera_pixel_offset.y));
747 RT_CHECK_ERROR(rtVariableSet1ui(camera_ID_RTvariable, params.camera_id));
748
749 // Set the 3 new camera parameters
750 RT_CHECK_ERROR(rtVariableSet1f(camera_viewplane_length_RTvariable, params.camera_viewplane_length));
751 RT_CHECK_ERROR(rtVariableSet1f(camera_pixel_solid_angle_RTvariable, params.camera_pixel_solid_angle));
752 RT_CHECK_ERROR(rtVariableSet2i(camera_resolution_full_RTvariable, params.camera_resolution_full.x, params.camera_resolution_full.y));
753
754 // Only zero camera buffer when starting a new camera or band count changes.
755 // Multiple tiles for the same camera accumulate into the same buffer without re-zeroing.
756 size_t total_pixels = params.camera_resolution_full.x * params.camera_resolution_full.y;
757 size_t buffer_size = total_pixels * params.num_bands_launch;
758 if (params.camera_id != current_camera_launch_id || params.num_bands_launch != current_launch_band_count) {
759 if (buffer_size > 0) {
760 zeroBuffer1D(radiation_in_camera_RTbuffer, buffer_size);
761 }
762 current_camera_launch_id = params.camera_id;
763 current_launch_band_count = params.num_bands_launch;
764 }
765
766 // Validate context to ensure acceleration structure is built and buffers are synchronized
767 RT_CHECK_ERROR(rtContextValidate(OptiX_Context));
768
769 // Launch camera rays: dimension = (antialiasing_samples, resolution.x, resolution.y) for pixel sampling
770 RT_CHECK_ERROR(rtContextLaunch3D(OptiX_Context, RAYTYPE_CAMERA, params.antialiasing_samples, params.camera_resolution.x, params.camera_resolution.y));
771}
772
774 if (!is_initialized) {
775 helios_runtime_error("ERROR (OptiX6Backend::launchPixelLabelRays): Backend not initialized.");
776 }
777
778 // Set launch parameters
779 launchParamsToVariables(params);
780
781 // Set only essential parameters for pixel coordinate calculations
782 // Camera orientation (position/direction) is inherited from camera rendering
783 // (This matches master's behavior where pixel labeling reuses camera settings)
784 RT_CHECK_ERROR(rtVariableSet1f(camera_viewplane_length_RTvariable, params.camera_viewplane_length));
785 RT_CHECK_ERROR(rtVariableSet1f(camera_pixel_solid_angle_RTvariable, params.camera_pixel_solid_angle));
786 RT_CHECK_ERROR(rtVariableSet2i(camera_resolution_full_RTvariable, params.camera_resolution_full.x, params.camera_resolution_full.y));
787
788 // Set camera pixel offset for tiling
789 RT_CHECK_ERROR(rtVariableSet1ui(camera_pixel_offset_x_RTvariable, params.camera_pixel_offset.x));
790 RT_CHECK_ERROR(rtVariableSet1ui(camera_pixel_offset_y_RTvariable, params.camera_pixel_offset.y));
791
792 // NOTE: Camera pixel buffers must be zeroed BEFORE the tile loop, not here!
793 // Zeroing happens in zeroCameraPixelBuffers() called from RadiationModel.cpp
794
795 // Launch pixel label rays: dimension = (1, resolution.x, resolution.y) - no antialiasing
796 RT_CHECK_ERROR(rtContextLaunch3D(OptiX_Context, RAYTYPE_PIXEL_LABEL, 1, params.camera_resolution.x, params.camera_resolution.y));
797}
798
800 if (!is_initialized) {
801 helios_runtime_error("ERROR (OptiX6Backend::getRadiationResults): Backend not initialized.");
802 }
803
804 // Extract results from OptiX buffers
805 buffersToResults(results);
806
807 // Set dimension information
808 results.num_primitives = current_primitive_count;
809 results.num_bands = current_band_count;
810 results.num_sources = current_source_count;
811 results.num_cameras = current_camera_count;
812}
813
814void OptiX6Backend::getCameraResults(std::vector<float> &pixel_data, std::vector<uint> &pixel_labels, std::vector<float> &pixel_depths, uint camera_id, const helios::int2 &resolution) {
815
816 if (!is_initialized) {
817 helios_runtime_error("ERROR (OptiX6Backend::getCameraResults): Backend not initialized.");
818 }
819
820 // Extract camera pixel data from buffers
821 pixel_data = getOptiXbufferData(radiation_in_camera_RTbuffer);
822 pixel_labels = getOptiXbufferData_ui(camera_pixel_label_RTbuffer);
823 pixel_depths = getOptiXbufferData(camera_pixel_depth_RTbuffer);
824}
825
826void OptiX6Backend::zeroRadiationBuffers(size_t launch_band_count) {
827 if (!is_initialized) {
828 helios_runtime_error("ERROR (OptiX6Backend::zeroRadiationBuffers): Backend not initialized.");
829 }
830
831 // Validation: launch bands cannot exceed global bands
832 if (launch_band_count > current_band_count) {
833 helios_runtime_error("ERROR (OptiX6Backend::zeroRadiationBuffers): launch_band_count (" + std::to_string(launch_band_count) + ") exceeds current_band_count (" + std::to_string(current_band_count) + ").");
834 }
835
836 // Zero all radiation result buffers (use current_band_count for global accumulation)
837 // Note: Bbox primitives don't accumulate radiation (they only wrap rays),
838 // so buffers are sized for real primitives only
839 size_t buffer_size = current_primitive_count * current_band_count;
840 if (buffer_size > 0) {
841 zeroBuffer1D(radiation_in_RTbuffer, buffer_size);
842 zeroBuffer1D(radiation_out_top_RTbuffer, buffer_size);
843 zeroBuffer1D(radiation_out_bottom_RTbuffer, buffer_size);
844 zeroBuffer1D(scatter_buff_top_RTbuffer, buffer_size);
845 zeroBuffer1D(scatter_buff_bottom_RTbuffer, buffer_size);
846 }
847
848 // Zero camera scatter buffers (use launch_band_count for per-launch sizing)
849 // Camera scatter uses same indexing as regular scatter: [primitive][band]
850 if (current_camera_count > 0) {
851 size_t cam_scatter_size = current_primitive_count * launch_band_count;
852 if (cam_scatter_size > 0) {
853 zeroBuffer1D(scatter_buff_top_cam_RTbuffer, cam_scatter_size);
854 zeroBuffer1D(scatter_buff_bottom_cam_RTbuffer, cam_scatter_size);
855 }
856 }
857
858 // Zero specular buffer (use current_band_count for global accumulation)
859 size_t specular_size = current_source_count * current_camera_count * current_primitive_count * current_band_count;
860 if (specular_size > 0) {
861 zeroBuffer1D(radiation_specular_RTbuffer, specular_size);
862 }
863
864 // Zero sky energy buffer
865 if (current_band_count > 0) {
866 zeroBuffer1D(Rsky_RTbuffer, current_band_count);
867 }
868}
869
871 if (!is_initialized) {
872 helios_runtime_error("ERROR (OptiX6Backend::zeroScatterBuffers): Backend not initialized.");
873 }
874
875 // Zero primitive scatter buffers (between iterations)
876 size_t buffer_size = current_primitive_count * current_band_count;
877 if (buffer_size > 0) {
878 zeroBuffer1D(scatter_buff_top_RTbuffer, buffer_size);
879 zeroBuffer1D(scatter_buff_bottom_RTbuffer, buffer_size);
880 }
881
882 // NOTE: Camera scatter buffers are NOT zeroed here
883 // They accumulate across all scatter iterations and are only zeroed once in zeroRadiationBuffers()
884}
885
886void OptiX6Backend::zeroCameraScatterBuffers(size_t launch_band_count) {
887 if (!is_initialized) {
888 helios_runtime_error("ERROR (OptiX6Backend::zeroCameraScatterBuffers): Backend not initialized.");
889 }
890
891 // Validation: launch bands cannot exceed global bands
892 if (launch_band_count > current_band_count) {
893 helios_runtime_error("ERROR (OptiX6Backend::zeroCameraScatterBuffers): launch_band_count (" + std::to_string(launch_band_count) + ") exceeds current_band_count (" + std::to_string(current_band_count) + ").");
894 }
895
896 // Zero camera scatter buffers (use launch_band_count for per-launch sizing)
897 if (current_camera_count > 0) {
898 size_t buffer_size = current_primitive_count * launch_band_count;
899 if (buffer_size > 0) {
900 zeroBuffer1D(scatter_buff_top_cam_RTbuffer, buffer_size);
901 zeroBuffer1D(scatter_buff_bottom_cam_RTbuffer, buffer_size);
902 }
903 }
904}
905
907 if (!is_initialized) {
908 helios_runtime_error("ERROR (OptiX6Backend::zeroCameraPixelBuffers): Backend not initialized.");
909 }
910
911 // Zero pixel label and depth buffers for full resolution
912 size_t total_pixels = resolution.x * resolution.y;
913 if (total_pixels > 0) {
914 zeroBuffer1D(camera_pixel_label_RTbuffer, total_pixels);
915 zeroBuffer1D(camera_pixel_depth_RTbuffer, total_pixels);
916 }
917}
918
920 if (!is_initialized) {
921 helios_runtime_error("ERROR (OptiX6Backend::copyScatterToRadiation): Backend not initialized.");
922 }
923
924 // Copy scatter buffer contents to radiation_out buffers
925 copyBuffer1D(scatter_buff_top_RTbuffer, radiation_out_top_RTbuffer);
926 copyBuffer1D(scatter_buff_bottom_RTbuffer, radiation_out_bottom_RTbuffer);
927}
928
929void OptiX6Backend::uploadRadiationOut(const std::vector<float> &radiation_out_top, const std::vector<float> &radiation_out_bottom) {
930 if (!is_initialized) {
931 helios_runtime_error("ERROR (OptiX6Backend::uploadRadiationOut): Backend not initialized.");
932 }
933
934 if (!radiation_out_top.empty()) {
935 initializeBuffer1Df(radiation_out_top_RTbuffer, radiation_out_top);
936 }
937 if (!radiation_out_bottom.empty()) {
938 initializeBuffer1Df(radiation_out_bottom_RTbuffer, radiation_out_bottom);
939 }
940}
941
942void OptiX6Backend::uploadCameraScatterBuffers(const std::vector<float> &scatter_top_cam, const std::vector<float> &scatter_bottom_cam) {
943 if (!is_initialized) {
944 helios_runtime_error("ERROR (OptiX6Backend::uploadCameraScatterBuffers): Backend not initialized.");
945 }
946
947 if (!scatter_top_cam.empty()) {
948 initializeBuffer1Df(scatter_buff_top_cam_RTbuffer, scatter_top_cam);
949 }
950 if (!scatter_bottom_cam.empty()) {
951 initializeBuffer1Df(scatter_buff_bottom_cam_RTbuffer, scatter_bottom_cam);
952 }
953}
954
955void OptiX6Backend::uploadSourceFluxes(const std::vector<float> &fluxes) {
956 if (!is_initialized) {
957 helios_runtime_error("ERROR (OptiX6Backend::uploadSourceFluxes): Backend not initialized.");
958 }
959
960 if (!fluxes.empty()) {
961 initializeBuffer1Df(source_fluxes_RTbuffer, fluxes);
962 }
963}
964
965void OptiX6Backend::uploadSourceFluxesCam(const std::vector<float> &fluxes_cam) {
966 // No-op for OptiX backend - source_fluxes_cam is uploaded as part of updateSources()
967 // via sourcesToBuffers() which calls initializeBuffer1Df(source_fluxes_cam_RTbuffer, fluxes_cam)
968 // OptiX handles camera spectral weights through its own mechanism in the source data structure
969}
970
972 if (!is_initialized) {
973 std::cout << "Backend not initialized - cannot query GPU memory." << std::endl;
974 return;
975 }
976
977 // Query OptiX memory usage
978 RTsize memory_used;
979 RT_CHECK_ERROR(rtContextGetAttribute(OptiX_Context, RT_CONTEXT_ATTRIBUTE_AVAILABLE_DEVICE_MEMORY, sizeof(RTsize), &memory_used));
980
981 // Memory info available via backend->queryGPUMemory() - removed automatic output for cleaner tests
982}
983
984std::string OptiX6Backend::getBackendName() const {
985 return "OptiX 6.5";
986}
987
988// ========== Error Handling Helpers ==========
989
990static void sutilReportError(const char *message) {
991 fprintf(stderr, "OptiX Error: %s\n", message);
992#if defined(_WIN32) && defined(RELEASE_PUBLIC)
993 {
994 char s[2048];
995 sprintf(s, "OptiX Error: %s", message);
996 MessageBox(0, s, "OptiX Error", MB_OK | MB_ICONWARNING | MB_SYSTEMMODAL);
997 }
998#endif
999}
1000
1001static void sutilHandleError(RTcontext context, RTresult code, const char *file, int line) {
1002 const char *message;
1003 char s[2048];
1004 rtContextGetErrorString(context, code, &message);
1005 sprintf(s, "%s\n(%s:%d)", message, file, line);
1006 sutilReportError(s);
1007 exit(1);
1008}
1009
1010// ========== Private Helper Methods: Buffer Management ==========
1011
1012void OptiX6Backend::addBuffer(const char *name, RTbuffer &buffer, RTvariable &variable, RTbuffertype type, RTformat format, size_t dimension) {
1013 RT_CHECK_ERROR(rtBufferCreate(OptiX_Context, type, &buffer));
1014 RT_CHECK_ERROR(rtBufferSetFormat(buffer, format));
1015 RT_CHECK_ERROR(rtContextDeclareVariable(OptiX_Context, name, &variable));
1016 RT_CHECK_ERROR(rtVariableSetObject(variable, buffer));
1017 if (dimension == 1) {
1018 zeroBuffer1D(buffer, 1);
1019 } else if (dimension == 2) {
1020 zeroBuffer2D(buffer, helios::make_int2(1, 1));
1021 } else {
1022 helios_runtime_error("ERROR (OptiX6Backend::addBuffer): invalid buffer dimension of " + std::to_string(dimension) + ", must be 1 or 2.");
1023 }
1024}
1025
1026void OptiX6Backend::zeroBuffer1D(RTbuffer &buffer, size_t bsize) {
1027 RTformat format;
1028 RT_CHECK_ERROR(rtBufferGetFormat(buffer, &format));
1029
1030 RT_CHECK_ERROR(rtBufferSetSize1D(buffer, bsize));
1031
1032 void *ptr;
1033 RT_CHECK_ERROR(rtBufferMap(buffer, &ptr));
1034
1035 if (format == RT_FORMAT_FLOAT) {
1036 float *data = (float *) ptr;
1037 for (size_t i = 0; i < bsize; i++) {
1038 data[i] = 0.0f;
1039 }
1040 } else if (format == RT_FORMAT_FLOAT2) {
1041 optix::float2 *data = (optix::float2 *) ptr;
1042 for (size_t i = 0; i < bsize; i++) {
1043 data[i] = optix::make_float2(0, 0);
1044 }
1045 } else if (format == RT_FORMAT_FLOAT3) {
1046 optix::float3 *data = (optix::float3 *) ptr;
1047 for (size_t i = 0; i < bsize; i++) {
1048 data[i] = optix::make_float3(0, 0, 0);
1049 }
1050 } else if (format == RT_FORMAT_FLOAT4) {
1051 optix::float4 *data = (optix::float4 *) ptr;
1052 for (size_t i = 0; i < bsize; i++) {
1053 data[i] = optix::make_float4(0, 0, 0, 0);
1054 }
1055 } else if (format == RT_FORMAT_UNSIGNED_INT) {
1056 uint *data = (uint *) ptr;
1057 for (size_t i = 0; i < bsize; i++) {
1058 data[i] = 0;
1059 }
1060 } else if (format == RT_FORMAT_INT) {
1061 int *data = (int *) ptr;
1062 for (size_t i = 0; i < bsize; i++) {
1063 data[i] = 0;
1064 }
1065 } else if (format == RT_FORMAT_INT2) {
1066 optix::int2 *data = (optix::int2 *) ptr;
1067 for (size_t i = 0; i < bsize; i++) {
1068 data[i] = optix::make_int2(0, 0);
1069 }
1070 } else if (format == RT_FORMAT_BYTE) {
1071 char *data = (char *) ptr;
1072 for (size_t i = 0; i < bsize; i++) {
1073 data[i] = 0;
1074 }
1075 } else {
1076 RT_CHECK_ERROR(rtBufferUnmap(buffer));
1077 helios_runtime_error("ERROR (OptiX6Backend::zeroBuffer1D): Unsupported buffer format.");
1078 }
1079
1080 RT_CHECK_ERROR(rtBufferUnmap(buffer));
1081}
1082
1083void OptiX6Backend::zeroBuffer2D(RTbuffer &buffer, const helios::int2 &bsize) {
1084 RTformat format;
1085 RT_CHECK_ERROR(rtBufferGetFormat(buffer, &format));
1086
1087 if (format == RT_FORMAT_FLOAT) {
1088 std::vector<std::vector<float>> array(bsize.y, std::vector<float>(bsize.x, 0.0f));
1089 initializeBuffer2Df(buffer, array);
1090 } else if (format == RT_FORMAT_FLOAT2) {
1091 std::vector<std::vector<helios::vec2>> array(bsize.y, std::vector<helios::vec2>(bsize.x, helios::make_vec2(0, 0)));
1092 initializeBuffer2Dfloat2(buffer, array);
1093 } else if (format == RT_FORMAT_FLOAT3) {
1094 std::vector<std::vector<optix::float3>> array(bsize.y, std::vector<optix::float3>(bsize.x, optix::make_float3(0, 0, 0)));
1095 initializeBuffer2Dfloat3(buffer, array);
1096 } else {
1097 helios_runtime_error("ERROR (OptiX6Backend::zeroBuffer2D): Unsupported buffer format.");
1098 }
1099}
1100
1101void OptiX6Backend::initializeBuffer1Df(RTbuffer &buffer, const std::vector<float> &array) {
1102 size_t bsize = array.size();
1103 RT_CHECK_ERROR(rtBufferSetSize1D(buffer, bsize));
1104
1105 RTformat format;
1106 RT_CHECK_ERROR(rtBufferGetFormat(buffer, &format));
1107 if (format != RT_FORMAT_FLOAT) {
1108 helios_runtime_error("ERROR (OptiX6Backend::initializeBuffer1Df): Buffer must have type float.");
1109 }
1110
1111 void *ptr;
1112 RT_CHECK_ERROR(rtBufferMap(buffer, &ptr));
1113 float *data = (float *) ptr;
1114 for (size_t i = 0; i < bsize; i++) {
1115 data[i] = array[i];
1116 }
1117 RT_CHECK_ERROR(rtBufferUnmap(buffer));
1118}
1119
1120void OptiX6Backend::initializeBuffer1Dui(RTbuffer &buffer, const std::vector<uint> &array) {
1121 size_t bsize = array.size();
1122 RT_CHECK_ERROR(rtBufferSetSize1D(buffer, bsize));
1123
1124 RTformat format;
1125 RT_CHECK_ERROR(rtBufferGetFormat(buffer, &format));
1126 if (format != RT_FORMAT_UNSIGNED_INT) {
1127 helios_runtime_error("ERROR (OptiX6Backend::initializeBuffer1Dui): Buffer must have type unsigned int.");
1128 }
1129
1130 void *ptr;
1131 RT_CHECK_ERROR(rtBufferMap(buffer, &ptr));
1132 uint *data = (uint *) ptr;
1133 for (size_t i = 0; i < bsize; i++) {
1134 data[i] = array[i];
1135 }
1136 RT_CHECK_ERROR(rtBufferUnmap(buffer));
1137}
1138
1139void OptiX6Backend::initializeBuffer1Di(RTbuffer &buffer, const std::vector<int> &array) {
1140 size_t bsize = array.size();
1141 RT_CHECK_ERROR(rtBufferSetSize1D(buffer, bsize));
1142
1143 RTformat format;
1144 RT_CHECK_ERROR(rtBufferGetFormat(buffer, &format));
1145 if (format != RT_FORMAT_INT) {
1146 helios_runtime_error("ERROR (OptiX6Backend::initializeBuffer1Di): Buffer must have type int.");
1147 }
1148
1149 void *ptr;
1150 RT_CHECK_ERROR(rtBufferMap(buffer, &ptr));
1151 int *data = (int *) ptr;
1152 for (size_t i = 0; i < bsize; i++) {
1153 data[i] = array[i];
1154 }
1155 RT_CHECK_ERROR(rtBufferUnmap(buffer));
1156}
1157
1158void OptiX6Backend::initializeBuffer1Dchar(RTbuffer &buffer, const std::vector<char> &array) {
1159 size_t bsize = array.size();
1160 RT_CHECK_ERROR(rtBufferSetSize1D(buffer, bsize));
1161
1162 RTformat format;
1163 RT_CHECK_ERROR(rtBufferGetFormat(buffer, &format));
1164 if (format != RT_FORMAT_BYTE) {
1165 helios_runtime_error("ERROR (OptiX6Backend::initializeBuffer1Dchar): Buffer must have type char.");
1166 }
1167
1168 void *ptr;
1169 RT_CHECK_ERROR(rtBufferMap(buffer, &ptr));
1170 char *data = (char *) ptr;
1171 for (size_t i = 0; i < bsize; i++) {
1172 data[i] = array[i];
1173 }
1174 RT_CHECK_ERROR(rtBufferUnmap(buffer));
1175}
1176
1177void OptiX6Backend::initializeBuffer1Dbool(RTbuffer &buffer, const std::vector<bool> &array) {
1178 size_t bsize = array.size();
1179 RT_CHECK_ERROR(rtBufferSetSize1D(buffer, bsize));
1180
1181 void *ptr;
1182 RT_CHECK_ERROR(rtBufferMap(buffer, &ptr));
1183 char *data = (char *) ptr;
1184 for (size_t i = 0; i < bsize; i++) {
1185 data[i] = array[i] ? 1 : 0;
1186 }
1187 RT_CHECK_ERROR(rtBufferUnmap(buffer));
1188}
1189
1190void OptiX6Backend::initializeBuffer1Dfloat2(RTbuffer &buffer, const std::vector<helios::vec2> &array) {
1191 size_t bsize = array.size();
1192 RT_CHECK_ERROR(rtBufferSetSize1D(buffer, bsize));
1193
1194 void *ptr;
1195 RT_CHECK_ERROR(rtBufferMap(buffer, &ptr));
1196 optix::float2 *data = (optix::float2 *) ptr;
1197 for (size_t i = 0; i < bsize; i++) {
1198 data[i] = optix::make_float2(array[i].x, array[i].y);
1199 }
1200 RT_CHECK_ERROR(rtBufferUnmap(buffer));
1201}
1202
1203void OptiX6Backend::initializeBuffer1Dfloat3(RTbuffer &buffer, const std::vector<helios::vec3> &array) {
1204 size_t bsize = array.size();
1205 RT_CHECK_ERROR(rtBufferSetSize1D(buffer, bsize));
1206
1207 void *ptr;
1208 RT_CHECK_ERROR(rtBufferMap(buffer, &ptr));
1209 optix::float3 *data = (optix::float3 *) ptr;
1210 for (size_t i = 0; i < bsize; i++) {
1211 data[i] = optix::make_float3(array[i].x, array[i].y, array[i].z);
1212 }
1213 RT_CHECK_ERROR(rtBufferUnmap(buffer));
1214}
1215
1216void OptiX6Backend::initializeBuffer1Dfloat4(RTbuffer &buffer, const std::vector<helios::vec4> &array) {
1217 size_t bsize = array.size();
1218 RT_CHECK_ERROR(rtBufferSetSize1D(buffer, bsize));
1219
1220 void *ptr;
1221 RT_CHECK_ERROR(rtBufferMap(buffer, &ptr));
1222 optix::float4 *data = (optix::float4 *) ptr;
1223 for (size_t i = 0; i < bsize; i++) {
1224 data[i] = optix::make_float4(array[i].x, array[i].y, array[i].z, array[i].w);
1225 }
1226 RT_CHECK_ERROR(rtBufferUnmap(buffer));
1227}
1228
1229void OptiX6Backend::initializeBuffer1Dint2(RTbuffer &buffer, const std::vector<helios::int2> &array) {
1230 size_t bsize = array.size();
1231 RT_CHECK_ERROR(rtBufferSetSize1D(buffer, bsize));
1232
1233 void *ptr;
1234 RT_CHECK_ERROR(rtBufferMap(buffer, &ptr));
1235 optix::int2 *data = (optix::int2 *) ptr;
1236 for (size_t i = 0; i < bsize; i++) {
1237 data[i] = optix::make_int2(array[i].x, array[i].y);
1238 }
1239 RT_CHECK_ERROR(rtBufferUnmap(buffer));
1240}
1241
1242void OptiX6Backend::initializeBuffer2Dfloat2(RTbuffer &buffer, const std::vector<std::vector<helios::vec2>> &array) {
1243 helios::int2 bsize;
1244 bsize.y = array.size();
1245 bsize.x = (bsize.y == 0) ? 0 : array.front().size();
1246
1247 RT_CHECK_ERROR(rtBufferSetSize2D(buffer, bsize.x, bsize.y));
1248
1249 void *ptr;
1250 RT_CHECK_ERROR(rtBufferMap(buffer, &ptr));
1251 optix::float2 *data = (optix::float2 *) ptr;
1252 for (int j = 0; j < bsize.y; j++) {
1253 for (int i = 0; i < bsize.x; i++) {
1254 data[i + j * bsize.x] = optix::make_float2(array[j][i].x, array[j][i].y);
1255 }
1256 }
1257 RT_CHECK_ERROR(rtBufferUnmap(buffer));
1258}
1259
1260void OptiX6Backend::initializeBuffer2Df(RTbuffer &buffer, const std::vector<std::vector<float>> &array) {
1261 helios::int2 bsize;
1262 bsize.y = array.size();
1263 bsize.x = (bsize.y == 0) ? 0 : array.front().size();
1264
1265 RT_CHECK_ERROR(rtBufferSetSize2D(buffer, bsize.x, bsize.y));
1266
1267 RTformat format;
1268 RT_CHECK_ERROR(rtBufferGetFormat(buffer, &format));
1269 if (format != RT_FORMAT_FLOAT) {
1270 helios_runtime_error("ERROR (OptiX6Backend::initializeBuffer2Df): Buffer must have type float.");
1271 }
1272
1273 void *ptr;
1274 RT_CHECK_ERROR(rtBufferMap(buffer, &ptr));
1275 float *data = (float *) ptr;
1276 for (int j = 0; j < bsize.y; j++) {
1277 for (int i = 0; i < bsize.x; i++) {
1278 data[i + j * bsize.x] = array[j][i];
1279 }
1280 }
1281 RT_CHECK_ERROR(rtBufferUnmap(buffer));
1282}
1283
1284void OptiX6Backend::initializeBuffer2Dui(RTbuffer &buffer, const std::vector<std::vector<uint>> &array) {
1285 helios::int2 bsize;
1286 bsize.y = array.size();
1287 bsize.x = (bsize.y == 0) ? 0 : array.front().size();
1288
1289 RT_CHECK_ERROR(rtBufferSetSize2D(buffer, bsize.x, bsize.y));
1290
1291 RTformat format;
1292 RT_CHECK_ERROR(rtBufferGetFormat(buffer, &format));
1293 if (format != RT_FORMAT_UNSIGNED_INT) {
1294 helios_runtime_error("ERROR (OptiX6Backend::initializeBuffer2Dui): Buffer must have type unsigned int.");
1295 }
1296
1297 void *ptr;
1298 RT_CHECK_ERROR(rtBufferMap(buffer, &ptr));
1299 uint *data = (uint *) ptr;
1300 for (int j = 0; j < bsize.y; j++) {
1301 for (int i = 0; i < bsize.x; i++) {
1302 data[i + j * bsize.x] = array[j][i];
1303 }
1304 }
1305 RT_CHECK_ERROR(rtBufferUnmap(buffer));
1306}
1307
1308void OptiX6Backend::initializeBuffer2Di(RTbuffer &buffer, const std::vector<std::vector<int>> &array) {
1309 helios::int2 bsize;
1310 bsize.y = array.size();
1311 bsize.x = (bsize.y == 0) ? 0 : array.front().size();
1312
1313 RT_CHECK_ERROR(rtBufferSetSize2D(buffer, bsize.x, bsize.y));
1314
1315 RTformat format;
1316 RT_CHECK_ERROR(rtBufferGetFormat(buffer, &format));
1317 if (format != RT_FORMAT_INT) {
1318 helios_runtime_error("ERROR (OptiX6Backend::initializeBuffer2Di): Buffer must have type int.");
1319 }
1320
1321 void *ptr;
1322 RT_CHECK_ERROR(rtBufferMap(buffer, &ptr));
1323 int *data = (int *) ptr;
1324 for (int j = 0; j < bsize.y; j++) {
1325 for (int i = 0; i < bsize.x; i++) {
1326 data[i + j * bsize.x] = array[j][i];
1327 }
1328 }
1329 RT_CHECK_ERROR(rtBufferUnmap(buffer));
1330}
1331
1332void OptiX6Backend::initializeBuffer2Dfloat3(RTbuffer &buffer, const std::vector<std::vector<helios::vec3>> &array) {
1333 // Convert helios::vec3 to optix::float3
1334 std::vector<std::vector<optix::float3>> optix_array;
1335 optix_array.resize(array.size());
1336 for (size_t j = 0; j < array.size(); j++) {
1337 optix_array[j].resize(array[j].size());
1338 for (size_t i = 0; i < array[j].size(); i++) {
1339 optix_array[j][i] = optix::make_float3(array[j][i].x, array[j][i].y, array[j][i].z);
1340 }
1341 }
1342 initializeBuffer2Dfloat3(buffer, optix_array);
1343}
1344
1345void OptiX6Backend::initializeBuffer2Dfloat3(RTbuffer &buffer, const std::vector<std::vector<optix::float3>> &array) {
1346 helios::int2 bsize;
1347 bsize.y = array.size();
1348 bsize.x = (bsize.y == 0) ? 0 : array.front().size();
1349
1350 RT_CHECK_ERROR(rtBufferSetSize2D(buffer, bsize.x, bsize.y));
1351
1352 RTformat format;
1353 RT_CHECK_ERROR(rtBufferGetFormat(buffer, &format));
1354 if (format != RT_FORMAT_FLOAT3) {
1355 helios_runtime_error("ERROR (OptiX6Backend::initializeBuffer2Dfloat3): Buffer must have type float3.");
1356 }
1357
1358 void *ptr;
1359 RT_CHECK_ERROR(rtBufferMap(buffer, &ptr));
1360 optix::float3 *data = (optix::float3 *) ptr;
1361 for (int j = 0; j < bsize.y; j++) {
1362 for (int i = 0; i < bsize.x; i++) {
1363 data[i + j * bsize.x] = array[j][i];
1364 }
1365 }
1366 RT_CHECK_ERROR(rtBufferUnmap(buffer));
1367}
1368
1369void OptiX6Backend::initializeBuffer3Dbool(RTbuffer &buffer, const std::vector<std::vector<std::vector<bool>>> &array) {
1370 // Template implementation for 3D buffers
1371 helios::int3 bsize;
1372 bsize.z = array.size();
1373 bsize.y = (bsize.z == 0) ? 0 : array.front().size();
1374 bsize.x = (bsize.y == 0) ? 0 : array.front().front().size();
1375
1376 RT_CHECK_ERROR(rtBufferSetSize3D(buffer, bsize.x, bsize.y, bsize.z));
1377
1378 void *ptr;
1379 RT_CHECK_ERROR(rtBufferMap(buffer, &ptr));
1380 char *data = (char *) ptr;
1381 for (int k = 0; k < bsize.z; k++) {
1382 for (int j = 0; j < bsize.y; j++) {
1383 for (int i = 0; i < bsize.x; i++) {
1384 data[i + j * bsize.x + k * bsize.x * bsize.y] = array[k][j][i] ? 1 : 0;
1385 }
1386 }
1387 }
1388 RT_CHECK_ERROR(rtBufferUnmap(buffer));
1389}
1390
1391void OptiX6Backend::copyBuffer1D(RTbuffer &source, RTbuffer &dest) {
1392 RTformat format;
1393 RT_CHECK_ERROR(rtBufferGetFormat(source, &format));
1394
1395 RTsize bsize;
1396 rtBufferGetSize1D(source, &bsize);
1397 rtBufferSetSize1D(dest, bsize);
1398
1399 if (format == RT_FORMAT_FLOAT) {
1400 void *ptr_src;
1401 RT_CHECK_ERROR(rtBufferMap(source, &ptr_src));
1402 float *data_src = (float *) ptr_src;
1403
1404 void *ptr_dest;
1405 RT_CHECK_ERROR(rtBufferMap(dest, &ptr_dest));
1406 float *data_dest = (float *) ptr_dest;
1407
1408 for (size_t i = 0; i < bsize; i++) {
1409 data_dest[i] = data_src[i];
1410 }
1411
1412 RT_CHECK_ERROR(rtBufferUnmap(source));
1413 RT_CHECK_ERROR(rtBufferUnmap(dest));
1414 } else {
1415 helios_runtime_error("ERROR (OptiX6Backend::copyBuffer1D): Only float buffers supported currently.");
1416 }
1417}
1418
1419std::vector<float> OptiX6Backend::getOptiXbufferData(RTbuffer buffer) {
1420 RTsize bsize;
1421 RT_CHECK_ERROR(rtBufferGetSize1D(buffer, &bsize));
1422
1423 void *ptr;
1424 RT_CHECK_ERROR(rtBufferMap(buffer, &ptr));
1425 float *data = (float *) ptr;
1426
1427 std::vector<float> result(bsize);
1428 for (size_t i = 0; i < bsize; i++) {
1429 result[i] = data[i];
1430 }
1431
1432 RT_CHECK_ERROR(rtBufferUnmap(buffer));
1433 return result;
1434}
1435
1436std::vector<uint> OptiX6Backend::getOptiXbufferData_ui(RTbuffer buffer) {
1437 RTsize bsize;
1438 RT_CHECK_ERROR(rtBufferGetSize1D(buffer, &bsize));
1439
1440 void *ptr;
1441 RT_CHECK_ERROR(rtBufferMap(buffer, &ptr));
1442 uint *data = (uint *) ptr;
1443
1444 std::vector<uint> result(bsize);
1445 for (size_t i = 0; i < bsize; i++) {
1446 result[i] = data[i];
1447 }
1448
1449 RT_CHECK_ERROR(rtBufferUnmap(buffer));
1450 return result;
1451}
1452
1453void OptiX6Backend::geometryToBuffers(const RayTracingGeometry &geometry) {
1454 // Convert backend-agnostic geometry data to OptiX buffers
1455
1456 // Transform matrices: 1D vector → 2D buffer [primitive+bbox][16]
1457 // Geometry data only contains transforms for real primitives (not bboxes)
1458 // We append identity transforms for bboxes here
1459 if (!geometry.transform_matrices.empty()) {
1460 size_t total_count = geometry.primitive_count + geometry.bbox_count;
1461 std::vector<std::vector<float>> transform_2d(total_count);
1462
1463 // Copy real primitive transforms
1464 for (size_t p = 0; p < geometry.primitive_count; p++) {
1465 transform_2d[p].resize(16);
1466 for (int i = 0; i < 16; i++) {
1467 transform_2d[p][i] = geometry.transform_matrices[p * 16 + i];
1468 }
1469 }
1470
1471 // Append identity transforms for bboxes
1472 for (size_t b = 0; b < geometry.bbox_count; b++) {
1473 size_t bbox_idx = geometry.primitive_count + b;
1474 transform_2d[bbox_idx].resize(16, 0.0f);
1475 transform_2d[bbox_idx][0] = 1.0f; // m00
1476 transform_2d[bbox_idx][5] = 1.0f; // m11
1477 transform_2d[bbox_idx][10] = 1.0f; // m22
1478 transform_2d[bbox_idx][15] = 1.0f; // m33
1479 }
1480
1481 initializeBuffer2Df(transform_matrix_RTbuffer, transform_2d);
1482 }
1483
1484 // Primitive types: append bbox entries for OptiX
1485 if (!geometry.primitive_types.empty()) {
1486 std::vector<uint> types_with_bbox = geometry.primitive_types;
1487 for (size_t i = 0; i < geometry.bbox_count; i++) {
1488 types_with_bbox.push_back(5); // type=5 for bbox
1489 }
1490 initializeBuffer1Dui(primitive_type_RTbuffer, types_with_bbox);
1491 }
1492
1493 // Primitive IDs: append bbox UUIDs for OptiX
1494 if (!geometry.primitive_IDs.empty()) {
1495 std::vector<uint> ids_with_bbox = geometry.primitive_IDs;
1496 for (size_t i = 0; i < geometry.bbox_count; i++) {
1497 ids_with_bbox.push_back(geometry.bbox_UUID_base + i);
1498 }
1499 initializeBuffer1Dui(primitiveID_RTbuffer, ids_with_bbox);
1500 }
1501
1502 // Primitive positions (UUID → array position lookup)
1503 if (!geometry.primitive_positions.empty()) {
1504 initializeBuffer1Dui(primitive_positions_RTbuffer, geometry.primitive_positions);
1505 }
1506
1507 // Object IDs: append bbox object IDs for OptiX
1508 if (!geometry.object_IDs.empty()) {
1509 std::vector<uint> obj_ids_with_bbox = geometry.object_IDs;
1510 for (size_t i = 0; i < geometry.bbox_count; i++) {
1511 obj_ids_with_bbox.push_back(geometry.primitive_count + i);
1512 }
1513 initializeBuffer1Dui(objectID_RTbuffer, obj_ids_with_bbox);
1514 }
1515
1516 // Two-sided flags: append bbox flags for OptiX
1517 if (!geometry.twosided_flags.empty()) {
1518 std::vector<char> flags_with_bbox = geometry.twosided_flags;
1519 for (size_t i = 0; i < geometry.bbox_count; i++) {
1520 flags_with_bbox.push_back(1); // bboxes are two-sided
1521 }
1522 initializeBuffer1Dchar(twosided_flag_RTbuffer, flags_with_bbox);
1523 }
1524
1525 // Solid fractions: append bbox fractions for OptiX
1526 if (!geometry.solid_fractions.empty()) {
1527 std::vector<float> fractions_with_bbox = geometry.solid_fractions;
1528 for (size_t i = 0; i < geometry.bbox_count; i++) {
1529 fractions_with_bbox.push_back(1.0f); // bboxes are fully solid
1530 }
1531 initializeBuffer1Df(primitive_solid_fraction_RTbuffer, fractions_with_bbox);
1532 }
1533
1534 // Object subdivisions: append bbox subdivisions for OptiX
1535 if (!geometry.object_subdivisions.empty()) {
1536 std::vector<helios::int2> subdivs_with_bbox = geometry.object_subdivisions;
1537 for (size_t i = 0; i < geometry.bbox_count; i++) {
1538 subdivs_with_bbox.push_back(helios::make_int2(1, 1)); // no subdivisions
1539 }
1540 initializeBuffer1Dint2(object_subdivisions_RTbuffer, subdivs_with_bbox);
1541 }
1542
1543 // Patch vertices: std::vector<vec3> → 2D buffer [patch][4]
1544 if (geometry.patch_count > 0 && !geometry.patches.vertices.empty()) {
1545 std::vector<std::vector<helios::vec3>> patch_verts_2d(geometry.patch_count);
1546 for (size_t p = 0; p < geometry.patch_count; p++) {
1547 patch_verts_2d[p].resize(4);
1548 for (int v = 0; v < 4; v++) {
1549 patch_verts_2d[p][v] = geometry.patches.vertices[p * 4 + v];
1550 }
1551 }
1552 initializeBuffer2Dfloat3(patch_vertices_RTbuffer, patch_verts_2d);
1553
1554 // Patch UUIDs
1555 initializeBuffer1Dui(patch_UUID_RTbuffer, geometry.patches.UUIDs);
1556 }
1557
1558 // Triangle vertices: std::vector<vec3> → 2D buffer [triangle][3]
1559 if (geometry.triangle_count > 0 && !geometry.triangles.vertices.empty()) {
1560 std::vector<std::vector<helios::vec3>> tri_verts_2d(geometry.triangle_count);
1561 for (size_t t = 0; t < geometry.triangle_count; t++) {
1562 tri_verts_2d[t].resize(3);
1563 for (int v = 0; v < 3; v++) {
1564 tri_verts_2d[t][v] = geometry.triangles.vertices[t * 3 + v];
1565 }
1566 }
1567 initializeBuffer2Dfloat3(triangle_vertices_RTbuffer, tri_verts_2d);
1568
1569 // Triangle UUIDs
1570 initializeBuffer1Dui(triangle_UUID_RTbuffer, geometry.triangles.UUIDs);
1571 }
1572
1573 // Disk geometry
1574 if (geometry.disk_count > 0) {
1575 initializeBuffer1Dfloat3(disk_centers_RTbuffer, geometry.disk_centers);
1576 initializeBuffer1Df(disk_radii_RTbuffer, geometry.disk_radii);
1577 initializeBuffer1Dfloat3(disk_normals_RTbuffer, geometry.disk_normals);
1578 initializeBuffer1Dui(disk_UUID_RTbuffer, geometry.disk_UUIDs);
1579 }
1580
1581 // Tile vertices: std::vector<vec3> → 2D buffer [tile][4]
1582 if (geometry.tile_count > 0 && !geometry.tiles.vertices.empty()) {
1583 std::vector<std::vector<helios::vec3>> tile_verts_2d(geometry.tile_count);
1584 for (size_t t = 0; t < geometry.tile_count; t++) {
1585 tile_verts_2d[t].resize(4);
1586 for (int v = 0; v < 4; v++) {
1587 tile_verts_2d[t][v] = geometry.tiles.vertices[t * 4 + v];
1588 }
1589 }
1590 initializeBuffer2Dfloat3(tile_vertices_RTbuffer, tile_verts_2d);
1591
1592 initializeBuffer1Dui(tile_UUID_RTbuffer, geometry.tiles.UUIDs);
1593 }
1594
1595 // Voxel vertices: std::vector<vec3> → 2D buffer [voxel][8]
1596 if (geometry.voxel_count > 0 && !geometry.voxels.vertices.empty()) {
1597 std::vector<std::vector<helios::vec3>> voxel_verts_2d(geometry.voxel_count);
1598 for (size_t v = 0; v < geometry.voxel_count; v++) {
1599 voxel_verts_2d[v].resize(8);
1600 for (int vtx = 0; vtx < 8; vtx++) {
1601 voxel_verts_2d[v][vtx] = geometry.voxels.vertices[v * 8 + vtx];
1602 }
1603 }
1604 initializeBuffer2Dfloat3(voxel_vertices_RTbuffer, voxel_verts_2d);
1605 initializeBuffer1Dui(voxel_UUID_RTbuffer, geometry.voxels.UUIDs);
1606 }
1607
1608 // Bbox vertices: std::vector<vec3> → 2D buffer [bbox][4]
1609 // Bbox faces are 4-vertex rectangles (verified in primitiveIntersection.cu:390-393)
1610 if (geometry.bbox_count > 0 && !geometry.bboxes.vertices.empty()) {
1611 std::vector<std::vector<helios::vec3>> bbox_verts_2d(geometry.bbox_count);
1612 for (size_t b = 0; b < geometry.bbox_count; b++) {
1613 bbox_verts_2d[b].resize(4);
1614 for (int v = 0; v < 4; v++) {
1615 bbox_verts_2d[b][v] = geometry.bboxes.vertices[b * 4 + v];
1616 }
1617 }
1618 initializeBuffer2Dfloat3(bbox_vertices_RTbuffer, bbox_verts_2d);
1619 initializeBuffer1Dui(bbox_UUID_RTbuffer, geometry.bboxes.UUIDs);
1620 }
1621
1622 // Object subdivisions
1623 if (!geometry.object_subdivisions.empty()) {
1624 initializeBuffer1Dint2(object_subdivisions_RTbuffer, geometry.object_subdivisions);
1625 }
1626
1627 // Texture masks
1628 if (!geometry.mask_data.empty()) {
1629 // Convert 1D bool array to 3D structure
1630 // CRITICAL: All masks must have same dimensions for 3D buffer, so pad to max size
1631 int max_width = 0, max_height = 0;
1632 for (const auto &size: geometry.mask_sizes) {
1633 max_width = std::max(max_width, size.x);
1634 max_height = std::max(max_height, size.y);
1635 }
1636
1637 std::vector<std::vector<std::vector<bool>>> mask_3d;
1638 size_t offset = 0;
1639 for (size_t m = 0; m < geometry.mask_sizes.size(); m++) {
1640 int width = geometry.mask_sizes[m].x;
1641 int height = geometry.mask_sizes[m].y;
1642 // Pad to max dimensions (padded regions will be false)
1643 std::vector<std::vector<bool>> mask_2d(max_height, std::vector<bool>(max_width, false));
1644 for (int y = 0; y < height; y++) {
1645 for (int x = 0; x < width; x++) {
1646 mask_2d[y][x] = geometry.mask_data[offset++];
1647 }
1648 }
1649 mask_3d.push_back(mask_2d);
1650 }
1651 initializeBuffer3Dbool(maskdata_RTbuffer, mask_3d);
1652 initializeBuffer1Dint2(masksize_RTbuffer, geometry.mask_sizes);
1653 }
1654
1655 if (!geometry.mask_IDs.empty()) {
1656 initializeBuffer1Di(maskID_RTbuffer, geometry.mask_IDs);
1657 }
1658
1659 // UV data
1660 // uv_IDs contains position indices (not offsets), used by CUDA to access uvdata[vertex][position]
1661 // uv_data is stored sequentially: 4 UVs per primitive that has UVs
1662 if (!geometry.uv_data.empty()) {
1663 // Convert 1D vec2 array to 2D structure: uv_2d[position][vertex]
1664 std::vector<std::vector<helios::vec2>> uv_2d(geometry.primitive_count);
1665 size_t uv_offset = 0;
1666 for (size_t p = 0; p < geometry.primitive_count; p++) {
1667 int uv_id = geometry.uv_IDs[p];
1668 if (uv_id >= 0) {
1669 // This primitive has UVs - read next 4 from uv_data
1670 uv_2d[p].resize(4);
1671 for (int v = 0; v < 4 && uv_offset < geometry.uv_data.size(); v++) {
1672 uv_2d[p][v] = geometry.uv_data[uv_offset++];
1673 }
1674 } else {
1675 // No UVs - use default
1676 uv_2d[p] = {helios::make_vec2(0, 0), helios::make_vec2(1, 0), helios::make_vec2(1, 1), helios::make_vec2(0, 1)};
1677 }
1678 }
1679 initializeBuffer2Dfloat2(uvdata_RTbuffer, uv_2d);
1680 }
1681
1682 if (!geometry.uv_IDs.empty()) {
1683 initializeBuffer1Di(uvID_RTbuffer, geometry.uv_IDs);
1684 }
1685}
1686
1687void OptiX6Backend::materialsToBuffers(const RayTracingMaterial &materials) {
1688 // Upload material properties to OptiX buffers
1689 // Indexing: [source * Nbands * Nprims + prim * Nbands + band]
1690 // This matches CUDA formula: Nprimitives * Nbands_global * source_ID + Nbands_global * origin_UUID + b_global
1691
1692 if (!materials.reflectivity.empty()) {
1693 initializeBuffer1Df(rho_RTbuffer, materials.reflectivity);
1694 }
1695
1696 if (!materials.transmissivity.empty()) {
1697 initializeBuffer1Df(tau_RTbuffer, materials.transmissivity);
1698 }
1699
1700 if (!materials.reflectivity_cam.empty()) {
1701 initializeBuffer1Df(rho_cam_RTbuffer, materials.reflectivity_cam);
1702 }
1703
1704 if (!materials.transmissivity_cam.empty()) {
1705 initializeBuffer1Df(tau_cam_RTbuffer, materials.transmissivity_cam);
1706 }
1707
1708 if (!materials.specular_exponent.empty()) {
1709 initializeBuffer1Df(specular_exponent_RTbuffer, materials.specular_exponent);
1710 }
1711
1712 if (!materials.specular_scale.empty()) {
1713 initializeBuffer1Df(specular_scale_RTbuffer, materials.specular_scale);
1714 }
1715
1716 // Translucent cover (glass/plastic) material buffers. The host vectors are always full-size
1717 // ([source][primitive][band]); upload them unconditionally so the device buffers are validly
1718 // sized, and gate the any-hit work with glass_enabled.
1719 bool any_glass = false;
1720 for (char g: materials.is_glass) {
1721 if (g != 0) {
1722 any_glass = true;
1723 break;
1724 }
1725 }
1726 if (any_glass) {
1727 // The device per-band cover-transmittance accumulator (PerRayData::cover_transmittance) is a
1728 // fixed-size array of HELIOS_MAX_RADIATION_BANDS (32, defined in RayTracing.cuh). Fail fast if
1729 // the band count exceeds it rather than silently corrupting energy.
1730 constexpr size_t max_radiation_bands = 32; // must match HELIOS_MAX_RADIATION_BANDS / GLASS_MAX_BANDS
1731 if (materials.num_bands > max_radiation_bands) {
1732 helios_runtime_error("ERROR (OptiX6Backend): translucent cover (glass) materials are in use with " + std::to_string(materials.num_bands) + " radiation bands, which exceeds the compile-time maximum of " +
1733 std::to_string(max_radiation_bands) + " (HELIOS_MAX_RADIATION_BANDS). Reduce the number of bands or increase the cap.");
1734 }
1735 }
1736 if (!materials.glass_n.empty()) {
1737 initializeBuffer1Df(glass_n_RTbuffer, materials.glass_n);
1738 }
1739 if (!materials.glass_KL.empty()) {
1740 initializeBuffer1Df(glass_KL_RTbuffer, materials.glass_KL);
1741 }
1742 if (!materials.is_glass.empty()) {
1743 // Widen char -> float (1.0/0.0) for the float-typed device buffer.
1744 std::vector<float> is_glass_f(materials.is_glass.begin(), materials.is_glass.end());
1745 initializeBuffer1Df(is_glass_RTbuffer, is_glass_f);
1746 }
1747 RT_CHECK_ERROR(rtVariableSet1ui(glass_enabled_RTvariable, any_glass ? 1u : 0u));
1748}
1749
1750void OptiX6Backend::sourcesToBuffers(const std::vector<RayTracingSource> &sources) {
1751 // Convert source data to OptiX buffers
1752
1753 if (sources.empty()) {
1754 return;
1755 }
1756
1757 std::vector<helios::vec3> positions;
1758 std::vector<helios::vec2> widths;
1759 std::vector<helios::vec3> rotations;
1760 std::vector<uint> types;
1761 std::vector<float> fluxes;
1762 std::vector<float> fluxes_cam;
1763
1764 for (const auto &source: sources) {
1765 positions.push_back(source.position);
1766 widths.push_back(source.width);
1767 rotations.push_back(source.rotation);
1768 types.push_back(source.type);
1769
1770 // Flatten flux arrays
1771 for (float flux: source.fluxes) {
1772 fluxes.push_back(flux);
1773 }
1774 for (float flux: source.fluxes_cam) {
1775 fluxes_cam.push_back(flux);
1776 }
1777 }
1778
1779 initializeBuffer1Dfloat3(source_positions_RTbuffer, positions);
1780 initializeBuffer1Dfloat2(source_widths_RTbuffer, widths);
1781 initializeBuffer1Dfloat3(source_rotations_RTbuffer, rotations);
1782 initializeBuffer1Dui(source_types_RTbuffer, types);
1783 initializeBuffer1Df(source_fluxes_RTbuffer, fluxes);
1784 initializeBuffer1Df(source_fluxes_cam_RTbuffer, fluxes_cam);
1785}
1786
1787void OptiX6Backend::diffuseToBuffers(const std::vector<float> &flux, const std::vector<float> &extinction, const std::vector<helios::vec3> &peak_dir, const std::vector<float> &dist_norm, const std::vector<float> &sky_energy) {
1788 // Upload diffuse radiation parameters
1789
1790 if (!flux.empty()) {
1791 initializeBuffer1Df(diffuse_flux_RTbuffer, flux);
1792 }
1793
1794 if (!extinction.empty()) {
1795 initializeBuffer1Df(diffuse_extinction_RTbuffer, extinction);
1796 }
1797
1798 if (!peak_dir.empty()) {
1799 initializeBuffer1Dfloat3(diffuse_peak_dir_RTbuffer, peak_dir);
1800 }
1801
1802 if (!dist_norm.empty()) {
1803 initializeBuffer1Df(diffuse_dist_norm_RTbuffer, dist_norm);
1804 }
1805
1806 if (!sky_energy.empty()) {
1807 initializeBuffer1Df(Rsky_RTbuffer, sky_energy);
1808 }
1809}
1810
1811void OptiX6Backend::skyModelToBuffers(const std::vector<helios::vec4> &sky_radiance_params, const std::vector<float> &camera_sky_radiance, const helios::vec3 &sun_direction, const std::vector<float> &solar_disk_radiance, float solar_disk_cos_angle,
1812 const std::vector<float> &camera_diffuse_flux, const std::vector<uint32_t> &band_emission_flag) {
1813 // Upload sky model parameters for camera rendering
1814
1815 if (!sky_radiance_params.empty()) {
1816 initializeBuffer1Dfloat4(sky_radiance_params_RTbuffer, sky_radiance_params);
1817 }
1818
1819 if (!camera_sky_radiance.empty()) {
1820 initializeBuffer1Df(camera_sky_radiance_RTbuffer, camera_sky_radiance);
1821 }
1822
1823 // Set sun direction variable
1824 RT_CHECK_ERROR(rtVariableSet3f(sun_direction_RTvariable, sun_direction.x, sun_direction.y, sun_direction.z));
1825
1826 if (!solar_disk_radiance.empty()) {
1827 initializeBuffer1Df(solar_disk_radiance_RTbuffer, solar_disk_radiance);
1828 }
1829
1830 if (!camera_diffuse_flux.empty()) {
1831 initializeBuffer1Df(camera_diffuse_flux_RTbuffer, camera_diffuse_flux);
1832 }
1833
1834 if (!band_emission_flag.empty()) {
1835 std::vector<uint> flags_u(band_emission_flag.begin(), band_emission_flag.end());
1836 initializeBuffer1Dui(band_emission_flag_RTbuffer, flags_u);
1837 }
1838
1839 // Set solar disk angular size
1840 RT_CHECK_ERROR(rtVariableSet1f(solar_disk_cos_angle_RTvariable, solar_disk_cos_angle));
1841}
1842
1843void OptiX6Backend::launchParamsToVariables(const RayTracingLaunchParams &params) {
1844 // Set common launch parameters as OptiX variables
1845
1846 RT_CHECK_ERROR(rtVariableSet1ui(random_seed_RTvariable, params.random_seed));
1847 RT_CHECK_ERROR(rtVariableSet1ui(launch_offset_RTvariable, params.launch_offset));
1848 RT_CHECK_ERROR(rtVariableSet1ui(Nbands_global_RTvariable, params.num_bands_global));
1849 RT_CHECK_ERROR(rtVariableSet1ui(Nbands_launch_RTvariable, params.num_bands_launch));
1850 RT_CHECK_ERROR(rtVariableSet1ui(launch_face_RTvariable, params.launch_face));
1851 RT_CHECK_ERROR(rtVariableSet1ui(scattering_iteration_RTvariable, params.scattering_iteration));
1852
1853 // Band launch flags
1854 if (!params.band_launch_flag.empty()) {
1855 initializeBuffer1Dbool(band_launch_flag_RTbuffer, params.band_launch_flag);
1856 }
1857
1858 // Specular reflection flag
1859 uint specular_enabled = params.specular_reflection_enabled ? 1 : 0;
1860 RT_CHECK_ERROR(rtVariableSet1ui(specular_reflection_enabled_RTvariable, specular_enabled));
1861}
1862
1863void OptiX6Backend::buffersToResults(RayTracingResults &results) {
1864 // Extract radiation results from OptiX buffers
1865
1866 results.radiation_in = getOptiXbufferData(radiation_in_RTbuffer);
1867 results.radiation_out_top = getOptiXbufferData(radiation_out_top_RTbuffer);
1868 results.radiation_out_bottom = getOptiXbufferData(radiation_out_bottom_RTbuffer);
1869 results.scatter_buff_top = getOptiXbufferData(scatter_buff_top_RTbuffer);
1870 results.scatter_buff_bottom = getOptiXbufferData(scatter_buff_bottom_RTbuffer);
1871
1872 // Extract camera scatter buffers (if cameras present)
1873 // Use current_camera_count since results.num_cameras not set yet
1874 if (current_camera_count > 0) {
1875 results.scatter_buff_top_cam = getOptiXbufferData(scatter_buff_top_cam_RTbuffer);
1876 results.scatter_buff_bottom_cam = getOptiXbufferData(scatter_buff_bottom_cam_RTbuffer);
1877 }
1878
1879 results.radiation_specular = getOptiXbufferData(radiation_specular_RTbuffer);
1880 results.sky_energy = getOptiXbufferData(Rsky_RTbuffer);
1881}