On 17 September 2017 at 19:01, Gwan-gyeong Mun <elong...@gmail.com> wrote:
> It implements the egl swrast related features for tizen platform on 
> platform_tizen.c
>
> It works with libtpl-egl (Tizen Porting Layer for egl) and libtbm (Tizen 
> Buffer Manager)
> where back buffers of windows are backed by GEM objects. In Tizen a native 
> window
> has a queue (tbm_surface_queue) of back buffers allocated by the WL_TBM
> (wayland client case, WL_TBM is abbreviation of wayland-tbm protocol) or gbm
> (tizen has implements gbm with tbm) or tbm through tbm_backend.
>
> For each frame, EGL needs to
>
>   dequeue the next back buffer - tizen_window_dequeue_buffer()
>   render to the buffer
>   enqueue the buffer - tizen_window_enqueue_buffer()
>
> After enqueuing, the buffer is no longer valid to EGL.
>
> Referenced documents:
> [1] 
> https://www.x.org/wiki/Events/XDC2016/Program/XDC2016_Tizen_Window_System_EGL_Vulkan.pdf
> [2] https://wiki.tizen.org/wiki/3.0_Porting_Guide/Graphics_and_UI/libtpl-egl
> [3] https://wiki.tizen.org/wiki/TBM
>
> Signed-off-by: Mun Gwan-gyeong <elong...@gmail.com>
> ---
>  src/egl/Makefile.am                   |   6 +
>  src/egl/drivers/dri2/platform_tizen.c | 618 
> ++++++++++++++++++++++++++++++++++
>  2 files changed, 624 insertions(+)
>  create mode 100644 src/egl/drivers/dri2/platform_tizen.c
>
> diff --git a/src/egl/Makefile.am b/src/egl/Makefile.am
> index 8ff1ffaba1..ef0c47ad76 100644
> --- a/src/egl/Makefile.am
> +++ b/src/egl/Makefile.am
> @@ -104,6 +104,12 @@ libEGL_common_la_LIBADD += $(ANDROID_LIBS)
>  dri2_backend_FILES += drivers/dri2/platform_android.c
>  endif
>
> +if HAVE_PLATFORM_TIZEN
> +AM_CFLAGS += $(TIZEN_CFLAGS)
> +libEGL_common_la_LIBADD += $(TIZEN_LIBS)
> +dri2_backend_FILES += drivers/dri2/platform_tizen.c
> +endif
> +
>  AM_CFLAGS += \
>         -I$(top_srcdir)/src/loader \
>         -I$(top_builddir)/src/egl/drivers/dri2 \
> diff --git a/src/egl/drivers/dri2/platform_tizen.c 
> b/src/egl/drivers/dri2/platform_tizen.c
> new file mode 100644
> index 0000000000..efdf79682b
> --- /dev/null
> +++ b/src/egl/drivers/dri2/platform_tizen.c
> @@ -0,0 +1,618 @@
> +/*
> + * Mesa 3-D graphics library
> + *
> + * Copyright (C) 2017 Samsung Electronics co., Ltd. All Rights Reserved
> + *
> + * Based on platform_android, which has
> + *
> + * Copyright (C) 2010-2011 Chia-I Wu <olva...@gmail.com>
> + * Copyright (C) 2010-2011 LunarG Inc.
> + * Copyright © 2011 Intel Corporation
> + *
> + * Permission is hereby granted, free of charge, to any person obtaining a
> + * copy of this software and associated documentation files (the "Software"),
> + * to deal in the Software without restriction, including without limitation
> + * the rights to use, copy, modify, merge, publish, distribute, sublicense,
> + * and/or sell copies of the Software, and to permit persons to whom the
> + * Software is furnished to do so, subject to the following conditions:
> + *
> + * The above copyright notice and this permission notice (including the next
> + * paragraph) shall be included in all copies or substantial portions of the
> + * Software.
> + *
> + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
> + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
> + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
> + * NONINFRINGEMENT.  IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
> + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
> + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
> + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
> + * DEALINGS IN THE SOFTWARE.
> + *
> + * Authors:
> + *    Gwan-gyeong Mun <elong...@gmail.com>
> + */
> +
> +#include <stdlib.h>
> +#include <stdio.h>
> +#include <string.h>
> +#include <xf86drm.h>
> +#include <dlfcn.h>
> +#include <sys/types.h>
> +#include <sys/stat.h>
> +#include <fcntl.h>
> +#include <unistd.h>
> +
> +#include "egl_dri2.h"
> +#include "egl_dri2_fallbacks.h"
> +#include "loader.h"
> +
> +static EGLBoolean
> +tizen_window_dequeue_buffer(struct dri2_egl_surface *dri2_surf)
> +{
> +   int width, height;
> +
> +   dri2_surf->tbm_surface = 
> tpl_surface_dequeue_buffer(dri2_surf->tpl_surface);
> +
> +   if (!dri2_surf->tbm_surface)
> +      return EGL_FALSE;
> +
> +   tbm_surface_internal_ref(dri2_surf->tbm_surface);
> +
> +   tpl_surface_get_size(dri2_surf->tpl_surface, &width, &height);
> +   if (dri2_surf->base.Width != width || dri2_surf->base.Height != height) {
> +      dri2_surf->base.Width = width;
> +      dri2_surf->base.Height = height;
> +   }
> +
> +   return EGL_TRUE;
> +}
> +
> +static EGLBoolean
> +tizen_window_enqueue_buffer_with_damage(_EGLDisplay *disp,
> +                                        struct dri2_egl_surface *dri2_surf,
> +                                        const EGLint *rects,
> +                                        EGLint n_rects)
> +{
> +   tpl_result_t ret;
> +
> +   /* To avoid blocking other EGL calls, release the display mutex before
> +    * we enter tizen_window_enqueue_buffer() and re-acquire the mutex upon
> +    * return.
> +    */
> +   mtx_unlock(&disp->Mutex);
> +
> +   if (n_rects < 1 || rects == NULL) {
> +      /* if there is no damage, call the normal API 
> tpl_surface_enqueue_buffer */
> +      ret = tpl_surface_enqueue_buffer(dri2_surf->tpl_surface,
> +                                       dri2_surf->tbm_surface);
> +   } else {
> +      /* if there are rectangles of damage region,
> +         call the API tpl_surface_enqueue_buffer_with_damage() */
> +      ret = tpl_surface_enqueue_buffer_with_damage(dri2_surf->tpl_surface,
> +                                                   dri2_surf->tbm_surface,
> +                                                   n_rects, rects);
> +   }
> +
> +   if (ret != TPL_ERROR_NONE) {
> +      _eglLog(_EGL_DEBUG, "%s : %d :tpl_surface_enqueue fail", __func__, 
> __LINE__);
> +   }
There's no need for brackets for a single line if statements.
Also: shouldn't we issue a _EGL_WARNING when the call fails and/or
return EGL_FALSE?

> +
> +   tbm_surface_internal_unref(dri2_surf->tbm_surface);
> +   dri2_surf->tbm_surface = NULL;
> +
> +   mtx_lock(&disp->Mutex);
> +
> +   return EGL_TRUE;
> +}
> +
> +static EGLBoolean
> +tizen_window_enqueue_buffer(_EGLDisplay *disp, struct dri2_egl_surface 
> *dri2_surf)
> +{
> +   return tizen_window_enqueue_buffer_with_damage(disp, dri2_surf, NULL, 0);
> +}
> +
> +static void
> +tizen_window_cancel_buffer(_EGLDisplay *disp, struct dri2_egl_surface 
> *dri2_surf)
> +{
> +   tizen_window_enqueue_buffer(disp, dri2_surf);
> +}
> +
> +static _EGLSurface *
> +tizen_create_surface(_EGLDriver *drv, _EGLDisplay *disp, EGLint type,
> +                     _EGLConfig *conf, void *native_window,
> +                     const EGLint *attrib_list)
> +{
> +   __DRIcreateNewDrawableFunc createNewDrawable;
> +   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;
> +   const __DRIconfig *config;
> +   tpl_surface_type_t tpl_surf_type = TPL_SURFACE_ERROR;
> +   tpl_result_t ret = TPL_ERROR_INVALID_PARAMETER;
> +
> +   dri2_surf = calloc(1, sizeof *dri2_surf);
> +   if (!dri2_surf) {
> +      _eglError(EGL_BAD_ALLOC, "tizen_create_surface");
> +      return NULL;
> +   }
> +
> +   if (!_eglInitSurface(&dri2_surf->base, disp, type, conf, attrib_list))
> +      goto cleanup_surface;
> +
> +   config = dri2_get_dri_config(dri2_conf, type,
> +                                dri2_surf->base.GLColorspace);
> +   if (!config)
> +      goto cleanup_surface;
> +
> +   if (type == EGL_WINDOW_BIT) {
> +      unsigned int alpha, depth;
> +
> +      if (!native_window) {
> +         _eglError(EGL_BAD_NATIVE_WINDOW, "tizen_create_surface needs vaild 
> native window");
> +         goto cleanup_surface;
> +      }
> +      dri2_surf->native_win = native_window;
> +
> +      dri2_dpy->core->getConfigAttrib(config, __DRI_ATTRIB_DEPTH_SIZE, 
> &depth);
> +      dri2_dpy->core->getConfigAttrib(config, __DRI_ATTRIB_ALPHA_SIZE, 
> &alpha);
> +
> +      ret = tpl_display_get_native_window_info(dri2_dpy->tpl_display,
> +                                               (tpl_handle_t)native_window,
> +                                               &dri2_surf->base.Width,
> +                                               &dri2_surf->base.Height,
> +                                               &dri2_surf->tbm_format,
> +                                               depth, alpha);
> +
> +      if (ret != TPL_ERROR_NONE || dri2_surf->tbm_format == 0) {
> +         _eglError(EGL_BAD_NATIVE_WINDOW, "tizen_create_surface fails on 
> tpl_display_get_native_window_info()");
> +         goto cleanup_surface;
> +      }
> +
> +      tpl_surf_type = TPL_SURFACE_TYPE_WINDOW;
> +   } else if (type == EGL_PIXMAP_BIT) {
> +
> +      if (!native_window) {
> +         _eglError(EGL_BAD_NATIVE_PIXMAP, "tizen_create_surface needs valid 
> native pixmap");
> +         goto cleanup_surface;
> +      }
> +      ret = tpl_display_get_native_pixmap_info(dri2_dpy->tpl_display,
> +                                               (tpl_handle_t)native_window,
> +                                               &dri2_surf->base.Width,
> +                                               &dri2_surf->base.Height,
> +                                               &dri2_surf->tbm_format);
> +
> +      if (ret != TPL_ERROR_NONE || dri2_surf->tbm_format == 0) {
> +         _eglError(EGL_BAD_NATIVE_PIXMAP, "tizen_create_surface fails on 
> tpl_display_get_native_pixmap_info");
> +         goto cleanup_surface;
> +      }
> +
> +      tpl_surf_type = TPL_SURFACE_TYPE_PIXMAP;
> +   } else {
> +      _eglError(EGL_BAD_NATIVE_WINDOW, "tizen_create_surface does not 
> support PBuffer");
> +      goto cleanup_surface;
> +   }
> +
> +   dri2_surf->tpl_surface = tpl_surface_create(dri2_dpy->tpl_display,
> +                                               (tpl_handle_t)native_window,
> +                                               tpl_surf_type,
> +                                               dri2_surf->tbm_format);
> +   if (!dri2_surf->tpl_surface)
> +      goto cleanup_surface;
> +

> +   createNewDrawable = dri2_dpy->swrast->createNewDrawable;
> +
> +   dri2_surf->dri_drawable = (*createNewDrawable)(dri2_dpy->dri_screen, 
> config,
> +                                                  dri2_surf);
This seems odd as-is. Assuming you're adding dri2/dri3 later on -
otherwise please drop the createNewDrawable variable.


> +    if (dri2_surf->dri_drawable == NULL) {
> +       _eglError(EGL_BAD_ALLOC, "createNewDrawable");
> +       goto cleanup_tpl_surface;
> +    }
> +
> +   return &dri2_surf->base;
> +
> +cleanup_tpl_surface:
> +   tpl_object_unreference((tpl_object_t *)dri2_surf->tpl_surface);
> +cleanup_surface:
> +   free(dri2_surf);
> +
> +   return NULL;
> +}
> +
> +static _EGLSurface *
> +tizen_create_window_surface(_EGLDriver *drv, _EGLDisplay *disp,
> +                            _EGLConfig *conf, void *native_window,
> +                            const EGLint *attrib_list)
> +{
> +   return tizen_create_surface(drv, disp, EGL_WINDOW_BIT, conf,
> +                               native_window, attrib_list);
> +}
> +
> +static _EGLSurface *
> +tizen_create_pixmap_surface(_EGLDriver *drv, _EGLDisplay *disp,
> +                            _EGLConfig *conf, void *native_pixmap,
> +                            const EGLint *attrib_list)
> +{
> +   return tizen_create_surface(drv, disp, EGL_PIXMAP_BIT, conf,
> +                               native_pixmap, attrib_list);
> +}
> +
> +static EGLBoolean
> +tizen_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);
> +
> +   if (dri2_surf->base.Type == EGL_WINDOW_BIT) {
> +      if (dri2_surf->tbm_surface)
Fold the two conditionals onto a single line. Same applies throughout
the patch/series.

> +         tizen_window_cancel_buffer(disp, dri2_surf);
> +   }
> +
> +   dri2_dpy->core->destroyDrawable(dri2_surf->dri_drawable);
> +
> +   tpl_object_unreference((tpl_object_t *)dri2_surf->tpl_surface);
> +
> +   free(dri2_surf);
> +
> +   return EGL_TRUE;
> +}
> +
> +static int
> +update_buffers(struct dri2_egl_surface *dri2_surf)
> +{
> +   if (dri2_surf->base.Type != EGL_WINDOW_BIT)
> +      return 0;
> +
> +   /* try to dequeue the next back buffer */
> +   if (!dri2_surf->tbm_surface && !tizen_window_dequeue_buffer(dri2_surf)) {
> +      _eglLog(_EGL_WARNING, "Could not dequeue buffer from native window");
> +      return -1;
> +   }
> +
> +   return 0;
> +}
> +
> +static EGLBoolean
> +tizen_swap_buffers_with_damage(_EGLDriver *drv, _EGLDisplay *disp,
> +                               _EGLSurface *draw, const EGLint *rects,
> +                               EGLint n_rects)
> +{
> +   struct dri2_egl_display *dri2_dpy = dri2_egl_display(disp);
> +   struct dri2_egl_surface *dri2_surf = dri2_egl_surface(draw);
> +
> +   if (dri2_surf->base.Type != EGL_WINDOW_BIT)
> +      return EGL_TRUE;
> +
> +   if (dri2_surf->tbm_surface)
> +      tizen_window_enqueue_buffer_with_damage(disp, dri2_surf, rects, 
> n_rects);
> +
> +   dri2_dpy->core->swapBuffers(dri2_surf->dri_drawable);
> +
> +   return EGL_TRUE;
> +}
> +
> +static EGLBoolean
> +tizen_swap_buffers(_EGLDriver *drv, _EGLDisplay *disp, _EGLSurface *draw)
> +{
> +   return tizen_swap_buffers_with_damage (drv, disp, draw, NULL, 0);
> +}
> +
> +static EGLBoolean
> +tizen_query_surface(_EGLDriver *drv, _EGLDisplay *dpy, _EGLSurface *surf,
> +                    EGLint attribute, EGLint *value)
> +{
> +   struct dri2_egl_display *dri2_dpy = dri2_egl_display(dpy);
> +   struct dri2_egl_surface *dri2_surf = dri2_egl_surface(surf);
> +   int width = 0, height = 0;
> +
> +   if (tpl_display_get_native_window_info(dri2_dpy->tpl_display,
> +                                          dri2_surf->native_win,
> +                                          &width, &height, NULL, 0, 0) != 
> TPL_ERROR_NONE)
> +      return EGL_FALSE;
> +
> +   switch (attribute) {
> +      case EGL_WIDTH:
> +         if (dri2_surf->base.Type == EGL_WINDOW_BIT && 
> dri2_surf->native_win) {
> +            *value = width;
> +            return EGL_TRUE;
> +         }
> +         break;
> +      case EGL_HEIGHT:
> +         if (dri2_surf->base.Type == EGL_WINDOW_BIT && 
> dri2_surf->native_win) {
> +            *value = height;
> +            return EGL_TRUE;
> +         }
> +         break;
> +      default:
> +         break;
> +   }
This seems strange - get the window dimensions, even if a) there's no
window and b) one does not ask for them?
Also indentation is off.

> +   return _eglQuerySurface(drv, dpy, surf, attribute, value);
> +}
> +
> +static int
> +tizen_swrast_get_stride_for_format(tbm_format format, int w)
> +{
> +   switch (format) {
> +   case TBM_FORMAT_RGB565:
> +      return 2 * w;
> +#pragma GCC diagnostic push
> +#pragma GCC diagnostic ignored "-Wswitch"
> +   case TBM_FORMAT_BGRA8888:
> +   case TBM_FORMAT_RGBA8888:
> +#pragma GCC diagnostic pop
> +   case TBM_FORMAT_RGBX8888:
> +   default:
> +      return 4 * w;
> +   }
> +}
You want something like gbm_bo_get_bpp.
No pragma magic, with sane default.


> +static void
> +tizen_swrast_get_image(__DRIdrawable * read,
> +                       int x, int y, int w, int h,
> +                       char *data, void *loaderPrivate)
> +{
> +   struct dri2_egl_surface *dri2_surf = loaderPrivate;
> +   tbm_surface_info_s surf_info;
> +   int ret = TBM_SURFACE_ERROR_NONE;
> +   int internal_stride, stride, i;
> +
> +   if (!dri2_surf->tbm_surface) {
> +      if (update_buffers(dri2_surf) < 0)
> +         return;
> +   }
> +
> +   ret = tbm_surface_map(dri2_surf->tbm_surface, TBM_SURF_OPTION_READ, 
> &surf_info);
> +
> +   if (ret != TBM_SURFACE_ERROR_NONE) {
> +      _eglLog(_EGL_WARNING, "Could not tbm_surface_map");
> +      return;
> +   }
> +
> +   internal_stride = surf_info.planes[0].stride;
> +   stride = w * 4;
You created a helper yet, use it to get the correct stride.

> +
> +   for (i = 0; i < h; i++) {
> +      memcpy(data + i * stride,
> +             surf_info.planes[0].ptr + (x + i) * internal_stride + y, 
> stride);
Nit: use something like fe2a6281b3b299998fe7399e7dbcc2077d773824

> +   }
> +
> +   tbm_surface_unmap(dri2_surf->tbm_surface);
> +}
> +
> +static void
> +tizen_swrast_put_image2(__DRIdrawable * draw, int op,
> +                        int x, int y, int w, int h, int stride,
> +                        char *data, void *loaderPrivate)
> +{
> +   struct dri2_egl_surface *dri2_surf = loaderPrivate;
> +   tbm_surface_info_s surf_info;
> +   int ret = TBM_SURFACE_ERROR_NONE;
> +   int internal_stride, i;
> +
> +   if (op != __DRI_SWRAST_IMAGE_OP_DRAW && op != __DRI_SWRAST_IMAGE_OP_SWAP)
> +      return;
> +
> +   if (dri2_surf->base.Type == EGL_WINDOW_BIT) {
> +      if (!dri2_surf->tbm_surface) {
> +         if (update_buffers(dri2_surf) < 0) {
> +            _eglLog(_EGL_WARNING, "Could not get native buffer");
> +            return;
> +        }
> +      }
> +
> +      ret = tbm_surface_map(dri2_surf->tbm_surface, TBM_SURF_OPTION_WRITE, 
> &surf_info);
> +      if (ret != TBM_SURFACE_ERROR_NONE) {
> +         _eglLog(_EGL_WARNING, "Could not tbm_surface_map");
> +         return;
> +      }
> +
> +      internal_stride = surf_info.planes[0].stride;
> +
> +      for (i = 0; i < h; i++) {
> +         memcpy(surf_info.planes[0].ptr + (x + i) * internal_stride + y,
> +                data + i * stride, stride);
Alike 3a5e3aa5a53cff55a5e31766d713a41ffa5a93d7 ?


> +      }
> +
> +      tbm_surface_unmap(dri2_surf->tbm_surface);
> +   }
> +}
> +
> +static void
> +tizen_swrast_put_image(__DRIdrawable * draw, int op,
> +                         int x, int y, int w, int h,
> +                         char *data, void *loaderPrivate)
> +{
> +   struct dri2_egl_surface *dri2_surf = loaderPrivate;
> +   int stride;
> +
> +   if (dri2_surf->base.Type == EGL_WINDOW_BIT) {
> +      if (!dri2_surf->tbm_surface) {
> +         if (update_buffers(dri2_surf) < 0) {
> +            _eglLog(_EGL_WARNING, "Could not get native buffer");
> +            return;
> +        }
> +      }
Indentation seems off.

> +
> +      stride = tizen_swrast_get_stride_for_format(dri2_surf->tbm_format, w);
> +      tizen_swrast_put_image2(draw, op, x, y, w, h, stride, data, 
> loaderPrivate);
> +   }
> +}
> +
> +static EGLBoolean
> +tizen_add_configs(_EGLDriver *drv, _EGLDisplay *dpy)
> +{
> +   struct dri2_egl_display *dri2_dpy = dri2_egl_display(dpy);
> +   int config_count = 0;
> +
> +   for (int i = 0; dri2_dpy->driver_configs[i]; i++) {
> +      struct dri2_egl_config *dri2_conf;
> +      unsigned int red, blue, green, alpha, depth;
> +      int surface_type = 0;
> +      tpl_bool_t is_slow;
> +      EGLint config_attrs[] = {
> +         EGL_NATIVE_VISUAL_ID, 0,
> +         EGL_NONE,
> +      };
> +      tpl_result_t res;
> +
> +      dri2_dpy->core->getConfigAttrib(dri2_dpy->driver_configs[i],
> +                                      __DRI_ATTRIB_RED_SIZE, &red);
> +      dri2_dpy->core->getConfigAttrib(dri2_dpy->driver_configs[i],
> +                                      __DRI_ATTRIB_GREEN_SIZE, &green);
> +      dri2_dpy->core->getConfigAttrib(dri2_dpy->driver_configs[i],
> +                                      __DRI_ATTRIB_BLUE_SIZE, &blue);
> +      dri2_dpy->core->getConfigAttrib(dri2_dpy->driver_configs[i],
> +                                      __DRI_ATTRIB_ALPHA_SIZE, &alpha);
> +      dri2_dpy->core->getConfigAttrib(dri2_dpy->driver_configs[i],
> +                                      __DRI_ATTRIB_DEPTH_SIZE, &depth);
> +
Would be great if we can avoid all there roundtrips. My Tizen
knowledge is non-existent to suggest alternatives :-\

> +      res = tpl_display_query_config(dri2_dpy->tpl_display, 
> TPL_SURFACE_TYPE_WINDOW,
> +                                     red, green, blue, alpha, depth,
> +                                     &config_attrs[1], &is_slow);
> +      if (res == TPL_ERROR_NONE)
> +         surface_type |= EGL_WINDOW_BIT;
> +
> +      res = tpl_display_query_config(dri2_dpy->tpl_display, 
> TPL_SURFACE_TYPE_PIXMAP,
> +                                     red, green, blue, alpha, depth,
> +                                     &config_attrs[1], &is_slow);
> +      if (res == TPL_ERROR_NONE)
> +         surface_type |= EGL_PIXMAP_BIT;
> +
This seems strange:
Can one have both pixmap and window bits set? If yes, what happens to
the visual ID - seems like it gets trashed.

-Emil
_______________________________________________
mesa-dev mailing list
mesa-dev@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/mesa-dev

Reply via email to