/*
 * Copyright (c) Advanced Micro Devices, Inc., or its affiliates.
 *
 * SPDX-License-Identifier: MIT
 */

/*
 * Mesa 3-D graphics library
 */

/* Mesa OpenGL inter-driver interoperability interface designed for but not
 * limited to OpenCL.
 *
 * This is a driver-agnostic, backward-compatible interface. The structures
 * are only allowed to grow. They can never shrink and their members can
 * never be removed, renamed, or redefined.
 *
 * The interface doesn't return a lot of static texture parameters like
 * width, height, etc. It mainly returns mutable buffer and texture view
 * parameters that can't be part of the texture allocation (because they are
 * mutable). If drivers want to return more data or want to return static
 * allocation parameters, they can do it in one of these two ways:
 * - attaching the data to the DMABUF handle in a driver-specific way
 * - passing the data via "out_driver_data" in the "in" structure.
 *
 * Mesa is expected to do a lot of error checking on behalf of OpenCL, such
 * as checking the target, miplevel, and texture completeness.
 *
 * OpenCL, on the other hand, needs to check if the display+context combo
 * is compatible with the OpenCL driver by querying the device information.
 * It also needs to check if the texture internal format and channel ordering
 * (returned in a driver-specific way) is supported by OpenCL, among other
 * things.
 */

#ifndef MESA_GLINTEROP_H
#define MESA_GLINTEROP_H

#include <stddef.h>
#include <stdint.h>

#ifdef __cplusplus
extern "C" {
#endif

/* Forward declarations to avoid inclusion of GL/glx.h */
#ifndef GLX_H
struct _XDisplay;
struct __GLXcontextRec;
#endif

/* Forward declarations to avoid inclusion of EGL/egl.h */
#ifndef __egl_h_
typedef void* EGLDisplay;
typedef void* EGLContext;
#endif

/** Returned error codes. */
enum {
  MESA_GLINTEROP_SUCCESS = 0,
  MESA_GLINTEROP_OUT_OF_RESOURCES,
  MESA_GLINTEROP_OUT_OF_HOST_MEMORY,
  MESA_GLINTEROP_INVALID_OPERATION,
  MESA_GLINTEROP_INVALID_VERSION,
  MESA_GLINTEROP_INVALID_DISPLAY,
  MESA_GLINTEROP_INVALID_CONTEXT,
  MESA_GLINTEROP_INVALID_TARGET,
  MESA_GLINTEROP_INVALID_OBJECT,
  MESA_GLINTEROP_INVALID_MIP_LEVEL,
  MESA_GLINTEROP_UNSUPPORTED
};

/** Access flags. */
enum {
  MESA_GLINTEROP_ACCESS_READ_WRITE = 0,
  MESA_GLINTEROP_ACCESS_READ_ONLY,
  MESA_GLINTEROP_ACCESS_WRITE_ONLY
};

#define MESA_GLINTEROP_DEVICE_INFO_VERSION 1

/**
 * Device information returned by Mesa.
 */
struct mesa_glinterop_device_info {
  /* The caller should set this to the version of the struct they support */
  /* The callee will overwrite it if it supports a lower version.
   *
   * The caller should check the value and access up-to the version supported
   * by the callee.
   */
  /* NOTE: Do not use the MESA_GLINTEROP_DEVICE_INFO_VERSION macro */
  uint32_t version;

  /* PCI location */
  uint32_t pci_segment_group;
  uint32_t pci_bus;
  uint32_t pci_device;
  uint32_t pci_function;

  /* Device identification */
  uint32_t vendor_id;
  uint32_t device_id;

  /* Structure version 1 ends here. */
};

#define MESA_GLINTEROP_EXPORT_IN_VERSION 1

/**
 * Input parameters to Mesa interop export functions.
 */
struct mesa_glinterop_export_in {
  /* The caller should set this to the version of the struct they support */
  /* The callee will overwrite it if it supports a lower version.
   *
   * The caller should check the value and access up-to the version supported
   * by the callee.
   */
  /* NOTE: Do not use the MESA_GLINTEROP_EXPORT_IN_VERSION macro */
  uint32_t version;

  /* One of the following:
   * - GL_TEXTURE_BUFFER
   * - GL_TEXTURE_1D
   * - GL_TEXTURE_2D
   * - GL_TEXTURE_3D
   * - GL_TEXTURE_RECTANGLE
   * - GL_TEXTURE_1D_ARRAY
   * - GL_TEXTURE_2D_ARRAY
   * - GL_TEXTURE_CUBE_MAP_ARRAY
   * - GL_TEXTURE_CUBE_MAP
   * - GL_TEXTURE_CUBE_MAP_POSITIVE_X
   * - GL_TEXTURE_CUBE_MAP_NEGATIVE_X
   * - GL_TEXTURE_CUBE_MAP_POSITIVE_Y
   * - GL_TEXTURE_CUBE_MAP_NEGATIVE_Y
   * - GL_TEXTURE_CUBE_MAP_POSITIVE_Z
   * - GL_TEXTURE_CUBE_MAP_NEGATIVE_Z
   * - GL_TEXTURE_2D_MULTISAMPLE
   * - GL_TEXTURE_2D_MULTISAMPLE_ARRAY
   * - GL_TEXTURE_EXTERNAL_OES
   * - GL_RENDERBUFFER
   * - GL_ARRAY_BUFFER
   */
  unsigned target;

  /* If target is GL_ARRAY_BUFFER, it's a buffer object.
   * If target is GL_RENDERBUFFER, it's a renderbuffer object.
   * If target is GL_TEXTURE_*, it's a texture object.
   */
  unsigned obj;

  /* Mipmap level. Ignored for non-texture objects. */
  unsigned miplevel;

  /* One of MESA_GLINTEROP_ACCESS_* flags. This describes how the exported
   * object is going to be used.
   */
  uint32_t access;

  /* Size of memory pointed to by out_driver_data. */
  uint32_t out_driver_data_size;

  /* If the caller wants to query driver-specific data about the OpenGL
   * object, this should point to the memory where that data will be stored.
   * This is expected to be a temporary staging memory. The pointer is not
   * allowed to be saved for later use by Mesa.
   */
  void* out_driver_data;
  /* Structure version 1 ends here. */
};

#define MESA_GLINTEROP_EXPORT_OUT_VERSION 1

/**
 * Outputs of Mesa interop export functions.
 */
struct mesa_glinterop_export_out {
  /* The caller should set this to the version of the struct they support */
  /* The callee will overwrite it if it supports a lower version.
   *
   * The caller should check the value and access up-to the version supported
   * by the callee.
   */
  /* NOTE: Do not use the MESA_GLINTEROP_EXPORT_OUT_VERSION macro */
  uint32_t version;

  /* The DMABUF handle. It must be closed by the caller using the POSIX
   * close() function when it's not needed anymore. Mesa is not responsible
   * for closing the handle.
   *
   * Not closing the handle by the caller will lead to a resource leak,
   * will prevent releasing the GPU buffer, and may prevent creating new
   * DMABUF handles within the process.
   */
  int dmabuf_fd;

  /* The mutable OpenGL internal format specified by glTextureView or
   * glTexBuffer. If the object is not one of those, the original internal
   * format specified by glTexStorage, glTexImage, or glRenderbufferStorage
   * will be returned.
   */
  unsigned internal_format;

  /* Buffer offset and size for GL_ARRAY_BUFFER and GL_TEXTURE_BUFFER.
   * This allows interop with suballocations (a buffer allocated within
   * a larger buffer).
   *
   * Parameters specified by glTexBufferRange for GL_TEXTURE_BUFFER are
   * applied to these and can shrink the range further.
   */
  ptrdiff_t buf_offset;
  ptrdiff_t buf_size;

  /* Parameters specified by glTextureView. If the object is not a texture
   * view, default parameters covering the whole texture will be returned.
   */
  unsigned view_minlevel;
  unsigned view_numlevels;
  unsigned view_minlayer;
  unsigned view_numlayers;

  /* The number of bytes written to out_driver_data. */
  uint32_t out_driver_data_written;
  /* Structure version 1 ends here. */
};

#if IS_LINUX
/**
 * Query device information.
 *
 * \param dpy        GLX display
 * \param context    GLX context
 * \param out        where to return the information
 *
 * \return MESA_GLINTEROP_SUCCESS or MESA_GLINTEROP_* != 0 on error
 */
int MesaGLInteropGLXQueryDeviceInfo(struct _XDisplay* dpy, struct __GLXcontextRec* context,
                                    struct mesa_glinterop_device_info* out);


/**
 * Same as MesaGLInteropGLXQueryDeviceInfo except that it accepts EGLDisplay
 * and EGLContext.
 */
int MesaGLInteropEGLQueryDeviceInfo(EGLDisplay dpy, EGLContext context,
                                    struct mesa_glinterop_device_info* out);


/**
 * Create and return a DMABUF handle corresponding to the given OpenGL
 * object, and return other parameters about the OpenGL object.
 *
 * \param dpy        GLX display
 * \param context    GLX context
 * \param in         input parameters
 * \param out        return values
 *
 * \return MESA_GLINTEROP_SUCCESS or MESA_GLINTEROP_* != 0 on error
 */
int MesaGLInteropGLXExportObject(struct _XDisplay* dpy, struct __GLXcontextRec* context,
                                 struct mesa_glinterop_export_in* in,
                                 struct mesa_glinterop_export_out* out);


/**
 * Same as MesaGLInteropGLXExportObject except that it accepts
 * EGLDisplay and EGLContext.
 */
int MesaGLInteropEGLExportObject(EGLDisplay dpy, EGLContext context,
                                 struct mesa_glinterop_export_in* in,
                                 struct mesa_glinterop_export_out* out);


typedef int(PFNMESAGLINTEROPGLXQUERYDEVICEINFOPROC)(struct _XDisplay* dpy,
                                                    struct __GLXcontextRec* context,
                                                    struct mesa_glinterop_device_info* out);
typedef int(PFNMESAGLINTEROPEGLQUERYDEVICEINFOPROC)(EGLDisplay dpy, EGLContext context,
                                                    struct mesa_glinterop_device_info* out);
typedef int(PFNMESAGLINTEROPGLXEXPORTOBJECTPROC)(struct _XDisplay* dpy,
                                                 struct __GLXcontextRec* context,
                                                 struct mesa_glinterop_export_in* in,
                                                 struct mesa_glinterop_export_out* out);
typedef int(PFNMESAGLINTEROPEGLEXPORTOBJECTPROC)(EGLDisplay dpy, EGLContext context,
                                                 struct mesa_glinterop_export_in* in,
                                                 struct mesa_glinterop_export_out* out);

#endif  // IS_LINUX

#ifdef __cplusplus
}
#endif

#endif /* MESA_GLINTEROP_H */
