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