Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add anariDistributedTutorial #256

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions examples/simple/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,13 @@ target_link_libraries(anariTutorialInitFromEnv PRIVATE anari::anari)
add_executable(anariInfo anariInfo.cpp)
target_link_libraries(anariInfo PRIVATE anari::anari_static)

option(USE_MPI "Enable MPI and Distributed Tutorial")
if (USE_MPI)
find_package(MPI REQUIRED)
add_executable(anariDistributedTutorial anariDistributedTutorial.cpp)
target_link_libraries(anariDistributedTutorial PRIVATE anari::anari stb_image MPI::MPI_CXX)
endif()

if (IN_SDK_SOURCE_TREE)
install(TARGETS anariInfo RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})

Expand Down
181 changes: 181 additions & 0 deletions examples/simple/anariDistributedTutorial.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,181 @@
// Copyright 2024 The Khronos Group
// SPDX-License-Identifier: Apache-2.0

#include <errno.h>
#include <mpi.h>
#include <stdint.h>
#include <stdio.h>
#include <array>

// anari
#define ANARI_EXTENSION_UTILITY_IMPL
#include "anari/anari_cpp.hpp"
#include "anari/anari_cpp/ext/std.h"
// stb_image
#include "stb_image_write.h"

using namespace anari::std_types;

static void statusFunc(const void *userData,
ANARIDevice device,
ANARIObject source,
ANARIDataType sourceType,
ANARIStatusSeverity severity,
ANARIStatusCode code,
const char *message)
{
(void)userData;
(void)device;
(void)source;
(void)sourceType;
(void)code;
if (severity == ANARI_SEVERITY_FATAL_ERROR) {
fprintf(stderr, "[FATAL] %s\n", message);
} else if (severity == ANARI_SEVERITY_ERROR) {
fprintf(stderr, "[ERROR] %s\n", message);
} else if (severity == ANARI_SEVERITY_WARNING) {
fprintf(stderr, "[WARN ] %s\n", message);
} else if (severity == ANARI_SEVERITY_PERFORMANCE_WARNING) {
fprintf(stderr, "[PERF ] %s\n", message);
} else if (severity == ANARI_SEVERITY_INFO) {
fprintf(stderr, "[INFO ] %s\n", message);
} else if (severity == ANARI_SEVERITY_DEBUG) {
fprintf(stderr, "[DEBUG] %s\n", message);
}
}

static void onFrameCompletion(const void *, anari::Device d, anari::Frame f)
{
printf("anari::Device(%p) finished rendering anari::Frame(%p)!\n", d, f);
}

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,
"MPI runtime must support either thread multiple or thread serialized.\n");
return 1;
}

int mpiRank = 0;
int mpiWorldSize = 0;
MPI_Comm_rank(MPI_COMM_WORLD, &mpiRank);
MPI_Comm_size(MPI_COMM_WORLD, &mpiWorldSize);

// image size
uvec2 imgSize = {1024 /*width*/, 768 /*height*/};

// camera
vec3 cam_pos{(mpiWorldSize + 1.f) / 2.f, 0.5f, -mpiWorldSize * 0.5f};
vec3 cam_up{0.f, 1.f, 0.f};
vec3 cam_view{0.f, 0.f, 1.f};

// all ranks specify the same rendering parameters, with the exception of
// the data to be rendered, which is distributed among the ranks
// triangle mesh data
vec3 vertex[] = {{(float)mpiRank, 0.0f, 3.5f},
{(float)mpiRank, 1.0f, 3.0f},
{mpiRank + 1.f, 0.0f, 3.0f},
{mpiRank + 1.f, 1.0f, 2.5f}};
vec4 color[] = {{0.0f, 0.0f, (mpiRank + 1.f) / mpiWorldSize, 1.0f},
{0.0f, 0.0f, (mpiRank + 1.f) / mpiWorldSize, 1.0f},
{0.0f, 0.0f, (mpiRank + 1.f) / mpiWorldSize, 1.0f},
{0.0f, 0.0f, (mpiRank + 1.f) / mpiWorldSize, 1.0f}};
uvec3 index[] = {{0, 1, 2}, {1, 2, 3}};

anari::Library lib = anari::loadLibrary("environment", statusFunc);

anari::Extensions extensions =
anari::extension::getDeviceExtensionStruct(lib, "default");

if (!extensions.ANARI_KHR_GEOMETRY_TRIANGLE)
printf("WARNING: device doesn't support ANARI_KHR_GEOMETRY_TRIANGLE\n");
if (!extensions.ANARI_KHR_CAMERA_PERSPECTIVE)
printf("WARNING: device doesn't support ANARI_KHR_CAMERA_PERSPECTIVE\n");
if (!extensions.ANARI_KHR_MATERIAL_MATTE)
printf("WARNING: device doesn't support ANARI_KHR_MATERIAL_MATTE\n");
if (!extensions.ANARI_KHR_FRAME_COMPLETION_CALLBACK) {
printf(
"INFO: device doesn't support ANARI_KHR_FRAME_COMPLETION_CALLBACK\n");
}

anari::Device d = anari::newDevice(lib, "distributed");
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This likely needs some discussion: do we want to define some convention / names to differentiate between a default device and a distributed one?
The (better?) alternative is to define an extension and search for devices supporting it, see also KhronosGroup/ANARI-Docs#131


// create and setup camera
auto camera = anari::newObject<anari::Camera>(d, "perspective");
anari::setParameter(
d, camera, "aspect", (float)imgSize[0] / (float)imgSize[1]);
anari::setParameter(d, camera, "position", cam_pos);
anari::setParameter(d, camera, "direction", cam_view);
anari::setParameter(d, camera, "up", cam_up);
anari::commitParameters(
d, camera); // commit objects to indicate setting parameters is done

// create and setup surface and mesh
auto mesh = anari::newObject<anari::Geometry>(d, "triangle");
anari::setParameterArray1D(d, mesh, "vertex.position", vertex, 4);
anari::setParameterArray1D(d, mesh, "vertex.color", color, 4);
anari::setParameterArray1D(d, mesh, "primitive.index", index, 2);
anari::commitParameters(d, mesh);

auto mat = anari::newObject<anari::Material>(d, "matte");
anari::setParameter(d, mat, "color", "color");
anari::commitParameters(d, mat);

// put the mesh into a surface
auto surface = anari::newObject<anari::Surface>(d);
anari::setAndReleaseParameter(d, surface, "geometry", mesh);
anari::setAndReleaseParameter(d, surface, "material", mat);
anari::commitParameters(d, surface);

// put the surface directly onto the world
auto world = anari::newObject<anari::World>(d);
anari::setParameterArray1D(d, world, "surface", &surface, 1);
anari::release(d, surface);
anari::commitParameters(d, world);

auto renderer = anari::newObject<anari::Renderer>(d, "default");
anari::commitParameters(d, renderer);

// create and setup frame
auto frame = anari::newObject<anari::Frame>(d);
anari::setParameter(d, frame, "size", imgSize);
anari::setParameter(d, frame, "channel.color", ANARI_UFIXED8_RGBA_SRGB);
anari::setAndReleaseParameter(d, frame, "renderer", renderer);
anari::setAndReleaseParameter(d, frame, "camera", camera);
anari::setAndReleaseParameter(d, frame, "world", world);
anari::setParameter(d,
frame,
"frameCompletionCallback",
(anari::FrameCompletionCallback)onFrameCompletion);
anari::commitParameters(d, frame);

// render one frame
anari::render(d, frame);
anari::wait(d, frame);

// on rank 0, access frame and write its content as PNG file
if (mpiRank == 0) {
auto fb = anari::map<uint32_t>(d, frame, "channel.color");
stbi_flip_vertically_on_write(1);
stbi_write_png("tutorialDistributed.png",
int(fb.width),
int(fb.height),
4,
fb.data,
4 * int(fb.width));
anari::unmap(d, frame, "channel.color");
}

// final cleanups
anari::release(d, frame);
anari::release(d, d);
anari::unloadLibrary(lib);

MPI_Finalize();

return 0;
}
2 changes: 1 addition & 1 deletion examples/simple/anariTutorial.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -191,7 +191,7 @@ int main(int argc, const char **argv)

anari::commitParameters(d, frame);

printf("rendering frame to firstFrame.png...\n");
printf("rendering frame to tutorial_cpp.png...\n");

// render one frame
anari::render(d, frame);
Expand Down
Loading