Blender V4.3
hiprt/device_impl.cpp
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2011-2023 Blender Foundation
2 *
3 * SPDX-License-Identifier: Apache-2.0 */
4
5#ifdef WITH_HIPRT
6
8
9# include "util/debug.h"
10# include "util/foreach.h"
11# include "util/log.h"
12# include "util/map.h"
13# include "util/md5.h"
14# include "util/path.h"
15# include "util/progress.h"
16# include "util/string.h"
17# include "util/system.h"
18# include "util/time.h"
19# include "util/types.h"
20# include "util/windows.h"
21
22# include "bvh/hiprt.h"
23
24# include "scene/hair.h"
25# include "scene/mesh.h"
26# include "scene/object.h"
27# include "scene/pointcloud.h"
28
30
31static void get_hiprt_transform(float matrix[][4], Transform &tfm)
32{
33 int row = 0;
34 int col = 0;
35 matrix[row][col++] = tfm.x.x;
36 matrix[row][col++] = tfm.x.y;
37 matrix[row][col++] = tfm.x.z;
38 matrix[row][col++] = tfm.x.w;
39 row++;
40 col = 0;
41 matrix[row][col++] = tfm.y.x;
42 matrix[row][col++] = tfm.y.y;
43 matrix[row][col++] = tfm.y.z;
44 matrix[row][col++] = tfm.y.w;
45 row++;
46 col = 0;
47 matrix[row][col++] = tfm.z.x;
48 matrix[row][col++] = tfm.z.y;
49 matrix[row][col++] = tfm.z.z;
50 matrix[row][col++] = tfm.z.w;
51}
52
53class HIPRTDevice;
54
55BVHLayoutMask HIPRTDevice::get_bvh_layout_mask(const uint /* kernel_features */) const
56{
57 return BVH_LAYOUT_HIPRT;
58}
59
60HIPRTDevice::HIPRTDevice(const DeviceInfo &info, Stats &stats, Profiler &profiler, bool headless)
61 : HIPDevice(info, stats, profiler, headless),
62 hiprt_context(NULL),
63 scene(NULL),
64 functions_table(NULL),
65 scratch_buffer_size(0),
66 scratch_buffer(this, "scratch_buffer", MEM_DEVICE_ONLY),
67 prim_visibility(this, "prim_visibility", MEM_GLOBAL),
68 instance_transform_matrix(this, "instance_transform_matrix", MEM_READ_ONLY),
69 transform_headers(this, "transform_headers", MEM_READ_ONLY),
70 user_instance_id(this, "user_instance_id", MEM_GLOBAL),
71 hiprt_blas_ptr(this, "hiprt_blas_ptr", MEM_READ_WRITE),
72 blas_ptr(this, "blas_ptr", MEM_GLOBAL),
73 custom_prim_info(this, "custom_prim_info", MEM_GLOBAL),
74 custom_prim_info_offset(this, "custom_prim_info_offset", MEM_GLOBAL),
75 prims_time(this, "prims_time", MEM_GLOBAL),
76 prim_time_offset(this, "prim_time_offset", MEM_GLOBAL)
77{
78 HIPContextScope scope(this);
79 global_stack_buffer = {0};
80 hiprtContextCreationInput hiprt_context_input = {0};
81 hiprt_context_input.ctxt = hipContext;
82 hiprt_context_input.device = hipDevice;
83 hiprt_context_input.deviceType = hiprtDeviceAMD;
84 hiprtError rt_result = hiprtCreateContext(
85 HIPRT_API_VERSION, hiprt_context_input, &hiprt_context);
86
87 if (rt_result != hiprtSuccess) {
88 set_error(string_printf("Failed to create HIPRT context"));
89 return;
90 }
91
92 rt_result = hiprtCreateFuncTable(
93 hiprt_context, Max_Primitive_Type, Max_Intersect_Filter_Function, functions_table);
94
95 if (rt_result != hiprtSuccess) {
96 set_error(string_printf("Failed to create HIPRT Function Table"));
97 return;
98 }
99
100 hiprtSetLogLevel(hiprtLogLevelNone);
101}
102
103HIPRTDevice::~HIPRTDevice()
104{
105 HIPContextScope scope(this);
106 user_instance_id.free();
107 prim_visibility.free();
108 hiprt_blas_ptr.free();
109 blas_ptr.free();
110 instance_transform_matrix.free();
111 transform_headers.free();
112 custom_prim_info_offset.free();
113 custom_prim_info.free();
114 prim_time_offset.free();
115 prims_time.free();
116
117 hiprtDestroyGlobalStackBuffer(hiprt_context, global_stack_buffer);
118 hiprtDestroyFuncTable(hiprt_context, functions_table);
119 hiprtDestroyScene(hiprt_context, scene);
120 hiprtDestroyContext(hiprt_context);
121}
122
123unique_ptr<DeviceQueue> HIPRTDevice::gpu_queue_create()
124{
125 return make_unique<HIPRTDeviceQueue>(this);
126}
127
128string HIPRTDevice::compile_kernel_get_common_cflags(const uint kernel_features)
129{
130 string cflags = HIPDevice::compile_kernel_get_common_cflags(kernel_features);
131
132 cflags += " -D __HIPRT__ ";
133
134 return cflags;
135}
136
137string HIPRTDevice::compile_kernel(const uint kernel_features, const char *name, const char *base)
138{
139 int major, minor;
140 hipDeviceGetAttribute(&major, hipDeviceAttributeComputeCapabilityMajor, hipDevId);
141 hipDeviceGetAttribute(&minor, hipDeviceAttributeComputeCapabilityMinor, hipDevId);
142 const std::string arch = hipDeviceArch(hipDevId);
143
144 if (!use_adaptive_compilation()) {
145 const string fatbin = path_get(string_printf("lib/%s_rt_gfx.hipfb.zst", name));
146 VLOG(1) << "Testing for pre-compiled kernel " << fatbin << ".";
147 if (path_exists(fatbin)) {
148 VLOG(1) << "Using precompiled kernel.";
149 return fatbin;
150 }
151 }
152
153 string source_path = path_get("source");
154 const string source_md5 = path_files_md5_hash(source_path);
155
156 string common_cflags = compile_kernel_get_common_cflags(kernel_features);
157 const string kernel_md5 = util_md5_string(source_md5 + common_cflags);
158
159 const string include_path = source_path;
160 const string cycles_bc = string_printf(
161 "cycles_%s_%s_%s.bc", name, arch.c_str(), kernel_md5.c_str());
162 const string cycles_bitcode = path_cache_get(path_join("kernels", cycles_bc));
163 const string fatbin_file = string_printf(
164 "cycles_%s_%s_%s.hipfb", name, arch.c_str(), kernel_md5.c_str());
165 const string fatbin = path_cache_get(path_join("kernels", fatbin_file));
166 const string hiprt_bc = string_printf(
167 "hiprt_%s_%s_%s.bc", name, arch.c_str(), kernel_md5.c_str());
168 const string hiprt_bitcode = path_cache_get(path_join("kernels", hiprt_bc));
169
170 const string hiprt_include_path = path_join(source_path, "kernel/device/hiprt");
171
172 VLOG(1) << "Testing for locally compiled kernel " << fatbin << ".";
173 if (path_exists(fatbin)) {
174 VLOG(1) << "Using locally compiled kernel.";
175 return fatbin;
176 }
177
178# ifdef _WIN32
179 if (!use_adaptive_compilation() && have_precompiled_kernels()) {
180 if (!hipSupportsDevice(hipDevId)) {
181 set_error(
182 string_printf("HIP backend requires compute capability 10.1 or up, but found %d.%d. "
183 "Your GPU is not supported.",
184 major,
185 minor));
186 }
187 else {
188 set_error(
189 string_printf("HIP binary kernel for this graphics card compute "
190 "capability (%d.%d) not found.",
191 major,
192 minor));
193 }
194 return string();
195 }
196# endif
197
198 const char *const hipcc = hipewCompilerPath();
199 if (hipcc == NULL) {
200 set_error(
201 "HIP hipcc compiler not found. "
202 "Install HIP toolkit in default location.");
203 return string();
204 }
205
206 const int hipcc_hip_version = hipewCompilerVersion();
207 VLOG_INFO << "Found hipcc " << hipcc << ", HIP version " << hipcc_hip_version << ".";
208 if (hipcc_hip_version < 40) {
209 printf(
210 "Unsupported HIP version %d.%d detected, "
211 "you need HIP 4.0 or newer.\n",
212 hipcc_hip_version / 10,
213 hipcc_hip_version % 10);
214 return string();
215 }
216
218
219 string rtc_options;
220 rtc_options.append(" --offload-arch=").append(arch.c_str());
221 rtc_options.append(" -D __HIPRT__");
222 rtc_options.append(" -ffast-math -O3 -std=c++17");
223 rtc_options.append(" -fgpu-rdc -c --gpu-bundle-output -c -emit-llvm");
224
225 source_path = path_join(path_join(source_path, "kernel"),
226 path_join("device", path_join(base, string_printf("%s.cpp", name))));
227
228 printf("Compiling %s and caching to %s", source_path.c_str(), fatbin.c_str());
229
230 double starttime = time_dt();
231
232 if (!path_exists(cycles_bitcode)) {
233
234 string command = string_printf("%s %s -I %s -I %s %s -o \"%s\"",
235 hipcc,
236 rtc_options.c_str(),
237 include_path.c_str(),
238 hiprt_include_path.c_str(),
239 source_path.c_str(),
240 cycles_bitcode.c_str());
241
242 printf("Compiling %sHIP kernel ...\n%s\n",
243 (use_adaptive_compilation()) ? "adaptive " : "",
244 command.c_str());
245
246# ifdef _WIN32
247 command = "call " + command;
248# endif
249 if (system(command.c_str()) != 0) {
250 set_error(
251 "Failed to execute compilation command, "
252 "see console for details.");
253 return string();
254 }
255 }
256
257 if (!path_exists(hiprt_bitcode)) {
258
259 rtc_options.append(" -x hip");
260 rtc_options.append(" -D HIPRT_BITCODE_LINKING ");
261
262 string source_path = path_join(hiprt_include_path, "/hiprt/impl/hiprt_kernels_bitcode.h");
263
264 string command = string_printf("%s %s -I %s %s -o \"%s\"",
265 hipcc,
266 rtc_options.c_str(),
267 hiprt_include_path.c_str(),
268 source_path.c_str(),
269 hiprt_bitcode.c_str());
270
271 printf("Compiling %sHIP kernel ...\n%s\n",
272 (use_adaptive_compilation()) ? "adaptive " : "",
273 command.c_str());
274
275# ifdef _WIN32
276 command = "call " + command;
277# endif
278 if (system(command.c_str()) != 0) {
279 set_error(
280 "Failed to execute compilation command, "
281 "see console for details.");
282 return string();
283 }
284 }
285
286 // After compilation, the bitcode produced is linked with HIP RT bitcode (containing
287 // implementations of HIP RT functions, e.g. traversal, to produce the final executable code
288 string linker_options;
289 linker_options.append(" --offload-arch=").append(arch.c_str());
290 linker_options.append(" -fgpu-rdc --hip-link --cuda-device-only ");
291
292 string linker_command = string_printf("clang++ %s \"%s\" \"%s\" -o \"%s\"",
293 linker_options.c_str(),
294 cycles_bitcode.c_str(),
295 hiprt_bitcode.c_str(),
296 fatbin.c_str());
297
298# ifdef _WIN32
299 linker_command = "call " + linker_command;
300# endif
301 if (system(linker_command.c_str()) != 0) {
302 set_error(
303 "Failed to execute linking command, "
304 "see console for details.");
305 return string();
306 }
307
308 printf("Kernel compilation finished in %.2lfs.\n", time_dt() - starttime);
309
310 return fatbin;
311}
312
313bool HIPRTDevice::load_kernels(const uint kernel_features)
314{
315 if (hipModule) {
316 if (use_adaptive_compilation()) {
317 VLOG(1) << "Skipping HIP kernel reload for adaptive compilation, not currently supported.";
318 }
319 return true;
320 }
321
322 if (hipContext == 0)
323 return false;
324
325 if (!support_device(kernel_features)) {
326 return false;
327 }
328
329 /* get kernel */
330 const char *kernel_name = "kernel";
331 string fatbin = compile_kernel(kernel_features, kernel_name);
332 if (fatbin.empty())
333 return false;
334
335 /* open module */
336 HIPContextScope scope(this);
337
338 string fatbin_data;
339 hipError_t result;
340
341 if (path_read_compressed_text(fatbin, fatbin_data)) {
342 result = hipModuleLoadData(&hipModule, fatbin_data.c_str());
343 }
344 else
345 result = hipErrorFileNotFound;
346
347 if (result != hipSuccess)
348 set_error(string_printf(
349 "Failed to load HIP kernel from '%s' (%s)", fatbin.c_str(), hipewErrorString(result)));
350
351 if (result == hipSuccess) {
352 kernels.load(this);
353 {
354 const DeviceKernel test_kernel = (kernel_features & KERNEL_FEATURE_NODE_RAYTRACE) ?
356 (kernel_features & KERNEL_FEATURE_MNEE) ?
359
360 HIPRTDeviceQueue queue(this);
361
362 device_ptr d_path_index = 0;
363 device_ptr d_render_buffer = 0;
364 int d_work_size = 0;
365 DeviceKernelArguments args(&d_path_index, &d_render_buffer, &d_work_size);
366
367 queue.init_execution();
368 queue.enqueue(test_kernel, 1, args);
369 queue.synchronize();
370 }
371 }
372
373 return (result == hipSuccess);
374}
375
376void HIPRTDevice::const_copy_to(const char *name, void *host, size_t size)
377{
378 HIPContextScope scope(this);
379 hipDeviceptr_t mem;
380 size_t bytes;
381
382 if (strcmp(name, "data") == 0) {
383 assert(size <= sizeof(KernelData));
384 KernelData *const data = (KernelData *)host;
385 *(hiprtScene *)&data->device_bvh = scene;
386 }
387
388 hip_assert(hipModuleGetGlobal(&mem, &bytes, hipModule, "kernel_params"));
389 assert(bytes == sizeof(KernelParamsHIPRT));
390
391# define KERNEL_DATA_ARRAY(data_type, data_name) \
392 if (strcmp(name, #data_name) == 0) { \
393 hip_assert(hipMemcpyHtoD(mem + offsetof(KernelParamsHIPRT, data_name), host, size)); \
394 return; \
395 }
397 KERNEL_DATA_ARRAY(IntegratorStateGPU, integrator_state)
398 KERNEL_DATA_ARRAY(int, user_instance_id)
399 KERNEL_DATA_ARRAY(uint64_t, blas_ptr)
400 KERNEL_DATA_ARRAY(int2, custom_prim_info_offset)
401 KERNEL_DATA_ARRAY(int2, custom_prim_info)
402 KERNEL_DATA_ARRAY(int, prim_time_offset)
403 KERNEL_DATA_ARRAY(float2, prims_time)
404
405# include "kernel/data_arrays.h"
406# undef KERNEL_DATA_ARRAY
407}
408
409hiprtGeometryBuildInput HIPRTDevice::prepare_triangle_blas(BVHHIPRT *bvh, Mesh *mesh)
410{
411 hiprtGeometryBuildInput geom_input;
412 geom_input.geomType = Triangle;
413
414 if (mesh->has_motion_blur()) {
415
416 const Attribute *attr_mP = mesh->attributes.find(ATTR_STD_MOTION_VERTEX_POSITION);
417 const float3 *vert_steps = attr_mP->data_float3();
418 const size_t num_verts = mesh->get_verts().size();
419 const size_t num_steps = mesh->get_motion_steps();
420 const size_t num_triangles = mesh->num_triangles();
421 const float3 *verts = mesh->get_verts().data();
422 int num_bounds = 0;
423
424 if (bvh->params.num_motion_triangle_steps == 0 || bvh->params.use_spatial_split) {
425 bvh->custom_primitive_bound.alloc(num_triangles);
426 bvh->custom_prim_info.resize(num_triangles);
427 for (uint j = 0; j < num_triangles; j++) {
428 Mesh::Triangle t = mesh->get_triangle(j);
431 for (size_t step = 0; step < num_steps - 1; step++) {
432 t.bounds_grow(vert_steps + step * num_verts, bounds);
433 }
434
435 if (bounds.valid()) {
436 bvh->custom_primitive_bound[num_bounds] = bounds;
437 bvh->custom_prim_info[num_bounds].x = j;
438 bvh->custom_prim_info[num_bounds].y = mesh->primitive_type();
439 num_bounds++;
440 }
441 }
442 }
443 else {
444 const int num_bvh_steps = bvh->params.num_motion_triangle_steps * 2 + 1;
445 const float num_bvh_steps_inv_1 = 1.0f / (num_bvh_steps - 1);
446
447 bvh->custom_primitive_bound.alloc(num_triangles * num_bvh_steps);
448 bvh->custom_prim_info.resize(num_triangles * num_bvh_steps);
449 bvh->prims_time.resize(num_triangles * num_bvh_steps);
450
451 for (uint j = 0; j < num_triangles; j++) {
452 Mesh::Triangle t = mesh->get_triangle(j);
453 float3 prev_verts[3];
454 t.motion_verts(verts, vert_steps, num_verts, num_steps, 0.0f, prev_verts);
455 BoundBox prev_bounds = BoundBox::empty;
456 prev_bounds.grow(prev_verts[0]);
457 prev_bounds.grow(prev_verts[1]);
458 prev_bounds.grow(prev_verts[2]);
459
460 for (int bvh_step = 1; bvh_step < num_bvh_steps; ++bvh_step) {
461 const float curr_time = (float)(bvh_step)*num_bvh_steps_inv_1;
462 float3 curr_verts[3];
463 t.motion_verts(verts, vert_steps, num_verts, num_steps, curr_time, curr_verts);
464 BoundBox curr_bounds = BoundBox::empty;
465 curr_bounds.grow(curr_verts[0]);
466 curr_bounds.grow(curr_verts[1]);
467 curr_bounds.grow(curr_verts[2]);
468 BoundBox bounds = prev_bounds;
469 bounds.grow(curr_bounds);
470 if (bounds.valid()) {
471 const float prev_time = (float)(bvh_step - 1) * num_bvh_steps_inv_1;
472 bvh->custom_primitive_bound[num_bounds] = bounds;
473 bvh->custom_prim_info[num_bounds].x = j;
474 bvh->custom_prim_info[num_bounds].y = mesh->primitive_type();
475 bvh->prims_time[num_bounds].x = curr_time;
476 bvh->prims_time[num_bounds].y = prev_time;
477 num_bounds++;
478 }
479 prev_bounds = curr_bounds;
480 }
481 }
482 }
483
484 bvh->custom_prim_aabb.aabbCount = num_bounds;
485 bvh->custom_prim_aabb.aabbStride = sizeof(BoundBox);
486 bvh->custom_primitive_bound.copy_to_device();
487 bvh->custom_prim_aabb.aabbs = (void *)bvh->custom_primitive_bound.device_pointer;
488
489 geom_input.type = hiprtPrimitiveTypeAABBList;
490 geom_input.primitive.aabbList = bvh->custom_prim_aabb;
491 geom_input.geomType = Motion_Triangle;
492 }
493 else {
494 size_t triangle_size = mesh->get_triangles().size();
495 void *triangle_data = mesh->get_triangles().data();
496
497 size_t vertex_size = mesh->get_verts().size();
498 void *vertex_data = mesh->get_verts().data();
499
500 bvh->triangle_mesh.triangleCount = mesh->num_triangles();
501 bvh->triangle_mesh.triangleStride = 3 * sizeof(int);
502 bvh->triangle_mesh.vertexCount = vertex_size;
503 bvh->triangle_mesh.vertexStride = sizeof(float3);
504
505 bvh->triangle_index.host_pointer = triangle_data;
506 bvh->triangle_index.data_elements = 1;
507 bvh->triangle_index.data_type = TYPE_INT;
508 bvh->triangle_index.data_size = triangle_size;
509 bvh->triangle_index.copy_to_device();
510 bvh->triangle_mesh.triangleIndices = (void *)(bvh->triangle_index.device_pointer);
511 // either has to set the host pointer to zero, or increment the refcount on triangle_data
512 bvh->triangle_index.host_pointer = 0;
513 bvh->vertex_data.host_pointer = vertex_data;
514 bvh->vertex_data.data_elements = 4;
515 bvh->vertex_data.data_type = TYPE_FLOAT;
516 bvh->vertex_data.data_size = vertex_size;
517 bvh->vertex_data.copy_to_device();
518 bvh->triangle_mesh.vertices = (void *)(bvh->vertex_data.device_pointer);
519 bvh->vertex_data.host_pointer = 0;
520
521 geom_input.type = hiprtPrimitiveTypeTriangleMesh;
522 geom_input.primitive.triangleMesh = bvh->triangle_mesh;
523 }
524
525 return geom_input;
526}
527
528hiprtGeometryBuildInput HIPRTDevice::prepare_curve_blas(BVHHIPRT *bvh, Hair *hair)
529{
530 hiprtGeometryBuildInput geom_input;
531
532 const PrimitiveType primitive_type = hair->primitive_type();
533 const size_t num_curves = hair->num_curves();
534 const size_t num_segments = hair->num_segments();
535 const Attribute *curve_attr_mP = NULL;
536
537 if (curve_attr_mP == NULL || bvh->params.num_motion_curve_steps == 0) {
538
539 bvh->custom_prim_info.resize(num_segments);
540 bvh->custom_primitive_bound.alloc(num_segments);
541 }
542 else {
543 size_t num_boxes = bvh->params.num_motion_curve_steps * 2 * num_segments;
544 bvh->custom_prim_info.resize(num_boxes);
545 bvh->prims_time.resize(num_boxes);
546 bvh->custom_primitive_bound.alloc(num_boxes);
547 curve_attr_mP = hair->attributes.find(ATTR_STD_MOTION_VERTEX_POSITION);
548 }
549
550 int num_bounds = 0;
551 float3 *curve_keys = hair->get_curve_keys().data();
552
553 for (uint j = 0; j < num_curves; j++) {
554 const Hair::Curve curve = hair->get_curve(j);
555 const float *curve_radius = &hair->get_curve_radius()[0];
556 int first_key = curve.first_key;
557 for (int k = 0; k < curve.num_keys - 1; k++) {
558 if (curve_attr_mP == NULL) {
559 float3 current_keys[4];
560 current_keys[0] = curve_keys[max(first_key + k - 1, first_key)];
561 current_keys[1] = curve_keys[first_key + k];
562 current_keys[2] = curve_keys[first_key + k + 1];
563 current_keys[3] = curve_keys[min(first_key + k + 2, first_key + curve.num_keys - 1)];
564
565 if (current_keys[0].x == current_keys[1].x && current_keys[1].x == current_keys[2].x &&
566 current_keys[2].x == current_keys[3].x && current_keys[0].y == current_keys[1].y &&
567 current_keys[1].y == current_keys[2].y && current_keys[2].y == current_keys[3].y &&
568 current_keys[0].z == current_keys[1].z && current_keys[1].z == current_keys[2].z &&
569 current_keys[2].z == current_keys[3].z)
570 continue;
571
573 curve.bounds_grow(k, &hair->get_curve_keys()[0], curve_radius, bounds);
574 if (bounds.valid()) {
575 int type = PRIMITIVE_PACK_SEGMENT(primitive_type, k);
576 bvh->custom_prim_info[num_bounds].x = j;
577 bvh->custom_prim_info[num_bounds].y = type;
578 bvh->custom_primitive_bound[num_bounds] = bounds;
579 num_bounds++;
580 }
581 }
582 else {
583 const size_t num_steps = hair->get_motion_steps();
584 const float4 *key_steps = curve_attr_mP->data_float4();
585 const size_t num_keys = hair->get_curve_keys().size();
586
587 if (bvh->params.num_motion_curve_steps == 0 || bvh->params.use_spatial_split) {
589 curve.bounds_grow(k, &hair->get_curve_keys()[0], curve_radius, bounds);
590 for (size_t step = 0; step < num_steps - 1; step++) {
591 curve.bounds_grow(k, key_steps + step * num_keys, bounds);
592 }
593 if (bounds.valid()) {
594 int type = PRIMITIVE_PACK_SEGMENT(primitive_type, k);
595 bvh->custom_prim_info[num_bounds].x = j;
596 bvh->custom_prim_info[num_bounds].y = type;
597 bvh->custom_primitive_bound[num_bounds] = bounds;
598 num_bounds++;
599 }
600 }
601 else {
602 const int num_bvh_steps = bvh->params.num_motion_curve_steps * 2 + 1;
603 const float num_bvh_steps_inv_1 = 1.0f / (num_bvh_steps - 1);
604
605 float4 prev_keys[4];
606 curve.cardinal_motion_keys(curve_keys,
607 curve_radius,
608 key_steps,
609 num_keys,
610 num_steps,
611 0.0f,
612 k - 1,
613 k,
614 k + 1,
615 k + 2,
616 prev_keys);
617 BoundBox prev_bounds = BoundBox::empty;
618 curve.bounds_grow(prev_keys, prev_bounds);
619
620 for (int bvh_step = 1; bvh_step < num_bvh_steps; ++bvh_step) {
621 const float curr_time = (float)(bvh_step)*num_bvh_steps_inv_1;
622 float4 curr_keys[4];
623 curve.cardinal_motion_keys(curve_keys,
624 curve_radius,
625 key_steps,
626 num_keys,
627 num_steps,
628 curr_time,
629 k - 1,
630 k,
631 k + 1,
632 k + 2,
633 curr_keys);
634 BoundBox curr_bounds = BoundBox::empty;
635 curve.bounds_grow(curr_keys, curr_bounds);
636 BoundBox bounds = prev_bounds;
637 bounds.grow(curr_bounds);
638 if (bounds.valid()) {
639 const float prev_time = (float)(bvh_step - 1) * num_bvh_steps_inv_1;
640 int packed_type = PRIMITIVE_PACK_SEGMENT(primitive_type, k);
641 bvh->custom_prim_info[num_bounds].x = j;
642 bvh->custom_prim_info[num_bounds].y = packed_type; // k
643 bvh->custom_primitive_bound[num_bounds] = bounds;
644 bvh->prims_time[num_bounds].x = curr_time;
645 bvh->prims_time[num_bounds].y = prev_time;
646 num_bounds++;
647 }
648 prev_bounds = curr_bounds;
649 }
650 }
651 }
652 }
653 }
654
655 bvh->custom_prim_aabb.aabbCount = num_bounds;
656 bvh->custom_prim_aabb.aabbStride = sizeof(BoundBox);
657 bvh->custom_primitive_bound.copy_to_device();
658 bvh->custom_prim_aabb.aabbs = (void *)bvh->custom_primitive_bound.device_pointer;
659
660 geom_input.type = hiprtPrimitiveTypeAABBList;
661 geom_input.primitive.aabbList = bvh->custom_prim_aabb;
662 geom_input.geomType = Curve;
663
664 return geom_input;
665}
666
667hiprtGeometryBuildInput HIPRTDevice::prepare_point_blas(BVHHIPRT *bvh, PointCloud *pointcloud)
668{
669 hiprtGeometryBuildInput geom_input;
670
671 const Attribute *point_attr_mP = NULL;
672 if (pointcloud->has_motion_blur()) {
673 point_attr_mP = pointcloud->attributes.find(ATTR_STD_MOTION_VERTEX_POSITION);
674 }
675
676 const float3 *points_data = pointcloud->get_points().data();
677 const float *radius_data = pointcloud->get_radius().data();
678 const size_t num_points = pointcloud->num_points();
679 const float4 *motion_data = (point_attr_mP) ? point_attr_mP->data_float4() : NULL;
680 const size_t num_steps = pointcloud->get_motion_steps();
681
682 int num_bounds = 0;
683
684 if (point_attr_mP == NULL) {
685 bvh->custom_prim_info.resize(num_points);
686 bvh->custom_primitive_bound.alloc(num_points);
687 for (uint j = 0; j < num_points; j++) {
688 const PointCloud::Point point = pointcloud->get_point(j);
690 point.bounds_grow(points_data, radius_data, bounds);
691 if (bounds.valid()) {
692 bvh->custom_primitive_bound[num_bounds] = bounds;
693 bvh->custom_prim_info[num_bounds].x = j;
694 bvh->custom_prim_info[num_bounds].y = PRIMITIVE_POINT;
695 num_bounds++;
696 }
697 }
698 }
699 else if (bvh->params.num_motion_point_steps == 0 || bvh->params.use_spatial_split) {
700 bvh->custom_prim_info.resize(num_points);
701 bvh->custom_primitive_bound.alloc(num_points);
702
703 for (uint j = 0; j < num_points; j++) {
704 const PointCloud::Point point = pointcloud->get_point(j);
706 point.bounds_grow(points_data, radius_data, bounds);
707 for (size_t step = 0; step < num_steps - 1; step++) {
708 point.bounds_grow(motion_data[step * num_points + j], bounds);
709 }
710 if (bounds.valid()) {
711 bvh->custom_primitive_bound[num_bounds] = bounds;
712 bvh->custom_prim_info[num_bounds].x = j;
713 bvh->custom_prim_info[num_bounds].y = PRIMITIVE_MOTION_POINT;
714 num_bounds++;
715 }
716 }
717 }
718 else {
719 const int num_bvh_steps = bvh->params.num_motion_point_steps * 2 + 1;
720 const float num_bvh_steps_inv_1 = 1.0f / (num_bvh_steps - 1);
721
722 bvh->custom_prim_info.resize(num_points * num_bvh_steps);
723 bvh->custom_primitive_bound.alloc(num_points * num_bvh_steps);
724 bvh->prims_time.resize(num_points * num_bvh_steps);
725
726 for (uint j = 0; j < num_points; j++) {
727 const PointCloud::Point point = pointcloud->get_point(j);
728 const size_t num_steps = pointcloud->get_motion_steps();
729 const float4 *point_steps = point_attr_mP->data_float4();
730
731 float4 prev_key = point.motion_key(
732 points_data, radius_data, point_steps, num_points, num_steps, 0.0f, j);
733 BoundBox prev_bounds = BoundBox::empty;
734 point.bounds_grow(prev_key, prev_bounds);
735
736 for (int bvh_step = 1; bvh_step < num_bvh_steps; ++bvh_step) {
737 const float curr_time = (float)(bvh_step)*num_bvh_steps_inv_1;
738 float4 curr_key = point.motion_key(
739 points_data, radius_data, point_steps, num_points, num_steps, curr_time, j);
740 BoundBox curr_bounds = BoundBox::empty;
741 point.bounds_grow(curr_key, curr_bounds);
742 BoundBox bounds = prev_bounds;
743 bounds.grow(curr_bounds);
744 if (bounds.valid()) {
745 const float prev_time = (float)(bvh_step - 1) * num_bvh_steps_inv_1;
746 bvh->custom_primitive_bound[num_bounds] = bounds;
747 bvh->custom_prim_info[num_bounds].x = j;
748 bvh->custom_prim_info[num_bounds].y = PRIMITIVE_MOTION_POINT;
749 bvh->prims_time[num_bounds].x = prev_time;
750 bvh->prims_time[num_bounds].y = curr_time;
751 num_bounds++;
752 }
753 prev_bounds = curr_bounds;
754 }
755 }
756 }
757
758 bvh->custom_prim_aabb.aabbCount = num_bounds;
759 bvh->custom_prim_aabb.aabbStride = sizeof(BoundBox);
760 bvh->custom_primitive_bound.copy_to_device();
761 bvh->custom_prim_aabb.aabbs = (void *)bvh->custom_primitive_bound.device_pointer;
762
763 geom_input.type = hiprtPrimitiveTypeAABBList;
764 geom_input.primitive.aabbList = bvh->custom_prim_aabb;
765 geom_input.geomType = Point;
766
767 return geom_input;
768}
769
770void HIPRTDevice::build_blas(BVHHIPRT *bvh, Geometry *geom, hiprtBuildOptions options)
771{
772 hiprtGeometryBuildInput geom_input = {};
773
774 switch (geom->geometry_type) {
775 case Geometry::MESH:
776 case Geometry::VOLUME: {
777 Mesh *mesh = static_cast<Mesh *>(geom);
778
779 if (mesh->num_triangles() == 0)
780 return;
781
782 geom_input = prepare_triangle_blas(bvh, mesh);
783 break;
784 }
785
786 case Geometry::HAIR: {
787 Hair *const hair = static_cast<Hair *const>(geom);
788
789 if (hair->num_segments() == 0)
790 return;
791
792 geom_input = prepare_curve_blas(bvh, hair);
793 break;
794 }
795
797 PointCloud *pointcloud = static_cast<PointCloud *>(geom);
798 if (pointcloud->num_points() == 0)
799 return;
800
801 geom_input = prepare_point_blas(bvh, pointcloud);
802 break;
803 }
804
805 default:
806 assert(geom_input.geomType != hiprtInvalidValue);
807 }
808
809 size_t blas_scratch_buffer_size = 0;
810 hiprtError rt_err = hiprtGetGeometryBuildTemporaryBufferSize(
811 hiprt_context, geom_input, options, blas_scratch_buffer_size);
812
813 if (rt_err != hiprtSuccess) {
814 set_error(string_printf("Failed to get scratch buffer size for BLAS!"));
815 }
816
817 rt_err = hiprtCreateGeometry(hiprt_context, geom_input, options, bvh->hiprt_geom);
818
819 if (rt_err != hiprtSuccess) {
820 set_error(string_printf("Failed to create BLAS!"));
821 }
822 bvh->geom_input = geom_input;
823 {
824 thread_scoped_lock lock(hiprt_mutex);
825 if (blas_scratch_buffer_size > scratch_buffer_size) {
826 scratch_buffer.alloc(blas_scratch_buffer_size);
827 scratch_buffer_size = blas_scratch_buffer_size;
828 scratch_buffer.zero_to_device();
829 }
830 rt_err = hiprtBuildGeometry(hiprt_context,
831 hiprtBuildOperationBuild,
832 bvh->geom_input,
833 options,
834 (void *)(scratch_buffer.device_pointer),
835 0,
836 bvh->hiprt_geom);
837 }
838 if (rt_err != hiprtSuccess) {
839 set_error(string_printf("Failed to build BLAS"));
840 }
841}
842
843hiprtScene HIPRTDevice::build_tlas(BVHHIPRT *bvh,
844 vector<Object *> objects,
845 hiprtBuildOptions options,
846 bool refit)
847{
848
849 size_t num_object = objects.size();
850 if (num_object == 0) {
851 return 0;
852 }
853
854 hiprtBuildOperation build_operation = refit ? hiprtBuildOperationUpdate :
855 hiprtBuildOperationBuild;
856
857 array<hiprtFrameMatrix> transform_matrix;
858
859 unordered_map<Geometry *, int2> prim_info_map;
860 size_t custom_prim_offset = 0;
861
862 unordered_map<Geometry *, int> prim_time_map;
863
864 size_t num_instances = 0;
865 int blender_instance_id = 0;
866
867 user_instance_id.alloc(num_object);
868 prim_visibility.alloc(num_object);
869 hiprt_blas_ptr.alloc(num_object);
870 blas_ptr.alloc(num_object);
871 transform_headers.alloc(num_object);
872 custom_prim_info_offset.alloc(num_object);
873 prim_time_offset.alloc(num_object);
874
875 foreach (Object *ob, objects) {
876 uint32_t mask = 0;
877 if (ob->is_traceable()) {
878 mask = ob->visibility_for_tracing();
879 }
880
881 Transform current_transform = ob->get_tfm();
882 Geometry *geom = ob->get_geometry();
883 bool transform_applied = geom->transform_applied;
884
885 BVHHIPRT *current_bvh = static_cast<BVHHIPRT *>(geom->bvh);
886 bool is_valid_geometry = current_bvh->geom_input.geomType != hiprtInvalidValue;
887 hiprtGeometry hiprt_geom_current = current_bvh->hiprt_geom;
888
889 hiprtFrameMatrix hiprt_transform_matrix = {{{0}}};
890 Transform identity_matrix = transform_identity();
891 get_hiprt_transform(hiprt_transform_matrix.matrix, identity_matrix);
892
893 if (is_valid_geometry) {
894 bool is_custom_prim = current_bvh->custom_prim_info.size() > 0;
895
896 if (is_custom_prim) {
897
898 bool has_motion_blur = current_bvh->prims_time.size() > 0;
899
900 unordered_map<Geometry *, int2>::iterator it = prim_info_map.find(geom);
901
902 if (prim_info_map.find(geom) != prim_info_map.end()) {
903
904 custom_prim_info_offset[blender_instance_id] = it->second;
905
906 if (has_motion_blur) {
907
908 prim_time_offset[blender_instance_id] = prim_time_map[geom];
909 }
910 }
911 else {
912 int offset = bvh->custom_prim_info.size();
913
914 prim_info_map[geom].x = offset;
915 prim_info_map[geom].y = custom_prim_offset;
916
917 bvh->custom_prim_info.resize(offset + current_bvh->custom_prim_info.size());
918 memcpy(bvh->custom_prim_info.data() + offset,
919 current_bvh->custom_prim_info.data(),
920 current_bvh->custom_prim_info.size() * sizeof(int2));
921
922 custom_prim_info_offset[blender_instance_id].x = offset;
923 custom_prim_info_offset[blender_instance_id].y = custom_prim_offset;
924
925 if (geom->geometry_type == Geometry::HAIR) {
926 custom_prim_offset += ((Hair *)geom)->num_curves();
927 }
928 else if (geom->geometry_type == Geometry::POINTCLOUD) {
929 custom_prim_offset += ((PointCloud *)geom)->num_points();
930 }
931 else {
932 custom_prim_offset += ((Mesh *)geom)->num_triangles();
933 }
934
935 if (has_motion_blur) {
936 int time_offset = bvh->prims_time.size();
937 prim_time_map[geom] = time_offset;
938
939 bvh->prims_time.resize(time_offset + current_bvh->prims_time.size());
940 memcpy(bvh->prims_time.data() + time_offset,
941 current_bvh->prims_time.data(),
942 current_bvh->prims_time.size() * sizeof(float2));
943
944 prim_time_offset[blender_instance_id] = time_offset;
945 }
946 else
947 prim_time_offset[blender_instance_id] = -1;
948 }
949 }
950 else
951 custom_prim_info_offset[blender_instance_id] = {-1, -1};
952
953 hiprtTransformHeader current_header = {0};
954 current_header.frameCount = 1;
955 current_header.frameIndex = transform_matrix.size();
956 if (ob->get_motion().size()) {
957 int motion_size = ob->get_motion().size();
958 assert(motion_size != 1);
959
960 array<Transform> tfm_array = ob->get_motion();
961 float time_iternval = 1 / (float)(motion_size - 1);
962 current_header.frameCount = motion_size;
963
964 vector<hiprtFrameMatrix> tfm_hiprt_mb;
965 tfm_hiprt_mb.resize(motion_size);
966 for (int i = 0; i < motion_size; i++) {
967 get_hiprt_transform(tfm_hiprt_mb[i].matrix, tfm_array[i]);
968 tfm_hiprt_mb[i].time = (float)i * time_iternval;
969 transform_matrix.push_back_slow(tfm_hiprt_mb[i]);
970 }
971 }
972 else {
973 if (transform_applied)
974 current_transform = identity_matrix;
975 get_hiprt_transform(hiprt_transform_matrix.matrix, current_transform);
976 transform_matrix.push_back_slow(hiprt_transform_matrix);
977 }
978
979 transform_headers[num_instances] = current_header;
980
981 user_instance_id[num_instances] = blender_instance_id;
982 prim_visibility[num_instances] = mask;
983 hiprt_blas_ptr[num_instances].geometry = hiprt_geom_current;
984 hiprt_blas_ptr[num_instances].type = hiprtInstanceTypeGeometry;
985 num_instances++;
986 }
987 blas_ptr[blender_instance_id] = (uint64_t)hiprt_geom_current;
988 blender_instance_id++;
989 }
990
991 int frame_count = transform_matrix.size();
992 hiprtSceneBuildInput scene_input_ptr = {0};
993 scene_input_ptr.instanceCount = num_instances;
994 scene_input_ptr.frameCount = frame_count;
995 scene_input_ptr.frameType = hiprtFrameTypeMatrix;
996
997 user_instance_id.copy_to_device();
998 prim_visibility.copy_to_device();
999 hiprt_blas_ptr.copy_to_device();
1000 blas_ptr.copy_to_device();
1001 transform_headers.copy_to_device();
1002 {
1003 instance_transform_matrix.alloc(frame_count);
1004 instance_transform_matrix.host_free();
1005 instance_transform_matrix.host_pointer = transform_matrix.data();
1006 instance_transform_matrix.data_elements = sizeof(hiprtFrameMatrix);
1007 instance_transform_matrix.data_type = TYPE_UCHAR;
1008 instance_transform_matrix.data_size = frame_count;
1009 instance_transform_matrix.copy_to_device();
1010 instance_transform_matrix.host_pointer = 0;
1011 }
1012
1013 scene_input_ptr.instanceMasks = (void *)prim_visibility.device_pointer;
1014 scene_input_ptr.instances = (void *)hiprt_blas_ptr.device_pointer;
1015 scene_input_ptr.instanceTransformHeaders = (void *)transform_headers.device_pointer;
1016 scene_input_ptr.instanceFrames = (void *)instance_transform_matrix.device_pointer;
1017
1018 hiprtScene scene = 0;
1019
1020 hiprtError rt_err = hiprtCreateScene(hiprt_context, scene_input_ptr, options, scene);
1021
1022 if (rt_err != hiprtSuccess) {
1023 set_error(string_printf("Failed to create TLAS"));
1024 }
1025
1026 size_t tlas_scratch_buffer_size;
1027 rt_err = hiprtGetSceneBuildTemporaryBufferSize(
1028 hiprt_context, scene_input_ptr, options, tlas_scratch_buffer_size);
1029
1030 if (rt_err != hiprtSuccess) {
1031 set_error(string_printf("Failed to get scratch buffer size for TLAS"));
1032 }
1033
1034 if (tlas_scratch_buffer_size > scratch_buffer_size) {
1035 scratch_buffer.alloc(tlas_scratch_buffer_size);
1036 scratch_buffer.zero_to_device();
1037 }
1038
1039 rt_err = hiprtBuildScene(hiprt_context,
1040 build_operation,
1041 scene_input_ptr,
1042 options,
1043 (void *)scratch_buffer.device_pointer,
1044 0,
1045 scene);
1046
1047 if (rt_err != hiprtSuccess) {
1048 set_error(string_printf("Failed to build TLAS"));
1049 }
1050
1051 scratch_buffer.free();
1052 scratch_buffer_size = 0;
1053
1054 if (bvh->custom_prim_info.size()) {
1055 size_t data_size = bvh->custom_prim_info.size();
1056 custom_prim_info.alloc(data_size);
1057 custom_prim_info.host_free();
1058 custom_prim_info.host_pointer = bvh->custom_prim_info.data();
1059 custom_prim_info.data_elements = 2;
1060 custom_prim_info.data_type = TYPE_INT;
1061 custom_prim_info.data_size = data_size;
1062 custom_prim_info.copy_to_device();
1063 custom_prim_info.host_pointer = 0;
1064
1065 custom_prim_info_offset.copy_to_device();
1066 }
1067
1068 if (bvh->prims_time.size()) {
1069 size_t data_size = bvh->prims_time.size();
1070 prims_time.alloc(data_size);
1071 prims_time.host_free();
1072 prims_time.host_pointer = bvh->prims_time.data();
1073 prims_time.data_elements = 2;
1074 prims_time.data_type = TYPE_FLOAT;
1075 prims_time.data_size = data_size;
1076 prims_time.copy_to_device();
1077 prims_time.host_pointer = 0;
1078
1079 prim_time_offset.copy_to_device();
1080 }
1081
1082 size_t table_ptr_size = 0;
1083 hipDeviceptr_t table_device_ptr;
1084
1085 hip_assert(hipModuleGetGlobal(&table_device_ptr, &table_ptr_size, hipModule, "kernel_params"));
1086
1087 size_t kernel_param_offset[4];
1088 int table_index = 0;
1089 kernel_param_offset[table_index++] = offsetof(KernelParamsHIPRT, table_closest_intersect);
1090 kernel_param_offset[table_index++] = offsetof(KernelParamsHIPRT, table_shadow_intersect);
1091 kernel_param_offset[table_index++] = offsetof(KernelParamsHIPRT, table_local_intersect);
1092 kernel_param_offset[table_index++] = offsetof(KernelParamsHIPRT, table_volume_intersect);
1093
1094 for (int index = 0; index < table_index; index++) {
1095
1096 hip_assert(hipMemcpyHtoD(
1097 table_device_ptr + kernel_param_offset[index], &functions_table, sizeof(device_ptr)));
1098 }
1099
1100 return scene;
1101}
1102
1103void HIPRTDevice::build_bvh(BVH *bvh, Progress &progress, bool refit)
1104{
1105 progress.set_substatus("Building HIPRT acceleration structure");
1106
1107 hiprtBuildOptions options;
1108 options.buildFlags = hiprtBuildFlagBitPreferHighQualityBuild;
1109
1110 BVHHIPRT *bvh_rt = static_cast<BVHHIPRT *>(bvh);
1111 HIPContextScope scope(this);
1112
1113 if (!bvh_rt->is_tlas()) {
1114 vector<Geometry *> geometry = bvh_rt->geometry;
1115 assert(geometry.size() == 1);
1116 Geometry *geom = geometry[0];
1117 build_blas(bvh_rt, geom, options);
1118 }
1119 else {
1120
1121 const vector<Object *> objects = bvh_rt->objects;
1122 if (scene) {
1123 hiprtDestroyScene(hiprt_context, scene);
1124 }
1125 scene = build_tlas(bvh_rt, objects, options, refit);
1126 }
1127}
1129
1130#endif
unsigned int uint
struct Curve Curve
struct BoundBox BoundBox
volatile int lock
static btDbvtVolume bounds(btDbvtNode **leaves, int count)
Definition btDbvt.cpp:299
void refit(btStridingMeshInterface *triangles, const btVector3 &aabbMin, const btVector3 &aabbMax)
SIMD_FORCE_INLINE const btScalar & z() const
Return the z value.
Definition btQuadWord.h:117
Attribute * find(ustring name) const
void resize(Geometry *geom, AttributePrimitive prim, bool reserve_only)
float3 * data_float3()
float4 * data_float4()
Definition bvh/bvh.h:66
Type geometry_type
bool transform_applied
bool has_motion_blur() const
AttributeSet attributes
Definition hair.h:14
void set_substatus(const string &substatus_)
Definition progress.h:274
size_t size() const
void push_back_slow(const T &t)
#define printf
@ MEM_READ_WRITE
@ MEM_DEVICE_ONLY
@ MEM_READ_ONLY
CCL_NAMESPACE_BEGIN struct Options options
#define KERNEL_DATA_ARRAY(type, name)
Definition data_arrays.h:6
#define CCL_NAMESPACE_END
#define NULL
#define offsetof(t, d)
draw_view in_light_buf[] float
draw_view push_constant(Type::INT, "radiance_src") .push_constant(Type capture_info_buf storage_buf(1, Qualifier::READ, "ObjectBounds", "bounds_buf[]") .push_constant(Type draw_view int
static float verts[][3]
uint col
#define PRIMITIVE_PACK_SEGMENT(type, segment)
PrimitiveType
@ PRIMITIVE_MOTION_POINT
@ PRIMITIVE_POINT
@ ATTR_STD_MOTION_VERTEX_POSITION
KernelData
@ BVH_LAYOUT_HIPRT
#define KERNEL_FEATURE_NODE_RAYTRACE
#define KERNEL_FEATURE_MNEE
DeviceKernel
@ DEVICE_KERNEL_INTEGRATOR_SHADE_SURFACE
@ DEVICE_KERNEL_INTEGRATOR_SHADE_SURFACE_RAYTRACE
@ DEVICE_KERNEL_INTEGRATOR_SHADE_SURFACE_MNEE
#define VLOG_INFO
Definition log.h:72
#define VLOG(severity)
Definition log.h:34
ccl_device_inline float4 mask(const int4 mask, const float4 a)
string util_md5_string(const string &str)
Definition md5.cpp:373
T step(const T &edge, const T &value)
VecBase< float, 4 > float4
int BVHLayoutMask
Definition params.h:51
string path_cache_get(const string &sub)
Definition path.cpp:362
string path_get(const string &sub)
Definition path.cpp:339
string path_files_md5_hash(const string &dir)
Definition path.cpp:612
string path_join(const string &dir, const string &file)
Definition path.cpp:417
bool path_exists(const string &path)
Definition path.cpp:565
void path_create_directories(const string &filepath)
Definition path.cpp:648
bool path_read_compressed_text(const string &path, string &text)
Definition path.cpp:754
#define min(a, b)
Definition sort.c:32
unsigned int uint32_t
Definition stdint.h:80
unsigned __int64 uint64_t
Definition stdint.h:90
CCL_NAMESPACE_BEGIN string string_printf(const char *format,...)
Definition string.cpp:23
__forceinline void grow(const float3 &pt)
Definition boundbox.h:36
int first_key
Definition hair.h:20
void motion_verts(const float3 *verts, const float3 *vert_steps, size_t num_verts, size_t num_steps, float time, float3 r_verts[3]) const
void bounds_grow(const float3 *verts, BoundBox &bounds) const
bool is_traceable() const
uint visibility_for_tracing() const
Point get_point(int i) const
size_t num_points() const
void resize(int numpoints)
float4 y
Definition transform.h:24
float4 x
Definition transform.h:24
float4 z
Definition transform.h:24
std::unique_lock< std::mutex > thread_scoped_lock
Definition thread.h:30
CCL_NAMESPACE_BEGIN double time_dt()
Definition time.cpp:36
ccl_device_inline Transform transform_identity()
Definition transform.h:296
float max
uint64_t device_ptr
Definition util/types.h:45