Eine aufbereitete Darstellung der Quelle

 
     
 
 
Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 

Benutzer

Quelle  gdkdmabuf.c

  Sprache: C
 

/* gdkdmabuf.c
 *
 * Copyright 2023  Red Hat, Inc.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library. If not, see <http://www.gnu.org/licenses/>.
 */


#include "config.h"

#include "gdkdmabufprivate.h"

#include "gdkdebugprivate.h"
#include "gdkdmabuffourccprivate.h"
#include "gdkdmabuftextureprivate.h"
#include "gdkmemoryformatprivate.h"

#ifdef HAVE_DMABUF
#include <fcntl.h>
#include <sys/mman.h>
#include <sys/ioctl.h>
#include <linux/dma-buf.h>
#ifdef HAVE_UDMABUF
#include <linux/udmabuf.h>
#endif
#include <epoxy/egl.h>
#include <errno.h>

GdkDmabufFormats *
gdk_dmabuf_get_mmap_formats (void)
{
  static GdkDmabufFormats *formats = NULL;

  if (formats == NULL)
    {
      GdkDmabufFormatsBuilder *builder;
      guint32 fourcc;
      gsize i;

      builder = gdk_dmabuf_formats_builder_new ();

      for (i = 0; i < GDK_MEMORY_N_FORMATS; i++)
        {
          fourcc = gdk_memory_format_get_dmabuf_rgb_fourcc (i);
          if (fourcc != 0)
            {
              GDK_DEBUG (DMABUF,
                         "mmap advertises dmabuf format %.4s::%016" G_GINT64_MODIFIER "x as RGB",
                         (char *) &fourcc, (guint64) DRM_FORMAT_MOD_LINEAR);
              gdk_dmabuf_formats_builder_add_format (builder, fourcc, DRM_FORMAT_MOD_LINEAR);
            }
          fourcc = gdk_memory_format_get_dmabuf_yuv_fourcc (i);
          if (fourcc != 0)
            {
              GDK_DEBUG (DMABUF,
                         "mmap advertises dmabuf format %.4s::%016" G_GINT64_MODIFIER "x as YUV",
                         (char *) &fourcc, (guint64) DRM_FORMAT_MOD_LINEAR);
              gdk_dmabuf_formats_builder_add_format (builder, fourcc, DRM_FORMAT_MOD_LINEAR);
            }
        }

      formats = gdk_dmabuf_formats_builder_free_to_formats (builder);
    }

  return formats;
}

const guchar *
gdk_dmabuf_mmap (int    dmabuf_fd,
                 gsize *out_size)
{
  gsize size;
  const guchar *result;

  size = lseek (dmabuf_fd, 0, SEEK_END);
  if (size == (off_t) -1)
    {
      g_warning ("Failed to seek dmabuf: %s", g_strerror (errno));
      return NULL;
    }

  /* be a good citizen and seek back to the start, as the docs recommend */
  lseek (dmabuf_fd, 0, SEEK_SET);

  if (gdk_dmabuf_ioctl (dmabuf_fd,
                        DMA_BUF_IOCTL_SYNC,
                        &(struct dma_buf_sync) { DMA_BUF_SYNC_START|DMA_BUF_SYNC_READ }) < 0)
    {
      g_warning ("Failed to sync dmabuf before mmap(): %s", g_strerror (errno));
      /* not a fatal error, but might cause glitches */
    }

  result = mmap (NULL, size, PROT_READ, MAP_SHARED, dmabuf_fd, 0);
  if (result == NULL || result == MAP_FAILED)
    {
      g_warning ("Failed to mmap dmabuf: %s", g_strerror (errno));
      return NULL;
    }

  *out_size = size;

  return result;
}

void
gdk_dmabuf_munmap (int           dmabuf_fd,
                   const guchar *addr,
                   gsize         size)
{
  munmap ((void *) addr, size);

  if (gdk_dmabuf_ioctl (dmabuf_fd,
                        DMA_BUF_IOCTL_SYNC,
                        &(struct dma_buf_sync) { DMA_BUF_SYNC_END|DMA_BUF_SYNC_READ }) < 0)
    {
      g_warning ("Failed to sync dmabuf after munmap(): %s", g_strerror (errno));
      /* not a fatal error, but might cause glitches */
    }
}

static gboolean
gdk_dmabuf_do_download_mmap (GdkTexture            *texture,
                             guchar                *data,
                             const GdkMemoryLayout *layout,
                             GdkColorState         *color_state)
{
  const GdkDmabuf *dmabuf;
  GdkMemoryLayout dmabuf_layout;
  const guchar *src_data[GDK_DMABUF_MAX_PLANES];
  gsize sizes[GDK_DMABUF_MAX_PLANES];
  gsize needs_unmap[GDK_DMABUF_MAX_PLANES] = { FALSE, };
  gsize i, j;
  gboolean retval = FALSE;

  dmabuf = gdk_dmabuf_texture_get_dmabuf (GDK_DMABUF_TEXTURE (texture));

  if (dmabuf->modifier != DRM_FORMAT_MOD_LINEAR)
    return FALSE;

  for (i = 0; i < dmabuf->n_planes; i++)
    {
      if (dmabuf->planes[i].fd == -1)
        {
          g_assert (i > 0);
          src_data[i] = src_data[0];
          sizes[i] = sizes[0];
          continue;
        }

      for (j = 0; j < i; j++)
        {
          if (dmabuf->planes[i].fd == dmabuf->planes[j].fd)
            break;
        }
      if (j < i)
        {
          src_data[i] = src_data[j];
          sizes[i] = sizes[j];
          continue;
        }

      src_data[i] = gdk_dmabuf_mmap (dmabuf->planes[i].fd, &sizes[i]);
      if (src_data[i] == NULL)
        goto out;
      needs_unmap[i] = TRUE;
    }

  if (gdk_memory_layout_init_from_dmabuf (&dmabuf_layout,
                                          dmabuf,
                                          gdk_memory_format_alpha (gdk_texture_get_format (texture)) == GDK_MEMORY_ALPHA_PREMULTIPLIED,
                                          gdk_texture_get_width (texture),
                                          gdk_texture_get_height (texture)))
    {
      gdk_memory_convert (data,
                          layout,
                          gdk_texture_get_color_state (texture),
                          src_data[0],
                          &dmabuf_layout,
                          gdk_texture_get_color_state (texture));
      retval = TRUE;
    }

  GDK_DISPLAY_DEBUG (gdk_dmabuf_texture_get_display (GDK_DMABUF_TEXTURE (texture)), DMABUF,
                     "Used mmap for downloading %dx%d dmabuf (format %.4s:%#" G_GINT64_MODIFIER "x)",
                     gdk_texture_get_width (texture), gdk_texture_get_height (texture),
                     (char *)&dmabuf->fourcc, dmabuf->modifier);

out:
  for (i = 0; i < dmabuf->n_planes; i++)
    {
      if (!needs_unmap[i])
        continue;

      gdk_dmabuf_munmap (dmabuf->planes[i].fd, src_data[i], sizes[i]);
    }

  return retval;
}

gboolean
gdk_dmabuf_download_mmap (GdkTexture            *texture,
                          guchar                *data,
                          const GdkMemoryLayout *layout,
                          GdkColorState         *color_state)
{
  GdkMemoryFormat src_format = gdk_texture_get_format (texture);
  GdkColorState *src_color_state = gdk_texture_get_color_state (texture);
  gboolean retval;

  if (layout->format == src_format)
    {
      retval = gdk_dmabuf_do_download_mmap (texture, data, layout, color_state);
      gdk_memory_convert_color_state (data,
                                      layout,
                                      src_color_state,
                                      color_state);
    }
  else
    {
      GdkMemoryLayout src_layout;
      guchar *src_data;

      gdk_memory_layout_init (&src_layout,
                              src_format,
                              gdk_texture_get_width (texture),
                              gdk_texture_get_height (texture),
                              1);

      src_data = g_new (guchar, src_layout.size);

      retval = gdk_dmabuf_do_download_mmap (texture, src_data, &src_layout, src_color_state);

      gdk_memory_convert (data,
                          layout,
                          color_state,
                          src_data,
                          &src_layout,
                          src_color_state);

      g_free (src_data);
    }

  return retval;
}

int
gdk_dmabuf_ioctl (int            fd,
                  unsigned long  request,
                  void          *arg)
{
  int ret;

  do {
    ret = ioctl (fd, request, arg);
  } while (ret == -1 && (errno == EINTR || errno == EAGAIN));

  return ret;
}

#if !defined(DMA_BUF_IOCTL_IMPORT_SYNC_FILE)
struct dma_buf_import_sync_file
{
  __u32 flags;
  __s32 fd;
};
#define DMA_BUF_IOCTL_IMPORT_SYNC_FILE _IOW(DMA_BUF_BASE, 3struct dma_buf_import_sync_file)
#endif

gboolean
gdk_dmabuf_import_sync_file (int     dmabuf_fd,
                             guint32 flags,
                             int     sync_file_fd)
{
  struct dma_buf_import_sync_file data = {
    .flags = flags,
    .fd = sync_file_fd,
  };

  if (gdk_dmabuf_ioctl (dmabuf_fd, DMA_BUF_IOCTL_IMPORT_SYNC_FILE, &data) != 0)
    {
      GDK_DEBUG (DMABUF, "Importing dmabuf sync failed: %s", g_strerror (errno));
      return FALSE;
    }
  
  return TRUE;
}

#if !defined(DMA_BUF_IOCTL_EXPORT_SYNC_FILE)
struct dma_buf_export_sync_file
{
  __u32 flags;
  __s32 fd;
};
#define DMA_BUF_IOCTL_EXPORT_SYNC_FILE _IOWR(DMA_BUF_BASE, 2struct dma_buf_export_sync_file)
#endif

int
gdk_dmabuf_export_sync_file (int     dmabuf_fd,
                             guint32 flags)
{
  struct dma_buf_export_sync_file data = {
    .flags = flags,
    .fd = -1,
  };

  if (gdk_dmabuf_ioctl (dmabuf_fd, DMA_BUF_IOCTL_EXPORT_SYNC_FILE, &data) != 0)
    {
      GDK_DEBUG (DMABUF, "Exporting dmabuf sync failed: %s", g_strerror (errno));
      return -1;
    }

  return data.fd;
}

/* 
 * Tries to sanitize the dmabuf to conform to the values expected
 * by Vulkan/EGL which should also be the values expected by
 * Wayland compositors
 *
 * We put these sanitized values into the GdkDmabufTexture, by
 * sanitizing the input from GdkDmabufTextureBuilder, which are
 * controlled by the callers.
 *
 * Things we do here:
 *
 * 1. Ignore non-linear modifiers.
 *
 * 2. Try and fix various inconsistencies between V4L and Mesa
 *    for linear modifiers, like the e.g. single-plane NV12.
 *
 * *** WARNING ***
 *
 * This function is not absolutely perfect, you do not have a
 * perfect dmabuf afterwards.
 *
 * In particular, it doesn't check sizes.
 *
 * *** WARNING ***
 */

gboolean
gdk_dmabuf_sanitize (GdkDmabuf        *dest,
                     gsize             width,
                     gsize             height,
                     const GdkDmabuf  *src,
                     GError          **error)
{
  if (src->n_planes > GDK_DMABUF_MAX_PLANES)
    {
      g_set_error (error,
                   GDK_DMABUF_ERROR, GDK_DMABUF_ERROR_UNSUPPORTED_FORMAT,
                   "GTK only support dmabufs with %u planes, not %u",
                   GDK_DMABUF_MAX_PLANES, src->n_planes);
      return FALSE;
    }

  *dest = *src;

  if (src->modifier)
    return TRUE;

  switch (dest->fourcc)
    {
      case DRM_FORMAT_NV12:
      case DRM_FORMAT_NV21:
      case DRM_FORMAT_NV16:
      case DRM_FORMAT_NV61:
        if (dest->n_planes == 1)
          {
            dest->n_planes = 2;
            dest->planes[1].fd = dest->planes[0].fd;
            dest->planes[1].stride = dest->planes[0].stride;
            dest->planes[1].offset = dest->planes[0].offset + dest->planes[0].stride * height;
          }
        break;

      case DRM_FORMAT_NV24:
      case DRM_FORMAT_NV42:
        if (dest->n_planes == 1)
          {
            dest->n_planes = 2;
            dest->planes[1].stride = dest->planes[0].stride * 2;
            dest->planes[1].offset = dest->planes[0].offset + dest->planes[0].stride * height;
          }
        break;

      case DRM_FORMAT_YUV410:
      case DRM_FORMAT_YVU410:
        if (dest->n_planes == 1)
          {
            dest->n_planes = 3;
            dest->planes[1].stride = (dest->planes[0].stride + 3) / 4;
            dest->planes[1].offset = dest->planes[0].offset + dest->planes[0].stride * height;
            dest->planes[2].stride = dest->planes[1].stride;
            dest->planes[2].offset = dest->planes[1].offset + dest->planes[1].stride * ((height + 3) / 4);
          }
        break;

      case DRM_FORMAT_YUV411:
      case DRM_FORMAT_YVU411:
        if (dest->n_planes == 1)
          {
            dest->n_planes = 3;
            dest->planes[1].stride = (dest->planes[0].stride + 3) / 4;
            dest->planes[1].offset = dest->planes[0].offset + dest->planes[0].stride * height;
            dest->planes[2].stride = dest->planes[1].stride;
            dest->planes[2].offset = dest->planes[1].offset + dest->planes[1].stride * height;
          }
        break;

      case DRM_FORMAT_YUV420:
      case DRM_FORMAT_YVU420:
        if (dest->n_planes == 1)
          {
            dest->n_planes = 3;
            dest->planes[1].stride = (dest->planes[0].stride + 1) / 2;
            dest->planes[1].offset = dest->planes[0].offset + dest->planes[0].stride * height;
            dest->planes[2].stride = dest->planes[1].stride;
            dest->planes[2].offset = dest->planes[1].offset + dest->planes[1].stride * ((height + 1) / 2);
          }
        break;

      case DRM_FORMAT_YUV422:
      case DRM_FORMAT_YVU422:
        if (dest->n_planes == 1)
          {
            dest->n_planes = 3;
            dest->planes[1].stride = (dest->planes[0].stride + 1) / 2;
            dest->planes[1].offset = dest->planes[0].offset + dest->planes[0].stride * height;
            dest->planes[2].stride = dest->planes[1].stride;
            dest->planes[2].offset = dest->planes[1].offset + dest->planes[1].stride * height;
          }
        break;

      case DRM_FORMAT_YUV444:
      case DRM_FORMAT_YVU444:
        if (dest->n_planes == 1)
          {
            dest->n_planes = 3;
            dest->planes[1].stride = dest->planes[0].stride;
            dest->planes[1].offset = dest->planes[0].offset + dest->planes[0].stride * height;
            dest->planes[2].stride = dest->planes[1].stride;
            dest->planes[2].offset = dest->planes[1].offset + dest->planes[1].stride * height;
          }
        break;

      default:
        break;
    }

  return TRUE;
}

/*
 * gdk_dmabuf_is_disjoint:
 * @dmabuf: a sanitized GdkDmabuf
 *
 * A dmabuf is considered disjoint if it uses more than
 * 1 inode.
 * Multiple file descriptors may exist when the creator
 * of the dmabuf just dup()ed once for every plane...
 *
 * Returns: %TRUE if the dmabuf is disjoint
 **/

gboolean
gdk_dmabuf_is_disjoint (const GdkDmabuf *dmabuf)
{
  struct stat first_stat;
  unsigned i;

  /* First, do a fast check */

  for (i = 1; i < dmabuf->n_planes; i++)
    {
      if (dmabuf->planes[i].fd == -1)
        continue;

      if (dmabuf->planes[0].fd != dmabuf->planes[i].fd)
        break;
    }

  if (i == dmabuf->n_planes)
    return FALSE;

  /* We have different fds, do the fancy check instead */

  if (fstat (dmabuf->planes[0].fd, &first_stat) != 0)
    return TRUE;

  for (i = 1; i < dmabuf->n_planes; i++)
    {
      struct stat plane_stat;

      if (dmabuf->planes[i].fd == -1)
        continue;

      if (fstat (dmabuf->planes[i].fd, &plane_stat) != 0)
        return TRUE;

      if (first_stat.st_ino != plane_stat.st_ino)
        return TRUE;
    }

  return FALSE;
}

static int
udmabuf_initialize (GError **error)
{
  static int udmabuf_fd;

  if (udmabuf_fd == 0)
    {
      udmabuf_fd = open ("/dev/udmabuf", O_RDWR);
      if (udmabuf_fd == -1)
        {
          g_set_error (error,
                       G_IO_ERROR, G_IO_ERROR_FAILED,
                       "Failed to open /dev/udmabuf: %s",
                       g_strerror (errno));
        }
    }

  return udmabuf_fd;
}

#define align(x,y) (((x) + (y) - 1) & ~((y) - 1))

/*<private>
 * gdk_dmabuf_new_for_bytes:
 * @bytes: the bytes to create a dmabuf for
 * @error: Return location for an error
 *
 * Creates a dmabuf representing the given bytes.
 *
 * Returns: The new dmabuf fd or -1 on error.
 **/

int
gdk_dmabuf_new_for_bytes (GBytes  *bytes,
                          GError **error)
{
  int udmabuf_fd;
  int mem_fd = -1;
  int dmabuf_fd = -1;
  gsize alignment, size;
  gpointer data;
  int res;

  g_return_val_if_fail (bytes != NULL, -1);

  if (!gdk_has_feature (GDK_FEATURE_DMABUF))
    {
      g_set_error_literal (error,
                           G_IO_ERROR, G_IO_ERROR_FAILED,
                           "Dmabuf support is disabled");
      return -1;
    }

  udmabuf_fd = udmabuf_initialize (error);
  if (udmabuf_fd < 0)
    return -1;

  alignment = sysconf (_SC_PAGE_SIZE);
  size = align (g_bytes_get_size (bytes), alignment);

#ifdef HAVE_MEMFD_CREATE
  mem_fd = memfd_create ("gtk", MFD_ALLOW_SEALING);
#endif
  if (mem_fd == -1)
    {
      g_set_error (error,
                   G_IO_ERROR, G_IO_ERROR_FAILED,
                   "Failed to create memfd: %s",
                   g_strerror (errno));
      goto out;
    }

  res = ftruncate (mem_fd, size);
  if (res == -1)
    {
      g_set_error (error,
                   G_IO_ERROR, G_IO_ERROR_FAILED,
                   "ftruncate on memfd failed: %s",
                    g_strerror (errno));
      goto out;
    }

  if (fcntl (mem_fd, F_ADD_SEALS, F_SEAL_SHRINK) < 0)
    {
      g_set_error (error,
                   G_IO_ERROR, G_IO_ERROR_FAILED,
                   "fcntl on memfd failed: %s",
                    g_strerror (errno));
      goto out;
    }

  data = mmap (NULL, size, PROT_WRITE, MAP_SHARED, mem_fd, 0);
  if (data == NULL || data == MAP_FAILED)
    {
      g_set_error (error,
                   G_IO_ERROR, G_IO_ERROR_FAILED,
                   "mmap failed: %s",
                    g_strerror (errno));
      goto out;
    }

  memcpy (data, g_bytes_get_data (bytes, NULL), g_bytes_get_size (bytes));

  munmap (data, size);

#ifdef HAVE_UDMABUF
  dmabuf_fd = ioctl (udmabuf_fd,
                     UDMABUF_CREATE,
                     &(struct udmabuf_create) {
                       .memfd = mem_fd,
                       .flags = UDMABUF_FLAGS_CLOEXEC,
                       .offset = 0,
                       .size = size
                     });
#endif

  if (dmabuf_fd < 0)
    {
      g_set_error (error,
                   G_IO_ERROR, G_IO_ERROR_FAILED,
                   "UDMABUF_CREATE ioctl failed: %s",
                    g_strerror (errno));
    }

out:
  if (mem_fd != -1)
    close (mem_fd);

  return dmabuf_fd;
}
#endif  /* HAVE_DMABUF */

void
gdk_dmabuf_close_fds (GdkDmabuf *dmabuf)
{
  guint i, j;

  for (i = 0; i < dmabuf->n_planes; i++)
    {
      if (dmabuf->planes[i].fd == -1)
        continue;

      for (j = 0; j < i; j++)
        {
          if (dmabuf->planes[i].fd == dmabuf->planes[j].fd)
            break;
        }
      if (i == j)
        g_close (dmabuf->planes[i].fd, NULL);
    }
}

gboolean
gdk_memory_layout_init_from_dmabuf (GdkMemoryLayout *self,
                                    const GdkDmabuf *dmabuf,
                                    gboolean         premultiplied,
                                    gsize            width,
                                    gsize            height)
{
  gboolean is_yuv;
  gsize i;

  if (dmabuf->modifier != DRM_FORMAT_MOD_LINEAR)
    return FALSE;

  if (!gdk_memory_format_find_by_dmabuf_fourcc (dmabuf->fourcc, premultiplied, &self->format, &is_yuv))
    return FALSE;

  if (!gdk_memory_format_is_block_boundary (self->format, width, height))
    return FALSE;

  g_return_val_if_fail (dmabuf->n_planes == gdk_memory_format_get_n_planes (self->format), FALSE);

  self->width = width;
  self->height = height;

  for (i = 0; i < dmabuf->n_planes; i++)
    {
      self->planes[i].offset = dmabuf->planes[i].offset;
      self->planes[i].stride = dmabuf->planes[i].stride;
    }

  self->size = self->planes[dmabuf->n_planes - 1].offset + 
               (self->height - 1) / gdk_memory_format_get_plane_block_height (self->format, dmabuf->n_planes - 1)
                                  * self->planes[dmabuf->n_planes - 1].stride +
               self->width / gdk_memory_format_get_plane_block_width (self->format, dmabuf->n_planes - 1)
                           * gdk_memory_format_get_plane_block_bytes (self->format, dmabuf->n_planes - 1);

  return TRUE;
}


Messung V0.5 in Prozent
C=98 H=96 G=96

¤ Dauer der Verarbeitung: 0.13 Sekunden  (vorverarbeitet am  2026-07-03) ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

PVS Prover

Isabelle Prover

NIST Cobol Testsuite

Cephes Mathematical Library

Vienna Development Method

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Software

     Quellcodebibliothek
     Eigene Quellcodes
     Fremde Quellcodes
     Suchen

Aktivitäten

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

Muße

     Gedichte
     Musik
     Bilder

Jenseits des Üblichen ....
    

Besucherstatistik

Besucherstatistik