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

additional documentation + remove unused code #174

Merged
merged 1 commit into from
Nov 27, 2023
Merged
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
5 changes: 2 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,11 @@ ANARI-SDK

This repository contains the source for the ANARI API SDK. This includes:

- [Front-end library](libs/anari)
- [Front-end library + implementation guide](libs/anari)
- [Device implementation utilties for implementations](libs/helium)
- [Example device implementation](libs/helide) (not intended for production use)
- [Example applications](examples/)
- [Interactive sample viewer](examples/viewer)
- [Unit tests](tests/unit)
- [Render tests](tests/render)

The 1.0 ANARI specification can be found on the Khronos website
Expand Down Expand Up @@ -109,7 +108,7 @@ get the library to load. For example it can be run with:

```bash
% export ANARI_LIBRARY=helide
% ./anariViewer /path/to/some/file.obj
% ./anariViewer
```

Alternatively, either `--library` or `-l` can be used on the viewer's command
Expand Down
114 changes: 114 additions & 0 deletions libs/anari/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
# The ANARI Device Implementation Guide

This directory contains the ANARI frontend library (`libanari`) which is the
core C API which applications directly use. This document will outline the
details of what is necessary in order to _implement_ a device, as using the
API itself is detailed at length in the ANARI specification.

The primary function of this library is to dispatch
[ANARI C API function calls](include/anari/anari.h) to the corresponding ANARI
devcie implementing the API (designated by an `ANARIDevice` handle in the
functioin signature). There are two core concepts that implementations must
implement, along with optional additional tools which can aid in implementing
various ANARI features.

The headers used to implement the required components of a device for this
frontend library are found in the [backend directory](include/anari/backend) of
the core installed headers. Implementations are primarily expected to build
their devices against an installation of the ANARI-SDK.

## Implementing ANARILibrary

The first required item that must be implemented is `ANARILibrary`. This is done
by subclassing [`LibraryImpl`](include/anari/backend/LibraryImpl.h) and
providing the required method definitions. `LibraryImpl` acts as the concrete
representation of `ANARILibrary`, where the frontend library uses this class to
manage all C API calls which are done on `ANARILibrary` (where the function's
first argument is the library).

Note that `anariLoadLibrary()` uses the follwing naming convention for
dynamically loading an `ANARILibrary` at runtime: `anari_library_[name]`, where
`[name]` matches the string passed to `anariLoadLibrary`. `[name]` is used to
construct a C function name which is looked up as the entry point needed to
create an instance of `LibraryImpl`. Implementations should use the
`ANARI_DEFINE_LIBRARY_ENTRYPOINT` macro at the bottom of `LibraryImpl.h` to
define this entrypoint function, where it is necessary to match the first macro
argument with `[name]` in the physical shared library file. For example, the
provided [`helide`](../helide) device on linux is compiles into the shared
library `libanari_library_helide.so`, and `helide` is the first argument to
`ANARI_DEFINE_LIBRARY_ENTRYPOINT`, as seen [here](../helide/HelideLibrary.cpp).

It is possible to directly construct a device if client applications directly
link your library itself, but it is highly recommended to always provide the
dynamic path via `anariLoadLibrary()` and `LibraryImpl` as this is the most
common way to create ANARI devices. The alternate method of directly
constructing a device via linking is show by the
[`anariTutorialDirectLink`](../../examples/simple/anariTutorialDirectLink.c)
sample app which includes a custom header created by `helide` to directly
create an instance of the `helide` device.

## Implementing ANARIDevice

The majority of the ANARI C API is implemented by subclassing
[`DeviceImpl`](include/anari/backend/DeviceImpl.h). Similar to implementing
`ANARILibrary`, `ANARIDevice` directly represents an instance of `DeviceImpl`
where the methods of the class correspond to functions in the ANARI API handled
by the device (where the function's first argument is the device). Device
implementations should always seek to make each instance of `DeviceImpl`
independent from each other by avoiding any shared state between them (i.e.
static state within the class or shared library).

Almost the entirety of `DeviceImpl` corresponds directly to functions found
in [`anari.h`](include/anari/anari.h). The only state held by `DeviceImpl` are
the default status callback and callback user pointer. `DeviceImpl` is intended
to be very minimal -- implementors who desire SDK-provided implementations of
much of the API should use the [`helium`](../helium) layer, which implements
many common concepts, but requires implementations to opt-in to various `helium`
abstractions and classes. The provided [`helide`](../helide) device demonstrates
ultimately how to implement `DeviceImpl` through using
[`helium::BaseDevice`](../helium/BaseDevice.h). Device implementors can use the
sum of `helium::BaseDevice` and
[`helide::HelideDevice`](../helide/HelideDevice.h) as a full example of
implementing ANARI. Further documentation of what functionality `helium`
provides can be found in its [README](../helium/README.md).

## Object query code generation

Devices should implement the device and object queries offered by the ANARI API
in order for applications to introspect what extensions (and their details) are
offered. However, these functions can be quite tedius and repetitive to
implement, so Python-based code generation tools are offered to let library and
device authors minimize the effort in writing them. They use JSON definition
files to generate C++ which can then be used to implement various query
functions.

All of the Python tooling is installed when `INSTALL_CODE_GEN_SCRIPTS` is
enabled when the SDK is installed. This can then be consumed by including the
`code_gen` component name in find package:

```cmake
find_package(anari REQUIRED COMPONENTS code_gen)
```

This brings the `anari_generate_queries()` CMake function into scope downstream,
which has the following signature:

```cmake
anari_generate_queries(
NAME [device_name]
PREFIX [device_prefix]
CPP_NAMESPACE [namespace]
JSON_DEFINITIONS_FILE [path/to/device/definitions.json]
)
```

This CMake function will create a target called `generate_[device_name]_device`,
which must be built manually in order to generate any C++. By invoking this
targets, a `[device_prefix]Queries.cpp/.h` are created, which can be included
in the local device build's source. Please refer to [helide](../helide) as an
example of how these components all go together.

Note that all core spec extensions are defined in a collection of
[JSON files](../../code_gen/api) that are referenced in the downstream JSON
definitions. It is recommened to copy an existing JSON definitions file and
modify it accordingly.
110 changes: 67 additions & 43 deletions libs/anari/include/anari/backend/DeviceImpl.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,50 +9,32 @@

#include "anari/backend/LibraryImpl.h"

// std
#include <map>
#include <string>
#include <vector>

namespace anari {

template <typename T, typename... Args>
using FactoryFcn = T *(*)(Args...);

template <typename T, typename... Args>
using FactoryMap = std::map<std::string, FactoryFcn<T, Args...>>;

template <typename T, typename... Args>
using FactoryVector = std::vector<FactoryFcn<T, Args...>>;

// should this use Args&&... and forwarding?
template <typename B, typename T, typename... Args>
inline B *allocate_object(Args... args)
{
return new T(args...);
}

struct ANARI_INTERFACE DeviceImpl
{
/////////////////////////////////////////////////////////////////////////////
// Main virtual interface to accepting API calls
/////////////////////////////////////////////////////////////////////////////

// Data Arrays //////////////////////////////////////////////////////////////
// Object creation //////////////////////////////////////////////////////////

// Implement anariNewArray1D()
virtual ANARIArray1D newArray1D(const void *appMemory,
ANARIMemoryDeleter deleter,
const void *userdata,
ANARIDataType,
uint64_t numItems1) = 0;

// Implement anariNewArray2D()
virtual ANARIArray2D newArray2D(const void *appMemory,
ANARIMemoryDeleter deleter,
const void *userdata,
ANARIDataType,
uint64_t numItems1,
uint64_t numItems2) = 0;

// Implement anariNewArray3D()
virtual ANARIArray3D newArray3D(const void *appMemory,
ANARIMemoryDeleter deleter,
const void *userdata,
Expand All @@ -61,86 +43,121 @@ struct ANARI_INTERFACE DeviceImpl
uint64_t numItems2,
uint64_t numItems3) = 0;

virtual void *mapArray(ANARIArray) = 0;
virtual void unmapArray(ANARIArray) = 0;
// Implement anariNewGeometry()
virtual ANARIGeometry newGeometry(const char *type) = 0;

// Renderable Objects ///////////////////////////////////////////////////////
// Implement anariNewMaterial()
virtual ANARIMaterial newMaterial(const char *material_type) = 0;

virtual ANARILight newLight(const char *type) = 0;
// Implement anariNewSampler()
virtual ANARISampler newSampler(const char *type) = 0;

virtual ANARICamera newCamera(const char *type) = 0;
// Implement anariNewSurface()
virtual ANARISurface newSurface() = 0;

virtual ANARIGeometry newGeometry(const char *type) = 0;
// Implement anariNewSpatialField()
virtual ANARISpatialField newSpatialField(const char *type) = 0;

virtual ANARISurface newSurface() = 0;
// Implement anariNewVolume()
virtual ANARIVolume newVolume(const char *type) = 0;

// Surface Meta-Data ////////////////////////////////////////////////////////

virtual ANARIMaterial newMaterial(const char *material_type) = 0;

virtual ANARISampler newSampler(const char *type) = 0;

// Instancing ///////////////////////////////////////////////////////////////
// Implement anariNewLight()
virtual ANARILight newLight(const char *type) = 0;

// Implement anariGroup()
virtual ANARIGroup newGroup() = 0;

// Implement anariNewInstance()
virtual ANARIInstance newInstance(const char *type) = 0;

// Top-level Worlds /////////////////////////////////////////////////////////

// Implement anariNewWorld()
virtual ANARIWorld newWorld() = 0;

// Implement anariNewCamera()
virtual ANARICamera newCamera(const char *type) = 0;

// Implement anariNewRenderer()
virtual ANARIRenderer newRenderer(const char *type) = 0;

// Implement anariNewFrame()
virtual ANARIFrame newFrame() = 0;

// Object + Parameter Lifetime Management ///////////////////////////////////

// Implement anariSetParameter()
virtual void setParameter(ANARIObject object,
const char *name,
ANARIDataType type,
const void *mem) = 0;

// Implement anariUnsetParameter()
virtual void unsetParameter(ANARIObject object, const char *name) = 0;

// Implement anariUnsetAllParameters()
virtual void unsetAllParameters(ANARIObject object) = 0;

// Implement anariMapParameterArray1D()
virtual void *mapParameterArray1D(ANARIObject object,
const char *name,
ANARIDataType dataType,
uint64_t numElements1,
uint64_t *elementStride) = 0;

// Implement anariMapParameterArray2D()
virtual void *mapParameterArray2D(ANARIObject object,
const char *name,
ANARIDataType dataType,
uint64_t numElements1,
uint64_t numElements2,
uint64_t *elementStride) = 0;

// Implement anariMapParameterArray3D()
virtual void *mapParameterArray3D(ANARIObject object,
const char *name,
ANARIDataType dataType,
uint64_t numElements1,
uint64_t numElements2,
uint64_t numElements3,
uint64_t *elementStride) = 0;

// Implement anariUnmapParameterArray()
virtual void unmapParameterArray(ANARIObject object, const char *name) = 0;

// Implement anariCommitParameters()
virtual void commitParameters(ANARIObject object) = 0;

// Implement anariRelease()
virtual void release(ANARIObject _obj) = 0;

// Implement anariRetain()
virtual void retain(ANARIObject _obj) = 0;

// Object Query Interface ///////////////////////////////////////////////////
// Implement anariMapArray()
virtual void *mapArray(ANARIArray) = 0;

// Implement anariUnmapArray()
virtual void unmapArray(ANARIArray) = 0;

// Implement anariGetProperty()
virtual int getProperty(ANARIObject object,
const char *name,
ANARIDataType type,
void *mem,
uint64_t size,
ANARIWaitMask mask) = 0;

// Object Query Interface ///////////////////////////////////////////////////

// Implement anariGetObjectSubtypes()
virtual const char **getObjectSubtypes(ANARIDataType objectType) = 0;

// Implement anariGetObjectInfo()
virtual const void *getObjectInfo(ANARIDataType objectType,
const char *objectSubtype,
const char *infoName,
ANARIDataType infoType) = 0;

// Implement anariGetParameterInfo()
virtual const void *getParameterInfo(ANARIDataType objectType,
const char *objectSubtype,
const char *parameterName,
Expand All @@ -150,30 +167,36 @@ struct ANARI_INTERFACE DeviceImpl

// FrameBuffer Manipulation /////////////////////////////////////////////////

virtual ANARIFrame newFrame() = 0;

// Implement anariFrameBufferMap
virtual const void *frameBufferMap(ANARIFrame fb,
const char *channel,
uint32_t *width,
uint32_t *height,
ANARIDataType *pixelType) = 0;

// Implement anariFrameBufferUnmap
virtual void frameBufferUnmap(ANARIFrame fb, const char *channel) = 0;

// Frame Rendering //////////////////////////////////////////////////////////

virtual ANARIRenderer newRenderer(const char *type) = 0;

// Implement anariRenderFrame()
virtual void renderFrame(ANARIFrame) = 0;

// Implement anariFrameReady()
virtual int frameReady(ANARIFrame, ANARIWaitMask) = 0;

// Implement anariDiscardFrame()
virtual void discardFrame(ANARIFrame) = 0;

/////////////////////////////////////////////////////////////////////////////
// Extension interface
/////////////////////////////////////////////////////////////////////////////

// Optionally allow creation of extension objects that do not fit any core
// handle types.
virtual ANARIObject newObject(const char *objectType, const char *type);

// Optionally allow dynamic lookup of special extension functions
virtual void (*getProcAddress(const char *name))(void);

/////////////////////////////////////////////////////////////////////////////
Expand All @@ -184,6 +207,7 @@ struct ANARI_INTERFACE DeviceImpl
DeviceImpl() = default;
virtual ~DeviceImpl() = default;

// Utility to get 'this' as an ANARIDevice handle
ANARIDevice this_device() const;

ANARIStatusCallback m_defaultStatusCB{nullptr};
Expand Down
Loading