diff --git a/12to11.c b/12to11.c
index 034cd08..fcc3ec2 100644
--- a/12to11.c
+++ b/12to11.c
@@ -28,35 +28,6 @@ along with 12to11. If not, see . */
/* Globals. */
Compositor compositor;
-static Visual *
-PickVisual (int *depth)
-{
- int n_visuals;
- XVisualInfo vinfo, *visuals;
- Visual *selection;
-
- vinfo.screen = DefaultScreen (compositor.display);
- vinfo.class = TrueColor;
- vinfo.depth = 32;
-
- visuals = XGetVisualInfo (compositor.display, (VisualScreenMask
- | VisualClassMask
- | VisualDepthMask),
- &vinfo, &n_visuals);
-
- if (n_visuals)
- {
- selection = visuals[0].visual;
- *depth = visuals[0].depth;
- XFree (visuals);
-
- return selection;
- }
-
- fprintf (stderr, "A 32-bit TrueColor visual was not found\n");
- exit (1);
-}
-
static Colormap
MakeColormap (void)
{
@@ -129,8 +100,6 @@ XLMain (int argc, char **argv)
compositor.wl_socket = socket;
compositor.wl_event_loop
= wl_display_get_event_loop (wl_display);
- compositor.visual = PickVisual (&compositor.n_planes);
- compositor.colormap = MakeColormap ();
InitXErrors ();
SubcompositorInit ();
@@ -138,6 +107,15 @@ XLMain (int argc, char **argv)
XLInitTimers ();
XLInitAtoms ();
+
+ /* Initialize renderers immediately after timers and atoms are set
+ up. */
+ InitRenderers ();
+
+ /* Set up the colormap. Initializing renderers should also cause
+ the visual to be set. */
+ compositor.colormap = MakeColormap ();
+
XLInitRROutputs ();
XLInitCompositor ();
XLInitSurfaces ();
diff --git a/12to11.man b/12to11.man
index 4bc7a68..701e6ae 100644
--- a/12to11.man
+++ b/12to11.man
@@ -67,6 +67,18 @@ variable, if set, forces ConfigureNotify events from the window
manager to be handled directly, without waiting some time for a
corresponding _NET_WM_STATE event to arrive. This is only
useful for debugging the window resizing logic.
+.PP
+The
+.B RENDERER
+variable, if set, controls the rendering backend used by the
+protocol translator. When set to
+.I help
+it prints a list of available rendering backends instead.
+.PP
+The
+.B RENDER_VISUAL
+variable, if set to a number, contains the ID of the visual used
+when the EGL rendering backend is in use.
.SH BUGS
There is a hard to catch bug where Wayland programs leaving the
fullscreen or maximized state may abruptly return to their maximized
diff --git a/Imakefile b/Imakefile
index 7315a2c..a17b35e 100644
--- a/Imakefile
+++ b/Imakefile
@@ -22,7 +22,8 @@ SRCS = 12to11.c run.c alloc.c fns.c output.c compositor.c \
frame_clock.c xerror.c ewmh.c timer.c subsurface.c seat.c \
data_device.c xdg_popup.c linux-dmabuf-unstable-v1.c dmabuf.c \
buffer.c select.c xdata.c xsettings.c dnd.c icon_surface.c \
- primary-selection-unstable-v1.c primary_selection.c
+ primary-selection-unstable-v1.c primary_selection.c \
+ renderer.c picture_renderer.c
OBJS = 12to11.o run.o alloc.o fns.o output.o compositor.o \
xdg-shell.o surface.o region.o shm.o atoms.o subcompositor.o \
@@ -30,7 +31,32 @@ OBJS = 12to11.o run.o alloc.o fns.o output.o compositor.o \
frame_clock.o xerror.o ewmh.o timer.o subsurface.o seat.o \
data_device.o xdg_popup.o linux-dmabuf-unstable-v1.o dmabuf.o \
buffer.o select.o xdata.o xsettings.o dnd.o icon_surface.o \
- primary-selection-unstable-v1.o primary_selection.o
+ primary-selection-unstable-v1.o primary_selection.o \
+ renderer.o picture_renderer.o
+
+GENHEADERS = transfer_atoms.h primary-selection-unstable-v1.h \
+ linux-dmabuf-unstable-v1.h xdg-shell.h
+
+#ifdef HaveEglSupport
+
+ EGL_SRCS = egl.c
+ EGL_OBJS = egl.o
+LOCAL_LIBRARIES ::= $(LOCAL_LIBRARIES) $(EGL) $(GLES)
+ SRCS ::= $(SRCS) $(EGL_SRCS)
+ OBJS ::= $(OBJS) $(EGL_OBJS)
+ DEFINES ::= $(DEFINES) -DHaveEglSupport
+
+shaders.h: shaders.txt shaders.awk
+ awk -f shaders.awk shaders.txt > $@
+
+depend:: shaders.h
+
+$(EGL_OBJS): shaders.h
+
+cleandir::
+ $(RM) shaders.h
+
+#endif
OPTIMIZE = -O0
ANALYZE = -fanalyzer
@@ -68,8 +94,11 @@ transfer_atoms.h: short_types.txt mime0.awk mime1.awk mime2.awk mime3.awk \
awk -f mime3.awk short_types.txt >> $@
awk -f mime4.awk short_types.txt >> $@
-$(OBJS): transfer_atoms.h primary-selection-unstable-v1.h \
- linux-dmabuf-unstable-v1.h xdg-shell.h
+$(OBJS): $(GENHEADERS)
+
+/* depend somehow does not depend on SRCS, even though some of OBJS
+ are generated. */
+depend:: $(GENHEADERS) $(SRCS)
linux-dmabuf-unstable-v1.h: linux-dmabuf-unstable-v1.xml
$(WAYLAND_SCANNER) server-header $< $@
diff --git a/README b/README
index a70619a..982b78a 100644
--- a/README
+++ b/README
@@ -31,6 +31,27 @@ extensions:
In addition, it requires Xlib to be built with the XCB transport, and
the XCB bindings for MIT-SHM and DRI3 to be available.
+Sometimes, it might be desirable to build with EGL, and use OpenGL ES
+2.0 for i.e. YUV video format support. To do so, uncomment the block
+of code for EGL support in libraries.def before running `xmkmf'. This
+will additionally require the EGL and GLESv2 development files, and
+for the following EGL and GLES extensions to be present at runtime:
+
+ EGL_EXT_platform_base
+ EGL_EXT_device_query
+ EGL_KHR_image_base
+ EGL_EXT_image_dma_buf_import_modifiers
+ EGL_EXT_image_dma_buf_import
+ EGL_EXT_buffer_age
+
+ GL_OES_EGL_image
+ GL_OES_EGL_image_external
+ GL_EXT_read_format_bgra
+ GL_EXT_unpack_subimage
+
+After building with EGL support, the renderer must be enabled by
+setting the environment variable "RENDERER" to "egl".
+
The following Wayland protocols are implemented to a more-or-less
complete degree:
@@ -58,6 +79,9 @@ This directory is organized as follows:
libraries.def - files for libraries that don't provide Imakefiles
*.xml - Wayland protocol definition source
*.c, *.h - C source code
+ *.awk - scripts used to generate headers
+ *.txt - text data used to generate some headers, i.e.
+ those containing MIME types or shaders
Building the source code is simple, provided that you have the
necessary libwayland-server library, wayland-scanner, pixman, XCB, and
diff --git a/buffer.c b/buffer.c
index 2392ea7..fd4a4f8 100644
--- a/buffer.c
+++ b/buffer.c
@@ -30,7 +30,6 @@ struct _DestroyListener
void *data;
};
-
void
XLRetainBuffer (ExtBuffer *buffer)
{
@@ -43,16 +42,10 @@ XLDereferenceBuffer (ExtBuffer *buffer)
buffer->funcs.dereference (buffer);
}
-Picture
-XLPictureFromBuffer (ExtBuffer *buffer)
+RenderBuffer
+XLRenderBufferFromBuffer (ExtBuffer *buffer)
{
- return buffer->funcs.get_picture (buffer);
-}
-
-Pixmap
-XLPixmapFromBuffer (ExtBuffer *buffer)
-{
- return buffer->funcs.get_pixmap (buffer);
+ return buffer->funcs.get_buffer (buffer);
}
unsigned int
diff --git a/compositor.h b/compositor.h
index 49f40fc..aa4ea82 100644
--- a/compositor.h
+++ b/compositor.h
@@ -94,6 +94,271 @@ typedef struct _PDataSource PDataSource;
extern Compositor compositor;
+/* Defined in renderer.c. */
+
+typedef struct _RenderFuncs RenderFuncs;
+typedef struct _BufferFuncs BufferFuncs;
+
+typedef union _RenderTarget RenderTarget;
+typedef union _RenderBuffer RenderBuffer;
+
+typedef struct _DmaBufAttributes DmaBufAttributes;
+typedef struct _SharedMemoryAttributes SharedMemoryAttributes;
+
+typedef struct _DrmFormat DrmFormat;
+typedef struct _ShmFormat ShmFormat;
+
+typedef enum _Operation Operation;
+
+typedef void (*DmaBufSuccessFunc) (RenderBuffer, void *);
+typedef void (*DmaBufFailureFunc) (void *);
+
+enum _Operation
+ {
+ OperationOver,
+ OperationSource,
+ };
+
+struct _SharedMemoryAttributes
+{
+ /* The format of the buffer. */
+ uint32_t format;
+
+ /* The offset, width, height, and stride of the buffer. */
+ int32_t offset, width, height, stride;
+
+ /* The pool file descriptor. */
+ int fd;
+
+ /* Pointer to a pointer to the pool data. */
+ void **data;
+
+ /* Size of the pool. */
+ size_t pool_size;
+};
+
+struct _DmaBufAttributes
+{
+ /* The file descriptors. They should be closed by the time the
+ callback returns. */
+ int fds[4];
+
+ /* The modifier. */
+ uint64_t modifier;
+
+ /* Strides. */
+ unsigned int strides[4];
+
+ /* Offsets. */
+ unsigned int offsets[4];
+
+ /* The number of planes set. */
+ int n_planes;
+
+ /* The width and height of the buffer. */
+ int width, height;
+
+ /* Flags. */
+ int flags;
+
+ /* The DRM format of the buffer. */
+ uint32_t drm_format;
+};
+
+union _RenderTarget
+{
+ /* The XID of the target resource, if that is what it is. */
+ XID xid;
+
+ /* The pointer to the target, if that is what it is. */
+ void *pointer;
+};
+
+union _RenderBuffer
+{
+ /* The XID of the buffer resource, if that is what it is. */
+ XID xid;
+
+ /* The pointer to the buffer, if that is what it is. */
+ void *pointer;
+};
+
+enum
+ {
+ /* The render target always preserves previously drawn contents;
+ IOW, target_age always returns 0. */
+ NeverAges = 1,
+ };
+
+struct _RenderFuncs
+{
+ /* Initialize the visual and depth. */
+ Bool (*init_render_funcs) (void);
+
+ /* Create a rendering target for the given window. */
+ RenderTarget (*target_from_window) (Window);
+
+ /* Create a rendering target for the given pixmap. */
+ RenderTarget (*target_from_pixmap) (Pixmap);
+
+ /* Set the target width and height. This can be NULL. */
+ void (*note_target_size) (RenderTarget, int, int);
+
+ /* Get an XRender Picture from the given rendering target. The
+ picture should only be used to create a cursor, and must be
+ destroyed by calling FreePictureFromTarget immediately
+ afterwards. */
+ Picture (*picture_from_target) (RenderTarget);
+
+ /* Free a picture created that way. */
+ void (*free_picture_from_target) (Picture);
+
+ /* Destroy the given rendering target. */
+ void (*destroy_render_target) (RenderTarget);
+
+ /* Begin rendering. This can be NULL, but if not, must be called
+ before any drawing operations. */
+ void (*start_render) (RenderTarget);
+
+ /* Fill the target with transparency in the given rectangles. */
+ void (*fill_boxes_with_transparency) (RenderTarget, pixman_box32_t *, int,
+ int, int);
+
+ /* Clear the given rectangle. */
+ void (*clear_rectangle) (RenderTarget, int, int, int, int);
+
+ /* Apply a projective transform to the given buffer. The first
+ argument is a scale factor. */
+ void (*apply_transform) (RenderBuffer, double);
+
+ /* Composite width, height, from the given buffer onto the given
+ target, at x, y. The arguments are: buffer, target, operation,
+ source_x, source_y, x, y, width, height. */
+ void (*composite) (RenderBuffer, RenderTarget, Operation, int, int,
+ int, int, int, int);
+
+ /* Reset the transform for the given buffer. */
+ void (*reset_transform) (RenderBuffer);
+
+ /* Finish rendering, and swap changes to display. May be NULL. */
+ void (*finish_render) (RenderTarget);
+
+ /* Return the age of the target. Value is a number not less than
+ -1, describing the "age" of the contents of the target.
+
+ -1 means the buffer contains no valid contents, and must be
+ redrawn from scratch. 0 means the buffer contains the contents
+ at the time of the last call to `finish_render', 1 means the
+ buffer contains the contents at the time of the second last
+ call to `finish_render', and so on.
+
+ Note that when a render target is first created, the renderer
+ may chose to return 0 instead of 1. */
+ int (*target_age) (RenderTarget);
+
+ /* Some flags. NeverAges means targets always preserve contents
+ that were previously drawn. */
+ int flags;
+};
+
+struct _DrmFormat
+{
+ /* The supported DRM format. */
+ uint32_t drm_format;
+
+ /* The supported modifier. */
+ uint64_t drm_modifier;
+
+ /* Backend specific flags associated with this DRM format. */
+ int flags;
+};
+
+struct _ShmFormat
+{
+ /* The Wayland type code of the format. */
+ uint32_t format;
+};
+
+struct _BufferFuncs
+{
+ /* Get DRM formats and modifiers supported by this renderer. Return
+ 0 formats if nothing is supported. */
+ DrmFormat *(*get_drm_formats) (int *);
+
+ /* Get the DRM device node. */
+ dev_t (*get_render_device) (Bool *);
+
+ /* Get SHM formats supported by this renderer. */
+ ShmFormat *(*get_shm_formats) (int *);
+
+ /* Create a buffer from the given dma-buf attributes. */
+ RenderBuffer (*buffer_from_dma_buf) (DmaBufAttributes *, Bool *);
+
+ /* Create a buffer from the given dma-buf attributes
+ asynchronously. */
+ void (*buffer_from_dma_buf_async) (DmaBufAttributes *, DmaBufSuccessFunc,
+ DmaBufFailureFunc, void *);
+
+ /* Create a buffer from the given shared memory attributes. */
+ RenderBuffer (*buffer_from_shm) (SharedMemoryAttributes *, Bool *);
+
+ /* Validate the shared memory attributes passed as args. Return
+ false if they are not valid. */
+ Bool (*validate_shm_params) (uint32_t, uint32_t, uint32_t, int32_t,
+ int32_t, size_t);
+
+ /* Free a buffer created from shared memory. */
+ void (*free_shm_buffer) (RenderBuffer);
+
+ /* Free a dma-buf buffer. */
+ void (*free_dmabuf_buffer) (RenderBuffer);
+
+ /* Notice that the given buffer has been damaged. May be NULL. If
+ the given NULL damage, assume that the entire buffer has been
+ damaged. Must be called at least once before any rendering can
+ be performed on the buffer. */
+ void (*update_buffer_for_damage) (RenderBuffer, pixman_region32_t *);
+
+ /* Called during renderer initialization. */
+ void (*init_buffer_funcs) (void);
+};
+
+extern int renderer_flags;
+
+extern void RegisterStaticRenderer (const char *, RenderFuncs *,
+ BufferFuncs *);
+extern void InitRenderers (void);
+
+extern RenderTarget RenderTargetFromWindow (Window);
+extern RenderTarget RenderTargetFromPixmap (Pixmap);
+extern void RenderNoteTargetSize (RenderTarget, int, int);
+extern Picture RenderPictureFromTarget (RenderTarget);
+extern void RenderFreePictureFromTarget (Picture);
+extern void RenderDestroyRenderTarget (RenderTarget);
+extern void RenderStartRender (RenderTarget);
+extern void RenderFillBoxesWithTransparency (RenderTarget, pixman_box32_t *,
+ int, int, int);
+extern void RenderClearRectangle (RenderTarget, int, int, int, int);
+extern void RenderApplyTransform (RenderBuffer, double);
+extern void RenderComposite (RenderBuffer, RenderTarget, Operation, int,
+ int, int, int, int, int);
+extern void RenderResetTransform (RenderBuffer);
+extern void RenderFinishRender (RenderTarget);
+extern int RenderTargetAge (RenderTarget);
+
+extern DrmFormat *RenderGetDrmFormats (int *);
+extern dev_t RenderGetRenderDevice (Bool *);
+extern ShmFormat *RenderGetShmFormats (int *);
+extern RenderBuffer RenderBufferFromDmaBuf (DmaBufAttributes *, Bool *);
+extern void RenderBufferFromDmaBufAsync (DmaBufAttributes *, DmaBufSuccessFunc,
+ DmaBufFailureFunc, void *);
+extern RenderBuffer RenderBufferFromShm (SharedMemoryAttributes *, Bool *);
+extern Bool RenderValidateShmParams (uint32_t, uint32_t, uint32_t, int32_t,
+ int32_t, size_t);
+extern void RenderFreeShmBuffer (RenderBuffer);
+extern void RenderFreeDmabufBuffer (RenderBuffer);
+extern void RenderUpdateBufferForDamage (RenderBuffer, pixman_region32_t *);
+
/* Defined in run.c. */
typedef struct _PollFd WriteFd;
@@ -189,6 +454,9 @@ extern Time XLGetServerTimeRoundtrip (void);
extern RootWindowSelection *XLSelectInputFromRootWindow (unsigned long);
extern void XLDeselectInputFromRootWindow (RootWindowSelection *);
+extern void XLRecordBusfault (void *, size_t);
+extern void XLRemoveBusfault (void *);
+
/* Defined in compositor.c. */
extern void XLInitCompositor (void);
@@ -209,8 +477,7 @@ struct _ExtBufferFuncs
{
void (*retain) (ExtBuffer *);
void (*dereference) (ExtBuffer *);
- Picture (*get_picture) (ExtBuffer *);
- Pixmap (*get_pixmap) (ExtBuffer *);
+ RenderBuffer (*get_buffer) (ExtBuffer *);
unsigned int (*width) (ExtBuffer *);
unsigned int (*height) (ExtBuffer *);
void (*release) (ExtBuffer *);
@@ -228,8 +495,7 @@ struct _ExtBuffer
extern void XLRetainBuffer (ExtBuffer *);
extern void XLDereferenceBuffer (ExtBuffer *);
-extern Picture XLPictureFromBuffer (ExtBuffer *);
-extern Pixmap XLPixmapFromBuffer (ExtBuffer *);
+extern RenderBuffer XLRenderBufferFromBuffer (ExtBuffer *);
extern unsigned int XLBufferWidth (ExtBuffer *);
extern unsigned int XLBufferHeight (ExtBuffer *);
extern void XLReleaseBuffer (ExtBuffer *);
@@ -255,7 +521,7 @@ extern void SubcompositorInit (void);
extern Subcompositor *MakeSubcompositor (void);
extern View *MakeView (void);
-extern void SubcompositorSetTarget (Subcompositor *, Picture);
+extern void SubcompositorSetTarget (Subcompositor *, RenderTarget *);
extern void SubcompositorInsert (Subcompositor *, View *);
extern void SubcompositorInsertBefore (Subcompositor *, View *, View *);
extern void SubcompositorInsertAfter (Subcompositor *, View *, View *);
@@ -883,8 +1149,6 @@ extern Cursor InitDefaultCursor (void);
/* Defined in dmabuf.c. */
extern void XLInitDmabuf (void);
-extern Bool XLHandleErrorForDmabuf (XErrorEvent *);
-extern Bool XLHandleOneXEventForDmabuf (XEvent *);
/* Defined in select.c. */
@@ -985,6 +1249,20 @@ extern Bool XLPDataSourceHasTarget (PDataSource *, const char *);
extern int XLPDataSourceTargetCount (PDataSource *);
extern void XLPDataSourceGetTargets (PDataSource *, Atom *);
+/* Defined in picture_renderer.c. */
+
+extern Bool HandleErrorForPictureRenderer (XErrorEvent *);
+extern Bool HandleOneXEventForPictureRenderer (XEvent *);
+extern void InitPictureRenderer (void);
+
+#ifdef HaveEglSupport
+
+/* Defined in egl.c. */
+
+extern void InitEgl (void);
+
+#endif
+
/* Utility functions that don't belong in a specific file. */
#define ArrayElements(arr) (sizeof (arr) / sizeof (arr)[0])
@@ -999,6 +1277,12 @@ extern void XLPDataSourceGetTargets (PDataSource *, Atom *);
#define SafeCmp(n1, n2) (((n1) > (n2)) - ((n1) < (n2)))
+#if __GNUC__ >= 7
+#define Fallthrough __attribute__ ((fallthrough))
+#else
+#define Fallthrough ((void) 0)
+#endif
+
/* Taken from intprops.h in gnulib. */
/* True if the real type T is signed. */
diff --git a/dmabuf.c b/dmabuf.c
index dc4d25c..ce5a3d1 100644
--- a/dmabuf.c
+++ b/dmabuf.c
@@ -43,34 +43,10 @@ typedef struct _FormatModifierPair FormatModifierPair;
enum
{
- IsUsed = 1,
+ IsUsed = 1,
+ IsCallbackData = (1 << 2),
};
-struct _DrmFormatInfo
-{
- /* The DRM format code. */
- uint32_t format_code;
-
- /* The X Windows depth. */
- int depth;
-
- /* The X Windows green, red, blue, and alpha masks. */
- int red, green, blue, alpha;
-
- /* The number of bits per pixel. */
- int bits_per_pixel;
-
- /* PictFormat associated with this format, or NULL if none were
- found. */
- XRenderPictFormat *format;
-
- /* List of supported screen modifiers. */
- uint64_t *supported_modifiers;
-
- /* Number of supported screen modifiers. */
- int n_supported_modifiers;
-};
-
struct _TemporarySetEntry
{
/* These fields mean the same as they do in the args to
@@ -92,18 +68,8 @@ struct _BufferParams
/* The struct wl_resource associated with this object. */
struct wl_resource *resource;
- /* Possible link to a global list of buffer params pending
- release. */
- BufferParams *next, *last;
-
- /* The XID of the buffer that will be created. */
- Pixmap pixmap;
-
/* The width and height of the buffer that will be created. */
int width, height;
-
- /* The DRM format. */
- uint32_t drm_format;
};
struct _Buffer
@@ -111,11 +77,8 @@ struct _Buffer
/* The ExtBuffer associated with this buffer. */
ExtBuffer buffer;
- /* The pixmap associated with this buffer. */
- Pixmap pixmap;
-
- /* The picture associated with this buffer. */
- Picture picture;
+ /* The RenderBuffer associated with this buffer. */
+ RenderBuffer render_buffer;
/* The width and height of this buffer. */
unsigned int width, height;
@@ -142,68 +105,6 @@ struct _FormatModifierPair
/* The wl_global associated with linux-dmabuf-unstable-v1. */
static struct wl_global *global_dmabuf;
-/* List of BufferParams pending success. */
-static BufferParams pending_success;
-
-/* The id of the next round trip event. */
-static uint64_t next_roundtrip_id;
-
-/* A window used to receive round trip events. */
-static Window round_trip_window;
-
-/* List of all supported DRM formats. */
-static DrmFormatInfo all_formats[] =
- {
- {
- .format_code = DRM_FORMAT_ARGB8888,
- .depth = 32,
- .red = 0xff0000,
- .green = 0xff00,
- .blue = 0xff,
- .alpha = 0xff000000,
- .bits_per_pixel = 32,
- },
- {
- .format_code = DRM_FORMAT_XRGB8888,
- .depth = 24,
- .red = 0xff0000,
- .green = 0xff00,
- .blue = 0xff,
- .alpha = 0,
- .bits_per_pixel = 32,
- },
- {
- .format_code = DRM_FORMAT_XBGR8888,
- .depth = 24,
- .blue = 0xff0000,
- .green = 0xff00,
- .red = 0xff,
- .alpha = 0,
- .bits_per_pixel = 32,
- },
- {
- .format_code = DRM_FORMAT_ABGR8888,
- .depth = 32,
- .blue = 0xff0000,
- .green = 0xff00,
- .red = 0xff,
- .alpha = 0xff000000,
- .bits_per_pixel = 32,
- },
- {
- .format_code = DRM_FORMAT_BGRA8888,
- .depth = 32,
- .blue = 0xff000000,
- .green = 0xff0000,
- .red = 0xff00,
- .alpha = 0xff,
- .bits_per_pixel = 32,
- },
- };
-
-/* The opcode of the DRI3 extension. */
-static int dri3_opcode;
-
/* File descriptor for the format table. */
static int format_table_fd;
@@ -214,6 +115,12 @@ static ssize_t format_table_size;
output-specific. */
static dev_t drm_device_node;
+/* DRM formats supported by the renderer. */
+static DrmFormat *supported_formats;
+
+/* Number of formats. */
+static int n_drm_formats;
+
static void
CloseFdsEarly (BufferParams *params)
{
@@ -234,14 +141,9 @@ ReleaseBufferParams (BufferParams *params)
if (!(params->flags & IsUsed))
CloseFdsEarly (params);
- /* If params is linked into the list of buffers pending success,
- remove it. */
-
- if (params->last)
- {
- params->next->last = params->last;
- params->last->next = params->next;
- }
+ /* params should not be destroyed if it is being used as callback
+ data. */
+ XLAssert (!(params->flags & IsCallbackData));
XLFree (params);
}
@@ -256,7 +158,9 @@ HandleParamsResourceDestroy (struct wl_resource *resource)
/* First, clear params->resource. */
params->resource = NULL;
- if (params->next)
+ if (params->flags & IsCallbackData)
+ /* If params is callback data, simply clear params->resource, and
+ wait for a callback to be called. */
return;
/* Next, destroy the params now, unless we are waiting for a buffer
@@ -350,119 +254,14 @@ Add (struct wl_client *client, struct wl_resource *resource, int32_t fd,
params->entries[plane_idx].modifier_lo = modifier_lo;
}
-static void
-ForceRoundTrip (void)
-{
- uint64_t id;
- XEvent event;
-
- /* Send an event with a monotonically increasing identifier to
- ourselves.
-
- Once the last event is received, create the actual buffers for
- each buffer resource for which error handlers have not run. */
-
- id = next_roundtrip_id++;
-
- memset (&event, 0, sizeof event);
-
- event.xclient.type = ClientMessage;
- event.xclient.window = round_trip_window;
- event.xclient.message_type = _XL_DMA_BUF_CREATED;
- event.xclient.format = 32;
-
- event.xclient.data.l[0] = id >> 31 >> 1;
- event.xclient.data.l[1] = id & 0xffffffff;
-
- XSendEvent (compositor.display, round_trip_window,
- False, NoEventMask, &event);
-}
-
-static int
-DepthForFormat (uint32_t drm_format, int *bits_per_pixel)
-{
- int i;
-
- for (i = 0; i < ArrayElements (all_formats); ++i)
- {
- if (all_formats[i].format_code == drm_format
- && all_formats[i].format)
- {
- *bits_per_pixel = all_formats[i].bits_per_pixel;
-
- return all_formats[i].depth;
- }
- }
-
- return -1;
-}
-
-Bool
-XLHandleErrorForDmabuf (XErrorEvent *error)
-{
- BufferParams *params, *next;
-
- if (error->request_code == dri3_opcode
- && error->minor_code == xDRI3BuffersFromPixmap)
- {
- /* Something chouldn't be created. Find what failed and unlink
- it. */
-
- next = pending_success.next;
-
- while (next != &pending_success)
- {
- params = next;
- next = next->next;
-
- if (params->pixmap == error->resourceid)
- {
- /* Unlink params. */
- params->next->last = params->last;
- params->last->next = params->next;
-
- params->next = NULL;
- params->last = NULL;
-
- /* Tell the client that buffer creation failed. It will
- then delete the object. */
- zwp_linux_buffer_params_v1_send_failed (params->resource);
-
- break;
- }
- }
-
- return True;
- }
-
- return False;
-}
-
-static XRenderPictFormat *
-PictFormatForFormat (uint32_t drm_format)
-{
- int i;
-
- for (i = 0; i < ArrayElements (all_formats); ++i)
- {
- if (all_formats[i].format_code == drm_format
- && all_formats[i].format)
- return all_formats[i].format;
- }
-
- /* This shouldn't happen, since the format was already verified in
- Create. */
- abort ();
-}
-
static void
DestroyBacking (Buffer *buffer)
{
if (--buffer->refcount)
return;
- XRenderFreePicture (compositor.display, buffer->picture);
- XFreePixmap (compositor.display, buffer->pixmap);
+ /* Free the renderer-specific dmabuf buffer. */
+ RenderFreeDmabufBuffer (buffer->render_buffer);
ExtBufferDestroy (&buffer->buffer);
XLFree (buffer);
@@ -508,24 +307,6 @@ DereferenceBufferFunc (ExtBuffer *buffer)
DestroyBacking (dmabuf_buffer);
}
-static Picture
-GetPictureFunc (ExtBuffer *buffer)
-{
- Buffer *dmabuf_buffer;
-
- dmabuf_buffer = (Buffer *) buffer;
- return dmabuf_buffer->picture;
-}
-
-static Pixmap
-GetPixmapFunc (ExtBuffer *buffer)
-{
- Buffer *dmabuf_buffer;
-
- dmabuf_buffer = (Buffer *) buffer;
- return dmabuf_buffer->pixmap;
-}
-
static unsigned int
WidthFunc (ExtBuffer *buffer)
{
@@ -551,20 +332,28 @@ ReleaseBufferFunc (ExtBuffer *buffer)
wl_buffer_send_release (((Buffer *) buffer)->resource);
}
+static RenderBuffer
+GetBufferFunc (ExtBuffer *buffer)
+{
+ Buffer *dmabuf_buffer;
+
+ dmabuf_buffer = (Buffer *) buffer;
+ return dmabuf_buffer->render_buffer;
+}
+
static Buffer *
-CreateBufferFor (BufferParams *params, uint32_t id)
+CreateBufferFor (BufferParams *params, RenderBuffer render_buffer,
+ uint32_t id)
{
Buffer *buffer;
- Picture picture;
struct wl_client *client;
- XRenderPictureAttributes picture_attrs;
buffer = XLSafeMalloc (sizeof *buffer);
client = wl_resource_get_client (params->resource);
if (!buffer)
{
- XFreePixmap (compositor.display, params->pixmap);
+ RenderFreeDmabufBuffer (render_buffer);
zwp_linux_buffer_params_v1_send_failed (params->resource);
return NULL;
@@ -576,18 +365,14 @@ CreateBufferFor (BufferParams *params, uint32_t id)
if (!buffer->resource)
{
- XFreePixmap (compositor.display, params->pixmap);
+ RenderFreeDmabufBuffer (render_buffer);
XLFree (buffer);
zwp_linux_buffer_params_v1_send_failed (params->resource);
return NULL;
}
- picture = XRenderCreatePicture (compositor.display, params->pixmap,
- PictFormatForFormat (params->drm_format),
- 0, &picture_attrs);
- buffer->pixmap = params->pixmap;
- buffer->picture = picture;
+ buffer->render_buffer = render_buffer;
buffer->width = params->width;
buffer->height = params->height;
buffer->destroy_listeners = NULL;
@@ -595,8 +380,7 @@ CreateBufferFor (BufferParams *params, uint32_t id)
/* Initialize function pointers. */
buffer->buffer.funcs.retain = RetainBufferFunc;
buffer->buffer.funcs.dereference = DereferenceBufferFunc;
- buffer->buffer.funcs.get_picture = GetPictureFunc;
- buffer->buffer.funcs.get_pixmap = GetPixmapFunc;
+ buffer->buffer.funcs.get_buffer = GetBufferFunc;
buffer->buffer.funcs.width = WidthFunc;
buffer->buffer.funcs.height = HeightFunc;
buffer->buffer.funcs.release = ReleaseBufferFunc;
@@ -610,274 +394,385 @@ CreateBufferFor (BufferParams *params, uint32_t id)
return buffer;
}
-static void
-FinishBufferCreation (void)
+#define ModifierHigh(mod) ((uint64_t) (mod) >> 31 >> 1)
+#define ModifierLow(mod) ((uint64_t) (mod) & 0xffffffff)
+
+static Bool
+IsFormatSupported (uint32_t format, uint32_t mod_hi, uint32_t mod_low)
{
- BufferParams *params, *next;
- Buffer *buffer;
+ int i;
- next = pending_success.next;
-
- while (next != &pending_success)
+ for (i = 0; i < n_drm_formats; ++i)
{
- params = next;
- next = next->next;
-
- if (params->resource)
- {
- buffer = CreateBufferFor (params, 0);
-
- /* Send the buffer to the client, unless creation
- failed. */
- if (buffer)
- zwp_linux_buffer_params_v1_send_created (params->resource,
- buffer->resource);
-
- /* Unlink params, since it's no longer pending creation. */
- params->next->last = params->last;
- params->last->next = params->next;
-
- params->next = NULL;
- params->last = NULL;
- }
- else
- {
- /* The resource is no longer present, so just delete it. */
- XFreePixmap (compositor.display, params->pixmap);
- ReleaseBufferParams (params);
- }
- }
-}
-
-Bool
-XLHandleOneXEventForDmabuf (XEvent *event)
-{
- uint64_t id, low, high;
-
- if (event->type == ClientMessage
- && event->xclient.message_type == _XL_DMA_BUF_CREATED)
- {
- /* Values are masked against 0xffffffff, as Xlib sign-extends
- those longs. */
- high = event->xclient.data.l[0] & 0xffffffff;
- low = event->xclient.data.l[1] & 0xffffffff;
- id = low | (high << 32);
-
- /* Ignore the message if the id is too old. */
- if (id < next_roundtrip_id)
- {
- /* Otherwise, it means buffer creation was successful.
- Complete all pending buffer creation. */
-
- FinishBufferCreation ();
- }
-
- return True;
+ if (format == supported_formats[i].drm_format
+ /* Also check that the modifiers have been announced as
+ supported. */
+ && ModifierHigh (supported_formats[i].drm_modifier) == mod_hi
+ && ModifierLow (supported_formats[i].drm_modifier) == mod_low)
+ /* A match was found, so this format is supported. */
+ return True;
}
+ /* No match was found, so complain that the format is invalid. This
+ does not catch non-obvious errors, such as unsupported flags,
+ which may cause buffer creation to fail after on. */
return False;
}
-#define CreateHeader \
- BufferParams *params; \
- int num_buffers, i, depth, bpp; \
- uint32_t mod_high, mod_low, all_flags; \
- int32_t *allfds; \
- \
- params = wl_resource_get_user_data (resource); \
- \
- if (params->flags & IsUsed) \
- { \
- wl_resource_post_error (resource, \
- ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_ALREADY_USED, \
- "the given params resource has already been used"); \
- return; \
- } \
- \
- /* Find the depth and bpp corresponding to the format. */ \
- depth = DepthForFormat (format, &bpp); \
- \
- /* Now mark the params resource as inert. */ \
- params->flags |= IsUsed; \
- \
- /* Retrieve how many buffers are attached to the temporary set, and \
- any set modifiers. */ \
- num_buffers = ExistingModifier (params, &mod_high, &mod_low); \
- \
- if (!num_buffers) \
- { \
- wl_resource_post_error (resource, \
- ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_INCOMPLETE, \
- "no fds were attached to this resource's temporary set"); \
- goto inert_error; \
- } \
- \
- if (params->entries[0].fd == -1) \
- { \
- wl_resource_post_error (resource, \
- ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_INCOMPLETE, \
- "no fd attached for plane 0 in the temporary set"); \
- goto inert_error; \
- } \
- \
- if ((params->entries[3].fd >= 0 || params->entries[2].fd >= 0) \
- && (params->entries[2].fd == -1 || params->entries[1].fd == -1)) \
- { \
- wl_resource_post_error (resource, \
- ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_INCOMPLETE, \
- "gap in planes attached to temporary set"); \
- goto inert_error; \
- } \
- \
- if (width < 0 || height < 0 || width > 65535 || height > 65535) \
- { \
- wl_resource_post_error (resource, \
- ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_OUT_OF_BOUNDS, \
- "size out of bounds for X server"); \
- goto inert_error; \
- } \
- \
- if (depth == -1) \
- { \
- if (wl_resource_get_version (resource) >= 4) \
- wl_resource_post_error (resource, \
- ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_INVALID_FORMAT, \
- "invalid format specified for version 4 resource"); \
- else \
- zwp_linux_buffer_params_v1_send_failed (resource); \
- \
- goto inert_error; \
- } \
- \
- all_flags = (ZWP_LINUX_BUFFER_PARAMS_V1_FLAGS_Y_INVERT \
- | ZWP_LINUX_BUFFER_PARAMS_V1_FLAGS_INTERLACED \
- | ZWP_LINUX_BUFFER_PARAMS_V1_FLAGS_BOTTOM_FIRST); \
- \
- if (flags & ~all_flags) \
- { \
- wl_resource_post_error (resource, \
- ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_INVALID_FORMAT, \
- "invalid dmabuf flags: %u", flags); \
- goto inert_error; \
- } \
- \
- if (flags) \
- { \
- /* Flags are not supported by wlroots, so I guess we don't have \
- to either. */ \
- zwp_linux_buffer_params_v1_send_failed (resource); \
- goto inert_error; \
- } \
- \
- /* Copy the file descriptors into a buffer. At this point, we know \
- there are no gaps in params->entries. */ \
- allfds = alloca (sizeof *allfds * num_buffers); \
- \
- for (i = 0; i < num_buffers; ++i) \
- allfds[i] = params->entries[i].fd; \
- \
- /* Make the request, and then link the buffer params object onto the \
- list of pending buffers. We don't know if the creation will be \
- rejected by the X server, so we first arrange to catch all errors \
- from DRI3PixmapFromBuffer(s), and send the create event the next \
- time we know that a roundtrip has happened without any errors \
- being raised. */ \
- \
- params->width = width; \
- params->height = height; \
- params->drm_format = format; \
- \
- params->pixmap = xcb_generate_id (compositor.conn)
+static void
+CreateSucceeded (RenderBuffer render_buffer, void *data)
+{
+ BufferParams *params;
+ Buffer *buffer;
+ /* Buffer creation was successful. If the resource was already
+ destroyed, then simply destroy buffer and free params.
+ Otherwise, send the created buffer to the client. */
-#define CreateFooter \
- inert_error: \
- /* We also have to close each added fd here, since XCB hasn't done \
- that for us. */ \
- \
- CloseFdsEarly (params)
+ params = data;
+
+ if (!params->resource)
+ {
+ RenderFreeDmabufBuffer (render_buffer);
+
+ /* Now, release the buffer params. Since the callback has been
+ run, it is no longer callback data. */
+ params->flags &= ~IsCallbackData;
+ ReleaseBufferParams (params);
+
+ return;
+ }
+
+ /* Mark the params object as no longer being callback data. */
+ params->flags &= ~IsCallbackData;
+
+ /* Create the buffer. */
+ buffer = CreateBufferFor (params, render_buffer, 0);
+
+ /* If buffer is NULL, then the failure message will already have
+ been sent. */
+ if (!buffer)
+ return;
+
+ /* Send the buffer to the client. */
+ zwp_linux_buffer_params_v1_send_created (params->resource,
+ buffer->resource);
+}
+
+static void
+CreateFailed (void *data)
+{
+ BufferParams *params;
+
+ /* Creation failed. If no resource is attached, simply free params.
+ Otherwise, send failed and wait for the client to destroy it. */
+ params = data;
+
+ params->flags &= ~IsCallbackData;
+
+ if (!params->resource)
+ ReleaseBufferParams (params);
+ else
+ zwp_linux_buffer_params_v1_send_failed (params->resource);
+}
static void
Create (struct wl_client *client, struct wl_resource *resource, int32_t width,
int32_t height, uint32_t format, uint32_t flags)
{
- CreateHeader;
+ BufferParams *params;
+ DmaBufAttributes attributes;
+ int num_buffers, i;
+ uint32_t mod_high, mod_low;
+ uint32_t all_flags;
- params->next = pending_success.next;
- params->last = &pending_success;
- pending_success.next->last = params;
- pending_success.next = params;
+ params = wl_resource_get_user_data (resource);
- /* Now, create the buffers. XCB will close the file descriptor for
- us once the output buffer is flushed. */
- xcb_dri3_pixmap_from_buffers (compositor.conn, params->pixmap,
- DefaultRootWindow (compositor.display),
- num_buffers, params->width, params->height,
- params->entries[0].offset,
- params->entries[0].stride,
- params->entries[1].offset,
- params->entries[1].stride,
- params->entries[2].offset,
- params->entries[2].stride,
- params->entries[3].offset,
- params->entries[3].stride, depth, bpp,
- (uint64_t) mod_high << 31 | mod_low, allfds);
+ if (params->flags & IsUsed)
+ {
+ wl_resource_post_error (resource,
+ ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_ALREADY_USED,
+ "the given params resource has already been used.");
+ return;
+ }
- /* And force a roundtrip event. */
- ForceRoundTrip ();
+ /* Now mark the params resource as inert. */
+ params->flags |= IsUsed;
+ /* And find out how many buffers are attached to the temporary set,
+ along with which modifiers are set. */
+ num_buffers = ExistingModifier (params, &mod_high, &mod_low);
+
+ if (!num_buffers)
+ {
+ wl_resource_post_error (resource,
+ ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_INCOMPLETE,
+ "no fds were attached to this resource's temporary set");
+ goto inert_error;
+ }
+
+ if (params->entries[0].fd == -1)
+ {
+ wl_resource_post_error (resource,
+ ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_INCOMPLETE,
+ "no fd attached for plane 0 in the temporary set");
+ goto inert_error;
+ }
+
+ if ((params->entries[3].fd >= 0 || params->entries[2].fd >= 0)
+ && (params->entries[2].fd == -1 || params->entries[1].fd == -1))
+ {
+ wl_resource_post_error (resource,
+ ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_INCOMPLETE,
+ "gap in planes attached to temporary set");
+ goto inert_error;
+ }
+
+ if (width < 0 || height < 0 || width > 65535 || height > 65535)
+ {
+ wl_resource_post_error (resource,
+ ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_OUT_OF_BOUNDS,
+ "size out of bounds for X server");
+ goto inert_error;
+ }
+
+ /* Check that the client did not define any invalid flags. */
+
+ all_flags = (ZWP_LINUX_BUFFER_PARAMS_V1_FLAGS_Y_INVERT
+ | ZWP_LINUX_BUFFER_PARAMS_V1_FLAGS_INTERLACED
+ | ZWP_LINUX_BUFFER_PARAMS_V1_FLAGS_BOTTOM_FIRST);
+
+ if (flags & ~all_flags)
+ {
+ wl_resource_post_error (resource,
+ ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_INVALID_FORMAT,
+ "invalid dmabuf flags: %u", flags);
+ goto inert_error;
+ }
+
+ /* Now, see if the format and modifier pair specified is supported.
+ If it is not, post an error for version 4 resources, and fail
+ creation for earlier ones. */
+ if (!IsFormatSupported (format, mod_high, mod_low))
+ {
+ if (wl_resource_get_version (resource) >= 4)
+ wl_resource_post_error (resource,
+ ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_INVALID_FORMAT,
+ "invalid format/modifiers specified for version 4"
+ " resource");
+ else
+ zwp_linux_buffer_params_v1_send_failed (resource);
+
+ goto inert_error;
+ }
+
+ /* Clear the buffer attributes structure. It has to be initialized
+ even if not completely used. */
+
+ memset (&attributes, 0, sizeof attributes);
+
+ /* Copy the file descriptors and other plane-specific information
+ into the buffer attributes structure. They are not closed here;
+ the renderer should do that itself upon both success and
+ failure. */
+
+ for (i = 0; i < num_buffers; ++i)
+ {
+ attributes.fds[i] = params->entries[i].fd;
+ attributes.strides[i] = params->entries[i].stride;
+ attributes.offsets[i] = params->entries[i].offset;
+ }
+
+ /* Provide the specified modifier in the buffer attributes
+ structure. */
+ attributes.modifier = ((uint64_t) mod_high << 32) | mod_low;
+
+ /* Set the number of planes specified. */
+ attributes.n_planes = num_buffers;
+
+ /* And the dimensions of the buffer. */
+ attributes.width = width;
+ attributes.height = height;
+
+ /* The format. */
+ attributes.drm_format = format;
+
+ /* The flags. */
+ attributes.flags = flags;
+
+ /* Set params->width and params->height. They are used by
+ CreateBufferFor. */
+ params->width = width;
+ params->height = height;
+
+ /* Mark params as callback and post asynchronous creation. This is
+ so that the parameters will not be destroyed until one of the
+ callback functions are called. */
+ params->flags |= IsCallbackData;
+
+ /* Post asynchronous creation and return. */
+ RenderBufferFromDmaBufAsync (&attributes, CreateSucceeded,
+ CreateFailed, params);
return;
- CreateFooter;
+ inert_error:
+ /* We must also close every file descriptor attached here, as XCB
+ has not done that for us yet. */
+ CloseFdsEarly (params);
}
static void
CreateImmed (struct wl_client *client, struct wl_resource *resource, uint32_t id,
int32_t width, int32_t height, uint32_t format, uint32_t flags)
{
- xcb_void_cookie_t check_cookie;
- xcb_generic_error_t *error;
+ BufferParams *params;
+ DmaBufAttributes attributes;
+ Bool error;
+ int num_buffers, i;
+ uint32_t mod_high, mod_low;
+ uint32_t all_flags;
+ RenderBuffer buffer;
- CreateHeader;
+ params = wl_resource_get_user_data (resource);
- /* Instead of creating buffers asynchronously, check for failures
- immediately. */
-
- check_cookie
- = xcb_dri3_pixmap_from_buffers_checked (compositor.conn, params->pixmap,
- DefaultRootWindow (compositor.display),
- num_buffers, params->width, params->height,
- params->entries[0].offset,
- params->entries[0].stride,
- params->entries[1].offset,
- params->entries[1].stride,
- params->entries[2].offset,
- params->entries[2].stride,
- params->entries[3].offset,
- params->entries[3].stride, depth, bpp,
- (uint64_t) mod_high << 31 | mod_low, allfds);
- error = xcb_request_check (compositor.conn, check_cookie);
-
- /* A platform-specific error occured creating this buffer.
- It is easiest to implement this by sending INVALID_WL_BUFFER. */
- if (error)
+ if (params->flags & IsUsed)
{
wl_resource_post_error (resource,
- ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_INVALID_WL_BUFFER,
- "platform specific error: response code %u, error code %u,"
- " serial %u, xid %u, minor %u, major %u", error->response_type,
- error->error_code, error->sequence, error->resource_id,
- error->minor_code, error->major_code);
- free (error);
+ ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_ALREADY_USED,
+ "the given params resource has already been used.");
+ return;
+ }
+
+ /* Now mark the params resource as inert. */
+ params->flags |= IsUsed;
+
+ /* And find out how many buffers are attached to the temporary set,
+ along with which modifiers are set. */
+ num_buffers = ExistingModifier (params, &mod_high, &mod_low);
+
+ if (!num_buffers)
+ {
+ wl_resource_post_error (resource,
+ ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_INCOMPLETE,
+ "no fds were attached to this resource's temporary set");
+ goto inert_error;
+ }
+
+ if (params->entries[0].fd == -1)
+ {
+ wl_resource_post_error (resource,
+ ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_INCOMPLETE,
+ "no fd attached for plane 0 in the temporary set");
+ goto inert_error;
+ }
+
+ if ((params->entries[3].fd >= 0 || params->entries[2].fd >= 0)
+ && (params->entries[2].fd == -1 || params->entries[1].fd == -1))
+ {
+ wl_resource_post_error (resource,
+ ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_INCOMPLETE,
+ "gap in planes attached to temporary set");
+ goto inert_error;
+ }
+
+ if (width < 0 || height < 0 || width > 65535 || height > 65535)
+ {
+ wl_resource_post_error (resource,
+ ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_OUT_OF_BOUNDS,
+ "size out of bounds for X server");
+ goto inert_error;
+ }
+
+ /* Check that the client did not define any invalid flags. */
+
+ all_flags = (ZWP_LINUX_BUFFER_PARAMS_V1_FLAGS_Y_INVERT
+ | ZWP_LINUX_BUFFER_PARAMS_V1_FLAGS_INTERLACED
+ | ZWP_LINUX_BUFFER_PARAMS_V1_FLAGS_BOTTOM_FIRST);
+
+ if (flags & ~all_flags)
+ {
+ wl_resource_post_error (resource,
+ ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_INVALID_FORMAT,
+ "invalid dmabuf flags: %u", flags);
+ goto inert_error;
+ }
+
+ /* Now, see if the format and modifier pair specified is supported.
+ If it is not, post an error for version 4 resources, and fail
+ creation for earlier ones. */
+ if (!IsFormatSupported (format, mod_high, mod_low))
+ {
+ if (wl_resource_get_version (resource) >= 4)
+ wl_resource_post_error (resource,
+ ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_INVALID_FORMAT,
+ "invalid format/modifiers specified for version 4"
+ " resource");
+ else
+ zwp_linux_buffer_params_v1_send_failed (resource);
+
+ goto inert_error;
+ }
+
+ /* Clear the buffer attributes structure. It has to be initialized
+ even if not completely used. */
+
+ memset (&attributes, 0, sizeof attributes);
+
+ /* Copy the file descriptors and other plane-specific information
+ into the buffer attributes structure. They are not closed here;
+ the renderer should do that itself upon both success and
+ failure. */
+
+ for (i = 0; i < num_buffers; ++i)
+ {
+ attributes.fds[i] = params->entries[i].fd;
+ attributes.strides[i] = params->entries[i].stride;
+ attributes.offsets[i] = params->entries[i].offset;
+ }
+
+ /* Provide the specified modifier in the buffer attributes
+ structure. */
+ attributes.modifier = ((uint64_t) mod_high << 32) | mod_low;
+
+ /* Set the number of planes specified. */
+ attributes.n_planes = num_buffers;
+
+ /* And the dimensions of the buffer. */
+ attributes.width = width;
+ attributes.height = height;
+
+ /* The format. */
+ attributes.drm_format = format;
+
+ /* The flags. */
+ attributes.flags = flags;
+
+ /* Set params->width and params->height. They are used by
+ CreateBufferFor. */
+ params->width = width;
+ params->height = height;
+
+ /* Now, try to create the buffer. Send failed should it actually
+ fail. */
+ error = False;
+ buffer = RenderBufferFromDmaBuf (&attributes, &error);
+
+ if (error)
+ {
+ /* The fds should have been closed by the renderer. */
+ zwp_linux_buffer_params_v1_send_failed (resource);
}
else
- /* Otherwise, the fds were successfully imported into the X
- server. */
- CreateBufferFor (params, id);
+ /* Otherwise, buffer creation was successful. Create the buffer
+ for the id. */
+ CreateBufferFor (params, buffer, id);
return;
- CreateFooter;
+ inert_error:
+ /* We must also close every file descriptor attached here, as XCB
+ has not done that for us yet. */
+ CloseFdsEarly (params);
}
static struct zwp_linux_buffer_params_v1_interface zwp_linux_buffer_params_v1_impl =
@@ -1035,157 +930,33 @@ static struct zwp_linux_dmabuf_v1_interface zwp_linux_dmabuf_v1_impl =
.get_surface_feedback = GetSurfaceFeedback,
};
-static DrmFormatInfo *
-FindFormatMatching (XRenderPictFormat *format)
-{
- unsigned long alpha, red, green, blue;
- int i;
-
- if (format->type != PictTypeDirect)
- /* No DRM formats are colormapped. */
- return NULL;
-
- alpha = format->direct.alphaMask << format->direct.alpha;
- red = format->direct.redMask << format->direct.red;
- green = format->direct.greenMask << format->direct.green;
- blue = format->direct.blueMask << format->direct.blue;
-
- for (i = 0; i < ArrayElements (all_formats); ++i)
- {
- if (all_formats[i].depth == format->depth
- && all_formats[i].red == red
- && all_formats[i].green == green
- && all_formats[i].blue == blue
- && all_formats[i].alpha == alpha)
- return &all_formats[i];
- }
-
- return NULL;
-}
-
-static void
-FindSupportedModifiers (void)
-{
- Window root_window;
- xcb_dri3_get_supported_modifiers_cookie_t *cookies;
- xcb_dri3_get_supported_modifiers_reply_t *reply;
- int i, length;
- uint64_t *mods;
-
- cookies = alloca (sizeof *cookies * ArrayElements (all_formats));
- root_window = DefaultRootWindow (compositor.display);
-
- for (i = 0; i < ArrayElements (all_formats); ++i)
- {
- if (all_formats[i].format)
- cookies[i]
- = xcb_dri3_get_supported_modifiers (compositor.conn,
- root_window, all_formats[i].depth,
- all_formats[i].bits_per_pixel);
- }
-
- for (i = 0; i < ArrayElements (all_formats); ++i)
- {
- if (!all_formats[i].format)
- continue;
-
- reply = xcb_dri3_get_supported_modifiers_reply (compositor.conn,
- cookies[i], NULL);
-
- if (!reply)
- continue;
-
- mods
- = xcb_dri3_get_supported_modifiers_screen_modifiers (reply);
- length
- = xcb_dri3_get_supported_modifiers_screen_modifiers_length (reply);
-
- all_formats[i].supported_modifiers = XLMalloc (sizeof *mods * length);
- all_formats[i].n_supported_modifiers = length;
-
- memcpy (all_formats[i].supported_modifiers, mods,
- sizeof *mods * length);
- free (reply);
- }
-}
-
-static Bool
-FindSupportedFormats (void)
-{
- int count;
- XRenderPictFormat *format;
- DrmFormatInfo *info;
- Bool supported;
-
- count = 0;
- supported = False;
-
- do
- {
- format = XRenderFindFormat (compositor.display, 0,
- NULL, count);
- count++;
-
- if (!format)
- break;
-
- info = FindFormatMatching (format);
-
- if (info && !info->format)
- info->format = format;
-
- if (info)
- supported = True;
- }
- while (format);
-
- return supported;
-}
-
-#define ModifierHigh(mod) ((uint64_t) (mod) >> 31 >> 1)
-#define ModifierLow(mod) ((uint64_t) (mod) & 0xffffffff)
-#define Mod(format, i) ((format)->supported_modifiers[i])
-
-static void
-SendModifiers (struct wl_resource *resource, DrmFormatInfo *format)
-{
- int i;
-
- for (i = 0; i < format->n_supported_modifiers; ++i)
- zwp_linux_dmabuf_v1_send_modifier (resource, format->format_code,
- ModifierHigh (Mod (format, i)),
- ModifierLow (Mod (format, i)));
-}
-
static void
SendSupportedFormats (struct wl_resource *resource)
{
int i;
- uint64_t invalid;
+ uint64_t modifier;
- invalid = DRM_FORMAT_MOD_INVALID;
-
- for (i = 0; i < ArrayElements (all_formats); ++i)
+ for (i = 0; i < n_drm_formats; ++i)
{
- /* We consider all formats for which picture formats exist as
- supported. Unfortunately, it seems that the formats DRI3 is
- willing to support slightly differs, so trying to create
- buffers for some of these formats may fail later. */
- if (all_formats[i].format)
+ if (wl_resource_get_version (resource) < 3)
{
- if (wl_resource_get_version (resource) < 3)
- /* Send a legacy format message. */
- zwp_linux_dmabuf_v1_send_format (resource,
- all_formats[i].format_code);
- else
- {
- zwp_linux_dmabuf_v1_send_modifier (resource,
- all_formats[i].format_code,
- ModifierHigh (invalid),
- ModifierLow (invalid));
+ /* Send a legacy format message, but only as long as the
+ format uses the default (invalid) modifier. */
- SendModifiers (resource, &all_formats[i]);
- }
+ if (supported_formats[i].drm_modifier == DRM_FORMAT_MOD_INVALID)
+ zwp_linux_dmabuf_v1_send_format (resource,
+ supported_formats[i].drm_format);
+ }
+ else
+ {
+ /* This client supports modifiers, so send everything. */
+
+ modifier = supported_formats[i].drm_modifier;
+
+ zwp_linux_dmabuf_v1_send_modifier (resource,
+ supported_formats[i].drm_format,
+ ModifierHigh (modifier),
+ ModifierLow (modifier));
}
}
}
@@ -1208,65 +979,27 @@ HandleBind (struct wl_client *client, void *data,
wl_resource_set_implementation (resource, &zwp_linux_dmabuf_v1_impl,
NULL, NULL);
- SendSupportedFormats (resource);
+ if (version < 4)
+ /* Versions later than 4 use the format table. */
+ SendSupportedFormats (resource);
}
static Bool
InitDrmDevice (void)
{
- xcb_dri3_open_cookie_t cookie;
- xcb_dri3_open_reply_t *reply;
- int *fds, fd;
- struct stat dev_stat;
+ Bool error;
- /* TODO: if this ever calls exec, set FD_CLOEXEC. TODO TODO
- implement multiple providers. */
- cookie = xcb_dri3_open (compositor.conn,
- DefaultRootWindow (compositor.display),
- None);
- reply = xcb_dri3_open_reply (compositor.conn, cookie, NULL);
+ error = False;
- if (!reply)
- return False;
-
- fds = xcb_dri3_open_reply_fds (compositor.conn, reply);
-
- if (!fds)
- {
- free (reply);
- return False;
- }
-
- fd = fds[0];
-
- if (fstat (fd, &dev_stat) != 0)
- {
- close (fd);
- free (reply);
-
- return False;
- }
-
- if (dev_stat.st_rdev)
- drm_device_node = dev_stat.st_rdev;
- else
- {
- close (fd);
- free (reply);
-
- return False;
- }
-
- close (fd);
- free (reply);
-
- return True;
+ /* This can either be a master node or a render node. */
+ drm_device_node = RenderGetRenderDevice (&error);
+ return !error;
}
static ssize_t
WriteFormatTable (void)
{
- int fd, i, m;
+ int fd, i;
ssize_t written;
FormatModifierPair pair;
@@ -1285,21 +1018,17 @@ WriteFormatTable (void)
{
fprintf (stderr, "Failed to allocate format table fd. "
"Hardware acceleration will probably be unavailable.\n");
- return 1;
+ return -1;
}
written = 0;
/* Write each format-modifier pair. */
- for (i = 0; i < ArrayElements (all_formats); ++i)
+ for (i = 0; i < n_drm_formats; ++i)
{
- if (!all_formats[i].format)
- continue;
-
- /* First, send the default implicit modifier pair. */
- pair.format = all_formats[i].format_code;
+ pair.format = supported_formats[i].drm_format;
pair.padding = 0;
- pair.modifier = DRM_FORMAT_MOD_INVALID;
+ pair.modifier = supported_formats[i].drm_modifier;
if (write (fd, &pair, sizeof pair) != sizeof pair)
/* Writing the modifier pair failed. Punt. */
@@ -1307,18 +1036,6 @@ WriteFormatTable (void)
/* Now tell the caller how much was written. */
written += sizeof pair;
-
- /* Next, write all the modifier pairs. */
- for (m = 0; m < all_formats[i].n_supported_modifiers; ++m)
- {
- pair.modifier = all_formats[i].supported_modifiers[m];
-
- if (write (fd, &pair, sizeof pair) != sizeof pair)
- /* Writing this pair failed, punt. */
- goto cancel;
-
- written += sizeof pair;
- }
}
format_table_fd = fd;
@@ -1329,32 +1046,30 @@ WriteFormatTable (void)
return -1;
}
-static void
-ReallyInitDmabuf (void)
+static Bool
+ReadSupportedFormats (void)
{
- XSetWindowAttributes attrs;
- size_t size;
+ /* Read supported formats from the renderer. If none are supported,
+ don't initialize dmabuf. */
+ supported_formats = RenderGetDrmFormats (&n_drm_formats);
- if (!FindSupportedFormats ())
- {
- fprintf (stderr, "No supported picture formats were found."
- " Hardware acceleration will not be available.\n");
- return;
- }
+ return n_drm_formats > 0;
+}
- /* Now look up which modifiers are supported for what formats. */
- FindSupportedModifiers ();
+void
+XLInitDmabuf (void)
+{
+ ssize_t size;
+
+ /* First, initialize supported formats. */
+ if (!ReadSupportedFormats ())
+ return;
/* And try to create the format table. */
size = WriteFormatTable ();
/* Create an unmapped, InputOnly window, that is used to receive
roundtrip events. */
- attrs.override_redirect = True;
- round_trip_window = XCreateWindow (compositor.display,
- DefaultRootWindow (compositor.display),
- -1, -1, 1, 1, 0, CopyFromParent, InputOnly,
- CopyFromParent, CWOverrideRedirect, &attrs);
global_dmabuf = wl_global_create (compositor.wl_display,
&zwp_linux_dmabuf_v1_interface,
@@ -1365,44 +1080,4 @@ ReallyInitDmabuf (void)
/* If the format table was successfully created, set its size. */
format_table_size = size;
-
- /* Initialize the sentinel node for buffer creation. */
- pending_success.next = &pending_success;
- pending_success.last = &pending_success;
-}
-
-void
-XLInitDmabuf (void)
-{
- xcb_dri3_query_version_cookie_t cookie;
- xcb_dri3_query_version_reply_t *reply;
- const xcb_query_extension_reply_t *ext;
-
- ext = xcb_get_extension_data (compositor.conn, &xcb_dri3_id);
- reply = NULL;
-
- if (ext && ext->present)
- {
- cookie = xcb_dri3_query_version (compositor.conn, 1, 2);
- reply = xcb_dri3_query_version_reply (compositor.conn, cookie,
- NULL);
-
- if (!reply)
- goto error;
-
- if (reply->major_version < 1
- || (reply->major_version == 1
- && reply->minor_version < 2))
- goto error;
-
- dri3_opcode = ext->major_opcode;
- ReallyInitDmabuf ();
- }
- else
- error:
- fprintf (stderr, "Warning: the X server does not support a new enough version of"
- " the DRI3 extension.\nHardware acceleration will not be available.\n");
-
- if (reply)
- free (reply);
}
diff --git a/fns.c b/fns.c
index a61bb09..32634fd 100644
--- a/fns.c
+++ b/fns.c
@@ -22,13 +22,17 @@ along with 12to11. If not, see . */
#include
#include
+#include
#include
#include
+#include
#include
#include "compositor.h"
+typedef struct _Busfault Busfault;
+
struct _RootWindowSelection
{
/* The next and last event selection records in this chain. */
@@ -38,9 +42,30 @@ struct _RootWindowSelection
unsigned long event_mask;
};
+struct _Busfault
+{
+ /* Nodes to the left and right. */
+ Busfault *left, *right;
+
+ /* Start of the ignored area. */
+ char *data;
+
+ /* Size of the ignored area. */
+ size_t ignored_area;
+
+ /* Height of this node. */
+ int height;
+};
+
/* Events that are being selected for on the root window. */
static RootWindowSelection root_window_events;
+/* All busfaults. */
+static Busfault *busfault_tree;
+
+/* Whether or not the SIGBUS handler has been installed. */
+static Bool bus_handler_installed;
+
void
XLListFree (XLList *list, void (*item_func) (void *))
{
@@ -473,3 +498,304 @@ XLDeselectInputFromRootWindow (RootWindowSelection *key)
XLFree (key);
ReselectRootWindowInput ();
}
+
+static int
+GetHeight (Busfault *busfault)
+{
+ if (!busfault)
+ return 0;
+
+ return busfault->height;
+}
+
+static void
+FixHeights (Busfault *busfault)
+{
+ XLAssert (busfault != NULL);
+
+ busfault->height = 1 + MAX (GetHeight (busfault->left),
+ GetHeight (busfault->right));
+}
+
+static void
+RotateLeft (Busfault **root)
+{
+ Busfault *old_root, *new_root, *old_middle;
+
+ /* Rotate *root->left to *root. */
+ old_root = *root;
+ new_root = old_root->left;
+ old_middle = new_root->right;
+ old_root->left = old_middle;
+ new_root->right = old_root;
+ *root = new_root;
+
+ /* Update heights. */
+ FixHeights ((*root)->right);
+ FixHeights (*root);
+}
+
+static void
+RotateRight (Busfault **root)
+{
+ Busfault *old_root, *new_root, *old_middle;
+
+ /* Rotate *root->right to *root. */
+ old_root = *root;
+ new_root = old_root->right;
+ old_middle = new_root->left;
+ old_root->right = old_middle;
+ new_root->left = old_root;
+ *root = new_root;
+
+ /* Update heights. */
+ FixHeights ((*root)->left);
+ FixHeights (*root);
+}
+
+static void
+RebalanceBusfault (Busfault **tree)
+{
+ if (*tree)
+ {
+ /* Check the left. It could be too tall. */
+ if (GetHeight ((*tree)->left)
+ > GetHeight ((*tree)->right) + 1)
+ {
+ /* The left side is unbalanced. Look for a taller
+ grandchild of tree->left. */
+
+ if (GetHeight ((*tree)->left->left)
+ > GetHeight ((*tree)->left->right))
+ RotateLeft (tree);
+ else
+ {
+ RotateRight (&(*tree)->left);
+ RotateLeft (tree);
+ }
+
+ return;
+ }
+ /* Then, check the right. */
+ else if (GetHeight ((*tree)->right)
+ > GetHeight ((*tree)->left) + 1)
+ {
+ /* The right side is unbalanced. Look for a taller
+ grandchild of tree->right. */
+
+ if (GetHeight ((*tree)->right->right)
+ > GetHeight ((*tree)->right->left))
+ RotateRight (tree);
+ else
+ {
+ RotateLeft (&(*tree)->right);
+ RotateRight (tree);
+ }
+
+ return;
+ }
+
+ /* Nothing was called, so fix heights. */
+ FixHeights (*tree);
+ }
+}
+
+static void
+RecordBusfault (Busfault **tree, Busfault *node)
+{
+ if (!*tree)
+ {
+ /* Tree is empty. Replace it with node. */
+ *tree = node;
+ node->left = NULL;
+ node->right = NULL;
+ node->height = 1;
+
+ return;
+ }
+
+ /* Record busfault into the correct subtree. */
+ if (node->data > (*tree)->data)
+ RecordBusfault (&(*tree)->right, node);
+ else
+ RecordBusfault (&(*tree)->left, node);
+
+ /* Balance the tree. */
+ RebalanceBusfault (tree);
+}
+
+static Busfault *
+DetectBusfault (Busfault *tree, char *address)
+{
+ /* This function is reentrant, but the functions that remove and
+ insert busfaults are not. */
+
+ if (!tree)
+ return NULL;
+ else if (address >= tree->data
+ && address < tree->data + tree->ignored_area)
+ return tree;
+
+ /* Continue searching in the tree. */
+ if (address > tree->data)
+ /* Search in the right node. */
+ return DetectBusfault (tree->right, address);
+
+ /* Search in the left. */
+ return DetectBusfault (tree->left, address);
+}
+
+static void
+DeleteMin (Busfault **tree, Busfault *out)
+{
+ Busfault *old_root;
+
+ /* Delete and return the minimum value of the tree. */
+
+ XLAssert (*tree != NULL);
+
+ if (!(*tree)->left)
+ {
+ /* *tree contains the smallest value. */
+ old_root = *tree;
+ out->data = old_root->data;
+ out->ignored_area = old_root->ignored_area;
+ *tree = old_root->right;
+ XLFree (old_root);
+ }
+ else
+ /* Keep looking to the left. */
+ DeleteMin (&(*tree)->left, out);
+
+ RebalanceBusfault (tree);
+}
+
+static void
+RemoveBusfault (Busfault **tree, char *data)
+{
+ Busfault *old_root;
+
+ if (!*tree)
+ /* There should always be a busfault. */
+ abort ();
+ else if ((*tree)->data == data)
+ {
+ if ((*tree)->right)
+ /* Replace with min value of right subtree. */
+ DeleteMin (&(*tree)->right, *tree);
+ else
+ {
+ /* Splice out old root. */
+ old_root = *tree;
+ *tree = (*tree)->left;
+ XLFree (old_root);
+ }
+ }
+ else if (data > (*tree)->data)
+ /* Delete child from the right. */
+ RemoveBusfault (&(*tree)->right, data);
+ else
+ /* Delete from the left. */
+ RemoveBusfault (&(*tree)->left, data);
+
+ RebalanceBusfault (tree);
+}
+
+static void
+HandleBusfault (int signal, siginfo_t *siginfo, void *ucontext)
+{
+ /* SIGBUS received. If the faulting address is currently part of a
+ shared memory buffer, ignore. Otherwise, print an error and
+ return. Only reentrant functions must be called within this
+ signal handler. */
+
+ if (DetectBusfault (busfault_tree, siginfo->si_addr))
+ return;
+
+ write (2, "unexpected bus fault\n",
+ sizeof "unexpected bus fault\n");
+ _exit (EXIT_FAILURE);
+}
+
+static void
+MaybeInstallBusHandler (void)
+{
+ struct sigaction act;
+
+ /* If the SIGBUS handler is already installed, return. */
+ if (bus_handler_installed)
+ return;
+
+ bus_handler_installed = 1;
+ memset (&act, 0, sizeof act);
+
+ /* Install a SIGBUS handler. When a client truncates the file
+ backing a shared memory buffer without notifying the compositor,
+ attempting to access the contents of the mapped memory beyond the
+ end of the file will result in SIGBUS being called, which we
+ handle here. */
+
+ act.sa_flags = SA_SIGINFO;
+ act.sa_sigaction = HandleBusfault;
+
+ if (sigaction (SIGBUS, &act, NULL))
+ {
+ perror ("sigaction");
+ abort ();
+ }
+}
+
+static void
+BlockSigbus (void)
+{
+ sigset_t sigset;
+
+ sigemptyset (&sigset);
+ sigaddset (&sigset, SIGBUS);
+
+ if (sigprocmask (SIG_BLOCK, &sigset, NULL))
+ {
+ perror ("sigprocmask");
+ abort ();
+ }
+}
+
+static void
+UnblockSigbus (void)
+{
+ sigset_t sigset;
+
+ sigemptyset (&sigset);
+
+ if (sigprocmask (SIG_BLOCK, &sigset, NULL))
+ {
+ perror ("sigprocmask");
+ abort ();
+ }
+}
+
+/* These must not overlap. */
+
+void
+XLRecordBusfault (void *data, size_t data_size)
+{
+ Busfault *node;
+
+ MaybeInstallBusHandler ();
+
+ BlockSigbus ();
+ node = XLMalloc (sizeof *node);
+ node->data = data;
+ node->ignored_area = data_size;
+
+ RecordBusfault (&busfault_tree, node);
+ UnblockSigbus ();
+}
+
+void
+XLRemoveBusfault (void *data)
+{
+ BlockSigbus ();
+ RemoveBusfault (&busfault_tree, data);
+ UnblockSigbus ();
+}
diff --git a/icon_surface.c b/icon_surface.c
index 22899ac..6884f3f 100644
--- a/icon_surface.c
+++ b/icon_surface.c
@@ -42,8 +42,8 @@ struct _IconSurface
/* The window used by this role. */
Window window;
- /* The picture associated with this role. */
- Picture picture;
+ /* The rendering target associated with this role. */
+ RenderTarget target;
/* The subcompositor associated with this role. */
Subcompositor *subcompositor;
@@ -87,7 +87,7 @@ ReleaseBacking (IconSurface *icon)
return;
/* Release all allocated resources. */
- XRenderFreePicture (compositor.display, icon->picture);
+ RenderDestroyRenderTarget (icon->target);
XDestroyWindow (compositor.display, icon->window);
/* And the association. */
@@ -372,7 +372,6 @@ XLGetIconSurface (Surface *surface)
{
IconSurface *role;
XSetWindowAttributes attrs;
- XRenderPictureAttributes picture_attrs;
unsigned int flags;
role = XLCalloc (1, sizeof *role);
@@ -410,20 +409,15 @@ XLGetIconSurface (Surface *surface)
PropModeReplace,
(unsigned char *) &_NET_WM_WINDOW_TYPE_DND, 1);
- /* Create a picture associated with the window. */
- role->picture = XRenderCreatePicture (compositor.display,
- role->window,
- /* TODO: get this from the
- visual instead. */
- compositor.argb_format,
- 0, &picture_attrs);
+ /* Create a target associated with the window. */
+ role->target = RenderTargetFromWindow (role->window);
/* Create a subcompositor associated with the window. */
role->subcompositor = MakeSubcompositor ();
role->clock = XLMakeFrameClockForWindow (role->window);
/* Set the subcompositor target and some callbacks. */
- SubcompositorSetTarget (role->subcompositor, role->picture);
+ SubcompositorSetTarget (role->subcompositor, &role->target);
SubcompositorSetBoundsCallback (role->subcompositor,
NoteBounds, role);
diff --git a/libraries.def b/libraries.def
index b36b8a2..06e153f 100644
--- a/libraries.def
+++ b/libraries.def
@@ -15,3 +15,12 @@ PIXMANINCLUDES = -I$(INCROOT)/pixman-1
system. */
WAYLAND_SCANNER = wayland-scanner
+
+/* Uncomment the following code if building with EGL support.
+
+#define HaveEglSupport
+
+EGL = -lEGL
+GLES = -lGLESv2
+
+*/
diff --git a/run.c b/run.c
index 2ffb6ee..5a897b1 100644
--- a/run.c
+++ b/run.c
@@ -143,8 +143,13 @@ HandleOneXEvent (XEvent *event)
if (XLHandleOneXEventForIconSurfaces (event))
return;
+#if 0
if (XLHandleOneXEventForDmabuf (event))
return;
+#endif
+
+ if (HandleOneXEventForPictureRenderer (event))
+ return;
if (XLHandleOneXEventForXData (event))
return;
diff --git a/seat.c b/seat.c
index dac20c0..2cd5602 100644
--- a/seat.c
+++ b/seat.c
@@ -513,7 +513,7 @@ struct _DeviceInfo
#define CursorFromRole(role) ((SeatCursor *) (role))
/* Subcompositor targets used inside cursor subframes. */
-static Picture cursor_subframe_picture;
+static RenderTarget cursor_subframe_target;
/* Its associated pixmap. */
static Pixmap cursor_subframe_pixmap;
@@ -856,22 +856,6 @@ ReleaseSeat (Seat *seat)
XLFree (seat);
}
-static Picture
-PictureForCursor (Drawable drawable)
-{
- XRenderPictureAttributes attrs;
- Picture picture;
-
- /* This is only required to pacfy -Wmaybe-uninitialized, since
- valuemask is 0. */
- memset (&attrs, 0, sizeof attrs);
-
- picture = XRenderCreatePicture (compositor.display, drawable,
- compositor.argb_format, 0,
- &attrs);
- return picture;
-}
-
static void
ComputeHotspot (SeatCursor *cursor, int min_x, int min_y,
int *x, int *y)
@@ -899,19 +883,24 @@ ComputeHotspot (SeatCursor *cursor, int min_x, int min_y,
}
static void
-ApplyCursor (SeatCursor *cursor, Picture picture,
+ApplyCursor (SeatCursor *cursor, RenderTarget target,
int min_x, int min_y)
{
Window window;
int x, y;
+ Picture picture;
if (cursor->cursor)
XFreeCursor (compositor.display, cursor->cursor);
ComputeHotspot (cursor, min_x, min_y, &x, &y);
+
+ picture = RenderPictureFromTarget (target);
cursor->cursor = XRenderCreateCursor (compositor.display,
picture, MAX (0, x),
MAX (0, y));
+ RenderFreePictureFromTarget (picture);
+
window = CursorWindow (cursor);
if (!(cursor->seat->flags & IsInert) && window != None)
@@ -923,9 +912,8 @@ ApplyCursor (SeatCursor *cursor, Picture picture,
static void
UpdateCursorFromSubcompositor (SeatCursor *cursor)
{
- static XRenderColor empty;
- Picture picture;
Pixmap pixmap;
+ RenderTarget target;
int min_x, min_y, max_x, max_y, width, height, x, y;
Bool need_clear;
@@ -963,13 +951,12 @@ UpdateCursorFromSubcompositor (SeatCursor *cursor)
pixmap = XCreatePixmap (compositor.display,
DefaultRootWindow (compositor.display),
width, height, compositor.n_planes);
- picture = PictureForCursor (pixmap);
+ target = RenderTargetFromPixmap (pixmap);
/* If the bounds extend beyond the subcompositor, clear the
picture. */
if (need_clear)
- XRenderFillRectangle (compositor.display, PictOpClear,
- picture, &empty, 0, 0, width, height);
+ RenderClearRectangle (target, 0, 0, width, height);
/* Garbage the subcompositor, since cursor contents are not
preserved. */
@@ -979,13 +966,13 @@ UpdateCursorFromSubcompositor (SeatCursor *cursor)
SubcompositorSetProjectiveTransform (cursor->subcompositor,
MAX (0, -x), MAX (0, -x));
- SubcompositorSetTarget (cursor->subcompositor, picture);
+ SubcompositorSetTarget (cursor->subcompositor, &target);
SubcompositorUpdate (cursor->subcompositor);
- SubcompositorSetTarget (cursor->subcompositor, None);
+ SubcompositorSetTarget (cursor->subcompositor, NULL);
- ApplyCursor (cursor, picture, min_x, min_y);
+ ApplyCursor (cursor, target, min_x, min_y);
- XRenderFreePicture (compositor.display, picture);
+ RenderDestroyRenderTarget (target);
XFreePixmap (compositor.display, pixmap);
}
@@ -1081,8 +1068,7 @@ ReleaseBuffer (Surface *surface, Role *role, ExtBuffer *buffer)
static Bool
Subframe (Surface *surface, Role *role)
{
- static XRenderColor empty;
- Picture picture;
+ RenderTarget target;
Pixmap pixmap;
int min_x, min_y, max_x, max_y, width, height, x, y;
Bool need_clear;
@@ -1129,13 +1115,12 @@ Subframe (Surface *surface, Role *role)
pixmap = XCreatePixmap (compositor.display,
DefaultRootWindow (compositor.display),
width, height, compositor.n_planes);
- picture = PictureForCursor (pixmap);
+ target = RenderTargetFromPixmap (pixmap);
/* If the bounds extend beyond the subcompositor, clear the
picture. */
if (need_clear)
- XRenderFillRectangle (compositor.display, PictOpClear,
- picture, &empty, 0, 0, width, height);
+ RenderClearRectangle (target, 0, 0, width, height);
/* Garbage the subcompositor, since cursor contents are not
preserved. */
@@ -1144,10 +1129,14 @@ Subframe (Surface *surface, Role *role)
/* Set the right transform if the hotspot is negative. */
SubcompositorSetProjectiveTransform (cursor->subcompositor,
MAX (0, -x), MAX (0, -x));
- SubcompositorSetTarget (cursor->subcompositor, picture);
- /* Set the subframe picture to the picture in question. */
- cursor_subframe_picture = picture;
+ /* Attach the rendering target. */
+ SubcompositorSetTarget (cursor->subcompositor, &target);
+
+ /* Set the subframe target and pixmap to the target and pixmap in
+ use. */
+ cursor_subframe_target = target;
+ cursor_subframe_pixmap = pixmap;
/* Return True to let the drawing proceed. */
return True;
@@ -1161,21 +1150,20 @@ EndSubframe (Surface *surface, Role *role)
cursor = CursorFromRole (role);
- if (cursor_subframe_picture)
+ if (cursor_subframe_pixmap != None)
{
/* First, compute the bounds of the subcompositor. */
SubcompositorBounds (cursor->subcompositor,
&min_x, &min_y, &max_x, &max_y);
/* Apply the cursor. */
- ApplyCursor (cursor, cursor_subframe_picture, min_x, min_y);
+ ApplyCursor (cursor, cursor_subframe_target, min_x, min_y);
/* Then, free the temporary target. */
- XRenderFreePicture (compositor.display,
- cursor_subframe_picture);
+ RenderDestroyRenderTarget (cursor_subframe_target);
/* Finally, clear the target. */
- SubcompositorSetTarget (cursor->subcompositor, None);
+ SubcompositorSetTarget (cursor->subcompositor, NULL);
}
else
ApplyEmptyCursor (cursor);
@@ -1183,6 +1171,8 @@ EndSubframe (Surface *surface, Role *role)
if (cursor_subframe_pixmap)
XFreePixmap (compositor.display,
cursor_subframe_pixmap);
+
+ cursor_subframe_pixmap = None;
}
static void
diff --git a/shm.c b/shm.c
index 0ebf24e..7e07442 100644
--- a/shm.c
+++ b/shm.c
@@ -21,11 +21,8 @@ along with 12to11. If not, see . */
#include
#include
#include
-#include
#include
-#include
-#include
#include "compositor.h"
@@ -52,24 +49,12 @@ typedef struct _Buffer
/* The ExtBuffer associated with this buffer. */
ExtBuffer buffer;
- /* The pixmap associated with this buffer. */
- Pixmap pixmap;
-
- /* The picture associated with this buffer. */
- Picture picture;
+ /* The rendering buffer associated with this buffer. */
+ RenderBuffer render_buffer;
/* The width and height of this buffer. */
unsigned int width, height;
- /* The stride of this buffer. */
- int stride;
-
- /* The offset of this buffer. */
- int offset;
-
- /* The format of this buffer. */
- uint32_t format;
-
/* The wl_resource corresponding to this buffer. */
struct wl_resource *resource;
@@ -93,6 +78,14 @@ DereferencePool (Pool *pool)
return;
munmap (pool->data, pool->size);
+
+ /* Cancel the busfault trap. */
+
+ if (pool->data != (void *) -1
+ /* If the pool is of size 0, no busfault was installed. */
+ && pool->size)
+ XLRemoveBusfault (pool->data);
+
close (pool->fd);
XLFree (pool);
}
@@ -115,8 +108,7 @@ DereferenceBuffer (Buffer *buffer)
if (--buffer->refcount)
return;
- XRenderFreePicture (compositor.display, buffer->picture);
- XFreePixmap (compositor.display, buffer->pixmap);
+ RenderFreeShmBuffer (buffer->render_buffer);
DereferencePool (buffer->pool);
ExtBufferDestroy (&buffer->buffer);
@@ -142,16 +134,10 @@ DereferenceBufferFunc (ExtBuffer *buffer)
DereferenceBuffer ((Buffer *) buffer);
}
-static Picture
-GetPictureFunc (ExtBuffer *buffer)
+static RenderBuffer
+GetBufferFunc (ExtBuffer *buffer)
{
- return ((Buffer *) buffer)->picture;
-}
-
-static Pixmap
-GetPixmapFunc (ExtBuffer *buffer)
-{
- return ((Buffer *) buffer)->pixmap;
+ return ((Buffer *) buffer)->render_buffer;
}
static unsigned int
@@ -175,20 +161,7 @@ DestroyBuffer (struct wl_client *client, struct wl_resource *resource)
static void
PrintBuffer (Buffer *buffer)
{
- int x, y;
- char *base;
- unsigned int *base32;
-
- for (y = 0; y < buffer->height; ++y)
- {
- base = buffer->pool->data;
- base += buffer->stride * y;
- base32 = (unsigned int *) base;
-
- for (x = 0; x < buffer->width; ++x)
- fprintf (stderr, "#x%8x ", base32[x]);
- fprintf (stderr, "\n");
- }
+ /* Not implemented. */
}
static void
@@ -197,37 +170,6 @@ PrintBufferFunc (ExtBuffer *buffer)
PrintBuffer ((Buffer *) buffer);
}
-static int
-DepthForFormat (uint32_t format)
-{
- switch (format)
- {
- case WL_SHM_FORMAT_ARGB8888:
- return 32;
-
- case WL_SHM_FORMAT_XRGB8888:
- return 24;
-
- default:
- return 0;
- }
-}
-
-static XRenderPictFormat *
-PictFormatForFormat (uint32_t format)
-{
- switch (format)
- {
- case WL_SHM_FORMAT_ARGB8888:
- return compositor.argb_format;
-
- case WL_SHM_FORMAT_XRGB8888:
- return compositor.xrgb_format;
-
- default:
- return 0;
- }
-}
static void
HandleBufferResourceDestroy (struct wl_resource *resource)
@@ -245,22 +187,35 @@ static const struct wl_buffer_interface wl_shm_buffer_impl =
.destroy = DestroyBuffer,
};
+static Bool
+IsFormatSupported (uint32_t format)
+{
+ ShmFormat *formats;
+ int nformats, i;
+
+ formats = RenderGetShmFormats (&nformats);
+
+ for (i = 0; i < nformats; ++i)
+ {
+ if (formats[i].format == format)
+ return True;
+ }
+
+ return False;
+}
+
static void
CreateBuffer (struct wl_client *client, struct wl_resource *resource,
uint32_t id, int32_t offset, int32_t width, int32_t height,
int32_t stride, uint32_t format)
{
- XRenderPictureAttributes picture_attrs;
Pool *pool;
Buffer *buffer;
- xcb_shm_seg_t seg;
- Pixmap pixmap;
- Picture picture;
- int fd, depth;
+ RenderBuffer render_buffer;
+ SharedMemoryAttributes attrs;
+ Bool failure;
- depth = DepthForFormat (format);
-
- if (!depth)
+ if (!IsFormatSupported (format))
{
wl_resource_post_error (resource, WL_SHM_ERROR_INVALID_FORMAT,
"the specified format is not supported");
@@ -269,9 +224,8 @@ CreateBuffer (struct wl_client *client, struct wl_resource *resource,
pool = wl_resource_get_user_data (resource);
- if (pool->size < offset || stride != width * 4
- || offset + stride * height > pool->size
- || offset < 0)
+ if (!RenderValidateShmParams (format, width, height,
+ offset, stride, pool->size))
{
wl_resource_post_error (resource, WL_SHM_ERROR_INVALID_STRIDE,
"invalid offset or stride, or pool too small");
@@ -314,46 +268,43 @@ CreateBuffer (struct wl_client *client, struct wl_resource *resource,
return;
}
- /* XCB closes fds after sending them. */
- fd = fcntl (pool->fd, F_DUPFD_CLOEXEC, 0);
+ attrs.format = format;
+ attrs.offset = offset;
+ attrs.width = width;
+ attrs.height = height;
+ attrs.stride = stride;
+ attrs.fd = pool->fd;
- if (fd < 0)
+ /* Pass a reference instead of the pointer itself. The pool will
+ stay valid as long as the buffer is still alive, and the data
+ pointer can change if the client resizes the pool. */
+ attrs.data = &pool->data;
+ attrs.pool_size = pool->size;
+
+ /* Now, create the renderer buffer. */
+ failure = False;
+ render_buffer = RenderBufferFromShm (&attrs, &failure);
+
+ /* If a platform specific error happened, fail now. */
+ if (failure)
{
wl_resource_destroy (buffer->resource);
XLFree (buffer);
wl_resource_post_error (resource, WL_SHM_ERROR_INVALID_FD,
- "fcntl: %s", strerror (errno));
+ "unknown error creating buffer");
return;
}
- seg = xcb_generate_id (compositor.conn);
- pixmap = xcb_generate_id (compositor.conn);
-
- xcb_shm_attach_fd (compositor.conn, seg, fd, false);
- xcb_shm_create_pixmap (compositor.conn, pixmap,
- DefaultRootWindow (compositor.display),
- width, height, depth, seg, offset);
- xcb_shm_detach (compositor.conn, seg);
-
- picture = XRenderCreatePicture (compositor.display, pixmap,
- PictFormatForFormat (format),
- 0, &picture_attrs);
-
- buffer->pixmap = pixmap;
- buffer->picture = picture;
+ buffer->render_buffer = render_buffer;
buffer->width = width;
buffer->height = height;
- buffer->stride = stride;
- buffer->offset = offset;
- buffer->format = format;
buffer->pool = pool;
buffer->refcount = 1;
/* Initialize function pointers. */
buffer->buffer.funcs.retain = RetainBufferFunc;
buffer->buffer.funcs.dereference = DereferenceBufferFunc;
- buffer->buffer.funcs.get_picture = GetPictureFunc;
- buffer->buffer.funcs.get_pixmap = GetPixmapFunc;
+ buffer->buffer.funcs.get_buffer = GetBufferFunc;
buffer->buffer.funcs.width = WidthFunc;
buffer->buffer.funcs.height = HeightFunc;
buffer->buffer.funcs.release = ReleaseBufferFunc;
@@ -390,6 +341,19 @@ ResizePool (struct wl_client *client, struct wl_resource *resource,
void *data;
pool = wl_resource_get_user_data (resource);
+
+ if (size == pool->size)
+ /* There is no need to do anything, since the pool is still the
+ same size. */
+ return;
+
+ if (size < pool->size)
+ {
+ wl_resource_post_error (resource, WL_SHM_ERROR_INVALID_FD,
+ "shared memory pools cannot be shrunk");
+ return;
+ }
+
data = mremap (pool->data, pool->size, size, MREMAP_MAYMOVE);
if (data == MAP_FAILED)
@@ -399,8 +363,17 @@ ResizePool (struct wl_client *client, struct wl_resource *resource,
return;
}
+ /* Now cancel the existing bus fault handler, should it have been
+ installed. */
+ if (pool->size)
+ XLRemoveBusfault (pool->data);
+
pool->size = size;
pool->data = data;
+
+ /* And add a new handler. */
+ if (pool->size)
+ XLRecordBusfault (pool->data, pool->size);
}
static const struct wl_shm_pool_interface wl_shm_pool_impl =
@@ -417,9 +390,8 @@ HandleResourceDestroy (struct wl_resource *resource)
}
static void
-CreatePool (struct wl_client *client,
- struct wl_resource *resource,
- uint32_t id, int fd, int size)
+CreatePool (struct wl_client *client, struct wl_resource *resource,
+ uint32_t id, int32_t fd, int32_t size)
{
Pool *pool;
@@ -459,6 +431,13 @@ CreatePool (struct wl_client *client,
pool, HandlePoolResourceDestroy);
pool->size = size;
+
+ /* Begin trapping SIGBUS from this pool. The client may truncate
+ the file without telling us, in which case accessing its contents
+ will cause crashes. */
+ if (pool->size)
+ XLRecordBusfault (pool->data, pool->size);
+
pool->fd = fd;
pool->refcount = 1;
@@ -473,10 +452,13 @@ static const struct wl_shm_interface wl_shm_impl =
static void
PostFormats (struct wl_resource *resource)
{
- /* TODO: don't hard-code visuals and be slightly more versatile. */
+ ShmFormat *formats;
+ int nformats, i;
- wl_shm_send_format (resource, WL_SHM_FORMAT_XRGB8888);
- wl_shm_send_format (resource, WL_SHM_FORMAT_ARGB8888);
+ formats = RenderGetShmFormats (&nformats);
+
+ for (i = 0; i < nformats; ++i)
+ wl_shm_send_format (resource, formats[i].format);
}
static void
@@ -544,40 +526,6 @@ InitRender (void)
void
XLInitShm (void)
{
- xcb_shm_query_version_reply_t *reply;
- xcb_shm_query_version_cookie_t cookie;
-
- /* This shouldn't be freed. */
- const xcb_query_extension_reply_t *ext;
-
- ext = xcb_get_extension_data (compositor.conn, &xcb_shm_id);
-
- if (!ext || !ext->present)
- {
- fprintf (stderr, "The MIT-SHM extension is not supported by this X server.\n");
- exit (1);
- }
-
- cookie = xcb_shm_query_version (compositor.conn);
- reply = xcb_shm_query_version_reply (compositor.conn,
- cookie, NULL);
-
- if (!reply)
- {
- fprintf (stderr, "The MIT-SHM extension on this X server is too old.\n");
- exit (1);
- }
- else if (reply->major_version < 1
- || (reply->major_version == 1
- && reply->minor_version < 2))
- {
- fprintf (stderr, "The MIT-SHM extension on this X server is too old"
- " to support POSIX shared memory.\n");
- exit (1);
- }
-
- free (reply);
-
InitRender ();
global_shm = wl_global_create (compositor.wl_display,
diff --git a/subcompositor.c b/subcompositor.c
index 0124aeb..4db9f5c 100644
--- a/subcompositor.c
+++ b/subcompositor.c
@@ -139,19 +139,21 @@ enum
{
/* This means that the view hierarchy has changed, and all
subcompositing optimisations should be skipped. */
- SubcompositorIsGarbaged = 1,
+ SubcompositorIsGarbaged = 1,
/* This means that the opaque region of one of the views
changed. */
- SubcompositorIsOpaqueDirty = (1 << 2),
+ SubcompositorIsOpaqueDirty = (1 << 2),
/* This means that the input region of one of the views
changed. */
- SubcompositorIsInputDirty = (1 << 3),
+ SubcompositorIsInputDirty = (1 << 3),
/* This means that there is at least one unmapped view in this
subcompositor. */
- SubcompositorIsPartiallyMapped = (1 << 4),
+ SubcompositorIsPartiallyMapped = (1 << 4),
/* This means that the subcompositor is frozen and updates should
do nothing. */
- SubcompositorIsFrozen = (1 << 5),
+ SubcompositorIsFrozen = (1 << 5),
+ /* This means that the subcompositor has a target attached. */
+ SubcompositorIsTargetAttached = (1 << 6),
};
#define IsGarbaged(subcompositor) \
@@ -179,6 +181,11 @@ enum
#define IsFrozen(subcompositor) \
((subcompositor)->state & SubcompositorIsFrozen)
+#define SetTargetAttached(subcompositor) \
+ ((subcompositor)->state |= SubcompositorIsTargetAttached)
+#define IsTargetAttached(subcompositor) \
+ ((subcompositor)->state & SubcompositorIsTargetAttached)
+
#ifndef TEST
enum
@@ -218,11 +225,6 @@ struct _View
into a compositor. */
Subcompositor *subcompositor;
-#ifndef TEST
- /* Picture this subcompositor draws to. */
- Picture picture;
-#endif
-
/* Pointer to the parent view. NULL if the parent is the
subcompositor itself. */
View *parent;
@@ -289,8 +291,8 @@ struct _Subcompositor
List *children, *last_children;
#ifndef TEST
- /* The picture that is rendered to. */
- Picture target;
+ /* Target this subcompositor draws to. */
+ RenderTarget target;
/* Function called when the opaque region changes. */
void (*opaque_change) (Subcompositor *, void *,
@@ -316,6 +318,14 @@ struct _Subcompositor
/* An additional offset to apply when drawing to the target. */
int tx, ty;
+
+ /* Buffers used to store that damage. */
+ pixman_region32_t prior_damage[2];
+
+ /* The damage region of previous updates. last_damage is what the
+ damage region was 1 update ago, and before_damage is what the
+ damage region was 2 updates ago. */
+ pixman_region32_t *last_damage, *before_damage;
#endif
};
@@ -330,10 +340,6 @@ enum
DoAll = 0xf,
};
-/* The identity transform. */
-
-static XTransform identity_transform;
-
#endif
@@ -411,6 +417,10 @@ MakeSubcompositor (void)
subcompositor->last = subcompositor->inferiors;
subcompositor->last_children = subcompositor->children;
+ /* Initialize the buffers used to store previous damage. */
+ pixman_region32_init (&subcompositor->prior_damage[0]);
+ pixman_region32_init (&subcompositor->prior_damage[1]);
+
return subcompositor;
}
@@ -562,9 +572,16 @@ SubcompositorUpdateBoundsForInsert (Subcompositor *subcompositor,
#ifndef TEST
void
-SubcompositorSetTarget (Subcompositor *compositor, Picture picture)
+SubcompositorSetTarget (Subcompositor *compositor,
+ RenderTarget *target_in)
{
- compositor->target = picture;
+ if (target_in)
+ {
+ compositor->target = *target_in;
+ SetTargetAttached (compositor);
+ }
+ else
+ compositor->state &= SubcompositorIsTargetAttached;
/* We don't know if the new picture has the previous state left
over. */
@@ -1171,6 +1188,19 @@ main (int argc, char **argv)
the target drawable. Afterwards, the damage region of each
inferior is cleared, and the process can begin again.
+ However, under some situations, the contents of the target drawable
+ may reflect what was drawn two or three invocations of
+ SubcompositorUpdate ago. To enable efficient updates when that is
+ the case, the subcompositor will keep track of the global damage
+ regions of the past two updates, and intersect the resulting global
+ damage region of each invocation with the appropriate number of
+ previous regions.
+
+ For simplicity's sake, the update inferior is reset to the first
+ view in the subcompositor's inferior list whenever the global
+ damage region is intersected with the damage region of a previous
+ update.
+
Such computation is not reliable, however, if the size or position
of a view changes. In the interest of keeping thing simple, every
inferior is composited onto the target drawable whenever a view
@@ -1560,21 +1590,10 @@ GetTxTy (int scale)
return 1.0 / (-scale + 1);
}
-static XTransform
-ViewGetTransform (View *view)
+static void
+ViewApplyTransform (View *view, RenderBuffer buffer)
{
- XTransform transform;
- double transform_value;
-
- /* Perform scaling in the transform matrix. */
-
- memset (&transform, 0, sizeof transform);
- transform_value = GetTxTy (view->scale);
- transform.matrix[0][0] = XDoubleToFixed (transform_value);
- transform.matrix[1][1] = XDoubleToFixed (transform_value);
- transform.matrix[2][2] = XDoubleToFixed (1);
-
- return transform;
+ RenderApplyTransform (buffer, GetTxTy (view->scale));
}
/* TODO: the callers of this can be optimized by setting the picture
@@ -1625,31 +1644,9 @@ static void
FillBoxesWithTransparency (Subcompositor *subcompositor,
pixman_box32_t *boxes, int nboxes)
{
- XRectangle *rects;
- static XRenderColor color;
- int i;
- Picture picture;
-
- picture = subcompositor->target;
-
- if (nboxes < 256)
- rects = alloca (sizeof *rects * nboxes);
- else
- rects = XLMalloc (sizeof *rects * nboxes);
-
- for (i = 0; i < nboxes; ++i)
- {
- rects[i].x = BoxStartX (boxes[i]) - subcompositor->min_x;
- rects[i].y = BoxStartY (boxes[i]) - subcompositor->min_y;
- rects[i].width = BoxWidth (boxes[i]);
- rects[i].height = BoxHeight (boxes[i]);
- }
-
- XRenderFillRectangles (compositor.display, PictOpClear, picture,
- &color, rects, nboxes);
-
- if (nboxes >= 256)
- XLFree (rects);
+ RenderFillBoxesWithTransparency (subcompositor->target, boxes,
+ nboxes, subcompositor->min_x,
+ subcompositor->min_y);
}
static Bool
@@ -1684,6 +1681,52 @@ SubcompositorIsEmpty (Subcompositor *subcompositor)
&& subcompositor->min_y == subcompositor->max_y);
}
+static void
+StorePreviousDamage (Subcompositor *subcompositor,
+ pixman_region32_t *update_region)
+{
+ pixman_region32_t *prior;
+
+ if (renderer_flags & NeverAges)
+ /* Aging never happens, so recording prior damage is
+ unnecessary. */
+ return;
+
+ /* Move last_damage to prior_damage if it already exists, and find
+ something to hold more damage and set it as last_damage. There
+ is no need to do this if the render target age never exceeds
+ 0. */
+
+ if (!subcompositor->last_damage)
+ subcompositor->last_damage = &subcompositor->prior_damage[0];
+ else if (!subcompositor->before_damage)
+ {
+ subcompositor->before_damage = subcompositor->last_damage;
+ subcompositor->last_damage = &subcompositor->prior_damage[1];
+ }
+ else
+ {
+ prior = subcompositor->before_damage;
+ subcompositor->before_damage = subcompositor->last_damage;
+ subcompositor->last_damage = prior;
+ }
+
+ /* NULL means use the bounds of the subcompositor. */
+ if (!update_region)
+ {
+ pixman_region32_fini (subcompositor->last_damage);
+ pixman_region32_init_rect (subcompositor->last_damage,
+ subcompositor->min_x,
+ subcompositor->min_y,
+ subcompositor->max_x,
+ subcompositor->max_y);
+ }
+ else
+ /* Copy the update region to last_damage. */
+ pixman_region32_copy (subcompositor->last_damage,
+ update_region);
+}
+
void
SubcompositorUpdate (Subcompositor *subcompositor)
{
@@ -1692,13 +1735,14 @@ SubcompositorUpdate (Subcompositor *subcompositor)
View *start, *original_start, *view, *first;
List *list;
pixman_box32_t *boxes, *extents, temp_boxes;
- int nboxes, i, op, tx, ty;
- Picture picture;
- XTransform transform;
+ int nboxes, i, tx, ty;
+ Operation op;
+ RenderBuffer buffer;
int min_x, min_y;
+ int age;
/* Just return if no target was specified. */
- if (subcompositor->target == None)
+ if (!IsTargetAttached (subcompositor))
return;
/* Likewise if the subcompositor is "frozen". */
@@ -1717,6 +1761,31 @@ SubcompositorUpdate (Subcompositor *subcompositor)
start = subcompositor->inferiors->next->view;
original_start = subcompositor->inferiors->next->view;
+ age = RenderTargetAge (subcompositor->target);
+
+ /* If there is not enough prior damage available to satisfy age, set
+ it to -1. */
+
+ if (age > 0 && !subcompositor->last_damage)
+ age = -1;
+
+ if (age > 2 && !subcompositor->before_damage)
+ age = -1;
+
+ /* If the subcompositor is garbaged, clear all prior damage. */
+ if (IsGarbaged (subcompositor))
+ {
+ if (subcompositor->last_damage)
+ pixman_region32_clear (subcompositor->last_damage);
+
+ if (subcompositor->before_damage)
+ pixman_region32_clear (subcompositor->before_damage);
+
+ /* Reset these fields to NULL, so we do not try to use them
+ later on. */
+ subcompositor->last_damage = NULL;
+ subcompositor->before_damage = NULL;
+ }
/* Clear the "is partially mapped" flag. It will be set later on if
there is actually a partially mapped view. */
@@ -1727,7 +1796,17 @@ SubcompositorUpdate (Subcompositor *subcompositor)
min_x, min_y, subcompositor->max_x,
subcompositor->max_y);
- if (!IsGarbaged (subcompositor))
+ /* Note the size of this subcompositor, so the viewport can be set
+ accordingly. */
+ RenderNoteTargetSize (subcompositor->target,
+ subcompositor->max_x - min_x + 1,
+ subcompositor->max_y - min_y + 1);
+
+ if (!IsGarbaged (subcompositor)
+ /* If the target contents are too old or invalid, we go down the
+ usual IsGarbaged code path, except we do not recompute the
+ input or opaque regions unless they are dirty. */
+ && (age >= 0 && age < 3))
{
start = NULL;
original_start = NULL;
@@ -1793,8 +1872,10 @@ SubcompositorUpdate (Subcompositor *subcompositor)
&update_region, &temp);
/* This view will obscure all preceding damaged areas,
- so make start here. */
- if (!pixman_region32_not_empty (&update_region))
+ so make start here. This optimization is disabled if
+ the target contents are too old, as prior damage
+ could reveal contents below. */
+ if (!pixman_region32_not_empty (&update_region) && !age)
{
start = list->view;
@@ -1834,6 +1915,14 @@ SubcompositorUpdate (Subcompositor *subcompositor)
if (pixman_region32_not_empty (&list->view->damage))
{
+ /* Update the attached buffer from the damage. This is
+ only required on some backends, where we have to
+ upload data from a shared memory buffer to the
+ graphics hardware. */
+
+ buffer = XLRenderBufferFromBuffer (view->buffer);
+ RenderUpdateBufferForDamage (buffer, &list->view->damage);
+
/* Translate the region into the subcompositor
coordinate space. */
pixman_region32_translate (&list->view->damage,
@@ -1906,13 +1995,48 @@ SubcompositorUpdate (Subcompositor *subcompositor)
}
pixman_region32_fini (&start_opaque);
+
+ /* First store previous damage. */
+ StorePreviousDamage (subcompositor, &update_region);
+
+ /* Now, apply any prior damage that might be required. */
+ if (age > 0)
+ pixman_region32_union (&update_region, &update_region,
+ /* This is checked to exist upon
+ entering this code path. */
+ subcompositor->last_damage);
+
+ if (age > 1)
+ pixman_region32_union (&update_region, &update_region,
+ /* This is checked to exist upon
+ entering this code path. */
+ subcompositor->before_damage);
}
else
{
/* To save from iterating over all the views twice, perform the
input and opaque region updates in the draw loop instead. */
- pixman_region32_init (&total_opaque);
- pixman_region32_init (&total_input);
+
+ if (IsGarbaged (subcompositor))
+ {
+ pixman_region32_init (&total_opaque);
+ pixman_region32_init (&total_input);
+ }
+ else
+ {
+ /* Otherwise, we are in the IsGarbaged code because the
+ target contents are too old. Only initialize the opaque
+ and input regions if they are dirty. */
+
+ if (IsOpaqueDirty (subcompositor))
+ pixman_region32_init (&total_opaque);
+
+ if (IsInputDirty (subcompositor))
+ pixman_region32_init (&total_input);
+ }
+
+ /* Either way, put something in the prior damage ring. */
+ StorePreviousDamage (subcompositor, NULL);
}
/* If there's nothing to do, return. */
@@ -1925,6 +2049,10 @@ SubcompositorUpdate (Subcompositor *subcompositor)
list = start->link;
first = NULL;
+ /* Begin rendering. This is unnecessary on XRender, but required on
+ EGL to make the surface current and set the viewport. */
+ RenderStartRender (subcompositor->target);
+
do
{
view = list->view;
@@ -1947,7 +2075,24 @@ SubcompositorUpdate (Subcompositor *subcompositor)
if (!view->buffer)
goto next_1;
- picture = XLPictureFromBuffer (view->buffer);
+ buffer = XLRenderBufferFromBuffer (view->buffer);
+
+ if (IsGarbaged (subcompositor))
+ /* Update the attached buffer from the damage. This is only
+ required on some backends, where we have to upload data
+ from a shared memory buffer to the graphics hardware.
+
+ As the damage cannot be trusted while the subcompositor is
+ update, pass NULL; this tells the renderer to update the
+ entire buffer.
+
+ Note that if the subcompositor is not garbaged, then this
+ has already been done. */
+ RenderUpdateBufferForDamage (buffer, NULL);
+ else if (age < 0 || age > 3)
+ /* The target contents are too old, but the damage can be
+ trusted. */
+ RenderUpdateBufferForDamage (buffer, &view->damage);
if (!first)
{
@@ -1955,7 +2100,7 @@ SubcompositorUpdate (Subcompositor *subcompositor)
with PictOpSrc so that transparency is applied correctly,
if it contains the entire update region. */
- if (IsGarbaged (subcompositor))
+ if (IsGarbaged (subcompositor) || age < 0 || age > 3)
{
extents = &temp_boxes;
@@ -1971,14 +2116,14 @@ SubcompositorUpdate (Subcompositor *subcompositor)
if (ViewContainsExtents (view, extents))
/* The update region is contained by the entire view, so
- use PictOpSrc. */
- op = PictOpSrc;
+ use source. */
+ op = OperationSource;
else
{
/* Otherwise, fill the whole update region with
transparency. */
- if (IsGarbaged (subcompositor))
+ if (IsGarbaged (subcompositor) || age < 0 || age > 3)
{
/* Use the entire subcompositor bounds if
garbaged. */
@@ -1993,24 +2138,19 @@ SubcompositorUpdate (Subcompositor *subcompositor)
FillBoxesWithTransparency (subcompositor,
boxes, nboxes);
- /* And use PictOpOver as usual. */
- op = PictOpOver;
+ /* And use over as usual. */
+ op = OperationOver;
}
}
else
- op = PictOpOver;
+ op = OperationOver;
first = view;
if (ViewHaveTransform (view))
- {
- transform = ViewGetTransform (view);
+ ViewApplyTransform (view, buffer);
- XRenderSetPictureTransform (compositor.display, picture,
- &transform);
- }
-
- if (!IsGarbaged (subcompositor))
+ if (!IsGarbaged (subcompositor) && (age >= 0 && age < 3))
{
/* First, obtain a new region that is the intersection of
the view with the global update region. */
@@ -2023,20 +2163,17 @@ SubcompositorUpdate (Subcompositor *subcompositor)
boxes = pixman_region32_rectangles (&temp, &nboxes);
for (i = 0; i < nboxes; ++i)
- XRenderComposite (compositor.display, op, picture,
- None, subcompositor->target,
- /* src-x. */
- BoxStartX (boxes[i]) - view->abs_x,
- /* src-y. */
- BoxStartY (boxes[i]) - view->abs_y,
- /* mask-x, mask-y. */
- 0, 0,
- /* dst-x. */
- BoxStartX (boxes[i]) - min_x + tx,
- /* dst-y. */
- BoxStartY (boxes[i]) - min_y + ty,
- /* width, height. */
- BoxWidth (boxes[i]), BoxHeight (boxes[i]));
+ RenderComposite (buffer, subcompositor->target, op,
+ /* src-x. */
+ BoxStartX (boxes[i]) - view->abs_x,
+ /* src-y. */
+ BoxStartY (boxes[i]) - view->abs_y,
+ /* dst-x. */
+ BoxStartX (boxes[i]) - min_x + tx,
+ /* dst-y. */
+ BoxStartY (boxes[i]) - min_y + ty,
+ /* width, height. */
+ BoxWidth (boxes[i]), BoxHeight (boxes[i]));
}
else
{
@@ -2045,26 +2182,29 @@ SubcompositorUpdate (Subcompositor *subcompositor)
earlier, since the compositor was garbaged. */
pixman_region32_clear (&view->damage);
- /* If the subcompositor is garbaged, composite the entire view
- to the right location. */
- XRenderComposite (compositor.display, op, picture,
- None, subcompositor->target,
- /* src-x, src-y. */
- 0, 0,
- /* mask-x, mask-y. */
- 0, 0,
- /* dst-x. */
- view->abs_x - min_x + tx,
- /* dst-y. */
- view->abs_y - min_y + ty,
- /* width. */
- ViewWidth (view),
- /* height. */
- ViewHeight (view));
+ /* If the subcompositor is garbaged, composite the entire
+ view to the right location. */
+ RenderComposite (buffer, subcompositor->target, op,
+ /* src-x, src-y. */
+ 0, 0,
+ /* dst-x. */
+ view->abs_x - min_x + tx,
+ /* dst-y. */
+ view->abs_y - min_y + ty,
+ /* width. */
+ ViewWidth (view),
+ /* height. */
+ ViewHeight (view));
/* Also adjust the opaque and input regions here. */
- if (pixman_region32_not_empty (&view->opaque))
+ if (pixman_region32_not_empty (&view->opaque)
+ /* If the subcompositor is garbaged, the opaque region
+ must always be updated. But if we are here because
+ the target is too old, it must only be updated if the
+ opaque region is also dirty. */
+ && (IsGarbaged (subcompositor)
+ || IsOpaqueDirty (subcompositor)))
{
/* Translate the region into the global coordinate
space. */
@@ -2084,7 +2224,10 @@ SubcompositorUpdate (Subcompositor *subcompositor)
-list->view->abs_y);
}
- if (pixman_region32_not_empty (&view->input))
+ if (pixman_region32_not_empty (&view->input)
+ /* Ditto for the input region. */
+ && (IsGarbaged (subcompositor)
+ || IsInputDirty (subcompositor)))
{
/* Translate the region into the global coordinate
space. */
@@ -2105,45 +2248,58 @@ SubcompositorUpdate (Subcompositor *subcompositor)
}
if (ViewHaveTransform (view))
- XRenderSetPictureTransform (compositor.display, picture,
- &identity_transform);
+ RenderResetTransform (buffer);
next_1:
list = list->next;
}
while (list != subcompositor->inferiors);
+ /* Swap changes to display. */
+ RenderFinishRender (subcompositor->target);
+
complete:
- if (IsGarbaged (subcompositor))
+ if (IsGarbaged (subcompositor)
+ || ((age < 0 || age > 3)
+ && (IsInputDirty (subcompositor)
+ || IsOpaqueDirty (subcompositor))))
{
- /* The opaque region changed, so run any callbacks. */
- if (subcompositor->opaque_change)
+ if (IsGarbaged (subcompositor)
+ || IsOpaqueDirty (subcompositor))
{
- /* Translate this to appear in the "virtual" coordinate
- space. */
- pixman_region32_translate (&total_opaque, -min_x, -min_y);
+ /* The opaque region changed, so run any callbacks. */
+ if (subcompositor->opaque_change)
+ {
+ /* Translate this to appear in the "virtual" coordinate
+ space. */
+ pixman_region32_translate (&total_opaque, -min_x, -min_y);
- subcompositor->opaque_change (subcompositor,
- subcompositor->opaque_change_data,
- &total_opaque);
+ subcompositor->opaque_change (subcompositor,
+ subcompositor->opaque_change_data,
+ &total_opaque);
+ }
+
+ pixman_region32_fini (&total_opaque);
}
- pixman_region32_fini (&total_opaque);
-
- /* The input region changed, so run any callbacks. */
- if (subcompositor->input_change)
+ if (IsGarbaged (subcompositor)
+ || IsInputDirty (subcompositor))
{
- /* Translate this to appear in the "virtual" coordinate
- space. */
- pixman_region32_translate (&total_input, -min_x, -min_y);
+ /* The input region changed, so run any callbacks. */
+ if (subcompositor->input_change)
+ {
+ /* Translate this to appear in the "virtual" coordinate
+ space. */
+ pixman_region32_translate (&total_input, -min_x, -min_y);
- subcompositor->input_change (subcompositor,
- subcompositor->input_change_data,
- &total_input);
+ subcompositor->input_change (subcompositor,
+ subcompositor->input_change_data,
+ &total_input);
+ }
+
+ pixman_region32_fini (&total_input);
}
-
- pixman_region32_fini (&total_input);
}
pixman_region32_fini (&temp);
@@ -2163,17 +2319,17 @@ SubcompositorExpose (Subcompositor *subcompositor, XEvent *event)
View *view;
int x, y, width, height, nboxes, min_x, min_y, tx, ty;
pixman_box32_t extents, *boxes;
- int op, i;
+ int i;
+ Operation op;
pixman_region32_t temp;
- Picture picture;
- XTransform transform;
+ RenderBuffer buffer;
/* Graphics exposures are not yet handled. */
if (event->type == GraphicsExpose)
return;
/* No target? No update. */
- if (subcompositor->target == None)
+ if (!IsTargetAttached (subcompositor))
return;
x = event->xexpose.x + subcompositor->min_x;
@@ -2200,6 +2356,10 @@ SubcompositorExpose (Subcompositor *subcompositor, XEvent *event)
list = subcompositor->inferiors;
+ /* Begin rendering. This is unnecessary on XRender, but required on
+ EGL to make the surface current and set the viewport. */
+ RenderStartRender (subcompositor->target);
+
do
{
if (!list->view)
@@ -2217,7 +2377,7 @@ SubcompositorExpose (Subcompositor *subcompositor, XEvent *event)
/* If the first mapped view contains everything, draw it with
PictOpSrc. */
if (!view && ViewContainsExtents (list->view, &extents))
- op = PictOpSrc;
+ op = OperationSource;
else
{
/* Otherwise, fill the region with transparency for the
@@ -2227,7 +2387,7 @@ SubcompositorExpose (Subcompositor *subcompositor, XEvent *event)
FillBoxesWithTransparency (subcompositor,
&extents, 1);
- op = PictOpOver;
+ op = OperationOver;
}
view = list->view;
@@ -2240,38 +2400,31 @@ SubcompositorExpose (Subcompositor *subcompositor, XEvent *event)
ViewHeight (view));
/* Composite the contents according to OP. */
- picture = XLPictureFromBuffer (view->buffer);
+ buffer = XLRenderBufferFromBuffer (view->buffer);
boxes = pixman_region32_rectangles (&temp, &nboxes);
- if (ViewHaveTransform (view))
- {
- /* Set up transforms if necessary. */
- transform = ViewGetTransform (view);
+ /* Update the attached buffer from any damage. */
+ RenderUpdateBufferForDamage (buffer, &list->view->damage);
- XRenderSetPictureTransform (compositor.display, picture,
- &transform);
- }
+ if (ViewHaveTransform (view))
+ ViewApplyTransform (view, buffer);
for (i = 0; i < nboxes; ++i)
- XRenderComposite (compositor.display, op, picture,
- None, subcompositor->target,
- /* src-x. */
- BoxStartX (boxes[i]) - view->abs_x,
- /* src-y. */
- BoxStartY (boxes[i]) - view->abs_y,
- /* mask-x, mask-y. */
- 0, 0,
- /* dst-x. */
- BoxStartX (boxes[i]) - min_x + tx,
- /* dst-y. */
- BoxStartY (boxes[i]) - min_y + ty,
- /* width, height. */
- BoxWidth (boxes[i]), BoxHeight (boxes[i]));
+ RenderComposite (buffer, subcompositor->target, op,
+ /* src-x. */
+ BoxStartX (boxes[i]) - view->abs_x,
+ /* src-y. */
+ BoxStartY (boxes[i]) - view->abs_y,
+ /* dst-x. */
+ BoxStartX (boxes[i]) - min_x + tx,
+ /* dst-y. */
+ BoxStartY (boxes[i]) - min_y + ty,
+ /* width, height. */
+ BoxWidth (boxes[i]), BoxHeight (boxes[i]));
/* Undo transforms that were applied. */
if (ViewHaveTransform (view))
- XRenderSetPictureTransform (compositor.display, picture,
- &identity_transform);
+ RenderResetTransform (buffer);
/* Free the scratch region used to compute the intersection. */
pixman_region32_fini (&temp);
@@ -2281,6 +2434,9 @@ SubcompositorExpose (Subcompositor *subcompositor, XEvent *event)
list = list->next;
}
while (list != subcompositor->inferiors);
+
+ /* Swap changes to display. */
+ RenderFinishRender (subcompositor->target);
}
void
@@ -2309,6 +2465,10 @@ SubcompositorFree (Subcompositor *subcompositor)
XLFree (subcompositor->children);
XLFree (subcompositor->inferiors);
+ /* Finalize the buffers used to store previous damage. */
+ pixman_region32_fini (&subcompositor->prior_damage[0]);
+ pixman_region32_fini (&subcompositor->prior_damage[1]);
+
XLFree (subcompositor);
}
@@ -2401,9 +2561,7 @@ ViewGetParent (View *view)
void
SubcompositorInit (void)
{
- identity_transform.matrix[0][0] = 1;
- identity_transform.matrix[1][1] = 1;
- identity_transform.matrix[2][2] = 1;
+ /* Nothing to do here... */
}
int
diff --git a/xdata.c b/xdata.c
index 84d8f57..13a15de 100644
--- a/xdata.c
+++ b/xdata.c
@@ -687,7 +687,7 @@ PostReceiveDirect (Time time, Atom selection, Atom target, int fd)
static void PostReceiveConversion (Time, Atom, Atom, int);
-#define ReceiveBody(selection, primary) \
+#define ReceiveBody(selection, primary) \
Time time; \
TargetMapping *translation; \
\
diff --git a/xdg_surface.c b/xdg_surface.c
index bde2169..2d345d5 100644
--- a/xdg_surface.c
+++ b/xdg_surface.c
@@ -91,8 +91,8 @@ struct _XdgRole
/* The window backing this role. */
Window window;
- /* The picture backing this role. */
- Picture picture;
+ /* The render target backing this role. */
+ RenderTarget target;
/* The subcompositor backing this role. */
Subcompositor *subcompositor;
@@ -775,7 +775,7 @@ ReleaseBacking (XdgRole *role)
XLXdgRoleDetachImplementation (&role->role, role->impl);
/* Release all allocated resources. */
- XRenderFreePicture (compositor.display, role->picture);
+ RenderDestroyRenderTarget (role->target);
XDestroyWindow (compositor.display, role->window);
/* And the association. */
@@ -1268,7 +1268,6 @@ XLGetXdgSurface (struct wl_client *client, struct wl_resource *resource,
{
XdgRole *role;
XSetWindowAttributes attrs;
- XRenderPictureAttributes picture_attrs;
unsigned int flags;
Surface *surface;
@@ -1356,18 +1355,14 @@ XLGetXdgSurface (struct wl_client *client, struct wl_resource *resource,
0, 0, 20, 20, 0, compositor.n_planes,
InputOutput, compositor.visual, flags,
&attrs);
- role->picture = XRenderCreatePicture (compositor.display,
- role->window,
- /* TODO: get this from the
- visual instead. */
- compositor.argb_format,
- 0, &picture_attrs);
+ role->target = RenderTargetFromWindow (role->window);
+
role->subcompositor = MakeSubcompositor ();
role->clock = XLMakeFrameClockForWindow (role->window);
XLFrameClockSetFreezeCallback (role->clock, HandleFreeze, role);
- SubcompositorSetTarget (role->subcompositor, role->picture);
+ SubcompositorSetTarget (role->subcompositor, &role->target);
SubcompositorSetInputCallback (role->subcompositor,
InputRegionChanged, role);
SubcompositorSetOpaqueCallback (role->subcompositor,
diff --git a/xerror.c b/xerror.c
index 596d8e9..6d8e20b 100644
--- a/xerror.c
+++ b/xerror.c
@@ -92,8 +92,13 @@ ErrorHandler (Display *display, XErrorEvent *event)
return 0;
}
+#if 0
if (XLHandleErrorForDmabuf (event))
return 0;
+#endif
+
+ if (HandleErrorForPictureRenderer (event))
+ return 0;
if (event->error_code == (xi_first_error + XI_BadDevice))
/* Various XI requests can result in XI_BadDevice errors if the