<div dir="ltr">Hi Chad,<br><br>Thanks for the review and good suggestions.  I am interested to know your opinion regarding an issue that comes up when implementing a back-buffered pbuffer.  The problem is we'll be creating a front renderbuffer, not a back renderbuffer if we pass in single buffered visuals.<br><br>For example, in intelCreateBuffer in the i965 driver, the back buffer is only created when double-buffered visuals are passed in:<br><br>>>_mesa_add_renderbuffer(fb, BUFFER_FRONT_LEFT, &rb->Base.Base);<br><br>>>if (mesaVis->doubleBufferMode) {<br>>>      rb = intel_create_renderbuffer(rgbFormat, num_samples);<br>>>      _mesa_add_renderbuffer(fb, BUFFER_BACK_LEFT, &rb->Base.Base)<br>>>}<br><br>Due to this, with a single-buffered configuration, we'll have to do the following in surfaceless_image_get_buffers:<br><br>>>buffers->image_mask |= __DRI_IMAGE_BUFFER_FRONT<br><br>not this:<br><br>>>buffers->image_mask |= __DRI_IMAGE_BUFFER_BACK<br><br>If this a problem, we'll have to pass in a parameter in the visual indicating we're trying to create an EGL pbuffer (i.e, mesaVis->is_egl_pbuffer).  Then we'll set the proper renderbuffer and framebuffer options from that config in several places.  Some these places include:<br><br><a href="http://pastebin.com/28ddebWF">http://pastebin.com/28ddebWF</a><br><br>(I just changed the single buffer defaults in that code snippet so I could test on my surfaceless platform, we'll have query from the updated visuals in a proper implementation)<br><br>Is the fact we'll work with the default front render buffer a problem?  If so, what's the best way to fix the issue?</div><div class="gmail_extra"><br><div class="gmail_quote">On Fri, May 6, 2016 at 9:27 PM, Chad Versace <span dir="ltr"><<a href="mailto:chad.versace@intel.com" target="_blank">chad.versace@intel.com</a>></span> wrote:<br><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><span class="">On 05/06/2016 03:39 PM, Stéphane Marchesin wrote:<br>
> On Fri, May 6, 2016 at 3:32 PM, Gurchetan Singh<br>
> <<a href="mailto:gurchetansingh@chromium.org">gurchetansingh@chromium.org</a>> wrote:<br>
>> This change enables the creation of pbuffer<br>
>> surfaces on the surfaceless platform.<br>
>><br>
>> V2: Use double-buffered pbuffer configuration<br>
><br>
> Reviewed-by: Stéphane Marchesin <<a href="mailto:marcheu@chromium.org">marcheu@chromium.org</a>><br>
><br>
> Chad, do you also want to take a look at it?<br>
<br>
</span>On a philosophical note, it's ironic that we're now creating a *surface* in the<br>
*surfaceless* platform. Don't you agree? Anyway, let's ignore the irony and<br>
focus on practical matters.<br>
<br>
There are a few bugs and minor style issues. See the comments below.<br>
<br>
There is also one major issue that needs discussion. I believe pbuffers<br>
are single-buffered, and that the single buffer is the back buffer.<br>
<br>
As precedent, platform_x11.c implements pbuffers as single-buffered.<br>
<br>
The relevant language in the EGL 1.5 spec is phrased badly, and could be<br>
interpreted either way: (a) pbuffers are double-buffered, or (b) pbuffers have<br>
only a back buffer.  If I recall correctly, an internal Khronos conversation in<br>
2014 arrived at conclusion (b).  Here are the relevant quotes from the spec:<br>
<br>
    - Some platforms may not allow rendering directly to the front buffer of<br>
      a window surface. When such windows are made current to a context, the<br>
      context will always have an EGL_RENDER_BUFFER attribute value of<br>
      EGL_BACK_BUFFER. From the client API point of view these surfaces have<br>
      only a back buffer and no front buffer, similar to pbuffer rendering (see<br>
      section 2.2.2).<br>
<br>
    - Querying EGL_RENDER_BUFFER [with eglQueryContext()] returns the buffer<br>
      which client API rendering is requested to use. [...] For a pbuffer<br>
      surface, it is always EGL_BACK_BUFFER.<br>
<br>
    - [When eglSwapBuffers() is called,] If surface is a back-buffered window<br>
      surface, then the color buffer is copied to the native window associated<br>
      with that surface. [Otherwise, if] surface is a single-buffered window,<br>
      pixmap, or pbuffer surface, eglSwapBuffers has no effect.<br>
<br>
The single-buffered nature has an impact on the implementation of<br>
eglSwapBuffers, according the last bullet. It's a no-op. As precedent,<br>
platform_x11.c correctly does nothing when swapping a pbuffer.<br>
<br>
If you think my interpretation of the spec is wrong, and that<br>
platform_x11.c is incorrect, then I'm open to discussing it. I'm<br>
especially interested to learn whether any non-Mesa EGL implementations<br>
treat pbuffers as double-buffered.<br>
<br>
(Also, this patch should probably set EGL_MIN/MAX_SWAP_INTERVAL to 0/0<br>
for pbuffer configs. But let's overlook that for now, as I don't think<br>
it's important for the initial implementation. Depending on how Google<br>
uses this patch, perhaps the swap interval bounds are never relevant.)<br>
<div><div class="h5"><br>
>> ---<br>
>>  src/egl/drivers/dri2/egl_dri2.h             |   8 +-<br>
>>  src/egl/drivers/dri2/platform_surfaceless.c | 219 +++++++++++++++++++++++++++-<br>
>>  2 files changed, 222 insertions(+), 5 deletions(-)<br>
>><br>
>> diff --git a/src/egl/drivers/dri2/egl_dri2.h b/src/egl/drivers/dri2/egl_dri2.h<br>
>> index ddb5f39..ecf9c76 100644<br>
>> --- a/src/egl/drivers/dri2/egl_dri2.h<br>
>> +++ b/src/egl/drivers/dri2/egl_dri2.h<br>
>> @@ -291,8 +291,14 @@ struct dri2_egl_surface<br>
>>     /* EGL-owned buffers */<br>
>>     __DRIbuffer           *local_buffers[__DRI_BUFFER_COUNT];<br>
>>  #endif<br>
>> -};<br>
>><br>
>> +#if defined(HAVE_SURFACELESS_PLATFORM)<br>
>> +      __DRIimage           *front;<br>
>> +      __DRIimage           *back;<br>
>> +      unsigned int         format;<br>
>> +#endif<br>
>> +<br>
>> +};<br>
>><br>
>>  struct dri2_egl_config<br>
>>  {<br>
>> diff --git a/src/egl/drivers/dri2/platform_surfaceless.c b/src/egl/drivers/dri2/platform_surfaceless.c<br>
>> index e0ddc12..08d5448 100644<br>
>> --- a/src/egl/drivers/dri2/platform_surfaceless.c<br>
>> +++ b/src/egl/drivers/dri2/platform_surfaceless.c<br>
>> @@ -23,6 +23,7 @@<br>
>>   * DEALINGS IN THE SOFTWARE.<br>
>>   */<br>
>><br>
>> +#include <stdbool.h><br>
>>  #include <stdlib.h><br>
>>  #include <stdio.h><br>
>>  #include <string.h><br>
>> @@ -37,9 +38,209 @@<br>
>>  #include "egl_dri2_fallbacks.h"<br>
>>  #include "loader.h"<br>
>><br>
>> +static __DRIimage*<br>
>> +surfaceless_alloc_image(struct dri2_egl_display *dri2_dpy,<br>
>> +                     struct dri2_egl_surface *dri2_surf)<br>
>> +{<br>
>> +   __DRIimage *img = NULL;<br>
>> +<br>
>> +   img = dri2_dpy->image->createImage(<br>
>> +            dri2_dpy->dri_screen,<br>
>> +            dri2_surf->base.Width,<br>
>> +            dri2_surf->base.Height,<br>
>> +            dri2_surf->format,<br>
>> +            0,<br>
>> +            NULL);<br>
>> +<br>
>> +   return img;<br>
>> +}<br>
<br>
</div></div>The img variable doesn't do much here. This function would be cleaner<br>
if it had no local variable.<br>
<span class=""><br>
>> +static void<br>
>> +surfaceless_free_images(struct dri2_egl_surface *dri2_surf)<br>
>> +{<br>
>> +   struct dri2_egl_display *dri2_dpy =<br>
>> +      dri2_egl_display(dri2_surf->base.Resource.Display);<br>
>> +<br>
>> +   if(dri2_surf->front) {<br>
>> +      dri2_dpy->image->destroyImage(dri2_surf->front);<br>
>> +      dri2_surf->front = NULL;<br>
>> +   }<br>
>> +   if(dri2_surf->back) {<br>
>> +      dri2_dpy->image->destroyImage(dri2_surf->back);<br>
>> +      dri2_surf->back = NULL;<br>
>> +   }<br>
>> +}<br>
<br>
</span>The style here needs fixing. The Mesa code style uses a space after 'if'. Typically,<br>
separate 'if' statements are also separated by a newline.<br>
<br>
if (meow) {<br>
moo;<br>
}<br>
<br>
if (woof) {<br>
quack;<br>
<span class="">}<br>
<br>
>> +static EGLBoolean<br>
>> +surfaceless_swap_buffers(_EGLDriver *drv, _EGLDisplay *disp, _EGLSurface *draw)<br>
>> +{<br>
>> +   struct dri2_egl_surface *dri2_surf = dri2_egl_surface(draw);<br>
>> +    __DRIimage *temp = dri2_surf->back;<br>
>> +<br>
>> +   dri2_surf->back = dri2_surf->front;<br>
>> +   dri2_surf->front = temp;<br>
>> +}<br>
<br>
</span>As discussed above, I believe the pbuffer should possess only a back<br>
buffer. And the EGL spec mandates eglSwapBuffers be a no-op for<br>
pbuffers.<br>
<br>
However, if you were going to swap buffers like this, the EGL spec would<br>
require that you flush the current drawable before swapping. To<br>
accomplish the flush, this function would need to call<br>
egl_dri2.c:dri2_flush_drawable_for_swapbuffers(). But that's all moot,<br>
as this function should actually be a no-op.<br>
<span class=""><br>
>> +static int<br>
>> +surfaceless_image_get_buffers(__DRIdrawable *driDrawable,<br>
>> +                        unsigned int format,<br>
>> +                        uint32_t *stamp,<br>
>> +                        void *loaderPrivate,<br>
>> +                        uint32_t buffer_mask,<br>
>> +                        struct __DRIimageList *buffers)<br>
>> +{<br>
>> +   struct dri2_egl_surface *dri2_surf = loaderPrivate;<br>
>> +   struct dri2_egl_display *dri2_dpy =<br>
>> +      dri2_egl_display(dri2_surf->base.Resource.Display);<br>
>> +   buffers->image_mask = 0;<br>
>> +   buffers->front = NULL;<br>
>> +   buffers->back = NULL;<br>
<br>
</span>Minor style issue. Please insert a newline between the "block" of variable<br>
declarations and the first non-declaration line. Consistency with other<br>
parts of Mesa helps everyone involved with the project.<br>
<span class=""><br>
>> +<br>
>> +   if (buffer_mask & __DRI_IMAGE_BUFFER_FRONT) {<br>
>> +      if (!dri2_surf->front)<br>
>> +         dri2_surf->front =<br>
>> +            surfaceless_alloc_image(dri2_dpy, dri2_surf);<br>
>> +<br>
>> +      buffers->image_mask |= __DRI_IMAGE_BUFFER_FRONT;<br>
>> +      buffers->front = dri2_surf->front;<br>
>> +   }<br>
>> +   if (buffer_mask & __DRI_IMAGE_BUFFER_BACK) {<br>
>> +      if (!dri2_surf->back)<br>
>> +         dri2_surf->back =<br>
>> +            surfaceless_alloc_image(dri2_dpy, dri2_surf);<br>
>> +<br>
>> +      buffers->image_mask |= __DRI_IMAGE_BUFFER_BACK;<br>
>> +      buffers->back = dri2_surf->back;<br>
>> +   }<br>
>> +<br>
>> +   return 1;<br>
>> +}<br>
<br>
</span>Again, please separate the 'if' statements with a newline.<br>
<span class=""><br>
>> +static _EGLSurface *<br>
>> +dri2_surfaceless_create_surface(_EGLDriver *drv, _EGLDisplay *disp, EGLint type,<br>
>> +                        _EGLConfig *conf, void *native_surface,<br>
>> +                        const EGLint *attrib_list)<br>
<br>
</span>The 'native_surface' parameter is misleading because there exist no native surfaces<br>
in the surfaceless platform. Please remove that parameter.<br>
<span class=""><br>
>> +{<br>
>> +   struct dri2_egl_display *dri2_dpy = dri2_egl_display(disp);<br>
>> +   struct dri2_egl_config *dri2_conf = dri2_egl_config(conf);<br>
>> +   struct dri2_egl_surface *dri2_surf;<br>
>> +   const __DRIconfig *config;<br>
>> +   bool srgb;<br>
>> +<br>
>> +   /* Make sure to calloc so all pointers<br>
>> +    * are originally NULL.<br>
>> +    */<br>
>> +   dri2_surf = calloc(1, sizeof *dri2_surf);<br>
>> +<br>
>> +   if(!dri2_surf)<br>
>> +      return NULL;<br>
<br>
</span>The above error handling should be:<br>
<br>
   if (!dri2_surf) {<br>
      _eglError(EGL_BAD_ALLOC, "eglCreatePbufferSurface");<br>
<div><div class="h5">      return NULL;<br>
   }<br>
<br>
>> +<br>
>> +   if (!_eglInitSurface(&dri2_surf->base, disp, type, conf, attrib_list))<br>
>> +      goto cleanup_surface;<br>
>> +<br>
>> +   /* Only double buffered configurations exist at this point (single buffered<br>
>> +    * configs were filtered out in surfaceless_add_configs_for_visuals).<br>
>> +    */<br>
>> +   srgb = (dri2_surf->base.GLColorspace == EGL_GL_COLORSPACE_SRGB_KHR);<br>
>> +   config = dri2_conf->dri_double_config[srgb];<br>
>> +<br>
>> +   if (!config)<br>
>> +      goto cleanup_surface;<br>
>> +<br>
>> +   dri2_surf->dri_drawable =<br>
>> +      (*dri2_dpy->dri2->createNewDrawable)(dri2_dpy->dri_screen, config,<br>
>> +                                           dri2_surf);<br>
>> +   if (dri2_surf->dri_drawable == NULL) {<br>
>> +      _eglError(EGL_BAD_ALLOC, "dri2->createNewDrawable");<br>
>> +      goto cleanup_surface;<br>
>> +    }<br>
>> +<br>
>> +   if (conf->RedSize == 5)<br>
>> +      dri2_surf->format = __DRI_IMAGE_FORMAT_RGB565;<br>
>> +   else if (conf->AlphaSize == 0)<br>
>> +      dri2_surf->format = __DRI_IMAGE_FORMAT_XRGB8888;<br>
>> +   else<br>
>> +      dri2_surf->format = __DRI_IMAGE_FORMAT_ARGB8888;<br>
>> +<br>
>> +   return &dri2_surf->base;<br>
>> +<br>
>> +   cleanup_surface:<br>
>> +      free(dri2_surf);<br>
>> +      return NULL;<br>
>> +}<br>
>> +<br>
>> +static EGLBoolean<br>
>> +surfaceless_destroy_surface(_EGLDriver *drv, _EGLDisplay *disp, _EGLSurface *surf)<br>
>> +{<br>
>> +   struct dri2_egl_display *dri2_dpy = dri2_egl_display(disp);<br>
>> +   struct dri2_egl_surface *dri2_surf = dri2_egl_surface(surf);<br>
>> +<br>
>> +   if (!_eglPutSurface(surf))<br>
>> +      return EGL_TRUE;<br>
>> +<br>
>> +   surfaceless_free_images(dri2_surf);<br>
>> +<br>
>> +   (*dri2_dpy->core->destroyDrawable)(dri2_surf->dri_drawable);<br>
>> +<br>
>> +   free(dri2_surf);<br>
>> +   return EGL_TRUE;<br>
>> +}<br>
>> +<br>
>> +static _EGLSurface *<br>
>> +dri2_surfaceless_create_pbuffer_surface(_EGLDriver *drv, _EGLDisplay *disp,<br>
>> +                                _EGLConfig *conf, const EGLint *attrib_list)<br>
>> +{<br>
>> +   return dri2_surfaceless_create_surface(drv, disp, EGL_PBUFFER_BIT, conf,<br>
>> +                                  NULL, attrib_list);<br>
>> +}<br>
>> +<br>
>> +static EGLBoolean<br>
>> +surfaceless_add_configs_for_visuals(_EGLDriver *drv, _EGLDisplay *dpy)<br>
>> +{<br>
>> +<br>
>> +   struct dri2_egl_display *dri2_dpy = dri2_egl_display(dpy);<br>
>> +<br>
>> +   unsigned int visuals[3][4] = {<br>
>> +      { 0xff0000, 0xff00, 0xff, 0xff000000 },   // ARGB8888<br>
>> +      { 0xff0000, 0xff00, 0xff, 0x0 },          // RGB888<br>
>> +      { 0xf800, 0x7e0, 0x1f, 0x0  },            // RGB565<br>
>> +   };<br>
<br>
</div></div>This function assumes the GL/GLES driver supports these three formats,<br>
in the specified bit order. That assumption holds for Intel, but may not<br>
hold for other drivers.<br>
<br>
A similar oversight in<br>
platform_android.c:droid_add_configs_for_visuals(), which also hardcodes<br>
a list of formats without querying the driver, causes crashes on Android<br>
on Intel platforms (according to what a coworker told me). Therefore,<br>
assumptions like this cause real bugs.<br>
<br>
Don't advertise configs with these formats without first confirming that<br>
the driver supports them by querying<br>
   dri2_dpy->core->getConfigAttrib(..., __DRI_ATTRIB_RED/GREEN/BLUE/ALPHA_MASK, ...)<br>
Look at platform_drm.c for a good example.<br>
<span class=""><br>
>> +<br>
>> +   int count, i, j;<br>
>> +<br>
>> +   count = 0;<br>
>> +   for (i = 0; i < ARRAY_SIZE(visuals); i++) {<br>
>> +      for (j = 0; dri2_dpy->driver_configs[j]; j++) {<br>
>> +         const EGLint surface_type = EGL_WINDOW_BIT | EGL_PBUFFER_BIT;<br>
<br>
</span>It's incorrect to advertise EGL_WINDOW_BIT. The surfaceless platform is only capable of<br>
eglCreatePbufferSurface(), not eglCreateWindowSurface().<br>
<div class="HOEnZb"><div class="h5"><br>
>> +         struct dri2_egl_config *dri2_conf;<br>
>> +         unsigned int double_buffered = 0;<br>
>> +<br>
>> +         dri2_dpy->core->getConfigAttrib(dri2_dpy->driver_configs[j],<br>
>> +            __DRI_ATTRIB_DOUBLE_BUFFER, &double_buffered);<br>
>> +<br>
>> +         /* support only double buffered configs */<br>
>> +         if (!double_buffered)<br>
>> +            continue;<br>
>> +<br>
>> +         dri2_conf = dri2_add_config(dpy, dri2_dpy->driver_configs[j],<br>
>> +               count + 1, surface_type, NULL, visuals[i]);<br>
>> +         if (dri2_conf)<br>
>> +            count++;<br>
>> +      }<br>
>> +   }<br>
>> +<br>
>> +   if (!count)<br>
>> +         _eglLog(_EGL_DEBUG, "Can't create surfaceless visuals");<br>
>> +<br>
>> +   return (count != 0);<br>
>> +}<br>
>> +<br>
>>  static struct dri2_egl_display_vtbl dri2_surfaceless_display_vtbl = {<br>
>>     .create_pixmap_surface = dri2_fallback_create_pixmap_surface,<br>
>> +   .create_pbuffer_surface = dri2_surfaceless_create_pbuffer_surface,<br>
>> +   .destroy_surface = surfaceless_destroy_surface,<br>
>>     .create_image = dri2_create_image_khr,<br>
>> +   .swap_buffers = surfaceless_swap_buffers,<br>
>>     .swap_interval = dri2_fallback_swap_interval,<br>
>>     .swap_buffers_with_damage = dri2_fallback_swap_buffers_with_damage,<br>
>>     .swap_buffers_region = dri2_fallback_swap_buffers_region,<br>
>> @@ -48,6 +249,7 @@ static struct dri2_egl_display_vtbl dri2_surfaceless_display_vtbl = {<br>
>>     .query_buffer_age = dri2_fallback_query_buffer_age,<br>
>>     .create_wayland_buffer_from_image = dri2_fallback_create_wayland_buffer_from_image,<br>
>>     .get_sync_values = dri2_fallback_get_sync_values,<br>
>> +   .get_dri_drawable = dri2_surface_get_dri_drawable,<br>
>>  };<br>
>><br>
>>  static void<br>
>> @@ -72,6 +274,12 @@ surfaceless_get_buffers_with_format(__DRIdrawable * driDrawable,<br>
>>     return dri2_surf->buffers;<br>
>>  }<br>
>><br>
>> +static const __DRIimageLoaderExtension image_loader_extension = {<br>
>> +   .base             = { __DRI_IMAGE_LOADER, 1 },<br>
>> +   .getBuffers       = surfaceless_image_get_buffers,<br>
>> +   .flushFrontBuffer = surfaceless_flush_front_buffer,<br>
>> +};<br>
>> +<br>
>>  #define DRM_RENDER_DEV_NAME  "%s/renderD%d"<br>
>><br>
>>  EGLBoolean<br>
>> @@ -127,7 +335,7 @@ dri2_initialize_surfaceless(_EGLDriver *drv, _EGLDisplay *disp)<br>
>>     dri2_dpy->dri2_loader_extension.getBuffersWithFormat =<br>
>>        surfaceless_get_buffers_with_format;<br>
>><br>
>> -   dri2_dpy->extensions[0] = &dri2_dpy->dri2_loader_extension.base;<br>
>> +   dri2_dpy->extensions[0] = &image_loader_extension.base;<br>
>>     dri2_dpy->extensions[1] = &image_lookup_extension.base;<br>
>>     dri2_dpy->extensions[2] = &use_invalidate.base;<br>
>>     dri2_dpy->extensions[3] = NULL;<br>
>> @@ -137,9 +345,9 @@ dri2_initialize_surfaceless(_EGLDriver *drv, _EGLDisplay *disp)<br>
>>        goto cleanup_driver;<br>
>>     }<br>
>><br>
>> -   for (i = 0; dri2_dpy->driver_configs[i]; i++) {<br>
>> -      dri2_add_config(disp, dri2_dpy->driver_configs[i],<br>
>> -                      i + 1, EGL_WINDOW_BIT, NULL, NULL);<br>
>> +   if (!surfaceless_add_configs_for_visuals(drv, disp)) {<br>
>> +      err = "DRI2: failed to add configs";<br>
>> +      goto cleanup_screen;<br>
>>     }<br>
>><br>
>>     disp->Extensions.KHR_image_base = EGL_TRUE;<br>
>> @@ -151,6 +359,9 @@ dri2_initialize_surfaceless(_EGLDriver *drv, _EGLDisplay *disp)<br>
>><br>
>>     return EGL_TRUE;<br>
>><br>
>> +cleanup_screen:<br>
>> +   dri2_dpy->core->destroyScreen(dri2_dpy->dri_screen);<br>
>> +<br>
>>  cleanup_driver:<br>
>>     dlclose(dri2_dpy->driver);<br>
>>     free(dri2_dpy->driver_name);<br>
>> --<br>
>> 2.1.2<br>
>><br>
>> _______________________________________________<br>
>> mesa-dev mailing list<br>
>> <a href="mailto:mesa-dev@lists.freedesktop.org">mesa-dev@lists.freedesktop.org</a><br>
>> <a href="https://lists.freedesktop.org/mailman/listinfo/mesa-dev" rel="noreferrer" target="_blank">https://lists.freedesktop.org/mailman/listinfo/mesa-dev</a><br>
</div></div></blockquote></div><br></div>