diff --git a/CMakeLists.txt b/CMakeLists.txt index aa58baf2c..7432d7723 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -18,6 +18,7 @@ option(HIGHMAP_ENABLE_TESTS "" ON) option(HIGHMAP_ENABLE_BENCHMARKS "" ON) option(HIGHMAP_ENABLE_PROFILE_BUILD "Enable build time profiling (-ftime-trace)" OFF) +option(HIGHMAP_ENABLE_TRACY "Enable optional Tracy CPU instrumentation" OFF) option(HIGHMAP_ENABLE_PCH "Enable Precompiled Headers (PCH)" OFF) # ----------------------------------------------------------------------------- diff --git a/HighMap/CMakeLists.txt b/HighMap/CMakeLists.txt index f272db211..009e59b9b 100644 --- a/HighMap/CMakeLists.txt +++ b/HighMap/CMakeLists.txt @@ -12,6 +12,13 @@ file(GLOB_RECURSE SOURCES ${PROJECT_SOURCE_DIR}/src/*.cpp) add_library(${PROJECT_NAME} STATIC ${SOURCES}) +if(HIGHMAP_ENABLE_TRACY) + find_package(Tracy CONFIG REQUIRED) + # Public because the tile dispatcher is instantiated by library consumers. + target_link_libraries(${PROJECT_NAME} PUBLIC Tracy::TracyClient) + target_compile_definitions(${PROJECT_NAME} PUBLIC HIGHMAP_ENABLE_TRACY) +endif() + target_include_directories( ${PROJECT_NAME} PUBLIC ${PROJECT_SOURCE_DIR}/include diff --git a/HighMap/include/highmap/carving.hpp b/HighMap/include/highmap/carving.hpp index 7245576cb..7d52c1189 100644 --- a/HighMap/include/highmap/carving.hpp +++ b/HighMap/include/highmap/carving.hpp @@ -246,11 +246,11 @@ void trench(Array &z, * @param[in] tile_size Spatial tile partition size. * @param[in] bbox Domain bounding box. */ -void trench(Array &z, - const Graph &graph, - const std::vector &relative_widths, - float width, - bool enable_width_depth_scaling = true, +void trench(Array &z, + const Graph &graph, + const std::vector &relative_widths, + float width, + bool enable_width_depth_scaling = true, RadialProfile radial_profile = RadialProfile::RP_SMOOTHSTEP_UPPER, float radial_profile_parameter = 2.f, ElevationLongitudinalProfile longitudinal_profile = @@ -268,7 +268,8 @@ void trench(Array &z, * * @param[in,out] z Target heightmap to carve in-place. * @param[in] graph Input flow network Graph. - * @param[in] width Target river width at the outlet(s). + * @param[in] width Target river width at the + * outlet(s). * @param[in] width_exponent Scaling exponent for width with * distance to outlet (default: 1.0). * @param[in] enable_width_depth_scaling Modulate width according to depth. diff --git a/HighMap/include/highmap/profiling.hpp b/HighMap/include/highmap/profiling.hpp new file mode 100644 index 000000000..ad8fd1e67 --- /dev/null +++ b/HighMap/include/highmap/profiling.hpp @@ -0,0 +1,28 @@ +#pragma once + +// Optional CPU instrumentation. No expressions are evaluated in normal builds. +#ifdef HIGHMAP_ENABLE_TRACY +#include + +#include +#ifndef TRACY_ENABLE +#error HIGHMAP_ENABLE_TRACY requires a Tracy client built with TRACY_ENABLE +#endif +#define HMAP_PROFILE_ZONE(name) ZoneScopedN(name) +#define HMAP_PROFILE_TEXT(text) \ + do \ + { \ + if (TracyIsConnected) \ + { \ + const auto &hmap_profile_text = (text); \ + const std::string_view hmap_profile_view(hmap_profile_text); \ + ZoneText(hmap_profile_view.data(), hmap_profile_view.size()); \ + } \ + } while (false) +#else +#define HMAP_PROFILE_ZONE(name) +#define HMAP_PROFILE_TEXT(text) \ + do \ + { \ + } while (false) +#endif diff --git a/HighMap/include/highmap/virtual_array/virtual_array.hpp b/HighMap/include/highmap/virtual_array/virtual_array.hpp index 8c3daa1c1..c2d2df58f 100644 --- a/HighMap/include/highmap/virtual_array/virtual_array.hpp +++ b/HighMap/include/highmap/virtual_array/virtual_array.hpp @@ -15,6 +15,7 @@ #include "highmap/filters.hpp" #include "highmap/logger.hpp" #include "highmap/math/core.hpp" +#include "highmap/profiling.hpp" #include "highmap/virtual_array/tile_region.hpp" #include "highmap/virtual_array/tile_storage.hpp" diff --git a/HighMap/include/highmap/virtual_array/virtual_array.inl b/HighMap/include/highmap/virtual_array/virtual_array.inl index 9cf50edb8..44b4a4006 100644 --- a/HighMap/include/highmap/virtual_array/virtual_array.inl +++ b/HighMap/include/highmap/virtual_array/virtual_array.inl @@ -21,6 +21,7 @@ struct TileAccess template void for_each_tile(const TileAccess &access, Func &&func, const ComputeMode &cm) { + HMAP_PROFILE_ZONE("HighMap::for_each_tile"); if (access.outputs.empty() && access.inputs.empty()) { hmap::log::error("no input or output VirtualArray"); @@ -30,6 +31,12 @@ void for_each_tile(const TileAccess &access, Func &&func, const ComputeMode &cm) const VirtualArray *ref_va = access.ref_va(); if (!ref_va) return; + HMAP_PROFILE_TEXT(to_string(cm.mode) + + " shape=" + std::to_string(ref_va->shape.x) + "x" + + std::to_string(ref_va->shape.y) + + " tile=" + std::to_string(ref_va->tile_shape.x) + "x" + + std::to_string(ref_va->tile_shape.y)); + // --- validate compatibility auto check = [&](const VirtualArray *va) @@ -56,25 +63,34 @@ void for_each_tile(const TileAccess &access, Func &&func, const ComputeMode &cm) auto region_dispatcher = [&](const TileRegion ®ion) { + HMAP_PROFILE_ZONE("HighMap::tile"); std::vector in_tiles; std::vector out_tiles; in_tiles.reserve(access.inputs.size()); out_tiles.reserve(access.outputs.size()); - for (auto *va : access.inputs) - in_tiles.push_back(va ? &va->storage->get_tile(region) : nullptr); - - for (auto *va : access.outputs) - out_tiles.push_back(va ? &va->storage->get_tile(region) : nullptr); + { + HMAP_PROFILE_ZONE("HighMap::tile.acquire"); + for (auto *va : access.inputs) + in_tiles.push_back(va ? &va->storage->get_tile(region) : nullptr); - func(in_tiles, out_tiles, region); + for (auto *va : access.outputs) + out_tiles.push_back(va ? &va->storage->get_tile(region) : nullptr); + } + { + HMAP_PROFILE_ZONE("HighMap::tile.algorithm"); + func(in_tiles, out_tiles, region); + } + { + HMAP_PROFILE_ZONE("HighMap::tile.release"); - for (auto *va : access.inputs) - if (va) va->storage->release_tile(region); + for (auto *va : access.inputs) + if (va) va->storage->release_tile(region); - for (auto *va : access.outputs) - if (va) va->storage->release_tile(region); + for (auto *va : access.outputs) + if (va) va->storage->release_tile(region); + } }; // reuse your existing modes @@ -96,6 +112,7 @@ void for_each_tile(const TileAccess &access, Func &&func, const ComputeMode &cm) if (cm.trim_storage) { + HMAP_PROFILE_ZONE("HighMap::trim_storage"); for (auto *va : access.inputs) if (va) va->trim_storage(); for (auto *va : access.outputs) @@ -175,6 +192,7 @@ void distributed_tile_loop(const VirtualArray &ref_va, RegionDispatcher &&dispatcher, int nthreads = 0) { + HMAP_PROFILE_ZONE("HighMap::distributed_tile_loop"); const int nx = ceil_div(ref_va.shape.x, ref_va.tile_shape.x); const int ny = ceil_div(ref_va.shape.y, ref_va.tile_shape.y); const int ntasks = nx * ny; @@ -210,6 +228,7 @@ void distributed_tile_loop(const VirtualArray &ref_va, template void single_array_compute(const TileAccess &access, Func &&func, int stride = 1) { + HMAP_PROFILE_ZONE("HighMap::single_array"); const VirtualArray *ref_va = access.ref_va(); if (!ref_va) return; @@ -232,16 +251,18 @@ void single_array_compute(const TileAccess &access, Func &&func, int stride = 1) shape_wrk = glm::ivec2(nx, ny); } - // project - for (auto *va : access.inputs) - arrays_in.push_back(va ? va->to_array(cm_local).resample_to_shape(shape_wrk) - : Array()); - - for (auto *va : access.outputs) - // TODO remove to_array(), left for retor-fit but misusage - arrays_out.push_back( - va ? va->to_array(cm_local).resample_to_shape(shape_wrk) : Array()); + { + HMAP_PROFILE_ZONE("HighMap::single_array.gather"); + // project + for (auto *va : access.inputs) + arrays_in.push_back( + va ? va->to_array(cm_local).resample_to_shape(shape_wrk) : Array()); + for (auto *va : access.outputs) + // TODO remove to_array(), left for retor-fit but misusage + arrays_out.push_back( + va ? va->to_array(cm_local).resample_to_shape(shape_wrk) : Array()); + } // create pointer vector for user func std::vector in_tiles; std::vector out_tiles; @@ -260,18 +281,24 @@ void single_array_compute(const TileAccess &access, Func &&func, int stride = 1) shape_wrk, {0, 0, 0, 0}); - func(in_tiles, out_tiles, va_region); + { + HMAP_PROFILE_ZONE("HighMap::single_array.algorithm"); + func(in_tiles, out_tiles, va_region); + } - // copy back to VirtualArrays (only outputs do not need to update const - // inputs) - for (size_t i = 0; i < access.outputs.size(); ++i) - if (access.outputs[i]) - { - if (shape_wrk != ref_va->shape) - access.outputs[i]->from_array_bicubic(arrays_out[i], cm_local); - else - access.outputs[i]->from_array(arrays_out[i], cm_local); - } + { + HMAP_PROFILE_ZONE("HighMap::single_array.scatter"); + // copy back to VirtualArrays (only outputs do not need to update const + // inputs) + for (size_t i = 0; i < access.outputs.size(); ++i) + if (access.outputs[i]) + { + if (shape_wrk != ref_va->shape) + access.outputs[i]->from_array_bicubic(arrays_out[i], cm_local); + else + access.outputs[i]->from_array(arrays_out[i], cm_local); + } + } } template @@ -279,6 +306,7 @@ void single_array_downscaled_compute(const TileAccess &access, Func &&func, float kc) { + HMAP_PROFILE_ZONE("HighMap::single_array_downscaled"); const VirtualArray *ref_va = access.ref_va(); if (!ref_va) return; @@ -301,16 +329,18 @@ void single_array_downscaled_compute(const TileAccess &access, shape_wrk = glm::ivec2(nx, ny); } - // project - for (auto *va : access.inputs) - arrays_in.push_back(va ? va->to_array(cm_local).resample_to_shape(shape_wrk) - : Array()); - - for (auto *va : access.outputs) - // TODO remove to_array(), left for retor-fit but misusage - arrays_out.push_back( - va ? va->to_array(cm_local).resample_to_shape(shape_wrk) : Array()); + { + HMAP_PROFILE_ZONE("HighMap::single_array.gather"); + // project + for (auto *va : access.inputs) + arrays_in.push_back( + va ? va->to_array(cm_local).resample_to_shape(shape_wrk) : Array()); + for (auto *va : access.outputs) + // TODO remove to_array(), left for retor-fit but misusage + arrays_out.push_back( + va ? va->to_array(cm_local).resample_to_shape(shape_wrk) : Array()); + } // create pointer vector for user func std::vector in_tiles; std::vector out_tiles; @@ -329,28 +359,34 @@ void single_array_downscaled_compute(const TileAccess &access, shape_wrk, {0, 0, 0, 0}); - func(in_tiles, out_tiles, va_region); + { + HMAP_PROFILE_ZONE("HighMap::single_array.algorithm"); + func(in_tiles, out_tiles, va_region); + } - // copy back to VirtualArrays (only outputs do not need to update const - // inputs) - for (size_t i = 0; i < access.outputs.size(); ++i) - if (access.outputs[i]) - { - // resample to initial shape and add higher frequency - Array out_full = access.outputs[i]->to_array(cm_local); - Array out_low_pass = out_full.resample_to_shape(shape_wrk) - .resample_to_shape_bicubic(ref_va->shape); + { + HMAP_PROFILE_ZONE("HighMap::single_array.scatter"); + // copy back to VirtualArrays (only outputs do not need to update const + // inputs) + for (size_t i = 0; i < access.outputs.size(); ++i) + if (access.outputs[i]) + { + // resample to initial shape and add higher frequency + Array out_full = access.outputs[i]->to_array(cm_local); + Array out_low_pass = out_full.resample_to_shape(shape_wrk) + .resample_to_shape_bicubic(ref_va->shape); - // resample coarse result and use it to replace the initial - // low-frequency content - arrays_out[i] = arrays_out[i].resample_to_shape_bicubic(ref_va->shape); + // resample coarse result and use it to replace the initial + // low-frequency content + arrays_out[i] = arrays_out[i].resample_to_shape_bicubic(ref_va->shape); - // TODO add post-filter - // laplace(arrays_out[i]); + // TODO add post-filter + // laplace(arrays_out[i]); - access.outputs[i]->from_array(arrays_out[i] + (out_full - out_low_pass), - cm_local); - } + access.outputs[i]->from_array(arrays_out[i] + (out_full - out_low_pass), + cm_local); + } + } } template diff --git a/HighMap/src/erosion/hydraulic_blur.cpp b/HighMap/src/erosion/hydraulic_blur.cpp index e9e852b58..83c9a30b2 100644 --- a/HighMap/src/erosion/hydraulic_blur.cpp +++ b/HighMap/src/erosion/hydraulic_blur.cpp @@ -7,6 +7,7 @@ #include "highmap/erosion/deprecated.hpp" #include "highmap/filters.hpp" #include "highmap/internal/validation.hpp" +#include "highmap/profiling.hpp" #include "highmap/range.hpp" namespace hmap @@ -14,20 +15,30 @@ namespace hmap void hydraulic_blur(Array &z, float vmax, float radius, float k_smoothing) { + HMAP_PROFILE_ZONE("HighMap::hydraulic_blur"); if (!validate_non_empty(z)) return; int ir = std::max(1, (int)(radius * z.shape.x)); Array z_clamped = z; - if (k_smoothing) - clamp_min_smooth(z_clamped, vmax, k_smoothing); - else - clamp_min_smooth(z_clamped, vmax); + { + HMAP_PROFILE_ZONE("HighMap::hydraulic_blur.clamp"); + if (k_smoothing) + clamp_min_smooth(z_clamped, vmax, k_smoothing); + else + clamp_min_smooth(z_clamped, vmax); + } Array z_filtered = z; - smooth_cpulse(z_filtered, ir); - - z = (z_clamped + z_filtered - vmax); + { + HMAP_PROFILE_ZONE("HighMap::hydraulic_blur.smooth"); + smooth_cpulse(z_filtered, ir); + } + + { + HMAP_PROFILE_ZONE("HighMap::hydraulic_blur.combine"); + z = (z_clamped + z_filtered - vmax); + } } } // namespace hmap diff --git a/HighMap/src/export/export_asset.cpp b/HighMap/src/export/export_asset.cpp index 21deb82e3..734fb385a 100644 --- a/HighMap/src/export/export_asset.cpp +++ b/HighMap/src/export/export_asset.cpp @@ -17,6 +17,7 @@ #include "highmap/internal/validation.hpp" #include "highmap/logger.hpp" #include "highmap/operator.hpp" +#include "highmap/profiling.hpp" #include #include @@ -36,6 +37,7 @@ void helper_build_mesh_masked(aiMesh *p_mesh, float elevation_scaling, bool fit_boundaries) { + HMAP_PROFILE_ZONE("HighMap::export_asset.mesh_masked"); float x_max = fit_boundaries ? 1.f : (array.shape.x > 0 ? 1.f - 1.f / (float)array.shape.x @@ -130,6 +132,7 @@ void helper_build_mesh(aiMesh *p_mesh, float max_error, bool fit_boundaries) { + HMAP_PROFILE_ZONE("HighMap::export_asset.mesh"); float x_max = fit_boundaries ? 1.f : (array.shape.x > 0 ? 1.f - 1.f / (float)array.shape.x @@ -206,7 +209,10 @@ void helper_build_mesh(aiMesh *p_mesh, const int max_points = 0; hmap::log::trace("remeshing (Delaunay)"); - tri.Run(max_error, max_triangles, max_points); + { + HMAP_PROFILE_ZONE("HighMap::export_asset.triangulate"); + tri.Run(max_error, max_triangles, max_points); + } auto points = tri.Points(elevation_scaling); auto triangles = tri.Triangles(); @@ -321,10 +327,11 @@ bool export_asset(const std::string &fname, std::string format_extension = "." + asset_export_format_as_string.at( export_format)[2]; - aiReturn err = exporter.Export(p_scene, - format_id, - fname + format_extension, - 0); + aiReturn err; + { + HMAP_PROFILE_ZONE("HighMap::export_asset.write"); + err = exporter.Export(p_scene, format_id, fname + format_extension, 0); + } delete p_scene; @@ -398,10 +405,11 @@ bool export_asset(const std::string &fname, std::string format_extension = "." + asset_export_format_as_string.at( export_format)[2]; - aiReturn err = exporter.Export(p_scene, - format_id, - fname + format_extension, - 0); + aiReturn err; + { + HMAP_PROFILE_ZONE("HighMap::export_asset.write"); + err = exporter.Export(p_scene, format_id, fname + format_extension, 0); + } delete p_scene; diff --git a/HighMap/src/virtual_array/array/sync_overlap_buffers.cpp b/HighMap/src/virtual_array/array/sync_overlap_buffers.cpp index 0f6bfcefb..7e358bb7b 100644 --- a/HighMap/src/virtual_array/array/sync_overlap_buffers.cpp +++ b/HighMap/src/virtual_array/array/sync_overlap_buffers.cpp @@ -7,6 +7,7 @@ #include "highmap/array.hpp" #include "highmap/logger.hpp" #include "highmap/math/core.hpp" +#include "highmap/profiling.hpp" #include "highmap/virtual_array/tile_region.hpp" #include "highmap/virtual_array/tile_storage.hpp" #include "highmap/virtual_array/virtual_array.hpp" @@ -16,6 +17,7 @@ namespace hmap void VirtualArray::sync_overlap_buffers(SyncOperation op) { + HMAP_PROFILE_ZONE("HighMap::VirtualArray::sync_overlap_buffers"); if (this->storage->max_live_tiles() < 2) { hmap::log::error( diff --git a/HighMap/src/virtual_array/array/virtual_array.cpp b/HighMap/src/virtual_array/array/virtual_array.cpp index 4042916af..99e61c133 100644 --- a/HighMap/src/virtual_array/array/virtual_array.cpp +++ b/HighMap/src/virtual_array/array/virtual_array.cpp @@ -14,6 +14,7 @@ #include "highmap/array.hpp" #include "highmap/internal/validation.hpp" #include "highmap/math/core.hpp" +#include "highmap/profiling.hpp" #include "highmap/virtual_array/tile_region.hpp" #include "highmap/virtual_array/tile_storage.hpp" #include "highmap/virtual_array/virtual_array.hpp" @@ -110,6 +111,7 @@ void VirtualArray::fill(float value, const ComputeMode &cm) void VirtualArray::from_array(const Array &array, const ComputeMode &cm) { + HMAP_PROFILE_ZONE("HighMap::VirtualArray::from_array"); if (!validate_non_empty(array)) return; auto lambda = [&array, this](Array &tile, const TileRegion ®ion) @@ -144,6 +146,7 @@ void VirtualArray::from_array(const Array &array, const ComputeMode &cm) void VirtualArray::from_array_bilinear(const Array &array, const ComputeMode &cm) { + HMAP_PROFILE_ZONE("HighMap::VirtualArray::from_array_bilinear"); if (!validate_non_empty(array)) return; auto lambda = [&array, this](Array &tile, const TileRegion ®ion) @@ -185,6 +188,7 @@ void VirtualArray::from_array_bilinear(const Array &array, void VirtualArray::from_array_bicubic(const Array &array, const ComputeMode &cm) { + HMAP_PROFILE_ZONE("HighMap::VirtualArray::from_array_bicubic"); if (!validate_non_empty(array)) return; auto lambda = [&array, this](Array &tile, const TileRegion ®ion) @@ -431,6 +435,7 @@ glm::ivec2 VirtualArray::tile_region_global_indices( Array VirtualArray::to_array(const glm::ivec2 &array_shape, const ComputeMode &cm) const { + HMAP_PROFILE_ZONE("HighMap::VirtualArray::to_array"); if (!validate_shape(array_shape)) return Array(); Array array(array_shape); diff --git a/HighMap/src/virtual_array/texture/virtual_texture.cpp b/HighMap/src/virtual_array/texture/virtual_texture.cpp index 28897e8c8..ac5e7b3d1 100644 --- a/HighMap/src/virtual_array/texture/virtual_texture.cpp +++ b/HighMap/src/virtual_array/texture/virtual_texture.cpp @@ -11,6 +11,7 @@ #include "highmap/array.hpp" #include "highmap/internal/validation.hpp" #include "highmap/logger.hpp" +#include "highmap/profiling.hpp" #include "highmap/texture.hpp" #include "highmap/virtual_array/tile_storage.hpp" #include "highmap/virtual_array/virtual_array.hpp" @@ -138,6 +139,7 @@ void VirtualTexture::fill(int c, float value, const ComputeMode &cm) void VirtualTexture::from_arrays(const std::vector &p_arrays, const ComputeMode &cm) { + HMAP_PROFILE_ZONE("HighMap::VirtualTexture::from_arrays"); if (!validate_non_empty(p_arrays, "Array pointer list")) return; const int nch = this->channels(); @@ -163,6 +165,7 @@ std::vector VirtualTexture::to_img_8bit(const glm::ivec2 &img_shape, const ComputeMode &cm, bool flip_y) const { + HMAP_PROFILE_ZONE("HighMap::VirtualTexture::to_img_8bit"); const int nch = this->channels(); if (nch != 3 && nch != 4) @@ -228,6 +231,7 @@ std::vector VirtualTexture::to_raw(const ComputeMode &cm, bool flip_y) Texture VirtualTexture::to_texture(const glm::ivec2 &img_shape, const ComputeMode &cm) const { + HMAP_PROFILE_ZONE("HighMap::VirtualTexture::to_texture"); if (!validate_shape(img_shape)) return Texture(); const int nch = this->channels(); diff --git a/README.md b/README.md index 9b242b184..b826f71a1 100644 --- a/README.md +++ b/README.md @@ -78,4 +78,8 @@ See the `examples` folder. ## Third Party Code This project includes code from external open-source projects. -See [THIRD_PARTY_LICENSES.md](THIRD_PARTY_LICENSES.md). \ No newline at end of file +See [THIRD_PARTY_LICENSES.md](THIRD_PARTY_LICENSES.md). + +## CPU profiling + +Optional Tracy instrumentation is described in [the profiling guide](docs/profiling.md). diff --git a/docs/profiling.md b/docs/profiling.md new file mode 100644 index 000000000..d507928c5 --- /dev/null +++ b/docs/profiling.md @@ -0,0 +1,26 @@ +# Optional Tracy CPU profiling + +`HIGHMAP_ENABLE_TRACY` defaults to `OFF`; normal builds do not require Tracy. +To enable it, install a Tracy client built with `TRACY_ENABLE=ON`, then configure +with `-DHIGHMAP_ENABLE_TRACY=ON -DTracy_DIR=/path/to/lib/cmake/Tracy`. +Use matching client and capture/profiler versions. On-demand clients are supported. + +The `highmap` CMake target publicly propagates both the client target and +`HIGHMAP_ENABLE_TRACY`, so consumers instantiate the public tile templates with +the same instrumentation as the library. Do not manually enable the macro in +only some translation units. Link a single client into the final executable. +Disabled hooks discard metadata expressions; enabled metadata is evaluated only +while connected. + +Zones cover VirtualArray conversion/write-back, overlap synchronization, +VirtualTexture conversion, shared tiled and full-array dispatch, HydraulicBlur +stages and asset mesh generation/export. Tile dispatch separates storage acquire, +callback and release; full-array dispatch separates gather, callback and scatter. +The downscaled scatter includes high-frequency reconstruction. No per-cell zones +are emitted. Many small tiles can still generate substantial capture traffic. + +These are CPU durations, including any synchronous library work and waits. +They are not GPU kernel/transfer timing or allocation tracking. Nested times +include their children. Distributed tile zones run on worker threads, so their +sum is not dispatch wall time. Conversion callbacks also appear as tile algorithms; +interpret them using the enclosing conversion zone and thread context.