diff --git a/CHANGELOG.md b/CHANGELOG.md index c9962aac5..f2203ad5e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,23 @@ Version History --------------- +### Changes in v2.7.1: + +- Use Open VKL v1.0.1 to fix sporadic slowdowns when rendering + structured regular and VDB volumes with the SciVis renderer +- Fix CMake variables and logic +- Fix crash when transferfunction.opacity = 0 +- Fix bug in MPI data-parallel rendering that caused rendering to hang +- Workaround dynamic linking issue on Windows in MPI distributed + rendering +- Correctly initialize renderFrame progress +- Improved performance of data-parallel rendering for scenes with + a large number of regions +- Expanded camera model support of the data-parallel renderer, + data-parallel rendering can now use all the camera models supported + by the scivis renderer +- Clarify documentation and error messages + ### Changes in v2.7.0: - Add support for transformation and camera Motion Blur (with the path diff --git a/README.md b/README.md index 8c15bb81c..06d06b412 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ OSPRay ====== -This is release v2.7.0 of Intel® OSPRay. For changes and new features +This is release v2.7.1 of Intel® OSPRay. For changes and new features see the [changelog](CHANGELOG.md). Visit http://www.ospray.org for more information. @@ -100,7 +100,7 @@ before you can build OSPRay you need the following prerequisites: `embree_DIR`. - OSPRay also heavily uses Intel [Open VKL](https://www.openvkl.org/), - installing version 1.0.0 or newer is required. If Open VKL is not + installing version 1.0.1 or newer is required. If Open VKL is not found by CMake its location can be hinted with the variable `openvkl_DIR`. @@ -1782,17 +1782,16 @@ group. By adding `OSPGeometricModel`s to the `clippingGeometry` array a clipping geometry feature is enabled. Geometries assigned to this parameter will be used as clipping geometries. Any supported geometry -can be used for clipping. The only requirement is that it has to -distinctly partition space into clipping and non-clipping one. These -include: spheres, boxes, infinite planes, closed meshes, closed -subdivisions and curves. All geometries and volumes assigned to -`geometry` or `volume` will be clipped. Use of clipping geometry that is -not closed (or infinite) will result in rendering artifacts. User can -decide which part of space is clipped by changing shading normals -orientation with the `invertNormals` flag of the -[GeometricModel](#geometricmodels). When more than single clipping -geometry is defined all clipping areas will be “added” together – an -union of these areas will be applied. +can be used for clipping[6], the only requirement is that it has to +distinctly partition space into clipping and non-clipping one. The use +of clipping geometry that is not closed or infinite could result in +rendering artifacts. User can decide which part of space is clipped by +changing shading normals orientation with the `invertNormals` flag of +the [GeometricModel](#geometricmodels). All geometries and volumes +assigned to `geometry` or `volume` will be clipped. All clipping +geometries from all groups and [Instances](#instances) will be combined +together – a union of these areas will be applied to all other objects +in the [world](#world). | Type | Name | Default | Description | |:-----------------------|:-----------------|--------:|:----------------------------------------------------------------------------------------------------------------------------------------------------------------| @@ -1848,7 +1847,7 @@ world has been committed. To get this information, call OSPBounds ospGetBounds(OSPObject); ``` -The result is returned in the provided `OSPBounds`[6] struct: +The result is returned in the provided `OSPBounds`[7] struct: ``` cpp typedef struct { @@ -1957,7 +1956,7 @@ renderers, the SciVis renderer supports the following parameters: Special parameters understood by the SciVis renderer. Note that the intensity (and color) of AO is deduced from an [ambient -light](#ambient-light) in the `lights` array.[7] If `aoSamples` is zero +light](#ambient-light) in the `lights` array.[8] If `aoSamples` is zero (the default) then ambient lights cause ambient illumination (without occlusion). @@ -2074,7 +2073,7 @@ the opacity `d`. Normal mapping can simulate small geometric features via the texture `map_Bump`. The normals *n* in the normal map are with respect to the local tangential shading coordinate system and are encoded as -½(*n* + 1), thus a texel (0.5, 0.5, 1)[8] represents the unperturbed +½(*n* + 1), thus a texel (0.5, 0.5, 1)[9] represents the unperturbed shading normal (0, 0, 1). Because of this encoding an sRGB gamma [texture](#texture) format is ignored and normals are always fetched as linear from a normal map. Note that the orientation of normal maps is @@ -3225,7 +3224,7 @@ ospTutorial A minimal working example demonstrating how to use OSPRay can be found at -[`apps/tutorials/ospTutorial.c`](https://github.com/ospray/ospray/blob/master/apps/ospTutorial/ospTutorial.c)[9]. +[`apps/tutorials/ospTutorial.c`](https://github.com/ospray/ospray/blob/master/apps/ospTutorial/ospTutorial.c)[10]. An example of building `ospTutorial.c` with CMake can be found in [`apps/tutorials/ospTutorialFindospray/`](https://github.com/ospray/ospray/tree/master/apps/ospTutorial/ospTutorialFindospray). @@ -3384,7 +3383,7 @@ ospMPIDistribTutorial A minimal working example demonstrating how to use OSPRay for rendering distributed data can be found at -[`modules/mpi/tutorials/ospMPIDistribTutorial.c`](https://github.com/ospray/ospray/blob/master/modules/mpi/tutorials/ospMPIDistribTutorial.c)[10]. +[`modules/mpi/tutorials/ospMPIDistribTutorial.c`](https://github.com/ospray/ospray/blob/master/modules/mpi/tutorials/ospMPIDistribTutorial.c)[11]. The compilation process via CMake is the similar to [`apps/tutorials/ospTutorialFindospray/`](https://github.com/ospray/ospray/tree/master/apps/ospTutorial/ospTutorialFindospray), @@ -3508,22 +3507,25 @@ voxel changes the quickest. [5] actually a parallelogram -[6] `OSPBounds` has essentially the same layout as the `OSP_BOX3F` +[6] including spheres, boxes, infinite planes, closed meshes, closed +subdivisions and curves + +[7] `OSPBounds` has essentially the same layout as the `OSP_BOX3F` [`OSPDataType`](#data). -[7] If there are multiple ambient lights then their contribution is +[8] If there are multiple ambient lights then their contribution is added. -[8] respectively (127, 127, 255) for 8 bit textures and +[9] respectively (127, 127, 255) for 8 bit textures and (32767, 32767, 65535) for 16 bit textures -[9] A C++ version that uses the C++ convenience wrappers of OSPRay’s C99 -API via +[10] A C++ version that uses the C++ convenience wrappers of OSPRay’s +C99 API via [`include/ospray/ospray_cpp.h`](https://github.com/ospray/ospray/blob/master/ospray/include/ospray/ospray_cpp.h) is available at [`apps/tutorials/ospTutorial.cpp`](https://github.com/ospray/ospray/blob/master/apps/ospTutorial/ospTutorial.cpp). -[10] A C++ version that uses the C++ convenience wrappers of OSPRay’s +[11] A C++ version that uses the C++ convenience wrappers of OSPRay’s C99 API via [`include/ospray/ospray_cpp.h`](https://github.com/ospray/ospray/blob/master/ospray/include/ospray/ospray_cpp.h) is available at diff --git a/apps/common/CMakeLists.txt b/apps/common/CMakeLists.txt index 52eb78855..5094b5c26 100644 --- a/apps/common/CMakeLists.txt +++ b/apps/common/CMakeLists.txt @@ -1,7 +1,7 @@ -## Copyright 2009-2020 Intel Corporation +## Copyright 2009-2021 Intel Corporation ## SPDX-License-Identifier: Apache-2.0 -if (NOT OSPRAY_APPS_BENCHARK AND +if (NOT OSPRAY_APPS_BENCHMARK AND NOT OSPRAY_APPS_EXAMPLES AND NOT OSPRAY_APPS_TESTING) return() diff --git a/cmake/ospray_options.cmake b/cmake/ospray_options.cmake index 05b222057..2f90f5d5b 100644 --- a/cmake/ospray_options.cmake +++ b/cmake/ospray_options.cmake @@ -13,7 +13,7 @@ set(OSPRAY_CMAKECONFIG_DIR set(RKCOMMON_VERSION_REQUIRED 1.7.0) set(EMBREE_VERSION_REQUIRED 3.13.1) -set(OPENVKL_VERSION_REQUIRED 1.0.0) +set(OPENVKL_VERSION_REQUIRED 1.0.1) set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}) set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}) diff --git a/cmake/ospray_version.cmake b/cmake/ospray_version.cmake index 2aab270de..cb1bdddf9 100644 --- a/cmake/ospray_version.cmake +++ b/cmake/ospray_version.cmake @@ -3,7 +3,7 @@ set(OSPRAY_VERSION_MAJOR 2) set(OSPRAY_VERSION_MINOR 7) -set(OSPRAY_VERSION_PATCH 0) +set(OSPRAY_VERSION_PATCH 1) set(OSPRAY_SOVERSION 2) set(OSPRAY_VERSION_GITHASH 0) set(OSPRAY_VERSION_NOTE "") diff --git a/doc/api.md b/doc/api.md index 0c844c736..c402b47e1 100644 --- a/doc/api.md +++ b/doc/api.md @@ -1697,16 +1697,16 @@ group. By adding `OSPGeometricModel`s to the `clippingGeometry` array a clipping geometry feature is enabled. Geometries assigned to this parameter will be used as clipping geometries. Any supported geometry -can be used for clipping. The only requirement is that it has to -distinctly partition space into clipping and non-clipping one. These -include: spheres, boxes, infinite planes, closed meshes, closed -subdivisions and curves. All geometries and volumes assigned to -`geometry` or `volume` will be clipped. Use of clipping geometry that is -not closed (or infinite) will result in rendering artifacts. User can -decide which part of space is clipped by changing shading normals -orientation with the `invertNormals` flag of the [GeometricModel]. When -more than single clipping geometry is defined all clipping areas will be -"added" together – an union of these areas will be applied. +can be used for clipping^[including spheres, boxes, infinite planes, +closed meshes, closed subdivisions and curves], the only requirement is +that it has to distinctly partition space into clipping and non-clipping +one. The use of clipping geometry that is not closed or infinite could +result in rendering artifacts. User can decide which part of space is +clipped by changing shading normals orientation with the `invertNormals` +flag of the [GeometricModel]. All geometries and volumes assigned to +`geometry` or `volume` will be clipped. All clipping geometries from all +groups and [Instances] will be combined together – a union of these +areas will be applied to all other objects in the [world]. -------------------- ---------------- ---------- -------------------------------------- Type Name Default Description diff --git a/doc/compilation.md b/doc/compilation.md index 9944abdb4..468a71052 100644 --- a/doc/compilation.md +++ b/doc/compilation.md @@ -44,7 +44,7 @@ before you can build OSPRay you need the following prerequisites: or newer is required. If Embree is not found by CMake its location can be hinted with the variable `embree_DIR`. - OSPRay also heavily uses Intel [Open VKL](https://www.openvkl.org/), - installing version 1.0.0 or newer is required. If Open VKL is not + installing version 1.0.1 or newer is required. If Open VKL is not found by CMake its location can be hinted with the variable `openvkl_DIR`. - OSPRay also provides an optional module implementing the `denoiser` diff --git a/modules/mpi/CMakeLists.txt b/modules/mpi/CMakeLists.txt index 39b97856e..e077741b9 100644 --- a/modules/mpi/CMakeLists.txt +++ b/modules/mpi/CMakeLists.txt @@ -13,7 +13,7 @@ cmake_dependent_option( OSPRAY_MPI_BUILD_TUTORIALS "Enable MPI module sample apps" ON - "OSPRAY_MODULE_MPI;OSPRAY_ENABLE_APPS" + "OSPRAY_MODULE_MPI;OSPRAY_ENABLE_APPS;OSPRAY_APPS_EXAMPLES" OFF ) diff --git a/modules/mpi/ospray/common/DistributedWorld.cpp b/modules/mpi/ospray/common/DistributedWorld.cpp index e55ca6627..4dfb2a474 100644 --- a/modules/mpi/ospray/common/DistributedWorld.cpp +++ b/modules/mpi/ospray/common/DistributedWorld.cpp @@ -16,55 +16,6 @@ namespace mpi { using namespace rkcommon; -void RegionScreenBounds::extend(const vec3f &p) -{ - if (p.z < 0) { - bounds = box2f(vec2f(0), vec2f(1)); - } else { - bounds.extend(vec2f(p.x * sign(p.z), p.y * sign(p.z))); - bounds.lower.x = clamp(bounds.lower.x); - bounds.upper.x = clamp(bounds.upper.x); - bounds.lower.y = clamp(bounds.lower.y); - bounds.upper.y = clamp(bounds.upper.y); - } -} - -Region::Region(const box3f &bounds, int id) : bounds(bounds), id(id) {} - -RegionScreenBounds Region::project(const Camera *camera) const -{ - RegionScreenBounds screen; - for (int k = 0; k < 2; ++k) { - vec3f pt; - pt.z = k == 0 ? bounds.lower.z : bounds.upper.z; - - for (int j = 0; j < 2; ++j) { - pt.y = j == 0 ? bounds.lower.y : bounds.upper.y; - - for (int i = 0; i < 2; ++i) { - pt.x = i == 0 ? bounds.lower.x : bounds.upper.x; - - ProjectedPoint proj = camera->projectPoint(pt); - screen.extend(proj.screenPos); - screen.depth = std::max(length(pt - camera->pos), screen.depth); - } - } - } - return screen; -} - -bool Region::operator==(const Region &b) const -{ - // TODO: Do we want users to specify the ID explitly? Or should we just - // assume that two objects with the same bounds have the same id? - return id == b.id; -} - -bool Region::operator<(const Region &b) const -{ - return id < b.id; -} - DistributedWorld::DistributedWorld() : mpiGroup(mpicommon::worker.dup()) { managedObjectType = OSP_WORLD; @@ -79,8 +30,8 @@ DistributedWorld::~DistributedWorld() box3f DistributedWorld::getBounds() const { box3f bounds; - for (const auto &r : allRegions) { - bounds.extend(r.bounds); + for (const auto &b : allRegions) { + bounds.extend(b); } return bounds; } @@ -129,16 +80,41 @@ void DistributedWorld::commit() // to the others to build the distributed world std::sort( myRegions.begin(), myRegions.end(), [](const box3f &a, const box3f &b) { - std::less op; - return op(a.lower, b.lower) - || (a.lower == b.lower && op(a.upper, b.upper)); + return a.lower < b.lower || (a.lower == b.lower && a.upper < b.upper); }); auto last = std::unique(myRegions.begin(), myRegions.end()); myRegions.erase(last, myRegions.end()); exchangeRegions(); - ispc::DistributedWorld_set(getIE(), allRegions.data(), allRegions.size()); + if (regionScene) { + rtcReleaseScene(regionScene); + regionScene = nullptr; + } + + if (allRegions.size() > 0) { + // Setup the boxes geometry which we'll use to leverage Embree for + // accurately determining region visibility + Data* allRegionsData = new Data(allRegions.data(), + OSP_BOX3F, + vec3ul(allRegions.size(), 1, 1), + vec3l(0)); + regionGeometry = new Boxes(); + regionGeometry->setParam("box", (ManagedObject *)allRegionsData); + regionGeometry->setDevice(embreeDevice); + regionGeometry->commit(); + + regionScene = rtcNewScene(embreeDevice); + rtcAttachGeometry(regionScene, regionGeometry->embreeGeometry); + rtcSetSceneFlags(regionScene, RTC_SCENE_FLAG_CONTEXT_FILTER_FUNCTION); + rtcCommitScene(regionScene); + + regionGeometry->refDec(); + allRegionsData->refDec(); + } + + ispc::DistributedWorld_set( + getIE(), allRegions.data(), allRegions.size(), regionScene); } void DistributedWorld::exchangeRegions() @@ -160,11 +136,11 @@ void DistributedWorld::exchangeRegions() for (const auto &b : myRegions) { auto fnd = std::find_if(allRegions.begin(), allRegions.end(), - [&](const Region &r) { return r.bounds == b; }); + [&](const box3f &r) { return r == b; }); int id = -1; if (fnd == allRegions.end()) { id = allRegions.size(); - allRegions.push_back(Region(b, id)); + allRegions.push_back(b); } else { id = std::distance(allRegions.begin(), fnd); } @@ -189,11 +165,11 @@ void DistributedWorld::exchangeRegions() for (const auto &b : recv) { auto fnd = std::find_if(allRegions.begin(), allRegions.end(), - [&](const Region &r) { return r.bounds == b; }); + [&](const box3f &r) { return r == b; }); int id = -1; if (fnd == allRegions.end()) { id = allRegions.size(); - allRegions.push_back(Region(b, id)); + allRegions.push_back(b); } else { id = std::distance(allRegions.begin(), fnd); } @@ -230,10 +206,3 @@ void DistributedWorld::exchangeRegions() } // namespace mpi } // namespace ospray -using namespace ospray::mpi; - -std::ostream &operator<<(std::ostream &os, const Region &r) -{ - os << "Region { id = " << r.id << ", bounds = " << r.bounds << " }"; - return os; -} diff --git a/modules/mpi/ospray/common/DistributedWorld.h b/modules/mpi/ospray/common/DistributedWorld.h index 7a3ef2a69..c9a28ccb6 100644 --- a/modules/mpi/ospray/common/DistributedWorld.h +++ b/modules/mpi/ospray/common/DistributedWorld.h @@ -9,45 +9,12 @@ #include "common/MPICommon.h" #include "common/World.h" #include "embree3/rtcore.h" +#include "geometry/Boxes.h" #include "rkcommon/math/box.h" namespace ospray { namespace mpi { -// The bounds of a region projected onto the image plane -struct RegionScreenBounds -{ - rkcommon::math::box2f bounds; - // The max-depth of the box, for sorting the compositing order - float depth = -std::numeric_limits::infinity(); - - // Extend the screen-space bounds to include p.xy - // TODO WILL depth is treated separately by just getting the projection - // along the camera view axis. So either we need the camera here, or - // should just take the z val to only tell us if we should fill the whole - // window. - void extend(const rkcommon::math::vec3f &p); -}; - -/* A region is defined by its bounds and an ID, which allows us to group - * ranks with the same region and switch to do image-parallel rendering - */ -struct Region -{ - rkcommon::math::box3f bounds; - int id = -1; - - Region() = default; - Region(const rkcommon::math::box3f &bounds, int id); - - RegionScreenBounds project(const Camera *camera) const; - - // Compiler can't find these when I define them outside in the public - // namespace!???? - bool operator==(const Region &b) const; - bool operator<(const Region &b) const; -}; - /* The distributed world provides the renderer with the view of the local * and distributed objects in the entire scene across the cluster. For * remote objects we only store their bounds and the ID of the rank which @@ -83,12 +50,14 @@ struct DistributedWorld : public World std::vector myRegions; // The global list of unique regions across all nodes, (including this // one), sorted by region id. - std::vector allRegions; + std::vector allRegions; std::vector myRegionIds; // The ranks which own each region std::unordered_map> regionOwners; + + Ref regionGeometry; + RTCScene regionScene = nullptr; }; } // namespace mpi } // namespace ospray -std::ostream &operator<<(std::ostream &os, const ospray::mpi::Region &r); diff --git a/modules/mpi/ospray/common/DistributedWorld.ih b/modules/mpi/ospray/common/DistributedWorld.ih index 415ac4620..991e93475 100644 --- a/modules/mpi/ospray/common/DistributedWorld.ih +++ b/modules/mpi/ospray/common/DistributedWorld.ih @@ -1,20 +1,15 @@ -// Copyright 2009-2020 Intel Corporation +// Copyright 2009-2021 Intel Corporation // SPDX-License-Identifier: Apache-2.0 #pragma once #include "ospray/common/World.ih" -struct Region -{ - box3f bounds; - int id; -}; - struct DistributedWorld { World super; - uniform Region *uniform regions; - uniform int numRegions; + box3f *uniform regions; + int numRegions; + RTCScene regionScene; }; diff --git a/modules/mpi/ospray/common/DistributedWorld.ispc b/modules/mpi/ospray/common/DistributedWorld.ispc index b2935227e..c78eedf2c 100644 --- a/modules/mpi/ospray/common/DistributedWorld.ispc +++ b/modules/mpi/ospray/common/DistributedWorld.ispc @@ -1,4 +1,4 @@ -// Copyright 2009-2020 Intel Corporation +// Copyright 2009-2021 Intel Corporation // SPDX-License-Identifier: Apache-2.0 #include "DistributedWorld.ih" @@ -11,10 +11,13 @@ export void *uniform DistributedWorld_create(void *uniform cppE) return (void *uniform)world; } -export void DistributedWorld_set( - void *uniform _world, void *uniform _regions, uniform int numRegions) +export void DistributedWorld_set(void *uniform _world, + void *uniform _regions, + uniform int numRegions, + void *uniform _regionScene) { DistributedWorld *uniform world = (DistributedWorld * uniform) _world; - world->regions = (uniform Region * uniform) _regions; + world->regions = (box3f * uniform) _regions; world->numRegions = numRegions; + world->regionScene = (RTCScene)_regionScene; } diff --git a/modules/mpi/ospray/render/DistributedLoadBalancer.cpp b/modules/mpi/ospray/render/DistributedLoadBalancer.cpp index 0b9abeaa0..0d040ce95 100644 --- a/modules/mpi/ospray/render/DistributedLoadBalancer.cpp +++ b/modules/mpi/ospray/render/DistributedLoadBalancer.cpp @@ -7,7 +7,7 @@ #include #include "../fb/DistributedFrameBuffer.h" #include "WriteMultipleTileOperation.h" -#include "camera/PerspectiveCamera.h" +#include "camera/Camera.h" #include "common/MPICommon.h" #include "common/Profiling.h" #include "distributed/DistributedRenderer.h" @@ -42,85 +42,58 @@ void Distributed::renderFrame( } } - if (!reinterpret_cast(camera)) { - throw std::runtime_error( - "DistributedRaycastRender only supports PerspectiveCamera"); - } - if (dfb->getLastRenderer() != renderer) { dfb->setTileOperation(renderer->tileOperation(), renderer); } dfb->startNewFrame(renderer->errorThreshold); - void *perFrameData = renderer->beginFrame(dfb, world); - - const auto fbSize = dfb->getNumPixels(); const size_t numRegions = world->allRegions.size(); - // Do a prepass and project each region's box to the screen to see - // which tiles it touches, and at what depth. - std::multimap regionOrdering; - std::vector projectedRegions( - numRegions, RegionScreenBounds()); - for (size_t i = 0; i < projectedRegions.size(); ++i) { - const auto &r = world->allRegions[i]; - auto &proj = projectedRegions[i]; - if (!r.bounds.empty()) { - proj = r.project(camera); - // Note that these bounds are very conservative, the bounds we - // compute below are much tighter, and better. We just use the depth - // from the projection to sort the tiles later - proj.bounds.lower = - max(proj.bounds.lower * fbSize - TILE_SIZE, vec2f(0.f)); - proj.bounds.upper = - min(proj.bounds.upper * fbSize + TILE_SIZE, vec2f(fbSize - 1.f)); - regionOrdering.insert(std::make_pair(proj.depth, i)); - } else { - proj = RegionScreenBounds(); - proj.depth = std::numeric_limits::infinity(); - regionOrdering.insert( - std::make_pair(std::numeric_limits::infinity(), i)); +#ifdef ENABLE_PROFILING + ProfilingPoint start = ProfilingPoint(); +#endif + std::set tilesForFrame; + for (int i = workerRank(); i < dfb->getTotalTiles(); i += workerSize()) { + const uint32_t tile_y = i / dfb->getNumTiles().x; + const uint32_t tile_x = i - tile_y * dfb->getNumTiles().x; + const vec2i tileID(tile_x, tile_y); + // Skip tiles that have been rendered to satisfactory error level + if (dfb->tileError(tileID) > renderer->errorThreshold) { + tilesForFrame.insert(i); } } - // Compute the sort order for the regions - std::vector sortOrder(numRegions, 0); - int depthIndex = 0; - for (const auto &e : regionOrdering) { - sortOrder[e.second] = depthIndex++; - } + const vec2i numTiles = dfb->getNumTiles(); + const vec2i fbSize = dfb->getNumPixels(); + for (const auto &id : world->myRegionIds) { + const auto &r = world->allRegions[id]; + box3f proj = camera->projectBox(r); + box2f screenRegion(vec2f(proj.lower) * fbSize, vec2f(proj.upper) * fbSize); + + // Pad the region a bit + screenRegion.lower = max(screenRegion.lower - TILE_SIZE, vec2f(0.f)); + screenRegion.upper = min(screenRegion.upper + TILE_SIZE, vec2f(fbSize)); - // Pre-compute the list of tiles that we actually need to render to, - // so we can get a better thread assignment when doing the real work - // TODO Will: is this going to help much? Going through all the rays - // to do this pre-pass may be too expensive, and instead we want - // to just do something coarser based on the projected regions - std::vector tilesForFrame; - for (const auto ®ion : world->allRegions) { - const auto &projection = projectedRegions[region.id]; - if (projection.bounds.empty() || projection.depth < 0) { + // Skip regions that are completely behind the camera + if (proj.upper.z < 0.f) { continue; } - // TODO: Should we push back by a few pixels as well just in case - // for the random sampling? May need to spill +/- a pixel? I'm not - // sure - const vec2i minTile(projection.bounds.lower.x / TILE_SIZE, - projection.bounds.lower.y / TILE_SIZE); - const vec2i numTiles = dfb->getNumTiles(); - const vec2i maxTile( - std::min(std::ceil(projection.bounds.upper.x / TILE_SIZE), - float(numTiles.x)), - std::min(std::ceil(projection.bounds.upper.y / TILE_SIZE), - float(numTiles.y))); - - tilesForFrame.reserve((maxTile.x - minTile.x) * (maxTile.y - minTile.y)); - for (int y = minTile.y; y < maxTile.y; ++y) { - for (int x = minTile.x; x < maxTile.x; ++x) { - // If we share ownership of this region but aren't responsible - // for rendering it to this tile, don't render it. - const size_t tileIndex = size_t(x) + size_t(y) * numTiles.x; - const auto &owners = world->regionOwners[region.id]; + + box2i tileRegion; + tileRegion.lower = screenRegion.lower / TILE_SIZE; + tileRegion.upper = vec2i(std::ceil(screenRegion.upper.x / TILE_SIZE), + std::ceil(screenRegion.upper.y / TILE_SIZE)); + tileRegion.upper = min(tileRegion.upper, numTiles); + for (int y = tileRegion.lower.y; y < tileRegion.upper.y; ++y) { + for (int x = tileRegion.lower.x; x < tileRegion.upper.x; ++x) { + // Skip tiles that have been rendered to satisfactory error level + if (dfb->tileError(vec2i(x, y)) <= renderer->errorThreshold) { + continue; + } + + const int tileIndex = x + y * numTiles.x; + const auto &owners = world->regionOwners[id]; const size_t numRegionOwners = owners.size(); const size_t ownerRank = std::distance(owners.begin(), owners.find(workerRank())); @@ -129,43 +102,24 @@ void Distributed::renderFrame( // the workload. const bool regionTileOwner = (tileIndex % numRegionOwners) == ownerRank; if (regionTileOwner) { - tilesForFrame.push_back(tileIndex); + tilesForFrame.insert(tileIndex); } } } } +#ifdef ENABLE_PROFILING + ProfilingPoint end = ProfilingPoint(); + std::cout << "Initial tile for frame determination " + << elapsedTimeMs(start, end) << "ms\n"; +#endif - // Be sure to include all the tiles that we're the owner for as well, - // in some cases none of our data might project to them. - for (int i = workerRank(); i < dfb->getTotalTiles(); i += workerSize()) { - tilesForFrame.push_back(i); - } - - std::sort(tilesForFrame.begin(), tilesForFrame.end()); - { - // Filter out any duplicate tiles, e.g. we double-count the background - // tile we have to render as the owner and our data projects to the - // same tile. - auto end = std::unique(tilesForFrame.begin(), tilesForFrame.end()); - tilesForFrame.erase(end, tilesForFrame.end()); - - // Filter any tiles which are finished due to reaching the - // error threshold. This should let TBB better allocate threads only - // to tiles that actually need work. - end = std::partition( - tilesForFrame.begin(), tilesForFrame.end(), [&](const int &i) { - const uint32_t tile_y = i / dfb->getNumTiles().x; - const uint32_t tile_x = i - tile_y * dfb->getNumTiles().x; - const vec2i tileID(tile_x, tile_y); - return dfb->tileError(tileID) > renderer->errorThreshold; - }); - tilesForFrame.erase(end, tilesForFrame.end()); - } - - // Render the tiles we've got to do now - tasking::parallel_for(tilesForFrame.size(), [&](size_t taskIndex) { - const int tileIndex = tilesForFrame[taskIndex]; - +#ifdef ENABLE_PROFILING + start = ProfilingPoint(); +#endif + tasking::parallel_for(tilesForFrame.size(), [&](size_t taskId) { + auto tileIter = tilesForFrame.begin(); + std::advance(tileIter, taskId); + const int tileIndex = *tileIter; const uint32_t numTiles_x = static_cast(dfb->getNumTiles().x); const uint32_t tile_y = tileIndex / numTiles_x; const uint32_t tile_x = tileIndex - tile_y * numTiles_x; @@ -217,6 +171,10 @@ void Distributed::renderFrame( myVisibleRegions.push_back(rid); } } + // If none of our regions are visible, we're done + if (myVisibleRegions.empty()) { + return; + } // TODO: Will it really be much benefit to run the regions in parallel // as well? We already are running in parallel on the tiles and the @@ -225,10 +183,10 @@ void Distributed::renderFrame( #define PARALLEL_REGION_RENDERING 1 #if PARALLEL_REGION_RENDERING tasking::parallel_for(myVisibleRegions.size(), [&](size_t vid) { - const size_t i = myVisibleRegions[vid]; + const size_t rid = myVisibleRegions[vid]; Tile __aligned(64) tile(tileID, fbSize, accumID); #else - for (const size_t &i : myVisibleRegions) { + for (const size_t &rid : myVisibleRegions) { Tile &tile = bgtile; #endif tile.generation = 1; @@ -238,8 +196,7 @@ void Distributed::renderFrame( // Note that we do need to double check here, since we could have // multiple shared regions projecting to the same tile, and we // could be the region tile owner for only some of those - const auto ®ion = world->allRegions[i]; - const auto &owners = world->regionOwners[region.id]; + const auto &owners = world->regionOwners[rid]; const size_t numRegionOwners = owners.size(); const size_t ownerRank = std::distance(owners.begin(), owners.find(workerRank())); @@ -249,12 +206,13 @@ void Distributed::renderFrame( renderer->renderRegionToTile(dfb, camera, world, - world->allRegions[i], + world->allRegions[rid], perFrameData, tile, tIdx); }); - tile.sortOrder = sortOrder[region.id]; + // Unused + // tile.sortOrder = sortOrder[rid]; dfb->setTile(tile); } #if PARALLEL_REGION_RENDERING @@ -263,6 +221,11 @@ void Distributed::renderFrame( } #endif }); +#ifdef ENABLE_PROFILING + end = ProfilingPoint(); + std::cout << "Local rendering for frame " << elapsedTimeMs(start, end) + << "ms\n"; +#endif dfb->waitUntilFinished(); renderer->endFrame(dfb, perFrameData); diff --git a/modules/mpi/ospray/render/distributed/AlphaCompositeTileOperation.cpp b/modules/mpi/ospray/render/distributed/AlphaCompositeTileOperation.cpp index 641ce13c7..de3571270 100644 --- a/modules/mpi/ospray/render/distributed/AlphaCompositeTileOperation.cpp +++ b/modules/mpi/ospray/render/distributed/AlphaCompositeTileOperation.cpp @@ -105,12 +105,16 @@ void LiveAlphaCompositeTile::process(const ospray::Tile &tile) if (missingInCurrentGeneration == 0) { // Sort for back-to-front blending - std::sort(bufferedTiles.begin(), - bufferedTiles.end(), - [](const std::unique_ptr &a, - const std::unique_ptr &b) { - return a->tile.sortOrder > b->tile.sortOrder; - }); + // TODO: This is redundant with sortAndBlend. We should actually just do + // this tile level sort, not sorting each pixel + /* + std::sort(bufferedTiles.begin(), + bufferedTiles.end(), + [](const std::unique_ptr &a, + const std::unique_ptr &b) { + return a->tile.sortOrder > b->tile.sortOrder; + }); + */ Tile **tileArray = STACK_BUFFER(Tile *, bufferedTiles.size()); std::transform(bufferedTiles.begin(), diff --git a/modules/mpi/ospray/render/distributed/DistributedRaycast.ispc b/modules/mpi/ospray/render/distributed/DistributedRaycast.ispc index 27d33f8e9..a2582100a 100644 --- a/modules/mpi/ospray/render/distributed/DistributedRaycast.ispc +++ b/modules/mpi/ospray/render/distributed/DistributedRaycast.ispc @@ -23,18 +23,6 @@ struct DistributedRaycastRenderer float volumeSamplingRate; }; -struct RegionInfo -{ - int numRegions; - int currentRegion; - bool computeVisibility; - // TODO: If the IDs are not "array indices" this gets a bit goofed up - // We should probably just remap the IDs when we move to generating them for - // the user - Region *uniform regions; - bool *uniform regionVisible; -}; - // The distributed raycast renderer uses its own volume interval integration // because we want to apply the jitter before offsetting our step size to stay // inside the region, not after. @@ -233,7 +221,7 @@ vec4f DRR_shadeSurface(const DistributedRaycastRenderer *uniform self, void DRR_renderRegionSample(DistributedRenderer *uniform _self, FrameBuffer *uniform fb, DistributedWorld *uniform world, - const Region *uniform region, + const box3f *uniform region, const vec2f ®ionInterval, void *uniform perFrameData, varying ScreenSample &sample) diff --git a/modules/mpi/ospray/render/distributed/DistributedRenderer.cpp b/modules/mpi/ospray/render/distributed/DistributedRenderer.cpp index 633255a1d..b6adad610 100644 --- a/modules/mpi/ospray/render/distributed/DistributedRenderer.cpp +++ b/modules/mpi/ospray/render/distributed/DistributedRenderer.cpp @@ -37,7 +37,7 @@ void DistributedRenderer::computeRegionVisibility(DistributedFrameBuffer *fb, void DistributedRenderer::renderRegionToTile(DistributedFrameBuffer *fb, Camera *camera, DistributedWorld *world, - const Region ®ion, + const box3f ®ion, void *perFrameData, Tile &tile, size_t jobID) const diff --git a/modules/mpi/ospray/render/distributed/DistributedRenderer.h b/modules/mpi/ospray/render/distributed/DistributedRenderer.h index 922946a9d..382589dff 100644 --- a/modules/mpi/ospray/render/distributed/DistributedRenderer.h +++ b/modules/mpi/ospray/render/distributed/DistributedRenderer.h @@ -15,7 +15,7 @@ namespace mpi { struct RegionInfo { int numRegions = 0; - Region *regions = nullptr; + box3f *regions = nullptr; bool *regionVisible = nullptr; }; @@ -35,7 +35,7 @@ struct DistributedRenderer : public Renderer void renderRegionToTile(DistributedFrameBuffer *fb, Camera *camera, DistributedWorld *world, - const Region ®ion, + const box3f ®ion, void *perFrameData, Tile &tile, size_t jobID) const; diff --git a/modules/mpi/ospray/render/distributed/DistributedRenderer.ih b/modules/mpi/ospray/render/distributed/DistributedRenderer.ih index ff0f83f74..c61a91fcc 100644 --- a/modules/mpi/ospray/render/distributed/DistributedRenderer.ih +++ b/modules/mpi/ospray/render/distributed/DistributedRenderer.ih @@ -1,4 +1,4 @@ -// Copyright 2009-2020 Intel Corporation +// Copyright 2009-2021 Intel Corporation // SPDX-License-Identifier: Apache-2.0 #pragma once @@ -24,7 +24,7 @@ typedef unmasked void (*DR_ComputeRegionVisibility)( typedef void (*DR_RenderRegionSampleFct)(DistributedRenderer *uniform self, FrameBuffer *uniform fb, DistributedWorld *uniform world, - const Region *uniform region, + const box3f *uniform region, const vec2f ®ionInterval, void *uniform perFrameData, varying ScreenSample &sample); @@ -34,7 +34,7 @@ typedef unmasked void (*DR_RenderRegionTileFct)( FrameBuffer *uniform fb, Camera *uniform camera, DistributedWorld *uniform world, - const Region *uniform region, + const box3f *uniform region, void *uniform perFrameData, uniform Tile &tile, uniform int taskIndex); diff --git a/modules/mpi/ospray/render/distributed/DistributedRenderer.ispc b/modules/mpi/ospray/render/distributed/DistributedRenderer.ispc index 95fae9864..afd166e35 100644 --- a/modules/mpi/ospray/render/distributed/DistributedRenderer.ispc +++ b/modules/mpi/ospray/render/distributed/DistributedRenderer.ispc @@ -4,10 +4,30 @@ #include "DistributedRenderer.ih" #include "common/DistributedWorld.ih" #include "common/Intersect.ih" +#include "common/Ray.ih" #include "common/World.ih" #include "math/random.ih" #include "render/util.ih" +unmasked void markRegionVisibleFilterFunction( + const RTCFilterFunctionNArguments *uniform args) +{ + if (!args->valid[programIndex]) { + return; + } + + EmbreeIntersectionContext *uniform ctx = + (EmbreeIntersectionContext * uniform) args->context; + + bool *uniform regionVisible = (bool *uniform)ctx->userPtr; + varying RTCHit *uniform hit = (varying RTCHit * uniform) args->hit; + regionVisible[hit->primID] = true; + + // Reject all hits, we want to find all intersections along the ray to find + // all visible regions + args->valid[programIndex] = 0; +} + unmasked void DR_default_computeRegionVisibility( DistributedRenderer *uniform self, FrameBuffer *uniform fb, @@ -27,14 +47,16 @@ unmasked void DR_default_computeRegionVisibility( CameraSample cameraSample; + const z_order_t *uniform zOrder = get_zorder(); + const uniform int begin = taskIndex * RENDERTILE_PIXELS_PER_JOB; const uniform int end = begin + RENDERTILE_PIXELS_PER_JOB; const uniform int startSampleID = max(tile.accumID, 0) * spp; for (uniform uint32 i = begin; i < end; i += programCount) { const uint32 index = i + programIndex; - screenSample.sampleID.x = tile.region.lower.x + z_order.xs[index]; - screenSample.sampleID.y = tile.region.lower.y + z_order.ys[index]; + screenSample.sampleID.x = tile.region.lower.x + zOrder->xs[index]; + screenSample.sampleID.y = tile.region.lower.y + zOrder->ys[index]; if ((screenSample.sampleID.x >= fb->size.x) || (screenSample.sampleID.y >= fb->size.y)) @@ -54,7 +76,7 @@ unmasked void DR_default_computeRegionVisibility( tMax = min(get1f(self->super.maxDepthTexture, lookup), inf); } - const uint32 pixel = z_order.xs[index] + (z_order.ys[index] * TILE_SIZE); + const uint32 pixel = zOrder->xs[index] + (zOrder->ys[index] * TILE_SIZE); // TODO: Does spp still show some block boundary artifacts? Check // and maybe just support spp = 1 in DR @@ -69,24 +91,22 @@ unmasked void DR_default_computeRegionVisibility( cameraSample.screen.y = (screenSample.sampleID.y + pixel_dv) * fb->rcpSize.y; - // TODO: fix correlations / better RNG - cameraSample.lens.x = pixel_du; - cameraSample.lens.y = pixel_dv; + // no DoF or MB per default + cameraSample.lens.x = 0.0f; + cameraSample.lens.y = 0.0f; + cameraSample.time = 0.5f; camera->initRay(camera, screenSample.ray, cameraSample); screenSample.ray.t = min(screenSample.ray.t, tMax); - for (uniform int r = 0; r < world->numRegions; ++r) { - if (!regionVisible[r] && !isEmpty(world->regions[r].bounds)) { - Intersections isect = intersectBox(screenSample.ray.org, - screenSample.ray.dir, - world->regions[r].bounds); - if (isect.entry.t < isect.exit.t - && isect.exit.t >= screenSample.ray.t0 - && isect.entry.t <= screenSample.ray.t) { - regionVisible[r] = true; - } - } + if (world->regionScene) { + uniform EmbreeIntersectionContext context; + rtcInitIntersectContext(&context.ectx); + context.ectx.filter = markRegionVisibleFilterFunction; + context.userPtr = regionVisible; + rtcIntersectV(world->regionScene, + &context.ectx, + (varying RTCRayHit * uniform) & screenSample.ray); } } @@ -102,7 +122,7 @@ unmasked void DR_default_computeRegionVisibility( void DR_default_renderRegionSample(DistributedRenderer *uniform self, FrameBuffer *uniform fb, DistributedWorld *uniform world, - const Region *uniform region, + const box3f *uniform region, const vec2f ®ionInterval, void *uniform perFrameData, varying ScreenSample &sample) @@ -117,7 +137,7 @@ unmasked void DR_default_renderRegionToTile(DistributedRenderer *uniform self, FrameBuffer *uniform fb, Camera *uniform camera, DistributedWorld *uniform world, - const Region *uniform region, + const box3f *uniform region, void *uniform perFrameData, uniform Tile &tile, uniform int taskIndex) @@ -130,14 +150,16 @@ unmasked void DR_default_renderRegionToTile(DistributedRenderer *uniform self, CameraSample cameraSample; + const z_order_t *uniform zOrder = get_zorder(); + const uniform int begin = taskIndex * RENDERTILE_PIXELS_PER_JOB; const uniform int end = begin + RENDERTILE_PIXELS_PER_JOB; const uniform int startSampleID = max(tile.accumID, 0) * spp; for (uniform uint32 i = begin; i < end; i += programCount) { const uint32 index = i + programIndex; - screenSample.sampleID.x = tile.region.lower.x + z_order.xs[index]; - screenSample.sampleID.y = tile.region.lower.y + z_order.ys[index]; + screenSample.sampleID.x = tile.region.lower.x + zOrder->xs[index]; + screenSample.sampleID.y = tile.region.lower.y + zOrder->ys[index]; if ((screenSample.sampleID.x >= fb->size.x) || (screenSample.sampleID.y >= fb->size.y)) @@ -161,7 +183,7 @@ unmasked void DR_default_renderRegionToTile(DistributedRenderer *uniform self, vec3f normal = make_vec3f(0.f); vec3f albedo = make_vec3f(0.f); - const uint32 pixel = z_order.xs[index] + (z_order.ys[index] * TILE_SIZE); + const uint32 pixel = zOrder->xs[index] + (zOrder->ys[index] * TILE_SIZE); // TODO: same note on spp > 1 issues for (uniform uint32 s = 0; s < spp; s++) { @@ -175,17 +197,18 @@ unmasked void DR_default_renderRegionToTile(DistributedRenderer *uniform self, cameraSample.screen.y = (screenSample.sampleID.y + pixel_dv) * fb->rcpSize.y; - // TODO: fix correlations / better RNG - cameraSample.lens.x = pixel_du; - cameraSample.lens.y = pixel_dv; + // no DoF or MB per default + cameraSample.lens.x = 0.0f; + cameraSample.lens.y = 0.0f; + cameraSample.time = 0.5f; camera->initRay(camera, screenSample.ray, cameraSample); screenSample.ray.t = min(screenSample.ray.t, tMax); // TODO: We could store and use the region t intervals from when // we did the visibility test? - Intersections isect = intersectBox( - screenSample.ray.org, screenSample.ray.dir, region->bounds); + Intersections isect = + intersectBox(screenSample.ray.org, screenSample.ray.dir, *region); if (isect.entry.t < isect.exit.t && isect.exit.t >= screenSample.ray.t0 && isect.entry.t <= screenSample.ray.t) { @@ -262,7 +285,7 @@ export void DistributedRenderer_renderRegionToTile(void *uniform _self, (FrameBuffer * uniform) fb, (Camera * uniform) camera, (DistributedWorld * uniform) world, - (const Region *uniform)region, + (const box3f *uniform)region, perFrameData, tile, taskIndex); @@ -307,7 +330,7 @@ export void DistributedRenderer_pick(const void *uniform _self, regionRay.t = min(ray.t, closestHit); const Intersections isect = - intersectBox(regionRay.org, regionRay.dir, world->regions[i].bounds); + intersectBox(regionRay.org, regionRay.dir, world->regions[i]); cif (isect.entry.t < isect.exit.t && isect.exit.t >= regionRay.t0 && isect.entry.t <= regionRay.t) { diff --git a/modules/mpi/tutorials/CMakeLists.txt b/modules/mpi/tutorials/CMakeLists.txt index 67b6dc26b..4fc143218 100644 --- a/modules/mpi/tutorials/CMakeLists.txt +++ b/modules/mpi/tutorials/CMakeLists.txt @@ -83,7 +83,6 @@ target_link_libraries(mpi_tutorial_common PUBLIC MPI::MPI_CXX ) - target_compile_definitions(mpi_tutorial_common PUBLIC -DOSPRAY_CPP_RKCOMMON_TYPES) ospray_create_mpi_tutorial(ospMPIDistribTutorialSpheres @@ -113,3 +112,12 @@ target_link_libraries(ospMPIDistribTutorialReplicated PUBLIC ospray_testing ) +ospray_create_mpi_tutorial(ospMPIDistributedExamples + ospMPIDistributedExamples.cpp +) +ospray_sign_target(ospMPIDistributedExamples) + +target_link_libraries(ospMPIDistributedExamples PUBLIC + ospray_testing +) + diff --git a/modules/mpi/tutorials/ospMPIDistribTutorialSpheres.cpp b/modules/mpi/tutorials/ospMPIDistribTutorialSpheres.cpp index e4a8a5ec6..fd986b71a 100644 --- a/modules/mpi/tutorials/ospMPIDistribTutorialSpheres.cpp +++ b/modules/mpi/tutorials/ospMPIDistribTutorialSpheres.cpp @@ -214,7 +214,7 @@ cpp::Instance makeLocalSpheres( sphereGeom.commit(); vec3f color(0.f, 0.f, (mpiRank + 1.f) / mpiWorldSize); - cpp::Material material(nullptr, "obj"); + cpp::Material material("", "obj"); material.setParam("kd", color); material.commit(); diff --git a/modules/mpi/tutorials/ospMPIDistributedExamples.cpp b/modules/mpi/tutorials/ospMPIDistributedExamples.cpp new file mode 100644 index 000000000..01aa8fedf --- /dev/null +++ b/modules/mpi/tutorials/ospMPIDistributedExamples.cpp @@ -0,0 +1,176 @@ +// Copyright 2021 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 + +/* This example generates the same data on each rank so that we can build + * off the existing OSPRay testing data generators, however each rank + * specifies a subregion as its local domain to render the data as if it was + * distributed. The scene to load can be passed as an argument to the + * application + */ + +#include +#include +#include +#include +#include +#include "GLFWDistribOSPRayWindow.h" +#include "ospray/ospray_cpp.h" +#include "ospray/ospray_cpp/ext/rkcommon.h" +#include "ospray/ospray_util.h" +#include "ospray_testing.h" + +using namespace ospray; +using namespace rkcommon; +using namespace rkcommon::math; + +bool computeDivisor(int x, int &divisor); + +// Compute an X x Y x Z grid to have 'num' grid cells, +// only gives a nice grid for numbers with even factors since +// we don't search for factors of the number, we just try dividing by two +vec3i computeGrid(int num); + +int main(int argc, char **argv) +{ + int mpiThreadCapability = 0; + MPI_Init_thread(&argc, &argv, MPI_THREAD_MULTIPLE, &mpiThreadCapability); + if (mpiThreadCapability != MPI_THREAD_MULTIPLE + && mpiThreadCapability != MPI_THREAD_SERIALIZED) { + fprintf(stderr, + "OSPRay requires the MPI runtime to support thread " + "multiple or thread serialized.\n"); + return 1; + } + + std::string sceneName = "gravity_spheres_volume"; + if (argc == 2) { + sceneName = argv[1]; + } + + int mpiRank = 0; + int mpiWorldSize = 0; + MPI_Comm_rank(MPI_COMM_WORLD, &mpiRank); + MPI_Comm_size(MPI_COMM_WORLD, &mpiWorldSize); + + std::cout << "OSPRay rank " << mpiRank << "/" << mpiWorldSize << "\n"; + + // load the MPI module, and select the MPI distributed device. Here we + // do not call ospInit, as we want to explicitly pick the distributed + // device. This can also be done by passing --osp:mpi-distributed when + // using ospInit, however if the user doesn't pass this argument your + // application will likely not behave as expected + ospLoadModule("mpi"); + + { + cpp::Device mpiDevice("mpiDistributed"); + mpiDevice.commit(); + mpiDevice.setCurrent(); + + // set an error callback to catch any OSPRay errors and exit the application + mpiDevice.setErrorCallback( + [](void *, OSPError error, const char *errorDetails) { + std::cerr << "OSPRay error: " << errorDetails << std::endl; + exit(error); + }); + + // Regions are assigned to ranks in 3D grid, determine which brick of the + // grid is owned by this rank + const vec3i distribGrid = computeGrid(mpiWorldSize); + const vec3i distribBrickId(mpiRank % distribGrid.x, + (mpiRank / distribGrid.x) % distribGrid.y, + mpiRank / (distribGrid.x * distribGrid.y)); + + std::cout << "scene = " << sceneName << "\n"; + auto builder = testing::newBuilder(sceneName); + testing::setParam(builder, "rendererType", "scivis"); + testing::commit(builder); + + auto group = testing::buildGroup(builder); + const box3f worldBounds = group.getBounds(); + + cpp::Instance instance(group); + instance.commit(); + + cpp::World world; + world.setParam("instance", cpp::CopiedData(instance)); + + cpp::Light light("ambient"); + light.setParam("visible", false); + light.commit(); + + world.setParam("light", cpp::CopiedData(light)); + + // Determine the bounds of this rank's region in world space + const vec3f distribBrickDims = worldBounds.size() / vec3f(distribGrid); + box3f localRegion(distribBrickId * distribBrickDims + worldBounds.lower, + distribBrickId * distribBrickDims + distribBrickDims + + worldBounds.lower); + + // Special case for the ospray test data: we might have geometry right at + // the region bounding box which will z-fight with the clipping region. If + // we have a region at the edge of the domain, apply some padding + for (int i = 0; i < 3; ++i) { + if (localRegion.lower[i] == worldBounds.lower[i]) { + localRegion.lower[i] -= 0.001; + } + if (localRegion.upper[i] == worldBounds.upper[i]) { + localRegion.upper[i] += 0.001; + } + } + + // Set our region that represents the bounds of the local data we own on + // this rank + world.setParam("region", cpp::CopiedData(localRegion)); + world.commit(); + testing::release(builder); + + // create OSPRay renderer + cpp::Renderer renderer("mpiRaycast"); + + // create a GLFW OSPRay window: this object will create and manage the + // OSPRay frame buffer and camera directly + auto glfwOSPRayWindow = + std::unique_ptr(new GLFWDistribOSPRayWindow( + vec2i{1024, 768}, worldBounds, world, renderer)); + + // start the GLFW main loop, which will continuously render + glfwOSPRayWindow->mainLoop(); + } + // cleanly shut OSPRay down + ospShutdown(); + + MPI_Finalize(); + + return 0; +} + +bool computeDivisor(int x, int &divisor) +{ + int upperBound = std::sqrt(x); + for (int i = 2; i <= upperBound; ++i) { + if (x % i == 0) { + divisor = i; + return true; + } + } + return false; +} + +// Compute an X x Y x Z grid to have 'num' grid cells, +// only gives a nice grid for numbers with even factors since +// we don't search for factors of the number, we just try dividing by two +vec3i computeGrid(int num) +{ + vec3i grid(1); + int axis = 0; + int divisor = 0; + while (computeDivisor(num, divisor)) { + grid[axis] *= divisor; + num /= divisor; + axis = (axis + 1) % 3; + } + if (num != 1) { + grid[axis] *= num; + } + return grid; +} diff --git a/ospray/api/API.cpp b/ospray/api/API.cpp index 88c949e9a..8112d323c 100644 --- a/ospray/api/API.cpp +++ b/ospray/api/API.cpp @@ -31,11 +31,12 @@ using ospray::api::deviceIsSet; #define THROW_IF_NULL_OBJECT(obj) THROW_IF_NULL(obj, "handle") #define THROW_IF_NULL_STRING(str) THROW_IF_NULL(str, "string") -#define ASSERT_DEVICE() \ - if (!deviceIsSet()) \ - throw std::runtime_error( \ - "OSPRay not yet initialized (most likely this means you tried to " \ - "call an ospray API function before first calling ospInit()), pid: " \ +#define ASSERT_DEVICE() \ + if (!deviceIsSet()) \ + throw std::runtime_error( \ + "no valid OSPRay device while calling an API function, likely" \ + " either before calling ospInit() / ospNewDevice() plus ospSetCurrentDevice()," \ + " or after calling ospShutdown() / ospDeviceRelease(); pid: " \ + std::to_string(getpid())) #define OSPRAY_CATCH_BEGIN \ diff --git a/ospray/camera/Camera.cpp b/ospray/camera/Camera.cpp index 9989fd11f..e20d034e2 100644 --- a/ospray/camera/Camera.cpp +++ b/ospray/camera/Camera.cpp @@ -10,10 +10,6 @@ namespace ospray { static FactoryMap g_cameraMap; -ProjectedPoint::ProjectedPoint(const vec3f &pos, float radius) - : screenPos(pos), radius(radius) -{} - Camera::Camera() { managedObjectType = OSP_CAMERA; @@ -40,6 +36,11 @@ std::string Camera::toString() const return "ospray::Camera"; } +box3f Camera::projectBox(const box3f &) const +{ + return box3f(vec3f(0.f), vec3f(1.f)); +} + void Camera::commit() { MotionTransform::commit(); @@ -85,8 +86,8 @@ void Camera::commit() if (!motionBlur) { // apply transform right away pos = xfmPoint(single_xfm, pos); - dir = xfmVector(single_xfm, dir); - up = xfmVector(single_xfm, up); + dir = normalize(xfmVector(single_xfm, dir)); + up = normalize(xfmVector(single_xfm, up)); } ispc::Camera_set(getIE(), @@ -98,11 +99,6 @@ void Camera::commit() embreeGeometry); } -ProjectedPoint Camera::projectPoint(const vec3f &) const -{ - NOT_IMPLEMENTED; -} - void Camera::setDevice(RTCDevice device) { embreeDevice = device; diff --git a/ospray/camera/Camera.h b/ospray/camera/Camera.h index 5029cd015..c69ff28b1 100644 --- a/ospray/camera/Camera.h +++ b/ospray/camera/Camera.h @@ -8,16 +8,6 @@ namespace ospray { -struct OSPRAY_SDK_INTERFACE ProjectedPoint -{ - //! The screen space position and depth - vec3f screenPos; - //! The radius of the projected disk, in the case of depth of field - float radius; - - ProjectedPoint(const vec3f &pos, float radius); -}; - //! base camera class abstraction /*! the base class itself does not do anything useful; look into perspectivecamera etc for that */ @@ -29,9 +19,12 @@ struct OSPRAY_SDK_INTERFACE Camera : public MotionTransform std::string toString() const override; void commit() override; - // Project the world space point to the screen, and return the screen-space - // point (in normalized screen-space coordinates) along with the depth - virtual ProjectedPoint projectPoint(const vec3f &p) const; + // Project the bounding box to the screen + // The projected box will be returned in normalized [0, 1] coordinates in + // the framebuffer, the z coordinate will store the min and max depth, in + // box.lower, box.upper respectively + // Assume no motion blur nor depth of field (true for SciVis) + virtual box3f projectBox(const box3f &b) const; static Camera *createInstance(const char *identifier); template diff --git a/ospray/camera/OrthographicCamera.cpp b/ospray/camera/OrthographicCamera.cpp index 97894312b..bb81ac9a7 100644 --- a/ospray/camera/OrthographicCamera.cpp +++ b/ospray/camera/OrthographicCamera.cpp @@ -33,4 +33,12 @@ void OrthographicCamera::commit() (const ispc::vec2f &)size); } +box3f OrthographicCamera::projectBox(const box3f &b) const +{ + box3f projection; + ispc::OrthographicCamera_projectBox( + getIE(), (const ispc::box3f &)b, (ispc::box3f &)projection); + return projection; +} + } // namespace ospray diff --git a/ospray/camera/OrthographicCamera.h b/ospray/camera/OrthographicCamera.h index c72b541da..fe25b72a5 100644 --- a/ospray/camera/OrthographicCamera.h +++ b/ospray/camera/OrthographicCamera.h @@ -1,4 +1,4 @@ -// Copyright 2009-2019 Intel Corporation +// Copyright 2009-2021 Intel Corporation // SPDX-License-Identifier: Apache-2.0 #pragma once @@ -40,6 +40,8 @@ struct OSPRAY_SDK_INTERFACE OrthographicCamera : public Camera virtual std::string toString() const override; virtual void commit() override; + box3f projectBox(const box3f &b) const override; + // Data members // float height; // size of the camera's image plane in y, in world coordinates diff --git a/ospray/camera/OrthographicCamera.ispc b/ospray/camera/OrthographicCamera.ispc index 055a5b853..d7819ac3d 100644 --- a/ospray/camera/OrthographicCamera.ispc +++ b/ospray/camera/OrthographicCamera.ispc @@ -81,3 +81,75 @@ export void OrthographicCamera_set(void *uniform _self, self->org = org - 0.5f * self->du_size - 0.5f * self->dv_up; // shift } } + +export void OrthographicCamera_projectBox( + void *uniform _self, const uniform box3f &box, uniform box3f &projection) +{ + OrthographicCamera *uniform self = (OrthographicCamera * uniform) _self; + // normalize to image plane size + const uniform vec3f dun = self->du_size / dot(self->du_size, self->du_size); + const uniform vec3f dvn = self->dv_up / dot(self->dv_up, self->dv_up); + + vec3f projectedPt = make_vec3f(-1.f, -1.f, 1e20f); + foreach (i = 0 ... 8) { + // Get the point we should be projecting + vec3f p; + switch (i) { + case 0: + p = box.lower; + break; + case 1: + p.x = box.upper.x; + p.y = box.lower.y; + p.z = box.lower.z; + break; + case 2: + p.x = box.upper.x; + p.y = box.upper.y; + p.z = box.lower.z; + break; + case 3: + p.x = box.lower.x; + p.y = box.upper.y; + p.z = box.lower.z; + break; + case 4: + p.x = box.lower.x; + p.y = box.lower.y; + p.z = box.upper.z; + break; + case 5: + p.x = box.upper.x; + p.y = box.lower.y; + p.z = box.upper.z; + break; + case 6: + p = box.upper; + break; + case 7: + p.x = box.lower.x; + p.y = box.upper.y; + p.z = box.upper.z; + break; + } + + // Project the point on to the film plane + const float depth = dot(p - self->org, self->dir); + const vec3f screenPt = p - depth * self->dir; + const vec3f screenDir = screenPt - self->org; + projectedPt.x = dot(screenDir, dun); + projectedPt.y = dot(screenDir, dvn); + projectedPt.z = depth; + } + + // Find the projection of all points that projected to the screen + if (projectedPt.z < 1e20f) { + projection.lower.x = reduce_min(projectedPt.x); + projection.lower.y = reduce_min(projectedPt.y); + projection.lower.z = reduce_min(projectedPt.z); + + projection.upper.x = reduce_max(projectedPt.x); + projection.upper.y = reduce_max(projectedPt.y); + projection.upper.z = reduce_max(projectedPt.z); + } +} diff --git a/ospray/camera/PerspectiveCamera.cpp b/ospray/camera/PerspectiveCamera.cpp index 20af1f110..1bc16ee61 100644 --- a/ospray/camera/PerspectiveCamera.cpp +++ b/ospray/camera/PerspectiveCamera.cpp @@ -41,6 +41,7 @@ void PerspectiveCamera::commit() break; } + vec2f imgPlaneSize; imgPlaneSize.y = 2.f * tanf(deg2rad(0.5f * fovy)); imgPlaneSize.x = imgPlaneSize.y * aspect; @@ -57,28 +58,15 @@ void PerspectiveCamera::commit() interpupillaryDistance); } -ProjectedPoint PerspectiveCamera::projectPoint(const vec3f &p) const +box3f PerspectiveCamera::projectBox(const box3f &b) const { - // We find the intersection of the ray through the point with the virtual - // film plane, then find the vector to this point from the origin of the - // film plane (screenDir) and project this point onto the x/y axes of - // the plane. - const vec3f v = p - pos; - const vec3f r = normalize(v); - const float denom = dot(-r, -dir); - if (denom == 0.0) { - return ProjectedPoint( - vec3f(-1, -1, std::numeric_limits::infinity()), -1); + if (stereoMode != OSP_STEREO_NONE) { + return box3f(vec3f(0.f), vec3f(1.f)); } - const float t = 1.0 / denom; - - const vec3f screenDir = r * t - dir_00; - const vec2f sp = vec2f(dot(screenDir, normalize(dir_du)), - dot(screenDir, normalize(dir_dv))) - / imgPlaneSize; - const float depth = sign(t) * length(v); - // TODO: Depth of field radius, stereo - return ProjectedPoint(vec3f(sp.x, sp.y, depth), 0); + box3f projection; + ispc::PerspectiveCamera_projectBox( + getIE(), (const ispc::box3f &)b, (ispc::box3f &)projection); + return projection; } } // namespace ospray diff --git a/ospray/camera/PerspectiveCamera.h b/ospray/camera/PerspectiveCamera.h index b181dc4dc..bcb71ca0b 100644 --- a/ospray/camera/PerspectiveCamera.h +++ b/ospray/camera/PerspectiveCamera.h @@ -1,9 +1,10 @@ -// Copyright 2009-2019 Intel Corporation +// Copyright 2009-2021 Intel Corporation // SPDX-License-Identifier: Apache-2.0 #pragma once #include "camera/Camera.h" +#include "rkcommon/math/box.h" namespace ospray { @@ -14,7 +15,8 @@ struct OSPRAY_SDK_INTERFACE PerspectiveCamera : public Camera virtual std::string toString() const override; virtual void commit() override; - virtual ProjectedPoint projectPoint(const vec3f &p) const override; + + box3f projectBox(const box3f &b) const override; // Data members // @@ -24,10 +26,6 @@ struct OSPRAY_SDK_INTERFACE PerspectiveCamera : public Camera float focusDistance; bool architectural; // orient image plane to be parallel to 'up' and shift the // lens - vec3f dir_00; - vec3f dir_du; - vec3f dir_dv; - vec2f imgPlaneSize; OSPStereoMode stereoMode; float interpupillaryDistance; // distance between the two cameras (stereo) diff --git a/ospray/camera/PerspectiveCamera.ispc b/ospray/camera/PerspectiveCamera.ispc index 917479904..32a594f46 100644 --- a/ospray/camera/PerspectiveCamera.ispc +++ b/ospray/camera/PerspectiveCamera.ispc @@ -22,6 +22,8 @@ struct PerspectiveCamera vec3f ipd_offset; // shift of camera position for left/right eye (only when // SIDE_BY_SIDE or TOP_BOTTOM); (0.5*ipd, focusDistance, 0) + vec2f imgPlaneSize; + float scaledAperture; // radius of aperture prescaled to focal plane, i.e., // divided by horizontal image plane size float aspect; // image plane size x / y @@ -40,7 +42,7 @@ void PerspectiveCamera_initRay(const Camera *uniform _self, const uniform bool sbs = self->stereoMode == OSP_STEREO_SIDE_BY_SIDE; varying float *uniform split = sbs ? &screen.x : &screen.y; - if (or(sbs, self->stereoMode == OSP_STEREO_TOP_BOTTOM)) { + if (or (sbs, self->stereoMode == OSP_STEREO_TOP_BOTTOM)) { *split *= 2.f; if (*split < 1.f) { org = org - self->ipd_offset; @@ -172,6 +174,7 @@ export void PerspectiveCamera_set(void *uniform _self, self->stereoMode = stereoMode; self->dir_00 = normalize(dir); self->org = org; + self->imgPlaneSize = imgPlaneSize; if (self->super.motionBlur) { self->super.initRay = PerspectiveCamera_initRayMB; @@ -215,3 +218,93 @@ export void PerspectiveCamera_set(void *uniform _self, } } } + +export void PerspectiveCamera_projectBox( + void *uniform _self, const uniform box3f &box, uniform box3f &projection) +{ + PerspectiveCamera *uniform self = (PerspectiveCamera * uniform) _self; + // The original viewing direction along the camera + const uniform vec3f dir = + normalize(self->dir_00 + 0.5f * self->du_size + 0.5f * self->dv_up); + const uniform vec3f dun = normalize(self->du_size) / self->imgPlaneSize.x; + const uniform vec3f dvn = normalize(self->dv_up) / self->imgPlaneSize.y; + + vec3f projectedPt = make_vec3f(-1.f, -1.f, 1e20f); + foreach (i = 0 ... 8) { + // Get the point we should be projecting + vec3f p; + switch (i) { + case 0: + p = box.lower; + break; + case 1: + p.x = box.upper.x; + p.y = box.lower.y; + p.z = box.lower.z; + break; + case 2: + p.x = box.upper.x; + p.y = box.upper.y; + p.z = box.lower.z; + break; + case 3: + p.x = box.lower.x; + p.y = box.upper.y; + p.z = box.lower.z; + break; + case 4: + p.x = box.lower.x; + p.y = box.lower.y; + p.z = box.upper.z; + break; + case 5: + p.x = box.upper.x; + p.y = box.lower.y; + p.z = box.upper.z; + break; + case 6: + p = box.upper; + break; + case 7: + p.x = box.lower.x; + p.y = box.upper.y; + p.z = box.upper.z; + break; + } + + // We find the intersection of the ray through the point with the virtual + // film plane, then find the vector to this point from the origin of the + // film plane (screenDir) and project this point onto the x/y axes of + // the plane. + const vec3f v = p - self->org; + const vec3f r = normalize(v); + const float denom = dot(neg(r), neg(dir)); + if (denom != 0.f) { + float t = 1.f / denom; + const vec3f screenDir = r * t - self->dir_00; + projectedPt.x = dot(screenDir, dun); + projectedPt.y = dot(screenDir, dvn); + projectedPt.z = signbits(t) ? -length(v) : length(v); + } + } + + // Find the projection of all points that projected to the screen + if (projectedPt.z < 1e20f) { + projection.lower.x = reduce_min(projectedPt.x); + projection.lower.y = reduce_min(projectedPt.y); + projection.lower.z = reduce_min(projectedPt.z); + + projection.upper.x = reduce_max(projectedPt.x); + projection.upper.y = reduce_max(projectedPt.y); + projection.upper.z = reduce_max(projectedPt.z); + } + // If some points are behind and some are in front mark the box + // as covering the full screen + if (projection.lower.z < 0.f && projection.upper.z > 0.f) { + projection.lower.x = 0.f; + projection.lower.y = 0.f; + + projection.upper.x = 1.f; + projection.upper.y = 1.f; + } +} diff --git a/ospray/common/Data.h b/ospray/common/Data.h index 119935a29..9bbda4447 100644 --- a/ospray/common/Data.h +++ b/ospray/common/Data.h @@ -1,4 +1,4 @@ -// Copyright 2009-2020 Intel Corporation +// Copyright 2009-2021 Intel Corporation // SPDX-License-Identifier: Apache-2.0 #pragma once @@ -321,14 +321,20 @@ inline const Ref> ManagedObject::getParamDataT( } } - if (required) - throw std::runtime_error(toString() + " must have '" + name - + "' array with element type " + stringFor(OSPTypeFor::value)); - else { + if (required) { + std::string msg(toString() + " must have '" + name + "' " + + std::to_string(DIM) + "D array with element type " + + stringFor(OSPTypeFor::value)); + if (data) + msg.append(", found " + std::to_string(data->dimensions) + + "D array with element type " + stringFor(data->type)); + throw std::runtime_error(msg); + } else { if (data) { postStatusMsg(OSP_LOG_DEBUG) - << toString() << " ignoring '" << name - << "' array with wrong element type (should be " + << toString() << " ignoring '" << name << "' " << data->dimensions + << "D array with element type " << stringFor(data->type) + << " (while looking for " << DIM << "D array of " << stringFor(OSPTypeFor::value) << ")"; } return nullptr; diff --git a/ospray/common/OSPCommon.cpp b/ospray/common/OSPCommon.cpp index 8f8ffaedb..3daf0cb85 100644 --- a/ospray/common/OSPCommon.cpp +++ b/ospray/common/OSPCommon.cpp @@ -811,7 +811,7 @@ void handleError(OSPError e, const std::string &message) } else { // NOTE: No device, but something should still get printed for the user to // debug the calling application. - std::cerr << "#ospray: INITIALIZATION ERROR --> " << message << std::endl; + std::cerr << "#ospray: INVALID device --> " << message << std::endl; } } diff --git a/ospray/common/World.h b/ospray/common/World.h index 0572bed4c..784e920bf 100644 --- a/ospray/common/World.h +++ b/ospray/common/World.h @@ -43,7 +43,7 @@ struct OSPRAY_SDK_INTERFACE World : public ManagedObject void setDevice(RTCDevice embreeDevice); - private: + protected: RTCDevice embreeDevice{nullptr}; }; diff --git a/ospray/fb/FrameBuffer.cpp b/ospray/fb/FrameBuffer.cpp index d18215ae8..3b84aabfa 100644 --- a/ospray/fb/FrameBuffer.cpp +++ b/ospray/fb/FrameBuffer.cpp @@ -75,6 +75,7 @@ utility::ArrayView FrameBuffer::getTileIDs() void FrameBuffer::beginFrame() { + frameProgress = 0.0f; cancelRender = false; frameID++; ispc::FrameBuffer_set_frameID(getIE(), frameID); @@ -87,6 +88,10 @@ std::string FrameBuffer::toString() const void FrameBuffer::setCompletedEvent(OSPSyncEvent event) { + if (event == OSP_NONE_FINISHED) + frameProgress = 0.0f; + if (event == OSP_FRAME_FINISHED) + frameProgress = 1.0f; stagesCompleted = event; } diff --git a/ospray/ispc_symbols.txt b/ospray/ispc_symbols.txt index a4d7bb4eb..945370f4f 100644 --- a/ospray/ispc_symbols.txt +++ b/ospray/ispc_symbols.txt @@ -15,3 +15,4 @@ clippingIntersectionFilterV___UM_un_3C_s_5B__c_unRTCFilterFunctionNArguments_5D_ delete_uniform___un_3C_unv_3E_, delete_uniform_, precomputedZOrder_create___, +get_zorder___, diff --git a/ospray/render/Renderer.ispc b/ospray/render/Renderer.ispc index cf031a01f..6b98b6d05 100644 --- a/ospray/render/Renderer.ispc +++ b/ospray/render/Renderer.ispc @@ -105,8 +105,8 @@ unmasked void Renderer_default_renderTile(Renderer *uniform self, screenSample.pos = cameraSample.screen; // no DoF or MB per default - cameraSample.lens.x = 0.5f; - cameraSample.lens.y = 0.5f; + cameraSample.lens.x = 0.0f; + cameraSample.lens.y = 0.0f; cameraSample.time = 0.5f; camera->initRay(camera, screenSample.ray, cameraSample); diff --git a/ospray/render/util.ih b/ospray/render/util.ih index e193125bc..2dd94992d 100644 --- a/ospray/render/util.ih +++ b/ospray/render/util.ih @@ -1,4 +1,4 @@ -// Copyright 2009-2020 Intel Corporation +// Copyright 2009-2021 Intel Corporation // SPDX-License-Identifier: Apache-2.0 #pragma once @@ -94,3 +94,5 @@ inline void precomputeZOrder() if (!z_order_initialized) precomputedZOrder_create(); } + +extern const z_order_t *uniform get_zorder(); diff --git a/ospray/render/util.ispc b/ospray/render/util.ispc index 03ce450e1..4eb2b43f3 100644 --- a/ospray/render/util.ispc +++ b/ospray/render/util.ispc @@ -1,4 +1,4 @@ -// Copyright 2009-2020 Intel Corporation +// Copyright 2009-2021 Intel Corporation // SPDX-License-Identifier: Apache-2.0 #include "common/World.ih" @@ -60,3 +60,8 @@ float computeAO(const World *uniform world, // cancel return 1.0f - (hits / (float)sampleCnt); } + +const z_order_t *uniform get_zorder() +{ + return &z_order; +} diff --git a/ospray/volume/VolumetricModel.cpp b/ospray/volume/VolumetricModel.cpp index 4145387ab..20f2a2b61 100644 --- a/ospray/volume/VolumetricModel.cpp +++ b/ospray/volume/VolumetricModel.cpp @@ -56,6 +56,13 @@ void VolumetricModel::commit() vklIntervalContext = vklNewIntervalIteratorContext(volume->vklSampler); std::vector valueRanges = transferFunction->getPositiveOpacityValueRanges(); + if (valueRanges.empty()) { + // volume made completely transparent and could be removed, which is + // awkward here + // set an "empty" interesting value range instead, which will lead to a + // quick out during volume interation + valueRanges.push_back(range1f(neg_inf, neg_inf)); + } VKLData valueRangeData = vklNewData( volume->vklDevice, valueRanges.size(), VKL_BOX1F, valueRanges.data()); vklSetData(vklIntervalContext, "valueRanges", valueRangeData); diff --git a/scripts/superbuild/CMakeLists.txt b/scripts/superbuild/CMakeLists.txt index 2b71d811c..36252739c 100644 --- a/scripts/superbuild/CMakeLists.txt +++ b/scripts/superbuild/CMakeLists.txt @@ -55,16 +55,21 @@ endif() option(ALWAYS_REBUILD "Force every project to always be rebuilt?" OFF) -option(DOWNLOAD_ISPC "Download ISPC or use the one found in ${PATH}?" ON) -set(ISPC_VERSION "1.16.1" CACHE STRING "Which version of ISPC to download?") -if (ISPC_VERSION STREQUAL "1.16.1") - if (APPLE) - set(ISPC_HASH "7dbce602d97227a9603aabfae6dc3b3aa24d1cd44f0ccfb5ae47ecd4d68e988e") - elseif (WIN32) - set(ISPC_HASH "b34de2c36aff2afaa56b669ea41f9e614a045564ca74fc0b138e17ccea4880b7") - else() - set(ISPC_HASH "88db3d0461147c10ed81053a561ec87d3e14265227c03318f4fcaaadc831037f") +option(DOWNLOAD_ISPC "Download ISPC or use the one found in the system environment?" ON) +if (DOWNLOAD_ISPC) + set(ISPC_VERSION "1.16.1" CACHE STRING "Which version of ISPC to download?") + mark_as_advanced(CLEAR ISPC_VERSION) + if (ISPC_VERSION STREQUAL "1.16.1") + if (APPLE) + set(ISPC_HASH "7dbce602d97227a9603aabfae6dc3b3aa24d1cd44f0ccfb5ae47ecd4d68e988e") + elseif (WIN32) + set(ISPC_HASH "b34de2c36aff2afaa56b669ea41f9e614a045564ca74fc0b138e17ccea4880b7") + else() + set(ISPC_HASH "88db3d0461147c10ed81053a561ec87d3e14265227c03318f4fcaaadc831037f") + endif() endif() +else() + mark_as_advanced(FORCE ISPC_VERSION) endif() set(RKCOMMON_VERSION "1.7.0" CACHE STRING "Which version of rkcommon to build?") @@ -72,21 +77,28 @@ if (RKCOMMON_VERSION STREQUAL "1.7.0") set(RKCOMMON_HASH "b2eabd1dc56fd9de7cae320711ae07696385dc3151a56b63562396d0f5e2708e") endif() -option(DOWNLOAD_TBB "Download TBB or use the only found in the system environment?" ON) -option(BUILD_TBB_FROM_SOURCE "Build TBB from source or use pre-built version?" OFF) -set(TBB_VERSION "2021.2.0" CACHE STRING "Which version of TBB to download?") -if (TBB_VERSION STREQUAL "2021.2.0") - if (BUILD_TBB_FROM_SOURCE) - set(TBB_HASH "d3a75d98701f53d3dbd663809c74e93348602d02bc33c2039b1cf61427f15278") - else() - if (APPLE) - set(TBB_HASH "ed6545190b747af9585ab8eedd5c8ccdf7cef304b6caa8db25803389fb24ec62") - elseif (WIN32) - set(TBB_HASH "9be37b1cb604a5905db0a15b2b893d85579fd0b2f1024859e1f75e96d7331a02") +option(DOWNLOAD_TBB "Download TBB or use the one found in the system environment?" ON) +if (DOWNLOAD_TBB) + set(TBB_VERSION "2021.3.0" CACHE STRING "Which version of TBB to download?") + mark_as_advanced(CLEAR TBB_VERSION) + option(BUILD_TBB_FROM_SOURCE "Build TBB from source or use pre-built version?" OFF) + mark_as_advanced(CLEAR BUILD_TBB_FROM_SOURCE) + if (TBB_VERSION STREQUAL "2021.3.0") + if (BUILD_TBB_FROM_SOURCE) + set(TBB_HASH "aadd36731bdc38702868303a8d08a7e34c5beb54031c99976533e2ca18d4e9ed") else() - set(TBB_HASH "bcaa0aa7c0d2851a730cb0f64ad120f06a7b63811c8cc49e51ea8e1579f51b05") + if (APPLE) + set(TBB_HASH "1356e744c29763ee0a6de06647954eb076798c0b7a5b3c1008108d1d76cd42a5") + elseif (WIN32) + set(TBB_HASH "90e2055cd4be55f79eedd3d50b2010bf05d1739309c4cdd219192d129e931093") + else() + set(TBB_HASH "3b2290f3db5521901d43490a5e92a414413a8fd2feac8b4bf763dbc04ba28f63") + endif() endif() endif() +else() + mark_as_advanced(FORCE TBB_VERSION) + mark_as_advanced(FORCE BUILD_TBB_FROM_SOURCE) endif() option(BUILD_EMBREE_FROM_SOURCE "Build Embree or use pre-built version?" ON) @@ -105,22 +117,29 @@ if (EMBREE_VERSION STREQUAL "3.13.1") endif() endif() -set(OPENVKL_VERSION "1.0.0" CACHE STRING "Which version of OpenVKL to build?") -if (OPENVKL_VERSION STREQUAL "1.0.0") - set(OPENVKL_HASH "c2df70bec784a5ae42e2931d76d4feae51700c0b42cd5c2428b8ce6a64fffc34") +set(OPENVKL_VERSION "1.0.1" CACHE STRING "Which version of OpenVKL to build?") +if (OPENVKL_VERSION STREQUAL "1.0.1") + set(OPENVKL_HASH "dc37113adb7de12e503202633e54854b88dd612c3f914c1b9a5ae195c4c6c1be") endif() option(BUILD_OIDN "Build OpenImageDenoise as a part of the superbuild." OFF) -option(BUILD_OIDN_FROM_SOURCE "Build OpenImageDenoise or use pre-built version?" OFF) -set(OIDN_VERSION "1.4.1" CACHE STRING "Which version of OpenImageDenoise to build?") -if (OIDN_VERSION STREQUAL "1.4.1") - if (APPLE) - set(OIDN_HASH "0ac6b2e16ff5f1b7ff75f19e5889f291bb36d66b84446bee06ff169cfe19f265") - elseif (WIN32) - set(OIDN_HASH "4ea0492de4fc47951ab5fc53b4e3433f39677d3fbfc1541eabc4ccee82bfc4d1") - else() - set(OIDN_HASH "efdf8fafb47017d7b1b321cbc88cb1bfcbdbe2746be57ed4a2ae90431fc9eb3a") +if (BUILD_OIDN) + set(OIDN_VERSION "1.4.1" CACHE STRING "Which version of OpenImageDenoise to build?") + mark_as_advanced(CLEAR OIDN_VERSION) + option(BUILD_OIDN_FROM_SOURCE "Build OpenImageDenoise or use pre-built version?" OFF) + mark_as_advanced(CLEAR BUILD_OIDN_FROM_SOURCE) + if (OIDN_VERSION STREQUAL "1.4.1") + if (APPLE) + set(OIDN_HASH "0ac6b2e16ff5f1b7ff75f19e5889f291bb36d66b84446bee06ff169cfe19f265") + elseif (WIN32) + set(OIDN_HASH "4ea0492de4fc47951ab5fc53b4e3433f39677d3fbfc1541eabc4ccee82bfc4d1") + else() + set(OIDN_HASH "efdf8fafb47017d7b1b321cbc88cb1bfcbdbe2746be57ed4a2ae90431fc9eb3a") + endif() endif() +else() + mark_as_advanced(FORCE OIDN_VERSION) + mark_as_advanced(FORCE BUILD_OIDN_FROM_SOURCE) endif() option(BUILD_GLFW "Build glfw for OSPRay examples or use one in existing environment?" ON)