Implement buffer transforms on the XRender backend

* 12to11.conf (XSHMFENCELIB): New library.
* 12to11.man: Document that surface transforms are now
supported.
* Imakefile (LOCAL_LIBRARIES): Add XSHMFENCELIB.
(SRCS, OBJS): Add fence_ring.c and fence_ring.o.
* README: Require xshmfence.
* buffer.c: Include stdio.h.
* compositor.h (enum _BufferTransform): New enum.
(struct _DrawParams): Add TransformSet.
(RotatesDimensions): New macro.
(struct _State): Add buffer transform.
* egl.c: Add TODO for buffer transforms.
* fns.c (TransformBox, XLTransformRegion): New functions.
* picture_renderer.c (struct _BufferActivityRecord): New field
`fence'.
(struct _PictureBuffer): Add buffer width and height.
(struct _DmaBufRecord, RecordBufferActivity, UnlinkActivityRecord)
(DestroyRenderTarget, GetBufferTransform, MaybeApplyTransform)
(Composite, FinishRender, BufferFromDmaBuf, FinishDmaBufRecord)
(BufferFromDmaBufAsync, BufferFromShm, FreeAnyBuffer): Record
width and height, and handle transforms; add some disabled
fencing code.
* subcompositor.c (struct _View): Add transform.  Rearrange
fields for alignment.
(MakeView): Initialize transform.
(InvertTransform): New function.
(BufferWidthAfterTransform, BufferHeightAfterTransform)
(TransformBufferDamage): New functions.
(ViewDamageBuffer): Transform buffer damage if necessary.
(ViewWidth, ViewHeight): Use width and height after transform.
(ViewComputeTransform): Apply transform.
* surface.c (ApplyBufferTransform): New function.
(ApplyViewport, CheckViewportValues, SavePendingState)
(InternalCommit, GetBufferTransform, SetBufferTransform,
InitState): Initialize and handle transforms.
(XLStateAttachBuffer, XLStateDetachBuffer): Delete unused
functions.
* time.c (InitTime): Require a newer version of the Sync
extension.
* transform.c (MatrixRotate, MatrixMirrorHorizontal): New
functions.
This commit is contained in:
hujianwei 2022-10-25 10:15:28 +00:00
parent 65a2e4de5b
commit 768f2d98b5
13 changed files with 596 additions and 64 deletions

View file

@ -14,6 +14,7 @@
PIXMANINCLUDES = -I$(INCROOT)/pixman-1 PIXMANINCLUDES = -I$(INCROOT)/pixman-1
XPRESENTLIB = -lXpresent XPRESENTLIB = -lXpresent
WAYLAND_SCANNER = wayland-scanner WAYLAND_SCANNER = wayland-scanner
XSHMFENCELIB = -lxshmfence
/* Uncomment the following code if building with EGL support. */ /* Uncomment the following code if building with EGL support. */

View file

@ -293,9 +293,6 @@ least support the
.B _NET_WM_SYNC_REQUEST .B _NET_WM_SYNC_REQUEST
window manager protocol will result in Wayland programs running badly. window manager protocol will result in Wayland programs running badly.
.PP .PP
In addition, surfaces transforms are not supported nor reported. The
vast majority of clients seem not to make use of this feature, and
implementing it would be a lot of trouble.
.SH "SEE ALSO" .SH "SEE ALSO"
X(7), Xorg(1) X(7), Xorg(1)
.SH AUTHORS .SH AUTHORS

View file

@ -11,7 +11,7 @@ DEPLIBS = $(DEPXLIB) $(DEPEXTENSIONLIB) $(DEPXRANDRLIB) $(DEPXRENDERLIB) \
LOCAL_LIBRARIES = $(XLIB) $(EXTENSIONLIB) $(XCBLIB) $(XCB) $(XCB_SHM) \ LOCAL_LIBRARIES = $(XLIB) $(EXTENSIONLIB) $(XCBLIB) $(XCB) $(XCB_SHM) \
$(XRANDRLIB) $(PIXMAN) $(XRENDERLIB) $(XILIB) $(XKBFILELIB) $(XFIXESLIB) \ $(XRANDRLIB) $(PIXMAN) $(XRENDERLIB) $(XILIB) $(XKBFILELIB) $(XFIXESLIB) \
$(XCB_DRI3) $(XCB_SHAPE) $(WAYLAND_SERVER) $(XCB_RANDR) $(DRM) \ $(XCB_DRI3) $(XCB_SHAPE) $(WAYLAND_SERVER) $(XCB_RANDR) $(DRM) \
$(XPRESENTLIB) $(XPRESENTLIB) $(XSHMFENCELIB)
INCLUDES := $(DRMINCLUDES) $(PIXMANINCLUDES) INCLUDES := $(DRMINCLUDES) $(PIXMANINCLUDES)
@ -25,7 +25,7 @@ SRCS = 12to11.c run.c alloc.c fns.c output.c compositor.c \
wp_viewporter.c decoration.c text_input.c \ wp_viewporter.c decoration.c text_input.c \
single_pixel_buffer.c drm_lease.c pointer_constraints.c \ single_pixel_buffer.c drm_lease.c pointer_constraints.c \
time.c relative_pointer.c keyboard_shortcuts_inhibit.c \ time.c relative_pointer.c keyboard_shortcuts_inhibit.c \
idle_inhibit.c process.c idle_inhibit.c process.c fence_ring.c
OBJS = 12to11.o run.o alloc.o fns.o output.o compositor.o \ OBJS = 12to11.o run.o alloc.o fns.o output.o compositor.o \
surface.o region.o shm.o atoms.o subcompositor.o positioner.o \ surface.o region.o shm.o atoms.o subcompositor.o positioner.o \
@ -37,7 +37,7 @@ OBJS = 12to11.o run.o alloc.o fns.o output.o compositor.o \
wp_viewporter.o decoration.o text_input.o \ wp_viewporter.o decoration.o text_input.o \
single_pixel_buffer.o drm_lease.o pointer_constraints.o \ single_pixel_buffer.o drm_lease.o pointer_constraints.o \
time.o relative_pointer.o keyboard_shortcuts_inhibit.o \ time.o relative_pointer.o keyboard_shortcuts_inhibit.o \
idle_inhibit.o process.o idle_inhibit.o process.o fence_ring.o
GENHEADERS = transfer_atoms.h GENHEADERS = transfer_atoms.h

4
README
View file

@ -98,8 +98,8 @@ This directory is organized as follows:
those containing MIME types or shaders those containing MIME types or shaders
Building the source code is simple, provided that you have the Building the source code is simple, provided that you have the
necessary libwayland-server library, pixman, XCB, DRM, and X extension necessary libwayland-server library, pixman, XCB, DRM, xshmfence, and
libraries installed: X extension libraries installed:
xmkmf # to generate the Makefile xmkmf # to generate the Makefile
make # to build the binary make # to build the binary

View file

@ -17,6 +17,8 @@ for more details.
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with 12to11. If not, see <https://www.gnu.org/licenses/>. */ along with 12to11. If not, see <https://www.gnu.org/licenses/>. */
#include <stdio.h>
#include "compositor.h" #include "compositor.h"
typedef struct _DestroyListener DestroyListener; typedef struct _DestroyListener DestroyListener;

View file

@ -157,6 +157,7 @@ typedef struct _DrmFormat DrmFormat;
typedef struct _ShmFormat ShmFormat; typedef struct _ShmFormat ShmFormat;
typedef enum _Operation Operation; typedef enum _Operation Operation;
typedef enum _BufferTransform BufferTransform;
typedef void *IdleCallbackKey; typedef void *IdleCallbackKey;
typedef void *PresentCompletionKey; typedef void *PresentCompletionKey;
@ -167,6 +168,18 @@ typedef void (*DmaBufFailureFunc) (void *);
typedef void (*BufferIdleFunc) (RenderBuffer, void *); typedef void (*BufferIdleFunc) (RenderBuffer, void *);
typedef void (*PresentCompletionFunc) (void *); typedef void (*PresentCompletionFunc) (void *);
enum _BufferTransform
{
Normal,
CounterClockwise90,
CounterClockwise180,
CounterClockwise270,
Flipped,
Flipped90,
Flipped180,
Flipped270,
};
enum _Operation enum _Operation
{ {
OperationOver, OperationOver,
@ -176,20 +189,27 @@ enum _Operation
enum enum
{ {
/* Scale has been set. */ /* Scale has been set. */
ScaleSet = 1, ScaleSet = 1,
/* Transform has been set. */
TransformSet = (1 << 1),
/* Offset has been set. */ /* Offset has been set. */
OffsetSet = (1 << 2), OffsetSet = (1 << 2),
/* Stretch has been set. */ /* Stretch has been set. */
StretchSet = (1 << 3), StretchSet = (1 << 3),
}; };
/* The transforms specified in the draw parameters are applied in the
following order: transform, scale, off_x, off_y, crop_width,
crop_height, stretch_width, stretch_height. */
struct _DrawParams struct _DrawParams
{ {
/* Which fields are set. */ /* Which fields are set. */
int flags; int flags;
/* A transform by which to rotate the buffer. */
BufferTransform transform;
/* A scale factor to apply to the buffer. */ /* A scale factor to apply to the buffer. */
double scale; double scale;
@ -643,6 +663,8 @@ extern void XLScaleRegion (pixman_region32_t *, pixman_region32_t *,
float, float); float, float);
extern void XLExtendRegion (pixman_region32_t *, pixman_region32_t *, extern void XLExtendRegion (pixman_region32_t *, pixman_region32_t *,
int, int); int, int);
extern void XLTransformRegion (pixman_region32_t *, pixman_region32_t *,
BufferTransform, int, int);
extern Time XLGetServerTimeRoundtrip (void); extern Time XLGetServerTimeRoundtrip (void);
extern RootWindowSelection *XLSelectInputFromRootWindow (unsigned long); extern RootWindowSelection *XLSelectInputFromRootWindow (unsigned long);
@ -786,6 +808,7 @@ extern void ViewSkip (View *);
extern void ViewUnskip (View *); extern void ViewUnskip (View *);
extern void ViewMoveFractional (View *, double, double); extern void ViewMoveFractional (View *, double, double);
extern void ViewSetTransform (View *, BufferTransform);
extern void ViewSetViewport (View *, double, double, double, double, extern void ViewSetViewport (View *, double, double, double, double,
double, double); double, double);
extern void ViewClearViewport (View *); extern void ViewClearViewport (View *);
@ -837,19 +860,26 @@ enum _RoleType
DndIconType, DndIconType,
}; };
#define RotatesDimensions(transform) \
((transform) == CounterClockwise90 \
|| (transform) == CounterClockwise270 \
|| (transform) == Flipped90 \
|| (transform) == Flipped270)
enum enum
{ {
PendingNone = 0, PendingNone = 0,
PendingOpaqueRegion = 1, PendingOpaqueRegion = 1,
PendingInputRegion = (1 << 2), PendingInputRegion = (1 << 2),
PendingDamage = (1 << 3), PendingDamage = (1 << 3),
PendingSurfaceDamage = (1 << 4), PendingSurfaceDamage = (1 << 4),
PendingBuffer = (1 << 5), PendingBuffer = (1 << 5),
PendingFrameCallbacks = (1 << 6), PendingFrameCallbacks = (1 << 6),
PendingBufferScale = (1 << 7), PendingBufferScale = (1 << 7),
PendingAttachments = (1 << 8), PendingAttachments = (1 << 8),
PendingViewportSrc = (1 << 9), PendingViewportSrc = (1 << 9),
PendingViewportDest = (1 << 10), PendingViewportDest = (1 << 10),
PendingBufferTransform = (1 << 11),
/* Flags here are stored in `pending' of the current state for /* Flags here are stored in `pending' of the current state for
space reasons. */ space reasons. */
@ -889,6 +919,9 @@ struct _State
/* The scale of this buffer. */ /* The scale of this buffer. */
int buffer_scale; int buffer_scale;
/* The buffer transform. */
BufferTransform transform;
/* List of frame callbacks. */ /* List of frame callbacks. */
FrameCallback frame_callbacks; FrameCallback frame_callbacks;
@ -1109,8 +1142,6 @@ extern void XLSurfaceReleaseRole (Surface *, Role *);
extern void XLDefaultCommit (Surface *); extern void XLDefaultCommit (Surface *);
extern void XLStateAttachBuffer (State *, ExtBuffer *);
extern void XLStateDetachBuffer (State *);
extern void XLSurfaceRunFrameCallbacks (Surface *, struct timespec); extern void XLSurfaceRunFrameCallbacks (Surface *, struct timespec);
extern void XLSurfaceRunFrameCallbacksMs (Surface *, uint32_t); extern void XLSurfaceRunFrameCallbacksMs (Surface *, uint32_t);
extern CommitCallback *XLSurfaceRunAtCommit (Surface *, extern CommitCallback *XLSurfaceRunAtCommit (Surface *,
@ -1661,6 +1692,8 @@ extern void MatrixIdentity (Matrix *);
extern void MatrixTranslate (Matrix *, float, float); extern void MatrixTranslate (Matrix *, float, float);
extern void MatrixScale (Matrix *, float, float); extern void MatrixScale (Matrix *, float, float);
extern void MatrixExport (Matrix *, XTransform *); extern void MatrixExport (Matrix *, XTransform *);
extern void MatrixRotate (Matrix *, float, float, float);
extern void MatrixMirrorHorizontal (Matrix *, float);
/* Defined in wp_viewporter.c. */ /* Defined in wp_viewporter.c. */
@ -1717,6 +1750,15 @@ extern void RunProcess (ProcessQueue *, char **);
extern ProcessQueue *MakeProcessQueue (void); extern ProcessQueue *MakeProcessQueue (void);
extern int ProcessPoll (struct pollfd *, nfds_t, struct timespec *); extern int ProcessPoll (struct pollfd *, nfds_t, struct timespec *);
/* Defined in fence_ring.c. */
typedef struct _Fence Fence;
extern Fence *GetFence (void);
extern void FenceRetain (Fence *);
extern void FenceAwait (Fence *);
extern XSyncFence FenceToXFence (Fence *);
/* Utility functions that don't belong in a specific file. */ /* Utility functions that don't belong in a specific file. */
#define ArrayElements(arr) (sizeof (arr) / sizeof (arr)[0]) #define ArrayElements(arr) (sizeof (arr) / sizeof (arr)[0])

2
egl.c
View file

@ -35,6 +35,8 @@ along with 12to11. If not, see <https://www.gnu.org/licenses/>. */
#include "linux-dmabuf-unstable-v1.h" #include "linux-dmabuf-unstable-v1.h"
/* TODO: implement buffer transforms. */
/* These are flags for the DrmFormats. */ /* These are flags for the DrmFormats. */
enum enum

95
fns.c
View file

@ -403,6 +403,101 @@ XLExtendRegion (pixman_region32_t *dst, pixman_region32_t *src,
XLFree (dst_rects); XLFree (dst_rects);
} }
static void
TransformBox (pixman_box32_t *box, BufferTransform transform,
int width, int height)
{
pixman_box32_t work;
switch (transform)
{
case Normal:
work = *box;
break;
case CounterClockwise90:
work.x1 = height - box->y2;
work.y1 = box->x1;
work.x2 = height - box->y1;
work.y2 = box->x2;
break;
case CounterClockwise180:
work.x1 = width - box->x2;
work.y1 = height - box->y2;
work.x2 = width - box->x1;
work.y2 = height - box->y1;
break;
case CounterClockwise270:
work.x1 = box->y1;
work.y1 = width - box->x2;
work.x2 = box->y2;
work.y2 = width - box->x1;
break;
case Flipped:
work.x1 = width - box->x2;
work.y1 = box->y1;
work.x2 = width - box->x1;
work.y2 = box->y2;
break;
case Flipped90:
work.x1 = box->y1;
work.y1 = box->x1;
work.x2 = box->y2;
work.y2 = box->x2;
break;
case Flipped180:
work.x1 = box->x1;
work.y1 = height - box->y2;
work.x2 = box->x2;
work.y2 = height - box->y1;
break;
case Flipped270:
work.x1 = height - box->y2;
work.y1 = width - box->x2;
work.x2 = height - box->y1;
work.y2 = width - box->x1;
break;
}
*box = work;
}
void
XLTransformRegion (pixman_region32_t *dst, pixman_region32_t *src,
BufferTransform transform, int width, int height)
{
int nrects, i;
pixman_box32_t *src_rects;
pixman_box32_t *dst_rects;
src_rects = pixman_region32_rectangles (src, &nrects);
if (nrects < 128)
dst_rects = alloca (nrects * sizeof *dst_rects);
else
dst_rects = XLMalloc (nrects * sizeof *dst_rects);
for (i = 0; i < nrects; ++i)
{
dst_rects[i] = src_rects[i];
/* width and height should be in the transformed space! */
TransformBox (&dst_rects[i], transform, width, height);
}
pixman_region32_fini (dst);
pixman_region32_init_rects (dst, dst_rects, nrects);
if (nrects >= 128)
XLFree (dst_rects);
}
int int
XLOpenShm (void) XLOpenShm (void)
{ {

View file

@ -81,6 +81,9 @@ struct _BufferActivityRecord
/* The counter ID. */ /* The counter ID. */
uint64_t id; uint64_t id;
/* The sync fence. */
Fence *fence;
/* The forward links to the three lists. */ /* The forward links to the three lists. */
BufferActivityRecord *buffer_next, *target_next, *global_next; BufferActivityRecord *buffer_next, *target_next, *global_next;
@ -127,6 +130,9 @@ struct _PictureBuffer
/* Flags. */ /* Flags. */
int flags; int flags;
/* The width and height of the buffer. */
short width, height;
/* The last draw params associated with the picture. */ /* The last draw params associated with the picture. */
DrawParams params; DrawParams params;
@ -138,6 +144,8 @@ struct _PictureBuffer
/* Ongoing buffer activity. */ /* Ongoing buffer activity. */
BufferActivityRecord activity; BufferActivityRecord activity;
int compositable;
}; };
enum enum
@ -264,6 +272,9 @@ struct _DmaBufRecord
/* The depth of the pixmap. */ /* The depth of the pixmap. */
int depth; int depth;
/* The width and height. */
short width, height;
}; };
/* Hash table mapping between presentation windows and targets. */ /* Hash table mapping between presentation windows and targets. */
@ -388,6 +399,9 @@ static PresentCompletionCallback all_completion_callbacks;
/* Whether or not direct presentation should be used. */ /* Whether or not direct presentation should be used. */
static Bool use_direct_presentation; static Bool use_direct_presentation;
/* Whether or not to use sync fences. Currently never set. */
static Bool use_sync_fences;
/* XRender, DRI3 and XPresent-based renderer. A RenderTarget is just /* XRender, DRI3 and XPresent-based renderer. A RenderTarget is just
a Picture. Here is a rough explanation of how the buffer release a Picture. Here is a rough explanation of how the buffer release
machinery works. machinery works.
@ -489,7 +503,7 @@ FindBufferActivityRecord (PictureBuffer *buffer, PictureTarget *target)
static void static void
RecordBufferActivity (PictureBuffer *buffer, PictureTarget *target, RecordBufferActivity (PictureBuffer *buffer, PictureTarget *target,
uint64_t roundtrip_id) uint64_t roundtrip_id, Fence *fence)
{ {
BufferActivityRecord *record; BufferActivityRecord *record;
@ -530,7 +544,23 @@ RecordBufferActivity (PictureBuffer *buffer, PictureTarget *target,
record->target = target; record->target = target;
} }
/* If there is already a fence, wait for it to complete. */
if (record->fence)
{
/* Flush any trigger fence request that might have been
made. */
XFlush (compositor.display);
/* Start waiting. */
FenceAwait (record->fence);
}
if (fence)
/* Retain the fence. */
FenceRetain (fence);
record->id = roundtrip_id; record->id = roundtrip_id;
record->fence = fence;
} }
static void static void
@ -599,6 +629,13 @@ MaybeRunIdleCallbacks (PictureBuffer *buffer, PictureTarget *target)
static void static void
UnlinkActivityRecord (BufferActivityRecord *record) UnlinkActivityRecord (BufferActivityRecord *record)
{ {
/* Wait for the sync fence. */
if (record->fence)
FenceAwait (record->fence);
record->fence = NULL;
record->buffer_last->buffer_next = record->buffer_next; record->buffer_last->buffer_next = record->buffer_next;
record->buffer_next->buffer_last = record->buffer_last; record->buffer_next->buffer_last = record->buffer_last;
record->target_last->target_next = record->target_next; record->target_last->target_next = record->target_next;
@ -943,6 +980,11 @@ DestroyRenderTarget (RenderTarget target)
activity_last = activity_record; activity_last = activity_record;
activity_record = activity_record->target_next; activity_record = activity_record->target_next;
if (activity_last->fence)
/* The TriggerFence request might not have been flushed. So
flush it now to avoid hangs. */
XFlush (compositor.display);
UnlinkActivityRecord (activity_last); UnlinkActivityRecord (activity_last);
XLFree (activity_last); XLFree (activity_last);
} }
@ -1099,6 +1141,15 @@ GetSourceY (DrawParams *params)
return 0.0; return 0.0;
} }
static BufferTransform
GetBufferTransform (DrawParams *params)
{
if (params->flags & TransformSet)
return params->transform;
return Normal;
}
static Bool static Bool
CompareStretch (DrawParams *params, DrawParams *other) CompareStretch (DrawParams *params, DrawParams *other)
{ {
@ -1115,6 +1166,82 @@ CompareStretch (DrawParams *params, DrawParams *other)
return True; return True;
} }
static void
ApplyInverseTransform (PictureBuffer *buffer, Matrix *matrix,
BufferTransform transform)
{
float width, height;
/* Note that the transform is applied in reverse, meaning that a
counterclockwise rotation is done clockwise, etc, as TRANSFORM
transforms destination coordinates to source ones. */
width = buffer->width;
height = buffer->height;
switch (transform)
{
case Normal:
break;
case CounterClockwise90:
/* Apply clockwise 270 degree rotation around the origin. */
MatrixRotate (matrix, M_PI * 1.5, 0, 0);
/* Translate y by the width. */
MatrixTranslate (matrix, 0, -width);
break;
case CounterClockwise180:
/* Apply clockwise 180 degree rotation around the center. */
MatrixRotate (matrix, M_PI, width / 2.0f, height / 2.0f);
break;
case CounterClockwise270:
/* Apply clockwise 90 degree rotation around the origin. */
MatrixRotate (matrix, M_PI * 0.5, 0, 0);
/* Translate by the height. */
MatrixTranslate (matrix, -height, 0);
break;
case Flipped:
/* Apply horizontal flip. */
MatrixMirrorHorizontal (matrix, width);
break;
case Flipped90:
/* Apply horizontal flip. */
MatrixMirrorHorizontal (matrix, width);
/* Apply clockwise 90 degree rotation around the origin. */
MatrixRotate (matrix, M_PI * 0.5, 0, 0);
/* Translate by the height. */
MatrixTranslate (matrix, -height, 0);
break;
case Flipped180:
/* Apply horizontal flip. */
MatrixMirrorHorizontal (matrix, width);
/* Apply clockwise 180 degree rotation around the center. */
MatrixRotate (matrix, M_PI, width / 2.0f, height / 2.0f);
break;
case Flipped270:
/* Apply horizontal flip. */
MatrixMirrorHorizontal (matrix, width);
/* Apply clockwise 270 degree rotation around the origin. */
MatrixRotate (matrix, M_PI * 1.5, 0, 0);
/* Translate y by the width. */
MatrixTranslate (matrix, 0, -width);
break;
}
}
static void static void
MaybeApplyTransform (PictureBuffer *buffer, DrawParams *params) MaybeApplyTransform (PictureBuffer *buffer, DrawParams *params)
{ {
@ -1124,6 +1251,8 @@ MaybeApplyTransform (PictureBuffer *buffer, DrawParams *params)
if (GetScale (params) == GetScale (&buffer->params) if (GetScale (params) == GetScale (&buffer->params)
&& GetSourceX (params) == GetSourceX (&buffer->params) && GetSourceX (params) == GetSourceX (&buffer->params)
&& GetSourceY (params) == GetSourceY (&buffer->params) && GetSourceY (params) == GetSourceY (&buffer->params)
&& (GetBufferTransform (params)
== GetBufferTransform (&buffer->params))
&& CompareStretch (params, &buffer->params)) && CompareStretch (params, &buffer->params))
/* Nothing changed. */ /* Nothing changed. */
return; return;
@ -1138,6 +1267,10 @@ MaybeApplyTransform (PictureBuffer *buffer, DrawParams *params)
{ {
MatrixIdentity (&ftransform); MatrixIdentity (&ftransform);
if (params->flags & TransformSet)
ApplyInverseTransform (buffer, &ftransform,
params->transform);
/* Note that these must be applied in the right order. First, /* Note that these must be applied in the right order. First,
the scale is applied. Then, the offset, and finally the the scale is applied. Then, the offset, and finally the
stretch. */ stretch. */
@ -1173,6 +1306,7 @@ Composite (RenderBuffer buffer, RenderTarget target,
{ {
PictureBuffer *picture_buffer; PictureBuffer *picture_buffer;
PictureTarget *picture_target; PictureTarget *picture_target;
XLList *tem;
picture_buffer = buffer.pointer; picture_buffer = buffer.pointer;
picture_target = target.pointer; picture_target = target.pointer;
@ -1191,8 +1325,19 @@ Composite (RenderBuffer buffer, RenderTarget target,
/* dst-x, dst-y, width, height. */ /* dst-x, dst-y, width, height. */
x, y, width, height); x, y, width, height);
for (tem = picture_target->buffers_used; tem; tem = tem->next)
{
/* Return if the buffer is already in the buffers_used list.
Otherwise, FinishRender will try to wait for a fence that has
not yet been triggered. */
if (tem->data == picture_buffer)
return;
}
/* Record pending buffer activity; the roundtrip message is then /* Record pending buffer activity; the roundtrip message is then
sent later. */ sent later. */
picture_target->buffers_used picture_target->buffers_used
= XLListPrepend (picture_target->buffers_used, picture_buffer); = XLListPrepend (picture_target->buffers_used, picture_buffer);
} }
@ -1203,22 +1348,41 @@ FinishRender (RenderTarget target, pixman_region32_t *damage)
XLList *tem, *last; XLList *tem, *last;
PictureTarget *pict_target; PictureTarget *pict_target;
uint64_t roundtrip_id; uint64_t roundtrip_id;
Fence *fence;
pict_target = target.pointer;
if (!pict_target->buffers_used)
/* No buffers were used. */
return;
/* Finish rendering. This function then sends a single roundtrip /* Finish rendering. This function then sends a single roundtrip
message and records buffer activity for each buffer involved in message and records buffer activity for each buffer involved in
the update based on that. */ the update based on that. */
roundtrip_id = SendRoundtripMessage (); roundtrip_id = SendRoundtripMessage ();
pict_target = target.pointer;
tem = pict_target->buffers_used; tem = pict_target->buffers_used;
if (use_sync_fences)
{
/* Get a sync fence, then trigger it. */
fence = GetFence ();
/* Trigger the fence. */
XSyncTriggerFence (compositor.display, FenceToXFence (fence));
}
else
fence = NULL;
while (tem) while (tem)
{ {
last = tem; last = tem;
tem = tem->next; tem = tem->next;
/* Record buffer activity on this one buffer. */ /* Record buffer activity on this one buffer. A reference to
the fence will be held. */
RecordBufferActivity (last->data, pict_target, RecordBufferActivity (last->data, pict_target,
roundtrip_id); roundtrip_id, fence);
/* Free the list element. */ /* Free the list element. */
XLFree (last); XLFree (last);
@ -1933,6 +2097,8 @@ BufferFromDmaBuf (DmaBufAttributes *attributes, Bool *error)
buffer->picture = picture; buffer->picture = picture;
buffer->pixmap = pixmap; buffer->pixmap = pixmap;
buffer->depth = depth; buffer->depth = depth;
buffer->width = attributes->width;
buffer->height = attributes->height;
/* Initialize the list of release records. */ /* Initialize the list of release records. */
buffer->pending.buffer_next = &buffer->pending; buffer->pending.buffer_next = &buffer->pending;
@ -2008,6 +2174,8 @@ FinishDmaBufRecord (DmaBufRecord *pending, Bool success)
buffer->picture = picture; buffer->picture = picture;
buffer->pixmap = pending->pixmap; buffer->pixmap = pending->pixmap;
buffer->depth = pending->depth; buffer->depth = pending->depth;
buffer->width = pending->width;
buffer->height = pending->height;
/* Initialize the list of release records. */ /* Initialize the list of release records. */
buffer->pending.buffer_next = &buffer->pending; buffer->pending.buffer_next = &buffer->pending;
@ -2110,6 +2278,9 @@ BufferFromDmaBufAsync (DmaBufAttributes *attributes,
= PictFormatForDmabufFormat (attributes->drm_format); = PictFormatForDmabufFormat (attributes->drm_format);
record->pixmap = pixmap; record->pixmap = pixmap;
record->depth = depth; record->depth = depth;
record->width = attributes->width;
record->height = attributes->height;
XLAssert (record->format != NULL); XLAssert (record->format != NULL);
record->next = pending_success.next; record->next = pending_success.next;
@ -2218,6 +2389,8 @@ BufferFromShm (SharedMemoryAttributes *attributes, Bool *error)
buffer->picture = picture; buffer->picture = picture;
buffer->pixmap = pixmap; buffer->pixmap = pixmap;
buffer->depth = depth; buffer->depth = depth;
buffer->width = attributes->width;
buffer->height = attributes->height;
/* Initialize the list of release records. */ /* Initialize the list of release records. */
buffer->pending.buffer_next = &buffer->pending; buffer->pending.buffer_next = &buffer->pending;
@ -2380,6 +2553,11 @@ FreeAnyBuffer (RenderBuffer buffer)
activity_last = activity_record; activity_last = activity_record;
activity_record = activity_record->buffer_next; activity_record = activity_record->buffer_next;
if (activity_last->fence)
/* The TriggerFence request might not have been flushed. So
flush it now to avoid hangs. */
XFlush (compositor.display);
UnlinkActivityRecord (activity_last); UnlinkActivityRecord (activity_last);
XLFree (activity_last); XLFree (activity_last);
} }

View file

@ -276,6 +276,10 @@ struct _View
/* Function called upon the view potentially being resized. */ /* Function called upon the view potentially being resized. */
void (*maybe_resized) (View *); void (*maybe_resized) (View *);
/* Some data associated with this view. Can be a surface or
something else. */
void *data;
/* The damaged and opaque regions. */ /* The damaged and opaque regions. */
pixman_region32_t damage, opaque; pixman_region32_t damage, opaque;
@ -289,16 +293,15 @@ struct _View
(or topmost parent if the view hierarchy is detached). */ (or topmost parent if the view hierarchy is detached). */
int abs_x, abs_y; int abs_x, abs_y;
/* Some data associated with this view. Can be a surface or
something else. */
void *data;
/* The scale of this view. */ /* The scale of this view. */
int scale; int scale;
/* Flags; whether or not this view is unmapped, etc. */ /* Flags; whether or not this view is unmapped, etc. */
int flags; int flags;
/* Any transform associated with this view. */
BufferTransform transform;
/* The viewport data. */ /* The viewport data. */
double src_x, src_y, crop_width, crop_height, dest_width, dest_height; double src_x, src_y, crop_width, crop_height, dest_width, dest_height;
@ -495,6 +498,8 @@ MakeView (void)
pixman_region32_init (&view->damage); pixman_region32_init (&view->damage);
pixman_region32_init (&view->opaque); pixman_region32_init (&view->opaque);
pixman_region32_init (&view->input); pixman_region32_init (&view->input);
view->transform = Normal;
#endif #endif
return view; return view;
@ -1627,6 +1632,60 @@ GetContentScale (int scale)
return -scale + 1; return -scale + 1;
} }
static BufferTransform
InvertTransform (BufferTransform transform)
{
switch (transform)
{
case CounterClockwise270:
return CounterClockwise90;
case CounterClockwise90:
return CounterClockwise270;
default:
return transform;
}
}
static int
BufferWidthAfterTransform (View *view)
{
if (RotatesDimensions (view->transform))
return XLBufferHeight (view->buffer);
return XLBufferWidth (view->buffer);
}
static int
BufferHeightAfterTransform (View *view)
{
if (RotatesDimensions (view->transform))
return XLBufferWidth (view->buffer);
return XLBufferHeight (view->buffer);
}
static void
TransformBufferDamage (pixman_region32_t *damage,
pixman_region32_t *source,
View *view)
{
int width, height;
BufferTransform inverse;
/* Invert the transform. */
inverse = InvertTransform (view->transform);
/* Calculate the width and height of the buffer after the
transform. */
width = XLBufferWidth (view->buffer);
height = XLBufferHeight (view->buffer);
/* Transform the damage. */
XLTransformRegion (damage, source, inverse, width, height);
}
void void
ViewDamageBuffer (View *view, pixman_region32_t *damage) ViewDamageBuffer (View *view, pixman_region32_t *damage)
{ {
@ -1638,9 +1697,10 @@ ViewDamageBuffer (View *view, pixman_region32_t *damage)
if (!view->buffer) if (!view->buffer)
return; return;
if (!view->scale && !IsViewported (view)) if (view->transform == Normal
/* There is no scale nor viewport. Just damage the view && !view->scale && !IsViewported (view))
directly. */ /* There is no scale, transform, nor viewport. Just damage the
view directly. */
ViewDamage (view, damage); ViewDamage (view, damage);
else else
{ {
@ -1651,8 +1711,17 @@ ViewDamageBuffer (View *view, pixman_region32_t *damage)
x_factor = GetContentScale (view->scale); x_factor = GetContentScale (view->scale);
y_factor = GetContentScale (view->scale); y_factor = GetContentScale (view->scale);
/* Scale the region. */ if (view->transform != Normal)
XLScaleRegion (&temp, damage, x_factor, y_factor); {
/* Transform the given buffer damage if need be. */
TransformBufferDamage (&temp, damage, view);
/* Scale the region. */
XLScaleRegion (&temp, &temp, x_factor, y_factor);
}
else
/* Scale the region. */
XLScaleRegion (&temp, damage, x_factor, y_factor);
/* Next, apply the viewport. */ /* Next, apply the viewport. */
if (IsViewported (view)) if (IsViewported (view))
@ -1671,9 +1740,9 @@ ViewDamageBuffer (View *view, pixman_region32_t *damage)
current buffer width/height. */ current buffer width/height. */
if (crop_width == -1) if (crop_width == -1)
{ {
crop_width = (XLBufferWidth (view->buffer) crop_width = (BufferWidthAfterTransform (view)
* GetContentScale (view->scale)); * GetContentScale (view->scale));
crop_height = (XLBufferHeight (view->buffer) crop_height = (BufferHeightAfterTransform (view)
* GetContentScale (view->scale)); * GetContentScale (view->scale));
} }
@ -1738,7 +1807,7 @@ ViewWidth (View *view)
scaling. */ scaling. */
return ceil (view->dest_width); return ceil (view->dest_width);
width = XLBufferWidth (view->buffer); width = BufferWidthAfterTransform (view);
if (view->scale < 0) if (view->scale < 0)
return ceil (width * (abs (view->scale) + 1)); return ceil (width * (abs (view->scale) + 1));
@ -1761,7 +1830,7 @@ ViewHeight (View *view)
scaling. */ scaling. */
return ceil (view->dest_height); return ceil (view->dest_height);
height = XLBufferHeight (view->buffer); height = BufferHeightAfterTransform (view);
if (view->scale < 0) if (view->scale < 0)
return ceil (height * (abs (view->scale) + 1)); return ceil (height * (abs (view->scale) + 1));
@ -1781,6 +1850,23 @@ ViewSetScale (View *view, int scale)
ViewAfterSizeUpdate (view); ViewAfterSizeUpdate (view);
} }
void
ViewSetTransform (View *view, BufferTransform transform)
{
BufferTransform old_transform;
if (view->transform == transform)
return;
old_transform = view->transform;
view->transform = transform;
if (RotatesDimensions (transform)
!= RotatesDimensions (old_transform))
/* Subcompositor bounds may have changed. */
ViewAfterSizeUpdate (view);
}
void void
ViewSetViewport (View *view, double src_x, double src_y, ViewSetViewport (View *view, double src_x, double src_y,
double crop_width, double crop_height, double crop_width, double crop_height,
@ -1829,6 +1915,12 @@ ViewComputeTransform (View *view, DrawParams *params, Bool draw)
params->off_x = 0.0; params->off_x = 0.0;
params->off_y = 0.0; params->off_y = 0.0;
if (view->transform != Normal)
{
params->flags |= TransformSet;
params->transform = view->transform;
}
if (view->scale) if (view->scale)
{ {
/* There is a scale, so set it. */ /* There is a scale, so set it. */
@ -1856,9 +1948,9 @@ ViewComputeTransform (View *view, DrawParams *params, Bool draw)
if (params->crop_width == -1) if (params->crop_width == -1)
{ {
params->crop_width = (XLBufferWidth (view->buffer) params->crop_width = (BufferWidthAfterTransform (view)
* GetContentScale (view->scale)); * GetContentScale (view->scale));
params->crop_height = (XLBufferHeight (view->buffer) params->crop_height = (BufferHeightAfterTransform (view)
* GetContentScale (view->scale)); * GetContentScale (view->scale));
} }
} }

103
surface.c
View file

@ -506,6 +506,13 @@ GetEffectiveScale (int scale)
return scale; return scale;
} }
static void
ApplyBufferTransform (Surface *surface)
{
ViewSetTransform (surface->view,
surface->current_state.transform);
}
static void static void
ApplyScale (Surface *surface) ApplyScale (Surface *surface)
{ {
@ -665,8 +672,12 @@ ApplyViewport (Surface *surface)
if (state->buffer) if (state->buffer)
{ {
max_width = XLBufferWidth (state->buffer); max_width = (RotatesDimensions (state->transform)
max_height = XLBufferHeight (state->buffer); ? XLBufferHeight (state->buffer)
: XLBufferWidth (state->buffer));
max_height = (RotatesDimensions (state->transform)
? XLBufferWidth (state->buffer)
: XLBufferHeight (state->buffer));
} }
else else
{ {
@ -787,8 +798,16 @@ CheckViewportValues (Surface *surface)
/* A buffer is attached and a viewport source rectangle is set; /* A buffer is attached and a viewport source rectangle is set;
check that it remains in bounds. */ check that it remains in bounds. */
width = XLBufferWidth (state->buffer); if (RotatesDimensions (state->transform))
height = XLBufferHeight (state->buffer); {
width = XLBufferHeight (state->buffer);
height = XLBufferWidth (state->buffer);
}
else
{
width = XLBufferWidth (state->buffer);
height = XLBufferHeight (state->buffer);
}
if (state->src_x + state->src_width - 1 >= width / state->buffer_scale if (state->src_x + state->src_width - 1 >= width / state->buffer_scale
|| state->src_y + state->src_height - 1 >= height / state->buffer_scale) || state->src_y + state->src_height - 1 >= height / state->buffer_scale)
@ -897,6 +916,9 @@ SavePendingState (Surface *surface)
surface->cached_state.buffer_scale surface->cached_state.buffer_scale
= surface->pending_state.buffer_scale; = surface->pending_state.buffer_scale;
if (surface->pending_state.pending & PendingBufferTransform)
surface->cached_state.transform = surface->pending_state.transform;
if (surface->pending_state.pending & PendingViewportDest) if (surface->pending_state.pending & PendingViewportDest)
{ {
surface->cached_state.dest_width surface->cached_state.dest_width
@ -1029,6 +1051,12 @@ InternalCommit (Surface *surface, State *pending)
ApplyScale (surface); ApplyScale (surface);
} }
if (pending->pending & PendingBufferTransform)
{
surface->current_state.transform = pending->transform;
ApplyBufferTransform (surface);
}
if (pending->pending & PendingInputRegion) if (pending->pending & PendingInputRegion)
{ {
pixman_region32_copy (&surface->current_state.input, pixman_region32_copy (&surface->current_state.input,
@ -1173,13 +1201,61 @@ Commit (struct wl_client *client, struct wl_resource *resource)
InternalCommit (surface, &surface->pending_state); InternalCommit (surface, &surface->pending_state);
} }
static Bool
GetBufferTransform (int32_t wayland_transform,
BufferTransform *transform)
{
switch (wayland_transform)
{
case WL_OUTPUT_TRANSFORM_NORMAL:
*transform = Normal;
return True;
case WL_OUTPUT_TRANSFORM_90:
*transform = CounterClockwise90;
return True;
case WL_OUTPUT_TRANSFORM_180:
*transform = CounterClockwise180;
return True;
case WL_OUTPUT_TRANSFORM_270:
*transform = CounterClockwise270;
return True;
case WL_OUTPUT_TRANSFORM_FLIPPED:
*transform = Flipped;
return True;
case WL_OUTPUT_TRANSFORM_FLIPPED_90:
*transform = Flipped90;
return True;
case WL_OUTPUT_TRANSFORM_FLIPPED_180:
*transform = Flipped180;
return True;
case WL_OUTPUT_TRANSFORM_FLIPPED_270:
*transform = Flipped270;
return True;
}
return False;
}
static void static void
SetBufferTransform (struct wl_client *client, struct wl_resource *resource, SetBufferTransform (struct wl_client *client, struct wl_resource *resource,
int32_t transform) int32_t transform)
{ {
if (transform != WL_OUTPUT_TRANSFORM_NORMAL) Surface *surface;
wl_resource_post_error (resource, WL_DISPLAY_ERROR_IMPLEMENTATION,
"this compositor does not support buffer transforms"); surface = wl_resource_get_user_data (resource);
if (!GetBufferTransform (transform, &surface->pending_state.transform))
wl_resource_post_error (resource, WL_SURFACE_ERROR_INVALID_TRANSFORM,
"invalid transform specified");
else
surface->pending_state.pending |= PendingBufferTransform;
} }
static void static void
@ -1249,6 +1325,7 @@ InitState (State *state)
state->pending = PendingNone; state->pending = PendingNone;
state->buffer = NULL; state->buffer = NULL;
state->buffer_scale = 1; state->buffer_scale = 1;
state->transform = Normal;
/* Initialize the sentinel node. */ /* Initialize the sentinel node. */
state->frame_callbacks.next = &state->frame_callbacks; state->frame_callbacks.next = &state->frame_callbacks;
@ -1508,18 +1585,6 @@ XLSurfaceReleaseRole (Surface *surface, Role *role)
RunUnmapCallbacks (surface); RunUnmapCallbacks (surface);
} }
void
XLStateAttachBuffer (State *state, ExtBuffer *buffer)
{
AttachBuffer (state, buffer);
}
void
XLStateDetachBuffer (State *state)
{
ClearBuffer (state);
}
/* Various other functions exported for roles. */ /* Various other functions exported for roles. */

6
time.c
View file

@ -303,6 +303,12 @@ InitTime (void)
supported = XSyncInitialize (compositor.display, supported = XSyncInitialize (compositor.display,
&xsync_major, &xsync_minor); &xsync_major, &xsync_minor);
if (xsync_major < 3 || (xsync_major == 3 && xsync_minor < 1))
{
fprintf (stderr, "Sync fences are not supported by this X server\n");
exit (1);
}
if (!supported) if (!supported)
{ {
fprintf (stderr, "A compatible version of the synchronization" fprintf (stderr, "A compatible version of the synchronization"

View file

@ -132,6 +132,58 @@ MatrixScale (Matrix *transform, float sx, float sy)
MatrixMultiply (copy, temp, transform); MatrixMultiply (copy, temp, transform);
} }
void
MatrixRotate (Matrix *transform, float theta, float x, float y)
{
Matrix temp, copy;
/* Translate the matrix to x, y, and then perform rotation by the
given angle in radians and translate back. As the transform is
being performed in the X coordinate system, the given angle
describes a clockwise rotation. */
MatrixIdentity (&temp);
memcpy (copy, transform, sizeof copy);
Index (temp, 0, 2) = x;
Index (temp, 1, 2) = y;
MatrixMultiply (copy, temp, transform);
MatrixIdentity (&temp);
memcpy (copy, transform, sizeof copy);
Index (temp, 0, 0) = cosf (theta);
Index (temp, 0, 1) = sinf (theta);
Index (temp, 1, 0) = -sinf (theta);
Index (temp, 1, 1) = cosf (theta);
MatrixMultiply (copy, temp, transform);
MatrixIdentity (&temp);
memcpy (copy, transform, sizeof copy);
Index (temp, 0, 2) = -x;
Index (temp, 1, 2) = -y;
MatrixMultiply (copy, temp, transform);
}
void
MatrixMirrorHorizontal (Matrix *transform, float width)
{
Matrix temp, copy;
/* Scale the matrix by -1, and then apply a tx of width, in effect
flipping the image horizontally. */
MatrixIdentity (&temp);
memcpy (copy, transform, sizeof copy);
Index (temp, 0, 0) = -1.0f;
Index (temp, 0, 2) = width;
MatrixMultiply (copy, temp, transform);
}
void void
MatrixExport (Matrix *transform, XTransform *xtransform) MatrixExport (Matrix *transform, XTransform *xtransform)
{ {