RenderKit / ospray

An Open, Scalable, Portable, Ray Tracing Based Rendering Engine for High-Fidelity Visualization
http://ospray.org
Apache License 2.0
1.02k stars 186 forks source link

Direct memory access to OSPData objects #208

Open stukowski opened 6 years ago

stukowski commented 6 years ago

I would like to discuss a suggestion for an API extension that could provide some performance benefits and reduce memory requirements of an application.

Currently, OSPRay only provides the ospNewData function, which requires that the data to be put into the buffer is already available upon initialization of the OSPData object. Since applications commonly hold geometry data in an internal format that is not compatible with OSPRay's format, a conversion is typically necessary before handing the data over to OSPRay. Thus, we need to perform the following steps:

  1. Allocate a temporary buffer (e.g. using std::vector).
  2. Copy the application's data to the temporary buffer and convert it to the format required by OSPRay.
  3. Call ospNewData with a pointer to the temporary buffer. ospNewData copies the data to its internal storage.

I know that ospNewData provides the OSP_DATA_SHARED_BUFFER flag, and this would avoid the extra data copy in step 3, but this places an extra burden on the application developer, who has to keep the temporary buffer alive for the entire duration of the rendering process and the application has no way of knowing when the reference-counted OSPData object actually goes out of scope. So the memory management should remain the responsibility of the OSPData object alone.

To circumvent this kind of problem, other graphics APIs such as OpenGL provide direct access to the internal memory of the buffer object. Using a pair of map/unmap calls, returning a pointer to the internal data array, it becomes possible for the application to directly write into the buffer and no extra memory copy is needed:

  1. Allocate a memory buffer using ospNewData. Specify the size/data type, but do not initialize the memory.
  2. Acquire a pointer to the internal memory buffer using ospMapData() (API function to be added).
  3. Copy the application's data to the buffer and convert it to the format required by OSPRay.
  4. Finish the operation by calling ospUnmapData() (or, perhaps, simply call ospCommit() to complete the transaction).
carsonbrownlee commented 6 years ago

I agree it would be useful to have an API interface for accessing the allocated data storage. We'll discuss this internally and see about whether we want to add this for the next major release.

Currently, there are two ways to go about this noting that OSPRay is not thread safe, so there shouldn't be anything accessing the data outside of a commit and render call. In cases like the async renderer we have in the exampleViewer, it is up to the user to ensure that data is not accessed while commits/render operations are being called.

  1. Create your own buffer with the shared_buffer flag as you mentioned above. This buffer can be modified and re-committed at will.
  2. Outside of the API, OSPData is osp::Data, which actually has a public data member variable "data". This is not clean by any way, but should get you there until we have a proper API addition.
johguenther commented 3 years ago

FYI, we plan to support both in v3: transferring ownership of app-created buffers to OSPRay (e.g. std::vector) and also a map/unmap API for arrays.

johguenther commented 1 year ago

OSPRay finally supports the "move-semantic" for data in v3.0 (via the new deleter callbacks of ospNewSharedData). The additional proposal to add a map API call for ospNewData data is already specified in ANARI (see https://github.com/KhronosGroup/ANARI-Docs/issues/54) and will eventually by supported as well by OSPRay (though "when" is not entirely clear..).