From b4ee06589e5794ba1ae09d263592bae3754570e3 Mon Sep 17 00:00:00 2001 From: oldosfan Date: Fri, 23 Sep 2022 08:44:37 +0000 Subject: [PATCH] Add support for EGL, for YUV image formats EGL support is still incomplete; stuff like scaling is missing. * 12to11.c (PickVisual, XLMain): Remove function. Initialize renderers, and rely on that to initialize the visuals. * 12to11.man: Document new environment variables. * Imakefile (SRCS, OBJS): Add picture_renderer.c and renderer.c and their associated objects. (GENHEADERS): New variable for generated headers. (EGL_SRCS, EGL_OBJS): New variables. (LOCAL_LIBRARIES, SRCS, OBJS, DEFINES): [HaveEglSupport]: Add EGL defines, libraries, and objects.:(shaders.h): New rule. (depend, cleandir): Depend on generated headers and SRCs. $($(OBJS)): Depend on $(GENHEADERS). * README: Write how to build with EGL support and update content descriptions. * buffer.c (XLPictureFromBuffer): Delete function. (XLRenderBufferFromBuffer): New function. (XLPixmapFromBuffer): Delete function. * compositor.h (enum _Operation, struct _SharedMemoryAttributes) (struct _DmaBufAttributes, union _RenderTarget) (union _RenderBuffer, struct _RenderFuncs, struct _DrmFormat) (struct _ShmFormat, struct _BufferFuncs, struct _ExtBufferFuncs): New structures. (Fallthrough): New define. Define to __attribute__ ((fallthrough)) wherever supported. Replace uses of Picture with RenderBuffer. * dmabuf.c (struct _DrmFormatInfo): Remove structures. (struct _BufferParams): Remove link. (struct _Buffer): Replace pixmap and picture with render buffer object. (dri3_opcode, all_formats, pending_success, next_roundtrip_id) (round_trip_window): Delete. (ReleaseBufferParams, HandleParamsResourceDestroy, ForceRoundTrip) (DepthForFormat, XLHandleErrorForDmabuf, PictFormatForFormat) (DestroyBacking, GetPictureFunc, GetPixmapFunc, GetBufferFunc) (CreateBufferFor, ModifierHigh, FinishBufferCreation) (XLHandleOneXEventForDmabuf, CreateHeader, Create, CreateImmed) (FindFormatMatching, FindSupportedModifiers, FindSupportedFormats) (SendSupportedFormats, HandleBind, InitDrmDevice, WriteFormatTable) (ReallyInitDmabuf, ReadSupportedFormats, XLInitDmabuf): Remove various functions and reimplement as a wrapper around the renderer abstraction, instead of DRI3. * fns.c (struct _Busfault): New structure. (busfault_tree, bus_handler_installed): New variables. (GetHeight, FixHeights, RotateLeft, RotateRight, RebalanceBusfault) (RecordBusfault, DetectBusfault, DeleteMin, RemoveBusfault) (HandleBusfault, MaybeInstallBusHandler, BlockSigbus) (UnblockSigbus, XLRecordBusfault, XLRemoveBusfaults): New functions to store non-overlapping regions of mapped memory in an AVL tree, and to ignore SIGBUS events that trap within. * icon_surface.c (struct _IconSurface): Replace picture with rendering target. (ReleaseBacking, XLGetIconSurface): Use rendering targets instead of pictures. * libraries.def: Add defines for EGL. * run.c (HandleOneXEvent): Replace old dmabuf code with renderer code. * seat.c (CursorFromRole, PictureForCursor, ApplyCursor) (UpdateCursorFromSubcompositor, Subframe, EndSubframe): Rewrite cursor code to use rendering targets and not pictures. * shm.c (Buffer, DereferencePool): Remove SIGBUS protection. (DereferenceBuffer): Reimplement in terms of renderer functions. (GetPictureFunc): Delete function. (GetBufferFunc): New function. (GetPixmapFunc): Delete function. (PrintBuffer): Remove now-broken implementation. (DepthForFormat, PictFormatForFormat): Delete functions, as they are now part of the rendering backend. (IsFormatSupported): New function. (CreateBuffer): Reimplement in terms of renderer functions. (ResizePool): Change SIGBUS protection for resize. (CreatePool): Protect buffer data from SIGBUS should a client truncate the file. (PostFormats): Reimplement in terms of RenderGetShmFormats. (XLInitShm): Stop initializing MIT-SHM. * subcompositor.c (IsTargetAttached): New flag and associated macros. (struct _View): Remove useless field. (struct _Subcompositor): Change target to RenderTarget. Add fields for prior damage. (MakeSubcompositor): Initialize new fields. (SubcompositorSetTarget): Accept RenderTarget, not picture, and set target attached flag. (ViewGetTransform): Delete function. (ViewApplyTransform): New function. (FillBoxesWithTransparency): Reimplement in terms of renderer functions. (StorePreviousDamage): New function. (SubcompositorUpdate): Reimplement in terms of renderer functions. Also, learn to deal with situations where the buffer reflects the state 1 to 2 swaps ago. (SubcompositorExpose): Reimplement in terms of renderer functions. (SubcompositorFree): Free new fields. (SubcompositorInit): Remove no longer required initialization of identity matrix. * xdata.c (ReceiveBody): Fix coding style. * xdg_surface.c (struct _XdgRole, ReleaseBacking, XLGetXdgSurface): Switch from pictures to RenderTargets. * xerror.c (ErrorHandler): Handle errors for picture renderer instead of dmabuf.c. --- 12to11.c | 40 +- 12to11.man | 12 + Imakefile | 37 +- README | 24 + buffer.c | 13 +- compositor.h | 298 +++++++++++- dmabuf.c | 1171 +++++++++++++++++------------------------------ fns.c | 326 +++++++++++++ icon_surface.c | 18 +- libraries.def | 9 + run.c | 5 + seat.c | 70 ++- shm.c | 244 ++++------ subcompositor.c | 496 +++++++++++++------- xdata.c | 2 +- xdg_surface.c | 17 +- xerror.c | 5 + 17 files changed, 1606 insertions(+), 1181 deletions(-) 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