/******************************************************************************
 * Copyright 2018 NVIDIA Corporation. All rights reserved.
 *****************************************************************************/
/// \file
/// \brief Asynchronous texture generation for use with shapes.

#ifndef NVIDIA_INDEX_IDISTRIBUTED_COMPUTE_DESTINATION_BUFFER_H
#define NVIDIA_INDEX_IDISTRIBUTED_COMPUTE_DESTINATION_BUFFER_H

#include <mi/base/interface_declare.h>
#include <mi/dice.h>

#include <nv/index/idistributed_data_subset.h>

namespace nv
{
namespace index
{

/// @ingroup nv_index_data_computing
///
/// The interface class enables the asynchronous generation of 2D texture
/// buffers and 3D volume data. Once the generation of compute-destination buffers
/// has been triggered, for example, by means of the interface class
/// \c IDistributed_compute_technique, the user-defined technique can populate the buffer
/// contents with the values required for later texture mapping and rendering.
/// Meanwhile, the rendering system may proceed to render datasets that
/// don't require a particular user-defined texturing technique.
///
/// In particular, the interface class enables leveraging designated compute
/// clusters for data generation while hiding the common network transfer and latency
/// issues.
///
/// If a compute-destination buffer has been filled the buffer can be made available to
/// the calling rendering system.
///
class IDistributed_compute_destination_buffer
  : public mi::base::Interface_declare<0xfaae5c5, 0x2701, 0x442e, 0x8f, 0x77, 0xe3, 0x3, 0xed, 0xd,
      0x6f, 0x5c>
{
public:
  /// The interface method returns the bounding box of the subregion for which
  /// the compute technique has been invoked. The rendering system
  /// initializes the bounding box that then can be accessed by an
  /// user-defined technique to control the texture generation.
  ///
  /// \return     The bounding box of the subregion, which is defined in the
  ///             global coordinate system.
  ///
  virtual mi::math::Bbox_struct<mi::Float32, 3> get_subregion_bbox() const = 0;

  /// Returns the 2D area that the subregion covers in screen space. Based on
  /// the given screen-space area a user-defined technique can choose, for
  /// instance, the least compute-intensive generation technique.
  ///
  /// The rendering system computes and initializes the screen-space coverage.
  ///
  /// \return     The screen-space area that the subregion covers defined in
  ///             window coordinates.
  ///
  virtual mi::math::Bbox_struct<mi::Uint32, 2> get_screen_space_area() const = 0;
};

/// @ingroup nv_index_data_computing
///
/// Compute-destination buffer for 3D volume generation techniques.
///
/// Upon applying an \c IDistributed_compute_technique attribute to a \c IRegular_volume scene
/// element
/// a \c IDistributed_compute_destination_buffer_3d_volume is passed to the \c
/// IDistributed_compute_technique::launch_compute()
/// method. In order to support type safe volume data generation typed specializations of this base
/// class are provided (e.g. \c IDistributed_compute_destination_buffer_3d_volume_uint8,
/// \c IDistributed_compute_destination_buffer_3d_volume_rgba8).
///
class IDistributed_compute_destination_buffer_3d_volume
  : public mi::base::Interface_declare<0xcb42c8dd, 0x4f80, 0x4dc3, 0x8a, 0x20, 0x98, 0xe1, 0x58,
      0x6, 0x37, 0x14, nv::index::IDistributed_compute_destination_buffer>
{
public:
  /// Transformation that should be applied on the written data.
  ///
  /// \note Experimental, subject to change!
  enum Data_transformation
  {
    DATA_TRANSFORMATION_NONE = 0,           ///< Pass through unchanged
    DATA_TRANSFORMATION_FLIP_AXIS_ORDER = 1 ///< Change voxel storage order (x/y/z to z/y/x)
  };

  /// Returns the bounding box of the volume brick for which the compute technique
  /// is required to generate values. This bounding box includes border voxels
  /// of the volume brick.
  ///
  /// The rendering system computes and initializes the bounding box of the volume brick.
  ///
  /// \return     The bounding box of the volume brick, defined in non-normalized
  ///             volume coordinates.
  ///
  virtual mi::math::Bbox_struct<mi::Sint32, 3> get_volume_brick_bbox() const = 0;

  /// Returns the clipped bounding box of the volume brick for which the compute technique
  /// is required to generate values. This bounding box describes the part of the
  /// complete volume brick which is potentially visible due to clipping the volume
  /// dataset by the global or volume defined regions of interest. This bounding box
  /// includes border voxels of the volume brick.
  ///
  /// The clipped bounding box describes the parts of the volume that are required to
  /// be written in order to achieve a consistent rendered image of the volume dataset.
  ///
  /// The rendering system computes and initializes the bounding box of the volume brick.
  ///
  /// \return     The clipped bounding box of the volume brick, defined in non-normalized
  ///             volume coordinates.
  ///
  virtual mi::math::Bbox_struct<mi::Sint32, 3> get_clipped_volume_brick_bbox() const = 0;

  /// Returns the bounding box of the volume brick for which the compute technique
  /// is required to generate values containing already computed voxel values.
  ///
  /// This bounding box can be used to reduce the computing workload when parts of
  /// the volume brick were already generated by a previous invocation of the compute
  /// technique, for instance when the global or volume defined regions of interest
  /// have changed.
  ///
  /// The rendering system computes and initializes the bounding box of the volume brick.
  ///
  /// \return     The bounding box of the volume brick containing already computed voxel
  ///             values, defined in non-normalized volume coordinates.
  ///
  virtual mi::math::Bbox_struct<mi::Sint32, 3> get_already_computed_volume_brick_bbox() const = 0;
};

/// @ingroup nv_index_data_computing
///
/// Intermediate-interface class for all typed volume compute-destination buffer interfaces.
/// According
/// to the actual dataset component type (e.g. Uint8, Uint16, Float32, Rgba8) the volume data can be
/// written to the destination buffer.
///
template <typename T>
class IDistributed_compute_destination_buffer_3d_volume_typed
  : public mi::base::Interface_declare<0x7b12fdb4, 0xd84c, 0x40a2, 0x9d, 0x6f, 0x24, 0xda, 0x8c,
      0xc1, 0xdd, 0x72, nv::index::IDistributed_compute_destination_buffer_3d_volume>
{
public:
  typedef T Value_type;

  /// Definition of internal buffer information.
  ///
  /// The internal buffer information can be queried using the \c get_internal_buffer_info() method
  /// to gain access to the internal buffer data for direct write operations. This enables zero-copy
  /// optimizations for implementations of \c IDistributed_compute_technique where large parts of
  /// the destination
  /// buffer can be written directly without going through the \c write() methods.
  ///
  /// \note The internal layout of the buffer of a volume brick is in a linear IJK layout with the
  ///       K-component running fastest.
  ///
  struct Internal_buffer_info
  {
    Value_type* data;         ///< Typed pointer to internal buffer data.
    mi::Size size;            ///< The size of the buffer in number of type elements.
    mi::Sint32 gpu_device_id; ///< GPU device id if the buffer is located on a GPU device,
                              ///< -1 to indicate a host buffer.
    bool is_pinned_memory;    ///< Flag indicating if a host buffer is a pinned (page-locked)
                              ///< memory area.
  };

public:
  /// Write a volume block to the destination buffer from a memory block in main memory.
  ///
  /// \param[in]  dst_range       Bounding box describing the offset and size of the volume
  ///                             block that will be written, defined in non-normalized
  ///                             volume coordinates.
  /// \param[in]  src_values      Source array of voxel values that will be written to
  ///                             this destination buffer.
  /// \param[in]  gpu_device_id   GPU device id if the passed buffer is located on a GPU device,
  ///                             otherwise this needs to be -1 to for a host memory buffer.
  /// \param[in]  transformation  Data transformation that should be applied while writing the
  ///                             volume block to the destination buffer.
  ///
  /// \returns                    True if the write operation was successful, false otherwise.
  ///
  virtual bool write(const mi::math::Bbox_struct<mi::Sint32, 3>& dst_range, const T* src_values,
    mi::Sint32 gpu_device_id, Data_transformation transformation = DATA_TRANSFORMATION_NONE) = 0;

  /// Write a volume block to the destination buffer from a \c IRDMA_buffer.
  ///
  /// \note The \c IRDMA_buffer can be located either in CPU or GPU memory. If a GPU-memory
  ///       buffer is passed additional memory copy operations may occur.
  ///
  /// \param[in]  dst_range       Bounding box describing the offset and size of the volume
  ///                             block that will be written, defined in non-normalized
  ///                             volume coordinates.
  /// \param[in]  src_rdma_buffer Source RDMA buffer of voxel values that will be written to
  ///                             this destination buffer.
  ///
  /// \returns                    True if the write operation was successful, false otherwise.
  ///
  virtual bool write(const mi::math::Bbox_struct<mi::Sint32, 3>& dst_range,
    const mi::neuraylib::IRDMA_buffer* src_rdma_buffer) = 0;

  /// Write multiple volume blocks to the destination buffer from a single \c IRDMA_buffer.
  /// The packing of the volume sub-blocks to be written from the \c IRDMA_buffer is defined
  /// in the passed \c src_rdma_buffer_offsets parameter. It this parameter is 0, the volume
  /// sub-blocks need to be tightly packed after each other in the source buffer.
  ///
  /// \note The \c IRDMA_buffer can be located either in CPU or GPU memory. If a GPU-memory
  ///       buffer is passed additional memory copy operations may occur.
  ///
  /// \param[in]  dst_ranges              Bounding boxes describing the offsets and sizes of the
  /// volume
  ///                                     blocks that will be written, defined in non-normalized
  ///                                     volume coordinates.
  /// \param[in]  nb_dst_ranges           The number of dst_range bounding boxes.
  /// \param[in]  src_rdma_buffer_offsets Array defining the offsets of the individual volume
  /// sub-blocks
  ///                                     in the linear source buffer. These offsets need to be
  ///                                     defined
  ///                                     as numbers of typed elements according the the voxel type
  ///                                     of
  ///                                     the volume brick. If this parameter is 0, the offsets will
  ///                                     be derived from the \c dst_ranges parameter assuming a
  ///                                     tight
  ///                                     packing of the volume sub-blocks in the source buffer.
  /// \param[in]  src_rdma_buffer         Source RDMA buffer of voxel values that will be written to
  ///                                     this destination buffer.
  ///
  /// \returns                            True if the write operation was successful, false
  /// otherwise.
  ///
  virtual bool write_multiple(const mi::math::Bbox_struct<mi::Sint32, 3>* dst_ranges,
    mi::Uint32 nb_dst_ranges, const mi::Size* src_rdma_buffer_offsets,
    const mi::neuraylib::IRDMA_buffer* src_rdma_buffer) = 0;

  /// Query the internal buffer information of the destination buffer instance to gain access
  /// to the internal buffer data for direct write operations.
  ///
  /// \returns                    An instance of \c Internal_buffer_info describing the internal
  ///                             buffer data for direct use.
  ///
  virtual Internal_buffer_info get_internal_buffer_info() const = 0;

  /// Creates an RDMA buffer that is a wrapper for of the internal buffer.
  ///
  /// \param[in]  rdma_ctx        The RDMA context used for wrapping the internal buffer.
  ///
  /// \returns                    he RDMA buffer wrapping the internal buffer data,
  ///                             or 0 in case of failure.
  ///
  virtual mi::neuraylib::IRDMA_buffer* wrap_internal_buffer(
    mi::neuraylib::IRDMA_context* rdma_ctx) const = 0;
};

/// @ingroup nv_index_data_computing
///
/// Single-channel 8bit unsigned per voxel volume compute destination buffer.
///
class IDistributed_compute_destination_buffer_3d_volume_uint8
  : public mi::base::Interface_declare<0x45065a39, 0xa261, 0x453f, 0xa7, 0xb4, 0x1, 0x25, 0xae,
      0x9c, 0x4, 0x4f, IDistributed_compute_destination_buffer_3d_volume_typed<mi::Uint8> >
{
};

/// @ingroup nv_index_data_computing
///
/// Single-channel 16bit unsigned per voxel volume compute destination buffer.
///
class IDistributed_compute_destination_buffer_3d_volume_uint16
  : public mi::base::Interface_declare<0x9fd3ee6d, 0x34ad, 0x4bc0, 0xa2, 0x16, 0x65, 0xb2, 0xb7,
      0x1e, 0x3b, 0x96, IDistributed_compute_destination_buffer_3d_volume_typed<mi::Uint16> >
{
};

/// @ingroup nv_index_data_computing
///
/// Single-channel 32bit floating point per voxel volume compute destination buffer.
///
class IDistributed_compute_destination_buffer_3d_volume_float32
  : public mi::base::Interface_declare<0xe8347ed0, 0x7980, 0x4ae8, 0xa1, 0xf3, 0x4b, 0x50, 0xd6,
      0xa, 0xcd, 0x24, IDistributed_compute_destination_buffer_3d_volume_typed<mi::Float32> >
{
};

/// @ingroup nv_index_data_computing
///
/// Four-channel 8bit per channel unsigned integer per voxel volume compute destination buffer.
///
class IDistributed_compute_destination_buffer_3d_volume_rgba8
  : public mi::base::Interface_declare<0xffb5524d, 0xc006, 0x41e9, 0x9e, 0x99, 0xfc, 0x6e, 0xd0,
      0xb, 0x38, 0x10, IDistributed_compute_destination_buffer_3d_volume_typed<
                                         mi::math::Vector_struct<mi::Uint8, 4> > >
{
};

/// @ingroup nv_index_data_computing
///
/// The interface class exposes intersections points that can be used, for
/// instance, to texture or shade the surface of geometry.
///
/// The IndeX library implements the serializable interface, which enables
/// user-defined classes to distribute an instance of the class to different
/// hosts in the cluster environment using DiCE's serialization and
/// deserialization mechanism.  Furthermore, the interface class exposes the
/// intersections points through the interface method
/// generate_intersection_points(). The internal implementation of this method
/// creates the intersection points on the fly whenever the method is
/// called. That is, all the intersection points never need to be communicated
/// to nodes in the cluster. Instead, the instance of the interface class itself
/// is sent to compute the intersection points on the remote machines on
/// request.
///
class IDistributed_compute_intersection_points
  : public mi::base::Interface_declare<0xd985cb6a, 0xe5c2, 0x4c40, 0x95, 0xe3, 0x29, 0xa7, 0x22,
      0x3e, 0x35, 0x9f, mi::neuraylib::ISerializable>
{
public:
  /// The interface method computes the intersections of rays with the
  /// geometry and returns a buffer of all intersection points to a
  /// user-defined computing class.  The renderer computes the intersection
  /// points based on the present viewing parameters so that all intersection
  /// points match the internal rendering.
  ///
  /// \param[out] nb_points The number of intersection points returned by the
  ///                       interface call.
  ///
  /// \return               The intersection points. They are defined in the local
  ///                       coordinate system of the geometry, that is, x and
  ///                       y will be in the range [0, extent], while the
  ///                       z-component will be 0. The ownership remains with the
  ///                       interface class.
  ///
  virtual const mi::math::Vector_struct<mi::Float32, 3>* generate_intersection_points(
    mi::Uint32& nb_points) = 0;
};

/// @ingroup nv_index_data_computing
///
/// Compute-destination buffer for 2D texture generation techniques.
///
/// Upon applying an \c IDistributed_compute_technique attribute to a \c IPlane or \c
/// IRegular_heightfield scene element
/// a \c IDistributed_compute_destination_buffer_2d_texture is passed to the \c
/// IDistributed_compute_technique::launch_compute()
/// method.
///
/// Based on the 2D area returned by \c get_screen_space_area() a user-defined compute technique can
/// choose, for
/// instance, an appropriate level-of-detail or the least compute-intensive generation technique,
/// for example,
/// by either rendering into a 2D buffer or a buffer that corresponds to the ray/geometry
/// intersection.
///
class IDistributed_compute_destination_buffer_2d_texture
  : public mi::base::Interface_declare<0x1ea87c20, 0xdae0, 0x4b81, 0xa3, 0x16, 0xa0, 0x52, 0x2d,
      0xf7, 0xb4, 0x3c, nv::index::IDistributed_compute_destination_buffer>
{
public:
  /// The layout of the buffer.
  ///
  /// The buffer's layout is defined by the user-defined texture generation
  /// technique.
  ///
  /// Setting the buffer layout to \c TWO_DIMENSIONAL_ARRAY is in most
  /// situations the most appropriate way to represent a texture tile. Then,
  /// all texture values are stored in consecutive order in x-first and y-last
  /// order and the value x=0 and y=0 represents the lower left texture value
  /// of the texture tile.
  ///
  /// The texture generation technique could set the layout to \c
  /// ONE_DIMENSIONAL_ARRAY to indicate that every entry in the texture buffer
  /// corresponds to a texture value for precomputed ray/object
  /// intersection. A ray/object intersection is only meaningful if the screen
  /// space area covered by the subregion is small compared to the size of the
  /// tile.
  ///
  enum Buffer_layout
  {
    ONE_DIMENSIONAL_ARRAY =
      0, ///< Buffer layout that corresponds to a 1D array of ray/geometry intersections
    TWO_DIMENSIONAL_ARRAY = 1 ///< Buffer layout that corresponds to a 2D array
  };

  /// Texture destination buffer format description.
  ///
  /// The buffer format defines the type and number of components of each texture element.
  ///
  enum Buffer_format
  {
    INTENSITY_UINT8 = 0, ///< 8-bit integer used for indexing a color table of 256 entries
    RGBA_UINT8 = 1,      ///< 8-bit integer per RGBA component
    RGBA_FLOAT32 = 2     ///< 32-bit float per RGBA component
  };

  /// Texture buffer configuration.
  ///
  /// This structure defines the basic buffer parameters required by the rendering system to
  /// allocate the required buffer storage (see \c generate_buffer_storage).
  ///
  /// The covered area is the 2D area of the geometry's surface that a 2D texture tile covers.
  /// The covered area is typically larger than the object's surface area because the returned
  /// 2D tile has to have integral width and height resolution while the surface area is given
  /// in floating point values. (See \c get_surface_area().)
  ///
  /// The associated compute technique has to compute and set the covered area. The covered area
  /// is required to enable the calling rendering system to compute the texture coordinates to
  /// correctly texture map the tile onto the geometry's 2D surface area.
  ///
  /// If the texture buffer layout specifies a 2D array, then the 2D resolution of the 2D tile needs
  /// to be specified. The resolution needs to be known to the calling rendering system to enable
  /// valid accesses to the texture's 2D array.
  ///
  /// If the texture buffer layout specifies an array of isolated texture values, then the
  /// resolution
  /// needs to be specified as (number of intersection points, 0) and each buffer entry at the end
  /// needs
  /// to correspond to exactly one intersection point and the number of texture values has to
  /// correspond to
  /// the number of intersections.
  ///
  struct Buffer_config
  {
    Buffer_layout layout;
    /// Texture destination buffer format.
    Buffer_format format;
    mi::math::Bbox_struct<mi::Float32, 2> covered_area;
    mi::math::Vector_struct<mi::Uint32, 2> resolution;
  };

  /// Generate buffer storage.
  ///
  /// This function initializes the buffer storage according to the passed instance of \c
  /// Buffer_config.
  /// At any point during the lifetime of an \c IDistributed_compute_destination_buffer_2d_texture
  /// instance there can only be a
  /// single valid buffer storage. Multiple calls to this function will invalidate previous
  /// instances of the buffer
  /// storage and generate a new valid instance according the newly passed buffer config.
  ///
  /// \param[in]  buffer_cfg      The texture buffer config parameters.
  ///
  /// \return                     True if a valid storage was generated, false otherwise.
  ///
  virtual bool generate_buffer_storage(const Buffer_config& buffer_cfg) = 0;

  /// Get the texture buffer config parameters of the currently valid buffer storage.
  ///
  /// When this function is called before initializing a valid buffer storage (\c
  /// generate_buffer_storage()) the
  /// returned instance of \c Buffer_config will describe an invalid mesh (i.e. covered_area and
  /// resolution will
  /// describe an empty buffer).
  ///
  /// \return     The texture buffer config parameters of the currently valid buffer storage.
  ///
  virtual const Buffer_config& get_buffer_config() const = 0;

  /// Get the current valid texture buffer storage.
  ///
  /// When this function is called before initializing a valid buffer storage (\c
  /// generate_buffer_storage()) the
  /// returned value will be invalid (i.e. value \c 0). The ownership with the returned buffer
  /// remains with
  /// the instance of \c IDistributed_compute_destination_buffer_2d_texture.
  ///
  /// \return     The currently valid texture buffer storage.
  ///
  virtual mi::Uint8* get_buffer_storage() const = 0;

  /// Returns the per-subregion data of the geometry associated with this
  /// buffer.  The data type depends on the type of geometry:
  /// - For heightfields, the data type is \c IRegular_heightfield_patch,
  ///   which allows access to individual height values.
  /// - Planes do not provide per-subregion data.
  ///
  /// \return Per-subregion data or 0 if no data is available.
  virtual const IDistributed_data_subset* get_subregion_geometry_data() const = 0;

  /// Returns the 2D surface area for which the generation of the texture
  /// buffer is requested.
  ///
  /// It is not required that the texture generation technique computes a
  /// texture that covers the 2D area exactly, but the texture usually is
  /// slightly larger. For that, the covered area has to be set appropriately
  /// by the user-defined texturing technique. (See set_covered_area().)
  ///
  /// \return The 2D area that shall be textured by the returned texture.  The
  ///         area is defined in the object's local coordinate system.
  ///
  virtual mi::math::Bbox_struct<mi::Float32, 2> get_surface_area() const = 0;

  /// The texture generation can rely on pre-computed ray/geometry
  /// intersections. The interface method exposes all ray/geometry
  /// intersection points with respect to the given subregion. The
  /// intersection points enable the technique to compute the corresponding
  /// texture values.
  ///
  /// \return An instance of the class \c IDistributed_compute_intersection_points that contains all
  ///         ray/geometry intersection points given in the object's local
  ///         coordinate system. The caller is responsible to releasing the
  ///         object.
  ///
  virtual IDistributed_compute_intersection_points* get_intersection_points() const = 0;
};

} // namespace index
} // namespace nv

#endif // NVIDIA_INDEX_IDISTRIBUTED_COMPUTE_DESTINATION_BUFFER_H
