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
XPRESENTLIB = -lXpresent
WAYLAND_SCANNER = wayland-scanner
XSHMFENCELIB = -lxshmfence
/* Uncomment the following code if building with EGL support. */

View file

@ -293,9 +293,6 @@ least support the
.B _NET_WM_SYNC_REQUEST
window manager protocol will result in Wayland programs running badly.
.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"
X(7), Xorg(1)
.SH AUTHORS

View file

@ -11,7 +11,7 @@ DEPLIBS = $(DEPXLIB) $(DEPEXTENSIONLIB) $(DEPXRANDRLIB) $(DEPXRENDERLIB) \
LOCAL_LIBRARIES = $(XLIB) $(EXTENSIONLIB) $(XCBLIB) $(XCB) $(XCB_SHM) \
$(XRANDRLIB) $(PIXMAN) $(XRENDERLIB) $(XILIB) $(XKBFILELIB) $(XFIXESLIB) \
$(XCB_DRI3) $(XCB_SHAPE) $(WAYLAND_SERVER) $(XCB_RANDR) $(DRM) \
$(XPRESENTLIB)
$(XPRESENTLIB) $(XSHMFENCELIB)
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 \
single_pixel_buffer.c drm_lease.c pointer_constraints.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 \
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 \
single_pixel_buffer.o drm_lease.o pointer_constraints.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

4
README
View file

@ -98,8 +98,8 @@ This directory is organized as follows:
those containing MIME types or shaders
Building the source code is simple, provided that you have the
necessary libwayland-server library, pixman, XCB, DRM, and X extension
libraries installed:
necessary libwayland-server library, pixman, XCB, DRM, xshmfence, and
X extension libraries installed:
xmkmf # to generate the Makefile
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
along with 12to11. If not, see <https://www.gnu.org/licenses/>. */
#include <stdio.h>
#include "compositor.h"
typedef struct _DestroyListener DestroyListener;

View file

@ -157,6 +157,7 @@ typedef struct _DrmFormat DrmFormat;
typedef struct _ShmFormat ShmFormat;
typedef enum _Operation Operation;
typedef enum _BufferTransform BufferTransform;
typedef void *IdleCallbackKey;
typedef void *PresentCompletionKey;
@ -167,6 +168,18 @@ typedef void (*DmaBufFailureFunc) (void *);
typedef void (*BufferIdleFunc) (RenderBuffer, void *);
typedef void (*PresentCompletionFunc) (void *);
enum _BufferTransform
{
Normal,
CounterClockwise90,
CounterClockwise180,
CounterClockwise270,
Flipped,
Flipped90,
Flipped180,
Flipped270,
};
enum _Operation
{
OperationOver,
@ -177,19 +190,26 @@ enum
{
/* Scale has been set. */
ScaleSet = 1,
/* Transform has been set. */
TransformSet = (1 << 1),
/* Offset has been set. */
OffsetSet = (1 << 2),
/* Stretch has been set. */
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
{
/* Which fields are set. */
int flags;
/* A transform by which to rotate the buffer. */
BufferTransform transform;
/* A scale factor to apply to the buffer. */
double scale;
@ -643,6 +663,8 @@ extern void XLScaleRegion (pixman_region32_t *, pixman_region32_t *,
float, float);
extern void XLExtendRegion (pixman_region32_t *, pixman_region32_t *,
int, int);
extern void XLTransformRegion (pixman_region32_t *, pixman_region32_t *,
BufferTransform, int, int);
extern Time XLGetServerTimeRoundtrip (void);
extern RootWindowSelection *XLSelectInputFromRootWindow (unsigned long);
@ -786,6 +808,7 @@ extern void ViewSkip (View *);
extern void ViewUnskip (View *);
extern void ViewMoveFractional (View *, double, double);
extern void ViewSetTransform (View *, BufferTransform);
extern void ViewSetViewport (View *, double, double, double, double,
double, double);
extern void ViewClearViewport (View *);
@ -837,6 +860,12 @@ enum _RoleType
DndIconType,
};
#define RotatesDimensions(transform) \
((transform) == CounterClockwise90 \
|| (transform) == CounterClockwise270 \
|| (transform) == Flipped90 \
|| (transform) == Flipped270)
enum
{
PendingNone = 0,
@ -850,6 +879,7 @@ enum
PendingAttachments = (1 << 8),
PendingViewportSrc = (1 << 9),
PendingViewportDest = (1 << 10),
PendingBufferTransform = (1 << 11),
/* Flags here are stored in `pending' of the current state for
space reasons. */
@ -889,6 +919,9 @@ struct _State
/* The scale of this buffer. */
int buffer_scale;
/* The buffer transform. */
BufferTransform transform;
/* List of frame callbacks. */
FrameCallback frame_callbacks;
@ -1109,8 +1142,6 @@ extern void XLSurfaceReleaseRole (Surface *, Role *);
extern void XLDefaultCommit (Surface *);
extern void XLStateAttachBuffer (State *, ExtBuffer *);
extern void XLStateDetachBuffer (State *);
extern void XLSurfaceRunFrameCallbacks (Surface *, struct timespec);
extern void XLSurfaceRunFrameCallbacksMs (Surface *, uint32_t);
extern CommitCallback *XLSurfaceRunAtCommit (Surface *,
@ -1661,6 +1692,8 @@ extern void MatrixIdentity (Matrix *);
extern void MatrixTranslate (Matrix *, float, float);
extern void MatrixScale (Matrix *, float, float);
extern void MatrixExport (Matrix *, XTransform *);
extern void MatrixRotate (Matrix *, float, float, float);
extern void MatrixMirrorHorizontal (Matrix *, float);
/* Defined in wp_viewporter.c. */
@ -1717,6 +1750,15 @@ extern void RunProcess (ProcessQueue *, char **);
extern ProcessQueue *MakeProcessQueue (void);
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. */
#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"
/* TODO: implement buffer transforms. */
/* These are flags for the DrmFormats. */
enum

95
fns.c
View file

@ -403,6 +403,101 @@ XLExtendRegion (pixman_region32_t *dst, pixman_region32_t *src,
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
XLOpenShm (void)
{

View file

@ -81,6 +81,9 @@ struct _BufferActivityRecord
/* The counter ID. */
uint64_t id;
/* The sync fence. */
Fence *fence;
/* The forward links to the three lists. */
BufferActivityRecord *buffer_next, *target_next, *global_next;
@ -127,6 +130,9 @@ struct _PictureBuffer
/* Flags. */
int flags;
/* The width and height of the buffer. */
short width, height;
/* The last draw params associated with the picture. */
DrawParams params;
@ -138,6 +144,8 @@ struct _PictureBuffer
/* Ongoing buffer activity. */
BufferActivityRecord activity;
int compositable;
};
enum
@ -264,6 +272,9 @@ struct _DmaBufRecord
/* The depth of the pixmap. */
int depth;
/* The width and height. */
short width, height;
};
/* 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. */
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
a Picture. Here is a rough explanation of how the buffer release
machinery works.
@ -489,7 +503,7 @@ FindBufferActivityRecord (PictureBuffer *buffer, PictureTarget *target)
static void
RecordBufferActivity (PictureBuffer *buffer, PictureTarget *target,
uint64_t roundtrip_id)
uint64_t roundtrip_id, Fence *fence)
{
BufferActivityRecord *record;
@ -530,7 +544,23 @@ RecordBufferActivity (PictureBuffer *buffer, PictureTarget *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->fence = fence;
}
static void
@ -599,6 +629,13 @@ MaybeRunIdleCallbacks (PictureBuffer *buffer, PictureTarget *target)
static void
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_next->buffer_last = record->buffer_last;
record->target_last->target_next = record->target_next;
@ -943,6 +980,11 @@ DestroyRenderTarget (RenderTarget target)
activity_last = activity_record;
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);
XLFree (activity_last);
}
@ -1099,6 +1141,15 @@ GetSourceY (DrawParams *params)
return 0.0;
}
static BufferTransform
GetBufferTransform (DrawParams *params)
{
if (params->flags & TransformSet)
return params->transform;
return Normal;
}
static Bool
CompareStretch (DrawParams *params, DrawParams *other)
{
@ -1115,6 +1166,82 @@ CompareStretch (DrawParams *params, DrawParams *other)
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
MaybeApplyTransform (PictureBuffer *buffer, DrawParams *params)
{
@ -1124,6 +1251,8 @@ MaybeApplyTransform (PictureBuffer *buffer, DrawParams *params)
if (GetScale (params) == GetScale (&buffer->params)
&& GetSourceX (params) == GetSourceX (&buffer->params)
&& GetSourceY (params) == GetSourceY (&buffer->params)
&& (GetBufferTransform (params)
== GetBufferTransform (&buffer->params))
&& CompareStretch (params, &buffer->params))
/* Nothing changed. */
return;
@ -1138,6 +1267,10 @@ MaybeApplyTransform (PictureBuffer *buffer, DrawParams *params)
{
MatrixIdentity (&ftransform);
if (params->flags & TransformSet)
ApplyInverseTransform (buffer, &ftransform,
params->transform);
/* Note that these must be applied in the right order. First,
the scale is applied. Then, the offset, and finally the
stretch. */
@ -1173,6 +1306,7 @@ Composite (RenderBuffer buffer, RenderTarget target,
{
PictureBuffer *picture_buffer;
PictureTarget *picture_target;
XLList *tem;
picture_buffer = buffer.pointer;
picture_target = target.pointer;
@ -1191,8 +1325,19 @@ Composite (RenderBuffer buffer, RenderTarget target,
/* dst-x, dst-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
sent later. */
picture_target->buffers_used
= XLListPrepend (picture_target->buffers_used, picture_buffer);
}
@ -1203,22 +1348,41 @@ FinishRender (RenderTarget target, pixman_region32_t *damage)
XLList *tem, *last;
PictureTarget *pict_target;
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
message and records buffer activity for each buffer involved in
the update based on that. */
roundtrip_id = SendRoundtripMessage ();
pict_target = target.pointer;
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)
{
last = tem;
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,
roundtrip_id);
roundtrip_id, fence);
/* Free the list element. */
XLFree (last);
@ -1933,6 +2097,8 @@ BufferFromDmaBuf (DmaBufAttributes *attributes, Bool *error)
buffer->picture = picture;
buffer->pixmap = pixmap;
buffer->depth = depth;
buffer->width = attributes->width;
buffer->height = attributes->height;
/* Initialize the list of release records. */
buffer->pending.buffer_next = &buffer->pending;
@ -2008,6 +2174,8 @@ FinishDmaBufRecord (DmaBufRecord *pending, Bool success)
buffer->picture = picture;
buffer->pixmap = pending->pixmap;
buffer->depth = pending->depth;
buffer->width = pending->width;
buffer->height = pending->height;
/* Initialize the list of release records. */
buffer->pending.buffer_next = &buffer->pending;
@ -2110,6 +2278,9 @@ BufferFromDmaBufAsync (DmaBufAttributes *attributes,
= PictFormatForDmabufFormat (attributes->drm_format);
record->pixmap = pixmap;
record->depth = depth;
record->width = attributes->width;
record->height = attributes->height;
XLAssert (record->format != NULL);
record->next = pending_success.next;
@ -2218,6 +2389,8 @@ BufferFromShm (SharedMemoryAttributes *attributes, Bool *error)
buffer->picture = picture;
buffer->pixmap = pixmap;
buffer->depth = depth;
buffer->width = attributes->width;
buffer->height = attributes->height;
/* Initialize the list of release records. */
buffer->pending.buffer_next = &buffer->pending;
@ -2380,6 +2553,11 @@ FreeAnyBuffer (RenderBuffer buffer)
activity_last = activity_record;
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);
XLFree (activity_last);
}

View file

@ -276,6 +276,10 @@ struct _View
/* Function called upon the view potentially being resized. */
void (*maybe_resized) (View *);
/* Some data associated with this view. Can be a surface or
something else. */
void *data;
/* The damaged and opaque regions. */
pixman_region32_t damage, opaque;
@ -289,16 +293,15 @@ struct _View
(or topmost parent if the view hierarchy is detached). */
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. */
int scale;
/* Flags; whether or not this view is unmapped, etc. */
int flags;
/* Any transform associated with this view. */
BufferTransform transform;
/* The viewport data. */
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->opaque);
pixman_region32_init (&view->input);
view->transform = Normal;
#endif
return view;
@ -1627,6 +1632,60 @@ GetContentScale (int scale)
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
ViewDamageBuffer (View *view, pixman_region32_t *damage)
{
@ -1638,9 +1697,10 @@ ViewDamageBuffer (View *view, pixman_region32_t *damage)
if (!view->buffer)
return;
if (!view->scale && !IsViewported (view))
/* There is no scale nor viewport. Just damage the view
directly. */
if (view->transform == Normal
&& !view->scale && !IsViewported (view))
/* There is no scale, transform, nor viewport. Just damage the
view directly. */
ViewDamage (view, damage);
else
{
@ -1651,6 +1711,15 @@ ViewDamageBuffer (View *view, pixman_region32_t *damage)
x_factor = GetContentScale (view->scale);
y_factor = GetContentScale (view->scale);
if (view->transform != Normal)
{
/* 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);
@ -1671,9 +1740,9 @@ ViewDamageBuffer (View *view, pixman_region32_t *damage)
current buffer width/height. */
if (crop_width == -1)
{
crop_width = (XLBufferWidth (view->buffer)
crop_width = (BufferWidthAfterTransform (view)
* GetContentScale (view->scale));
crop_height = (XLBufferHeight (view->buffer)
crop_height = (BufferHeightAfterTransform (view)
* GetContentScale (view->scale));
}
@ -1738,7 +1807,7 @@ ViewWidth (View *view)
scaling. */
return ceil (view->dest_width);
width = XLBufferWidth (view->buffer);
width = BufferWidthAfterTransform (view);
if (view->scale < 0)
return ceil (width * (abs (view->scale) + 1));
@ -1761,7 +1830,7 @@ ViewHeight (View *view)
scaling. */
return ceil (view->dest_height);
height = XLBufferHeight (view->buffer);
height = BufferHeightAfterTransform (view);
if (view->scale < 0)
return ceil (height * (abs (view->scale) + 1));
@ -1781,6 +1850,23 @@ ViewSetScale (View *view, int scale)
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
ViewSetViewport (View *view, double src_x, double src_y,
double crop_width, double crop_height,
@ -1829,6 +1915,12 @@ ViewComputeTransform (View *view, DrawParams *params, Bool draw)
params->off_x = 0.0;
params->off_y = 0.0;
if (view->transform != Normal)
{
params->flags |= TransformSet;
params->transform = view->transform;
}
if (view->scale)
{
/* There is a scale, so set it. */
@ -1856,9 +1948,9 @@ ViewComputeTransform (View *view, DrawParams *params, Bool draw)
if (params->crop_width == -1)
{
params->crop_width = (XLBufferWidth (view->buffer)
params->crop_width = (BufferWidthAfterTransform (view)
* GetContentScale (view->scale));
params->crop_height = (XLBufferHeight (view->buffer)
params->crop_height = (BufferHeightAfterTransform (view)
* GetContentScale (view->scale));
}
}

View file

@ -506,6 +506,13 @@ GetEffectiveScale (int scale)
return scale;
}
static void
ApplyBufferTransform (Surface *surface)
{
ViewSetTransform (surface->view,
surface->current_state.transform);
}
static void
ApplyScale (Surface *surface)
{
@ -665,8 +672,12 @@ ApplyViewport (Surface *surface)
if (state->buffer)
{
max_width = XLBufferWidth (state->buffer);
max_height = XLBufferHeight (state->buffer);
max_width = (RotatesDimensions (state->transform)
? XLBufferHeight (state->buffer)
: XLBufferWidth (state->buffer));
max_height = (RotatesDimensions (state->transform)
? XLBufferWidth (state->buffer)
: XLBufferHeight (state->buffer));
}
else
{
@ -787,8 +798,16 @@ CheckViewportValues (Surface *surface)
/* A buffer is attached and a viewport source rectangle is set;
check that it remains in bounds. */
if (RotatesDimensions (state->transform))
{
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
|| state->src_y + state->src_height - 1 >= height / state->buffer_scale)
@ -897,6 +916,9 @@ SavePendingState (Surface *surface)
surface->cached_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)
{
surface->cached_state.dest_width
@ -1029,6 +1051,12 @@ InternalCommit (Surface *surface, State *pending)
ApplyScale (surface);
}
if (pending->pending & PendingBufferTransform)
{
surface->current_state.transform = pending->transform;
ApplyBufferTransform (surface);
}
if (pending->pending & PendingInputRegion)
{
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);
}
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
SetBufferTransform (struct wl_client *client, struct wl_resource *resource,
int32_t transform)
{
if (transform != WL_OUTPUT_TRANSFORM_NORMAL)
wl_resource_post_error (resource, WL_DISPLAY_ERROR_IMPLEMENTATION,
"this compositor does not support buffer transforms");
Surface *surface;
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
@ -1249,6 +1325,7 @@ InitState (State *state)
state->pending = PendingNone;
state->buffer = NULL;
state->buffer_scale = 1;
state->transform = Normal;
/* Initialize the sentinel node. */
state->frame_callbacks.next = &state->frame_callbacks;
@ -1508,18 +1585,6 @@ XLSurfaceReleaseRole (Surface *surface, Role *role)
RunUnmapCallbacks (surface);
}
void
XLStateAttachBuffer (State *state, ExtBuffer *buffer)
{
AttachBuffer (state, buffer);
}
void
XLStateDetachBuffer (State *state)
{
ClearBuffer (state);
}
/* Various other functions exported for roles. */

6
time.c
View file

@ -303,6 +303,12 @@ InitTime (void)
supported = XSyncInitialize (compositor.display,
&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)
{
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);
}
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
MatrixExport (Matrix *transform, XTransform *xtransform)
{