<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>