Discussion:
GBM Surface
Kristian Høgsberg
2012-03-27 19:08:26 UTC
Permalink
Hi,

Here's a patch series from Ander to implement a new gbm feature: gbm
surface. The gbm library is useful for allocating surface for scanout
and rendering, but the application needs to create EGLImages for the
gbm bos and then renderbuffers and then render using an FBO. On top
of that, we have to rely on the (awkward) surfaceless extension to
make it all work. So gbm surface gives us a few things:

1) Independent from KMS. The API could be a lot simpler if we just
could say "EGL surface on this KMS crtc", we didn't want to tie it
to KMS for a few reasons: we need to be able to pageflip to gbm bos
from different sources (wayland clients); without the kms
integration we can do headless EGL; gbm is usable with OpenWF as
well; and finally, we may be able to implement the wayland egl
backend in terms of gbm surface, which will greatly simplify that.

2) Go through eglSwapBuffers as a end-of-frame indicator. This is
mainly to cover our back on architectures that require explicit
synchronization between multiple queues, or for deferred rendering
architectures that relies on eglSwapBuffers to trigger the actual
rendering.

3) Let the EGL implementation manage number of buffers (double or
triple buffer decision etc)

4) Provide a native window type for EGL on GBM. We avoid the
surfaceless extension.

5) Reduce boilerplate code for applications using GBM, EGL and KMS.

Here's how the eglkms.c example looks converted to gbm surface.

http://cgit.freedesktop.org/~krh/mesa-demos/diff/src/egl/opengl/eglkms.c?id=1c78c24366c24786eff3e08132754f7876539e57

thanks,
Kristian
Kristian Høgsberg
2012-03-27 19:11:17 UTC
Permalink
Post by Kristian Høgsberg
Hi,
Here's a patch series from Ander to implement a new gbm feature: gbm
surface.  The gbm library is useful for allocating surface for scanout
and rendering, but the application needs to create EGLImages for the
gbm bos and then renderbuffers and then render using an FBO.  On top
of that, we have to rely on the (awkward) surfaceless extension to
1) Independent from KMS.  The API could be a lot simpler if we just
  could say "EGL surface on this KMS crtc", we didn't want to tie it
  to KMS for a few reasons: we need to be able to pageflip to gbm bos
  from different sources (wayland clients); without the kms
  integration we can do headless EGL; gbm is usable with OpenWF as
  well; and finally, we may be able to implement the wayland egl
  backend in terms of gbm surface, which will greatly simplify that.
2) Go through eglSwapBuffers as a end-of-frame indicator.  This is
  mainly to cover our back on architectures that require explicit
  synchronization between multiple queues, or for deferred rendering
  architectures that relies on eglSwapBuffers to trigger the actual
  rendering.
3) Let the EGL implementation manage number of buffers (double or
  triple buffer decision etc)
4) Provide a native window type for EGL on GBM.  We avoid the
  surfaceless extension.
5) Reduce boilerplate code for applications using GBM, EGL and KMS.
Here's how the eglkms.c example looks converted to gbm surface.
http://cgit.freedesktop.org/~krh/mesa-demos/diff/src/egl/opengl/eglkms.c?id=1c78c24366c24786eff3e08132754f7876539e57
Ooops, wrong link, try this one:

http://cgit.freedesktop.org/~krh/mesa-demos/tree/src/egl/opengl/eglkms.c?h=gbm-surface

Kristian
Kristian Høgsberg
2012-03-27 19:08:27 UTC
Permalink
From: Ander Conselvan de Oliveira <***@intel.com>

The idea here is to be able to create an egl window surface from a
gbm_surface. This avoids the need for the surfaceless extension and
lets the EGL platform handle buffer allocation, while keeping the user
in charge of somehow presenting the buffers (using kms page flipping,
for example).

gbm_surface_lock_front_buffer() locks a surface's front buffer and
returns a gbm bo representing it. This bo should later be returned
to the gbm surface using gbm_surface_release_buffer().
---
src/gbm/backends/dri/gbm_dri.c | 49 +++++++++++++++++
src/gbm/backends/dri/gbm_driint.h | 15 +++++
src/gbm/main/gbm.c | 106 +++++++++++++++++++++++++++++++++++-
src/gbm/main/gbm.h | 17 ++++++
src/gbm/main/gbmint.h | 20 ++++++-
5 files changed, 202 insertions(+), 5 deletions(-)

diff --git a/src/gbm/backends/dri/gbm_dri.c b/src/gbm/backends/dri/gbm_dri.c
index 1e02287..305938b 100644
--- a/src/gbm/backends/dri/gbm_dri.c
+++ b/src/gbm/backends/dri/gbm_dri.c
@@ -387,6 +387,49 @@ gbm_dri_bo_create(struct gbm_device *gbm,
return &bo->base.base;
}

+static struct gbm_surface *
+gbm_dri_surface_create(struct gbm_device *gbm,
+ uint32_t width, uint32_t height, uint32_t format)
+{
+ struct gbm_dri_surface *surf;
+
+ surf = calloc(1, sizeof *surf);
+ if (surf == NULL)
+ return NULL;
+
+ surf->base.gbm = gbm;
+ surf->base.width = width;
+ surf->base.height = height;
+ surf->base.format = format;
+
+ return &surf->base;
+}
+
+static void
+gbm_dri_surface_destroy(struct gbm_surface *_surf)
+{
+ struct gbm_dri_surface *surf = gbm_dri_surface(_surf);
+
+ free(surf);
+}
+
+static struct gbm_bo *
+gbm_dri_surface_lock_front_buffer(struct gbm_surface *_surf)
+{
+ return NULL;
+}
+
+static void
+gbm_dri_surface_release_buffer(struct gbm_surface *_surf, struct gbm_bo *_bo)
+{
+}
+
+static int
+gbm_dri_surface_has_free_buffers(struct gbm_surface *_surf)
+{
+ return 0;
+}
+
static void
dri_destroy(struct gbm_device *gbm)
{
@@ -414,6 +457,12 @@ dri_device_create(int fd)
dri->base.base.is_format_supported = gbm_dri_is_format_supported;
dri->base.base.bo_destroy = gbm_dri_bo_destroy;
dri->base.base.destroy = dri_destroy;
+ dri->base.base.surface_create = gbm_dri_surface_create;
+ dri->base.base.surface_destroy = gbm_dri_surface_destroy;
+ dri->base.base.surface_lock_front_buffer =
+ gbm_dri_surface_lock_front_buffer;
+ dri->base.base.surface_release_buffer = gbm_dri_surface_release_buffer;
+ dri->base.base.surface_has_free_buffers = gbm_dri_surface_has_free_buffers;

dri->base.type = GBM_DRM_DRIVER_TYPE_DRI;
dri->base.base.name = "drm";
diff --git a/src/gbm/backends/dri/gbm_driint.h b/src/gbm/backends/dri/gbm_driint.h
index d801a08..514b5a6 100644
--- a/src/gbm/backends/dri/gbm_driint.h
+++ b/src/gbm/backends/dri/gbm_driint.h
@@ -61,6 +61,15 @@ struct gbm_dri_bo {
__DRIimage *image;
};

+struct gbm_dri_surface {
+ struct gbm_surface base;
+
+ __DRIbuffer *(*get_front_buffer)(struct gbm_dri_surface *, void *);
+ void (*release_buffer)(struct gbm_dri_surface *, __DRIbuffer *, void *);
+ int (*has_free_buffers)(void *);
+ void *dri_private;
+};
+
static inline struct gbm_dri_device *
gbm_dri_device(struct gbm_device *gbm)
{
@@ -73,6 +82,12 @@ gbm_dri_bo(struct gbm_bo *bo)
return (struct gbm_dri_bo *) bo;
}

+static inline struct gbm_dri_surface *
+gbm_dri_surface(struct gbm_surface *surface)
+{
+ return (struct gbm_dri_surface *) surface;
+}
+
char *
dri_fd_get_driver_name(int fd);

diff --git a/src/gbm/main/gbm.c b/src/gbm/main/gbm.c
index 9459720..23a6656 100644
--- a/src/gbm/main/gbm.c
+++ b/src/gbm/main/gbm.c
@@ -84,8 +84,7 @@ gbm_device_get_backend_name(struct gbm_device *gbm)
*/
int
gbm_device_is_format_supported(struct gbm_device *gbm,
- enum gbm_bo_format format,
- uint32_t usage)
+ uint32_t format, uint32_t usage)
{
return gbm->is_format_supported(gbm, format, usage);
}
@@ -263,7 +262,7 @@ gbm_bo_destroy(struct gbm_bo *bo)
GBM_EXPORT struct gbm_bo *
gbm_bo_create(struct gbm_device *gbm,
uint32_t width, uint32_t height,
- enum gbm_bo_format format, uint32_t usage)
+ uint32_t format, uint32_t usage)
{
if (width == 0 || height == 0)
return NULL;
@@ -305,3 +304,104 @@ gbm_bo_create_from_egl_image(struct gbm_device *gbm,
return gbm->bo_create_from_egl_image(gbm, egl_dpy, egl_image,
width, height, usage);
}
+
+/**
+ * Allocate a surface object
+ *
+ * \param gbm The gbm device returned from gbm_create_device()
+ * \param width The width for the surface
+ * \param height The height for the surface
+ * \param format The format to use for the surface
+ *
+ * \return A newly allocated surface that should be freed with
+ * gbm_surface_destroy() when no longer needed. If an error occurs
+ * during allocation %NULL will be returned.
+ *
+ * \sa enum gbm_bo_format for the list of formats
+ */
+GBM_EXPORT struct gbm_surface *
+gbm_surface_create(struct gbm_device *gbm,
+ uint32_t width, uint32_t height, uint32_t format)
+{
+ return gbm->surface_create(gbm, width, height, format);
+}
+
+/**
+ * Destroys the given surface and frees all resources associated with
+ * it.
+ *
+ * All buffers locked with gbm_surface_lock_front_buffer() should be
+ * released prior to calling this function.
+ *
+ * \param surf The surface
+ */
+GBM_EXPORT void
+gbm_surface_destroy(struct gbm_surface *surf)
+{
+ surf->gbm->surface_destroy(surf);
+}
+
+/**
+ * Lock the surface's current front buffer
+ *
+ * Lock rendering to the surface's current front buffer until it is
+ * released with gbm_surface_release_buffer().
+ *
+ * This function must be called exactly once after calling
+ * eglSwapBuffers. Calling it before any eglSwapBuffer has happened
+ * on the surface or two or more times after eglSwapBuffers is an
+ * error. A new bo representing the new front buffer is returned. On
+ * multiple invocations, all the returned bos must be released in
+ * order to release the actual surface buffer.
+ *
+ * \param surf The surface
+ *
+ * \return A newly allocated buffer object that should be released
+ * with gbm_surface_release_buffer() when no longer needed. This bo
+ * should not be destroyed using gbm_bo_destroy(). If an error occurs
+ * this function returns %NULL.
+ */
+GBM_EXPORT struct gbm_bo *
+gbm_surface_lock_front_buffer(struct gbm_surface *surf)
+{
+ return surf->gbm->surface_lock_front_buffer(surf);
+}
+
+/**
+ * Release a locked buffer obtained with gbm_surface_lock_front_buffer()
+ *
+ * The bo is destroyed after a call to this function and returns the
+ * underlying buffer to the gbm surface. Releasing a bo will
+ * typically make gbm_surface_has_free_buffer() return 1 and thus
+ * allow rendering the next frame, but not always.
+ *
+ * \param surf The surface
+ * \param bo The buffer object
+ */
+GBM_EXPORT void
+gbm_surface_release_buffer(struct gbm_surface *surf, struct gbm_bo *bo)
+{
+ surf->gbm->surface_release_buffer(surf, bo);
+}
+
+/**
+ * Return whether or not a surface has free (non-locked) buffers
+ *
+ * Before starting a new frame, the surface must have a buffer
+ * available for rendering. Initially, a gbm surface will have a free
+ * buffer, but after one of more buffers have been locked (\sa
+ * gbm_surface_lock_front_buffer()), the application must check for a
+ * free buffer before rendering.
+ *
+ * If a surface doesn't have a free buffer, the application must
+ * return a buffer to the surface using gbm_surface_release_buffer()
+ * and after that, the application can query for free buffers again.
+ *
+ * \param surf The surface
+ * \return 1 if the surface has free buffers, 0 otherwise
+ */
+GBM_EXPORT int
+gbm_surface_has_free_buffers(struct gbm_surface *surf)
+{
+ return surf->gbm->surface_has_free_buffers(surf);
+}
diff --git a/src/gbm/main/gbm.h b/src/gbm/main/gbm.h
index ecebf11..dce2bdf 100644
--- a/src/gbm/main/gbm.h
+++ b/src/gbm/main/gbm.h
@@ -44,6 +44,7 @@ extern "C" {

struct gbm_device;
struct gbm_bo;
+struct gbm_surface;

/**
* \mainpage The Generic Buffer Manager
@@ -247,6 +248,22 @@ gbm_bo_get_handle(struct gbm_bo *bo);
void
gbm_bo_destroy(struct gbm_bo *bo);

+struct gbm_surface *
+gbm_surface_create(struct gbm_device *gbm,
+ uint32_t width, uint32_t height, uint32_t format);
+
+struct gbm_bo *
+gbm_surface_lock_front_buffer(struct gbm_surface *surface);
+
+void
+gbm_surface_release_buffer(struct gbm_surface *surface, struct gbm_bo *bo);
+
+int
+gbm_surface_has_free_buffers(struct gbm_surface *surface);
+
+void
+gbm_surface_destroy(struct gbm_surface *surface);
+
#ifdef __cplusplus
}
#endif
diff --git a/src/gbm/main/gbmint.h b/src/gbm/main/gbmint.h
index 66c4c41..eee7fb6 100644
--- a/src/gbm/main/gbmint.h
+++ b/src/gbm/main/gbmint.h
@@ -59,18 +59,27 @@ struct gbm_device {

void (*destroy)(struct gbm_device *gbm);
int (*is_format_supported)(struct gbm_device *gbm,
- enum gbm_bo_format format,
+ uint32_t format,
uint32_t usage);

struct gbm_bo *(*bo_create)(struct gbm_device *gbm,
uint32_t width, uint32_t height,
- enum gbm_bo_format format,
+ uint32_t format,
uint32_t usage);
struct gbm_bo *(*bo_create_from_egl_image)(struct gbm_device *gbm,
void *egl_dpy, void *egl_img,
uint32_t width, uint32_t height,
uint32_t usage);
void (*bo_destroy)(struct gbm_bo *bo);
+
+ struct gbm_surface *(*surface_create)(struct gbm_device *gbm,
+ uint32_t width, uint32_t height,
+ uint32_t format);
+ struct gbm_bo *(*surface_lock_front_buffer)(struct gbm_surface *surface);
+ void (*surface_release_buffer)(struct gbm_surface *surface,
+ struct gbm_bo *bo);
+ int (*surface_has_free_buffers)(struct gbm_surface *surface);
+ void (*surface_destroy)(struct gbm_surface *surface);
};

/**
@@ -87,6 +96,13 @@ struct gbm_bo {
union gbm_bo_handle handle;
};

+struct gbm_surface {
+ struct gbm_device *gbm;
+ uint32_t width;
+ uint32_t height;
+ uint32_t format;
+};
+
struct gbm_backend {
const char *backend_name;
struct gbm_device *(*create_device)(int fd);
--
1.7.9.1
Jakob Bornecrantz
2012-03-27 19:27:22 UTC
Permalink
+
+   struct gbm_surface *(*surface_create)(struct gbm_device *gbm,
+                                         uint32_t width, uint32_t height,
+                                         uint32_t format);
+   struct gbm_bo *(*surface_lock_front_buffer)(struct gbm_surface *surface);
+   void (*surface_release_buffer)(struct gbm_surface *surface,
+                                  struct gbm_bo *bo);
+   int (*surface_has_free_buffers)(struct gbm_surface *surface);
+   void (*surface_destroy)(struct gbm_surface *surface);
Surface map and unmap needs a pipe_context for gallium,
and as such dri_context. I don't know what the best way to solve
that in gbm, either via a hidden dri_context[1] or via a
new gbm_context.

Cheers, Jakob.

[1] which I don't like since we can't get to it if with
fences since if we actually want to support better command
scheduling then what we currently have.
Kristian Høgsberg
2012-03-27 19:57:01 UTC
Permalink
Post by Jakob Bornecrantz
+
+   struct gbm_surface *(*surface_create)(struct gbm_device *gbm,
+                                         uint32_t width, uint32_t height,
+                                         uint32_t format);
+   struct gbm_bo *(*surface_lock_front_buffer)(struct gbm_surface *surface);
+   void (*surface_release_buffer)(struct gbm_surface *surface,
+                                  struct gbm_bo *bo);
+   int (*surface_has_free_buffers)(struct gbm_surface *surface);
+   void (*surface_destroy)(struct gbm_surface *surface);
Surface map and unmap needs a pipe_context for gallium,
and as such dri_context. I don't know what the best way to solve
that in gbm, either via a hidden dri_context[1] or via a
new gbm_context.
Maybe the lock/release names are confusing, but this isn't about cpu
access, it's about getting a gbm bo that corresponds to the frame just
rendered. We've talked about adding map/unmap to gbm bos, but that's
not what this series is about.

Kristian
Post by Jakob Bornecrantz
Cheers, Jakob.
[1] which I don't like since we can't get to it if with
fences since if we actually want to support better command
scheduling then what we currently have.
Kristian Høgsberg
2012-03-27 19:08:28 UTC
Permalink
From: Ander Conselvan de Oliveira <***@intel.com>

Pass a dri2_loader extension to the dri driver when gbm creates the dri
screen. The implementation jumps through pointers in the gbm device
so that an EGL on GBM implementation can provide the real implementations.
---
src/gbm/backends/dri/gbm_dri.c | 53 ++++++++++++++++++++++++++++++++++++-
src/gbm/backends/dri/gbm_driint.h | 15 ++++++++++-
2 files changed, 66 insertions(+), 2 deletions(-)

diff --git a/src/gbm/backends/dri/gbm_dri.c b/src/gbm/backends/dri/gbm_dri.c
index 305938b..9ac678a 100644
--- a/src/gbm/backends/dri/gbm_dri.c
+++ b/src/gbm/backends/dri/gbm_dri.c
@@ -54,6 +54,49 @@ dri_lookup_egl_image(__DRIscreen *screen, void *image, void *data)
return dri->lookup_image(screen, image, dri->lookup_user_data);
}

+static __DRIbuffer *
+dri_get_buffers(__DRIdrawable * driDrawable,
+ int *width, int *height,
+ unsigned int *attachments, int count,
+ int *out_count, void *data)
+{
+ struct gbm_dri_surface *surf = data;
+ struct gbm_dri_device *dri = gbm_dri_device(surf->base.gbm);
+
+ if (dri->get_buffers == NULL)
+ return NULL;
+
+ return dri->get_buffers(driDrawable, width, height, attachments,
+ count, out_count, surf->dri_private);
+}
+
+static void
+dri_flush_front_buffer(__DRIdrawable * driDrawable, void *data)
+{
+ struct gbm_dri_surface *surf = data;
+ struct gbm_dri_device *dri = gbm_dri_device(surf->base.gbm);
+
+ if (dri->flush_front_buffer != NULL)
+ dri->flush_front_buffer(driDrawable, surf->dri_private);
+}
+
+static __DRIbuffer *
+dri_get_buffers_with_format(__DRIdrawable * driDrawable,
+ int *width, int *height,
+ unsigned int *attachments, int count,
+ int *out_count, void *data)
+{
+ struct gbm_dri_surface *surf = data;
+ struct gbm_dri_device *dri = gbm_dri_device(surf->base.gbm);
+
+ if (dri->get_buffers_with_format == NULL)
+ return NULL;
+
+ return
+ dri->get_buffers_with_format(driDrawable, width, height, attachments,
+ count, out_count, surf->dri_private);
+}
+
static const __DRIuseInvalidateExtension use_invalidate = {
{ __DRI_USE_INVALIDATE, 1 }
};
@@ -63,6 +106,13 @@ static const __DRIimageLookupExtension image_lookup_extension = {
dri_lookup_egl_image
};

+const __DRIdri2LoaderExtension dri2_loader_extension = {
+ { __DRI_DRI2_LOADER, 3 },
+ dri_get_buffers,
+ dri_flush_front_buffer,
+ dri_get_buffers_with_format,
+};
+
struct dri_extension_match {
const char *name;
int version;
@@ -187,7 +237,8 @@ dri_screen_create(struct gbm_dri_device *dri)

dri->extensions[0] = &image_lookup_extension.base;
dri->extensions[1] = &use_invalidate.base;
- dri->extensions[2] = NULL;
+ dri->extensions[2] = &dri2_loader_extension.base;
+ dri->extensions[3] = NULL;

if (dri->dri2 == NULL)
return -1;
diff --git a/src/gbm/backends/dri/gbm_driint.h b/src/gbm/backends/dri/gbm_driint.h
index 514b5a6..3b7db65 100644
--- a/src/gbm/backends/dri/gbm_driint.h
+++ b/src/gbm/backends/dri/gbm_driint.h
@@ -36,6 +36,8 @@
#include <GL/gl.h> /* dri_interface needs GL types */
#include "GL/internal/dri_interface.h"

+struct gbm_dri_surface;
+
struct gbm_dri_device {
struct gbm_drm_device base;

@@ -47,12 +49,23 @@ struct gbm_dri_device {
__DRIdri2Extension *dri2;
__DRIimageExtension *image;
__DRI2flushExtension *flush;
+ __DRIdri2LoaderExtension *loader;

const __DRIconfig **driver_configs;
- const __DRIextension *extensions[3];
+ const __DRIextension *extensions[4];

__DRIimage *(*lookup_image)(__DRIscreen *screen, void *image, void *data);
void *lookup_user_data;
+
+ __DRIbuffer *(*get_buffers)(__DRIdrawable * driDrawable,
+ int *width, int *height,
+ unsigned int *attachments, int count,
+ int *out_count, void *data);
+ void (*flush_front_buffer)(__DRIdrawable * driDrawable, void *data);
+ __DRIbuffer *(*get_buffers_with_format)(__DRIdrawable * driDrawable,
+ int *width, int *height,
+ unsigned int *attachments, int count,
+ int *out_count, void *data);
};

struct gbm_dri_bo {
--
1.7.9.1
Kristian Høgsberg
2012-03-27 19:08:29 UTC
Permalink
From: Ander Conselvan de Oliveira <***@intel.com>

---
src/egl/drivers/dri2/egl_dri2.h | 18 ++
src/egl/drivers/dri2/platform_drm.c | 342 ++++++++++++++++++++++++++++++++++-
src/gbm/backends/dri/gbm_dri.c | 62 ++++++-
src/gbm/backends/dri/gbm_driint.h | 1 +
4 files changed, 420 insertions(+), 3 deletions(-)

diff --git a/src/egl/drivers/dri2/egl_dri2.h b/src/egl/drivers/dri2/egl_dri2.h
index 84ea0b6..fc31364 100644
--- a/src/egl/drivers/dri2/egl_dri2.h
+++ b/src/egl/drivers/dri2/egl_dri2.h
@@ -145,6 +145,15 @@ enum wayland_buffer_type {
};
#endif

+#ifdef HAVE_DRM_PLATFORM
+enum drm_buffer_type {
+ DRM_BUFFER_FRONT,
+ DRM_BUFFER_BACK,
+ DRM_BUFFER_THIRD,
+ DRM_BUFFER_COUNT
+};
+#endif
+
struct dri2_egl_surface
{
_EGLSurface base;
@@ -177,6 +186,15 @@ struct dri2_egl_surface
int format;
#endif

+#ifdef HAVE_DRM_PLATFORM
+ struct gbm_dri_surface *gbm_surf;
+ int drm_buffer_lock[DRM_BUFFER_COUNT];
+#ifndef HAVE_WAYLAND_PLATFORM
+ __DRIbuffer *dri_buffers[__DRI_BUFFER_COUNT];
+ __DRIbuffer *third_buffer;
+#endif
+#endif
+
#ifdef HAVE_ANDROID_PLATFORM
struct ANativeWindow *window;
struct ANativeWindowBuffer *buffer;
diff --git a/src/egl/drivers/dri2/platform_drm.c b/src/egl/drivers/dri2/platform_drm.c
index bd3d1e0..b16bc42 100644
--- a/src/egl/drivers/dri2/platform_drm.c
+++ b/src/egl/drivers/dri2/platform_drm.c
@@ -37,6 +37,339 @@

#include "egl_dri2.h"

+static __DRIbuffer *
+get_front_buffer(struct gbm_dri_surface *surf, void *data)
+{
+ struct dri2_egl_surface *dri2_surf = data;
+
+ (void) surf;
+
+ dri2_surf->drm_buffer_lock[DRM_BUFFER_FRONT]++;
+ return dri2_surf->dri_buffers[__DRI_BUFFER_FRONT_LEFT];
+}
+
+static void
+release_buffer(struct gbm_dri_surface *surf, __DRIbuffer *buffer, void *data)
+{
+ struct dri2_egl_surface *dri2_surf = data;
+
+ if (buffer == NULL)
+ return;
+
+ if (buffer == dri2_surf->third_buffer)
+ dri2_surf->drm_buffer_lock[DRM_BUFFER_THIRD]--;
+ else if (buffer->attachment == __DRI_BUFFER_FRONT_LEFT)
+ dri2_surf->drm_buffer_lock[DRM_BUFFER_FRONT]--;
+ else if (buffer->attachment == __DRI_BUFFER_BACK_LEFT)
+ dri2_surf->drm_buffer_lock[DRM_BUFFER_BACK]--;
+
+}
+
+static _EGLSurface *
+dri2_create_surface(_EGLDriver *drv, _EGLDisplay *disp, EGLint type,
+ _EGLConfig *conf, EGLNativeWindowType window,
+ const EGLint *attrib_list)
+{
+ struct dri2_egl_display *dri2_dpy = dri2_egl_display(disp);
+ struct dri2_egl_config *dri2_conf = dri2_egl_config(conf);
+ struct dri2_egl_surface *dri2_surf;
+ struct dri2_egl_buffer *dri2_buf;
+ struct gbm_dri_surface *surf;
+ int i;
+
+ (void) drv;
+
+ dri2_surf = malloc(sizeof *dri2_surf);
+ if (!dri2_surf) {
+ _eglError(EGL_BAD_ALLOC, "dri2_create_surface");
+ return NULL;
+ }
+
+ if (!_eglInitSurface(&dri2_surf->base, disp, type, conf, attrib_list))
+ goto cleanup_surf;
+
+ for (i = 0; i < __DRI_BUFFER_COUNT; ++i)
+ dri2_surf->dri_buffers[i] = NULL;
+
+ dri2_surf->third_buffer = NULL;
+
+ switch (type) {
+ case EGL_WINDOW_BIT:
+ surf = gbm_dri_surface((struct gbm_surface *) window);
+ dri2_surf->gbm_surf = surf;
+
+ dri2_surf->base.Width = surf->base.width;
+ dri2_surf->base.Height = surf->base.height;
+
+ surf->get_front_buffer = get_front_buffer;
+ surf->release_buffer = release_buffer;
+ surf->dri_private = dri2_surf;
+
+ break;
+ default:
+ goto cleanup_surf;
+ }
+
+ dri2_surf->dri_drawable =
+ (*dri2_dpy->dri2->createNewDrawable) (dri2_dpy->dri_screen,
+ dri2_conf->dri_double_config,
+ dri2_surf->gbm_surf);
+
+ if (dri2_surf->dri_drawable == NULL) {
+ _eglError(EGL_BAD_ALLOC, "dri2->createNewDrawable");
+ goto cleanup_surf;
+ }
+
+ return &dri2_surf->base;
+
+ cleanup_surf:
+ free(dri2_surf);
+
+ return NULL;
+}
+
+static _EGLSurface *
+dri2_create_window_surface(_EGLDriver *drv, _EGLDisplay *disp,
+ _EGLConfig *conf, EGLNativeWindowType window,
+ const EGLint *attrib_list)
+{
+ return dri2_create_surface(drv, disp, EGL_WINDOW_BIT, conf,
+ window, attrib_list);
+}
+
+static EGLBoolean
+dri2_destroy_surface(_EGLDriver *drv, _EGLDisplay *disp, _EGLSurface *surf)
+{
+ struct dri2_egl_display *dri2_dpy = dri2_egl_display(disp);
+ struct dri2_egl_surface *dri2_surf = dri2_egl_surface(surf);
+ int i;
+
+ if (!_eglPutSurface(surf))
+ return EGL_TRUE;
+
+ (*dri2_dpy->core->destroyDrawable)(dri2_surf->dri_drawable);
+
+ for (i = 0; i < __DRI_BUFFER_COUNT; i++)
+ if (dri2_surf->dri_buffers[i])
+ dri2_dpy->dri2->releaseBuffer(dri2_dpy->dri_screen,
+ dri2_surf->dri_buffers[i]);
+
+ free(surf);
+
+ return EGL_TRUE;
+}
+
+static void
+dri2_process_back_buffer(struct dri2_egl_surface *dri2_surf, unsigned format)
+{
+ struct dri2_egl_display *dri2_dpy =
+ dri2_egl_display(dri2_surf->base.Resource.Display);
+
+ (void) format;
+
+ switch (dri2_surf->base.Type) {
+ case EGL_WINDOW_BIT:
+ /* allocate a front buffer for our double-buffered window*/
+ if (dri2_surf->dri_buffers[__DRI_BUFFER_FRONT_LEFT] != NULL)
+ break;
+ dri2_surf->dri_buffers[__DRI_BUFFER_FRONT_LEFT] =
+ dri2_dpy->dri2->allocateBuffer(dri2_dpy->dri_screen,
+ __DRI_BUFFER_FRONT_LEFT, format,
+ dri2_surf->base.Width, dri2_surf->base.Height);
+ break;
+ default:
+ break;
+ }
+}
+
+static inline void
+pointer_swap(const void **p1, const void **p2)
+{
+ const void *tmp = *p1;
+ *p1 = *p2;
+ *p2 = tmp;
+}
+
+static void
+destroy_third_buffer(struct dri2_egl_surface *dri2_surf)
+{
+ struct dri2_egl_display *dri2_dpy =
+ dri2_egl_display(dri2_surf->base.Resource.Display);
+
+ if (dri2_surf->third_buffer == NULL)
+ return;
+
+ dri2_dpy->dri2->releaseBuffer(dri2_dpy->dri_screen,
+ dri2_surf->third_buffer);
+ dri2_surf->third_buffer = NULL;
+ dri2_surf->drm_buffer_lock[DRM_BUFFER_THIRD] = 0;
+}
+
+static void
+swap_drm_buffers(struct dri2_egl_surface *dri2_surf,
+ enum drm_buffer_type a, enum drm_buffer_type b)
+{
+ int tmp;
+
+ tmp = dri2_surf->drm_buffer_lock[a];
+ dri2_surf->drm_buffer_lock[a] = dri2_surf->drm_buffer_lock[b];
+ dri2_surf->drm_buffer_lock[b] = tmp;
+}
+
+static void
+swap_back_and_third(struct dri2_egl_surface *dri2_surf)
+{
+ if (dri2_surf->drm_buffer_lock[DRM_BUFFER_THIRD])
+ destroy_third_buffer(dri2_surf);
+
+ pointer_swap((const void **) &dri2_surf->dri_buffers[__DRI_BUFFER_BACK_LEFT],
+ (const void **) &dri2_surf->third_buffer);
+
+ swap_drm_buffers(dri2_surf, DRM_BUFFER_BACK, DRM_BUFFER_THIRD);
+}
+
+static void
+dri2_prior_buffer_creation(struct dri2_egl_surface *dri2_surf,
+ unsigned int type)
+{
+ switch (type) {
+ case __DRI_BUFFER_BACK_LEFT:
+ if (dri2_surf->drm_buffer_lock[DRM_BUFFER_BACK])
+ swap_back_and_third(dri2_surf);
+ else if (dri2_surf->third_buffer)
+ destroy_third_buffer(dri2_surf);
+ break;
+ default:
+ break;
+
+ }
+}
+
+static __DRIbuffer *
+dri2_get_buffers_with_format(__DRIdrawable * driDrawable,
+ int *width, int *height,
+ unsigned int *attachments, int count,
+ int *out_count, void *loaderPrivate)
+{
+ struct dri2_egl_surface *dri2_surf = loaderPrivate;
+ struct dri2_egl_display *dri2_dpy =
+ dri2_egl_display(dri2_surf->base.Resource.Display);
+ int i;
+
+ dri2_surf->buffer_count = 0;
+ for (i = 0; i < 2*count; i+=2) {
+ assert(attachments[i] < __DRI_BUFFER_COUNT);
+ assert(dri2_surf->buffer_count < 5);
+
+ dri2_prior_buffer_creation(dri2_surf, attachments[i]);
+
+ if (dri2_surf->dri_buffers[attachments[i]] == NULL) {
+
+ dri2_surf->dri_buffers[attachments[i]] =
+ dri2_dpy->dri2->allocateBuffer(dri2_dpy->dri_screen,
+ attachments[i], attachments[i+1],
+ dri2_surf->base.Width, dri2_surf->base.Height);
+
+ if (!dri2_surf->dri_buffers[attachments[i]])
+ continue;
+
+ if (attachments[i] == __DRI_BUFFER_BACK_LEFT)
+ dri2_process_back_buffer(dri2_surf, attachments[i+1]);
+ }
+
+ memcpy(&dri2_surf->buffers[dri2_surf->buffer_count],
+ dri2_surf->dri_buffers[attachments[i]],
+ sizeof(__DRIbuffer));
+
+ dri2_surf->buffer_count++;
+ }
+
+ assert(dri2_surf->dri_buffers[__DRI_BUFFER_BACK_LEFT]);
+
+ *out_count = dri2_surf->buffer_count;
+ if (dri2_surf->buffer_count == 0)
+ return NULL;
+
+ *width = dri2_surf->base.Width;
+ *height = dri2_surf->base.Height;
+
+ return dri2_surf->buffers;
+}
+
+static __DRIbuffer *
+dri2_get_buffers(__DRIdrawable * driDrawable,
+ int *width, int *height,
+ unsigned int *attachments, int count,
+ int *out_count, void *loaderPrivate)
+{
+ unsigned int *attachments_with_format;
+ __DRIbuffer *buffer;
+ const unsigned int format = 32;
+ int i;
+
+ attachments_with_format = calloc(count * 2, sizeof(unsigned int));
+ if (!attachments_with_format) {
+ *out_count = 0;
+ return NULL;
+ }
+
+ for (i = 0; i < count; ++i) {
+ attachments_with_format[2*i] = attachments[i];
+ attachments_with_format[2*i + 1] = format;
+ }
+
+ buffer =
+ dri2_get_buffers_with_format(driDrawable,
+ width, height,
+ attachments_with_format, count,
+ out_count, loaderPrivate);
+
+ free(attachments_with_format);
+
+ return buffer;
+}
+
+static void
+dri2_flush_front_buffer(__DRIdrawable * driDrawable, void *loaderPrivate)
+{
+ (void) driDrawable;
+ (void) loaderPrivate;
+}
+
+static EGLBoolean
+dri2_swap_buffers(_EGLDriver *drv, _EGLDisplay *disp, _EGLSurface *draw)
+{
+ struct dri2_egl_display *dri2_dpy = dri2_egl_display(disp);
+ struct dri2_egl_surface *dri2_surf = dri2_egl_surface(draw);
+ struct dri2_egl_driver *dri2_drv = dri2_egl_driver(drv);
+
+ if (dri2_surf->base.Type == EGL_WINDOW_BIT) {
+ pointer_swap(
+ (const void **) &dri2_surf->dri_buffers[__DRI_BUFFER_FRONT_LEFT],
+ (const void **) &dri2_surf->dri_buffers[__DRI_BUFFER_BACK_LEFT]);
+
+ dri2_surf->dri_buffers[__DRI_BUFFER_FRONT_LEFT]->attachment =
+ __DRI_BUFFER_FRONT_LEFT;
+ dri2_surf->dri_buffers[__DRI_BUFFER_BACK_LEFT]->attachment =
+ __DRI_BUFFER_BACK_LEFT;
+
+ swap_drm_buffers(dri2_surf, DRM_BUFFER_FRONT, DRM_BUFFER_BACK);
+ }
+
+ _EGLContext *ctx;
+ if (dri2_drv->glFlush) {
+ ctx = _eglGetCurrentContext();
+ if (ctx && ctx->DrawSurface == &dri2_surf->base)
+ dri2_drv->glFlush();
+ }
+
+ (*dri2_dpy->flush->flush)(dri2_surf->dri_drawable);
+ (*dri2_dpy->flush->invalidate)(dri2_surf->dri_drawable);
+
+ return EGL_TRUE;
+}
+
+
static _EGLImage *
dri2_create_image_khr_pixmap(_EGLDisplay *disp, _EGLContext *ctx,
EGLClientBuffer buffer, const EGLint *attr_list)
@@ -147,12 +480,19 @@ dri2_initialize_drm(_EGLDriver *drv, _EGLDisplay *disp)
dri2_dpy->gbm_dri->lookup_image = dri2_lookup_egl_image;
dri2_dpy->gbm_dri->lookup_user_data = disp;

+ dri2_dpy->gbm_dri->get_buffers = dri2_get_buffers;
+ dri2_dpy->gbm_dri->flush_front_buffer = dri2_flush_front_buffer;
+ dri2_dpy->gbm_dri->get_buffers_with_format = dri2_get_buffers_with_format;
+
dri2_setup_screen(disp);

for (i = 0; dri2_dpy->driver_configs[i]; i++)
dri2_add_config(disp, dri2_dpy->driver_configs[i],
- i + 1, 0, 0, NULL, NULL);
+ i + 1, 0, EGL_WINDOW_BIT, NULL, NULL);

+ drv->API.CreateWindowSurface = dri2_create_window_surface;
+ drv->API.DestroySurface = dri2_destroy_surface;
+ drv->API.SwapBuffers = dri2_swap_buffers;
drv->API.CreateImageKHR = dri2_drm_create_image_khr;

#ifdef HAVE_WAYLAND_PLATFORM
diff --git a/src/gbm/backends/dri/gbm_dri.c b/src/gbm/backends/dri/gbm_dri.c
index 9ac678a..6cc4aaa 100644
--- a/src/gbm/backends/dri/gbm_dri.c
+++ b/src/gbm/backends/dri/gbm_dri.c
@@ -467,18 +467,76 @@ gbm_dri_surface_destroy(struct gbm_surface *_surf)
static struct gbm_bo *
gbm_dri_surface_lock_front_buffer(struct gbm_surface *_surf)
{
- return NULL;
+ struct gbm_dri_device *dri = gbm_dri_device(_surf->gbm);
+ struct gbm_dri_surface *surf = gbm_dri_surface(_surf);
+ struct gbm_dri_bo *bo;
+ __DRIbuffer *dri_buffer;
+ int dri_format = 0;
+
+ if (surf->get_front_buffer == NULL)
+ return NULL;
+
+ switch (_surf->format) {
+ case GBM_BO_FORMAT_XRGB8888:
+ case GBM_FORMAT_XRGB8888:
+ dri_format = __DRI_IMAGE_FORMAT_XRGB8888;
+ break;
+ case GBM_BO_FORMAT_ARGB8888:
+ case GBM_FORMAT_ARGB8888:
+ dri_format = __DRI_IMAGE_FORMAT_ARGB8888;
+ break;
+ default:
+ return NULL;
+ }
+
+ dri_buffer = surf->get_front_buffer(surf, surf->dri_private);
+ if (!dri_buffer)
+ return NULL;
+
+ bo = calloc(1, sizeof *bo);
+ if (bo == NULL)
+ return NULL;
+
+ bo->dri_buffer = dri_buffer;
+ bo->base.base.gbm = _surf->gbm;
+ bo->base.base.width = _surf->width;
+ bo->base.base.height = _surf->height;
+
+ bo->image =
+ dri->image->createImageFromName(dri->screen,
+ _surf->width, _surf->height, dri_format,
+ dri_buffer->name, dri_buffer->pitch / 4,
+ bo);
+
+ dri->image->queryImage(bo->image, __DRI_IMAGE_ATTRIB_HANDLE,
+ &bo->base.base.handle.s32);
+ dri->image->queryImage(bo->image, __DRI_IMAGE_ATTRIB_STRIDE,
+ (int *) &bo->base.base.pitch);
+
+ return &bo->base.base;
}

static void
gbm_dri_surface_release_buffer(struct gbm_surface *_surf, struct gbm_bo *_bo)
{
+ struct gbm_dri_surface *surf = gbm_dri_surface(_surf);
+ struct gbm_dri_bo *bo = gbm_dri_bo(_bo);
+
+ if (surf->release_buffer && bo->dri_buffer)
+ surf->release_buffer(surf, bo->dri_buffer, surf->dri_private);
+
+ gbm_dri_bo_destroy(_bo);
}

static int
gbm_dri_surface_has_free_buffers(struct gbm_surface *_surf)
{
- return 0;
+ struct gbm_dri_surface *surf = gbm_dri_surface(_surf);
+
+ if (surf->has_free_buffers && surf->has_free_buffers(surf->dri_private))
+ return 1;
+ else
+ return 0;
}

static void
diff --git a/src/gbm/backends/dri/gbm_driint.h b/src/gbm/backends/dri/gbm_driint.h
index 3b7db65..786f36b 100644
--- a/src/gbm/backends/dri/gbm_driint.h
+++ b/src/gbm/backends/dri/gbm_driint.h
@@ -72,6 +72,7 @@ struct gbm_dri_bo {
struct gbm_drm_bo base;

__DRIimage *image;
+ __DRIbuffer *dri_buffer;
};

struct gbm_dri_surface {
--
1.7.9.1
Loading...