12to11/time.c
hujianwei a05a62d9d0 Rework subcompositor damage culling and use a back buffer in the pict renderer
Note that the EGL renderer has been broken by these changes.

* compositor.h (struct _RenderFuncs): Change arguments to
`finish_render'.  Add `cancel_completion_callback'.  Remove
`cancel_presentation'.
(struct _BufferFuncs): Add `is_buffer_opaque'.
* egl.c: Note that the EGL renderer is currently broken.
* fence_ring.c (FenceFree, struct _Fence, GetFence, FenceAwait)
(FenceToXFence): Repurpose fence code as a generic allocator of
fences.
* frame_clock.c (EndFrame): Adjust calculation of even value.
* picture_renderer.c (struct _BufferActivityRecord): Remove
`fence'.
(struct _PictureBuffer): Remove unused field.
(struct _PresentCompletionCallback): Remove unused fields
linking it to the target.
(struct _BackBuffer): New structure.
(IsBufferBusy, SetBufferBusy, ClearBufferBusy): New macros.
(struct _PictureTarget): Add back buffer and GC fields.
(use_sync_fences): Remove variable.
(RecordBufferActivity): Don't take fence.
(UnlinkActivityRecord): Don't wait on fence.
(FreeBackBuffer, FreeBackBuffers, CreateBackBuffer)
(MakePresentationCallback, SwapBackBuffers)
(SwapBackBuffersWithCopy, MaybeAwaitBuffer, GetNextBackBuffer)
(EnsurePicture): New functions.
(TargetFromDrawable): Don't create picture for window targets,
and create GC for those.
(NoteTargetSize): Free back buffers instead of creating
presentation windows.
(DestroyRenderTarget): Free back buffers.
(ViewContainsExtents): Delete function.
(FinishRender): Swap back buffers and handle callbacks.
(CancelCompletionCallback): New function.
(TargetAge): Implement accordingly.
(PresentToWindow, CancelPresentationCallback): Present to the
regular window instead of a separate ``presentation window''.
(CancelPresentation): Remove function.
(picture_render_funcs): Add CancelCompletionCallback; remove
NeverAges and CancelPresentation.
(BufferFromDmaBuf, FinishDmaBufRecord, BufferFromShm): Set
opaque flag accordingly.
(IsBufferOpaque): New function.
(picture_buffer_funcs): Add IsBufferOpaque.
(HandlePresentCompleteNotify, HandlePresentIdleNotify): Handle
back buffer release.

* renderer.c (RenderFinishRender, RenderCancelCompletionCallback)
(RenderCancelPresentation, RenderIsBufferOpaque): Adjust
accordingly.
* seat.c (InterpolateAxes): Fix NULL-pointer dereference.

* subcompositor.c (SetFrozen, IsFrozen): Get rid of unused
state.
(struct _View): Cache the width and height.  Add cull_region
field.
(struct _Subcompositor): Add `render_key' field.
(ViewAfterSizeUpdate): Set cached width and height.
(ViewAttachBuffer): Correctly handle size changes.
(ViewDamage): Apply buffer damage.
(ViewDamageBuffer): use ViewDamage.
(ApplyBufferDamage): New function.:(FillBoxesWithTransparency,
ViewContainsExtents, IntersectBoxes): Delete unused functions.
(RenderCompletedCallback): New function.
(NoViewsAfter): Delete function.
(SkipSlug): New slug.
(SubcompositorUpdateAncillary, TryPresent, AnyParentUnmapped)
(DoCull, DrawBackground, CompositeSingleView, InitBackground)
(SubcompositorComposite1, SubcompositorComposite): New
functions.  Implement new redisplay logic here.
(SubcompositorUpdate, CopyRegion): New function.
(SubcompositorExpose, SubcompositorFree): Clear render callback
if set.
(SubcompositorLookupView): Use cached width and height.
(SubcompositorFreeze, SubcompositorUnfreeze): Remove unused
functions.
* time.c (InitTime): Fix use of uninitialized value initializing
the Sync extension.
* transform.c (TransformBox): Pacify compiler warning.
2022-10-29 05:21:35 +00:00

337 lines
8.8 KiB
C
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/* Wayland compositor running on top of an X server.
Copyright (C) 2022 to various contributors.
This file is part of 12to11.
12to11 is free software: you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by the
Free Software Foundation, either version 3 of the License, or (at your
option) any later version.
12to11 is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
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 <string.h>
#include <stdio.h>
#include "compositor.h"
#include <X11/extensions/sync.h>
/* The latest known time. */
static Timestamp current_time;
/* Alarms used to keep track of the server time. */
static XSyncAlarm alarm_a, alarm_b;
/* The server time counter. */
static XSyncCounter counter;
/* The event and error bases of the synchronization extension. */
static int xsync_event_base, xsync_error_base;
/* Half a month; used as a threshold in various places. */
#define HalfMonth (1U << 31)
/* The max value of Time. */
#define MaxTime 0xffffffff
/* The protocol translator can run for more than 48 days. That makes
normal X timestamp handling unsafe, as the X server wraps around
timestamps after that much time. This function creates a Timestamp
from the server time while handling overflow. */
Timestamp
TimestampFromServerTime (Time time)
{
if (time >= current_time.milliseconds)
{
/* No overflow happened. */
current_time.milliseconds = time;
return current_time;
}
/* The server time wrapped around. */
current_time.months++;
current_time.milliseconds = time;
return current_time;
}
Timestamp
TimestampFromClientTime (Time time)
{
Timestamp timestamp;
timestamp.months = current_time.months;
timestamp.milliseconds = time;
/* Create a timestamp from a Time that may lie in the past.
Compensate for wraparound if the difference between the
timestamps is more than half a month. */
if (time < current_time.milliseconds
&& (current_time.milliseconds - time) >= HalfMonth)
timestamp.months += 1;
else if (time > current_time.milliseconds
&& (time - current_time.milliseconds) >= HalfMonth)
timestamp.months -= 1;
return timestamp;
}
TimestampDifference
CompareTimestamps (Timestamp a, Timestamp b)
{
if (a.months < b.months)
return Earlier;
if (a.months > b.months)
return Later;
if (a.milliseconds < b.milliseconds)
return Earlier;
if (a.milliseconds > b.milliseconds)
return Later;
return Same;
}
TimestampDifference
CompareTimeWith (Time a, Timestamp b)
{
return CompareTimestamps (TimestampFromClientTime (a), b);
}
/* Timestamp tracking. The code below treats INT64 as unsigned, since
that won't overflow until long in the far future. */
static XSyncCounter
FindSystemCounter (const char *name)
{
XSyncSystemCounter *system_counters;
int i, num_counters;
XSyncCounter counter;
num_counters = 0;
system_counters = XSyncListSystemCounters (compositor.display,
&num_counters);
counter = None;
for (i = 0; i < num_counters; ++i)
{
if (!strcmp (system_counters[i].name, name))
{
counter = system_counters[i].counter;
break;
}
/* Continue looking at the next counter. */
}
if (system_counters)
XSyncFreeSystemCounterList (system_counters);
return counter;
}
static uint64_t
ValueToScalar (XSyncValue value)
{
uint64_t low, high;
low = XSyncValueLow32 (value);
high = XSyncValueHigh32 (value);
return low | (high << 32);
}
static void
ScalarToValue (uint64_t scalar, XSyncValue *value)
{
XSyncIntsToValue (value, scalar & 0xffffffff, scalar >> 32);
}
static void
StartAlarms (XSyncCounter counter, XSyncValue current_value)
{
uint64_t scalar_value, target;
XSyncTrigger trigger;
XSyncAlarmAttributes attributes;
unsigned long value_mask;
scalar_value = ValueToScalar (current_value);
/* Delete existing alarms. */
if (alarm_a)
XSyncDestroyAlarm (compositor.display, alarm_a);
if (alarm_b)
XSyncDestroyAlarm (compositor.display, alarm_b);
value_mask = (XSyncCACounter | XSyncCATestType
| XSyncCAValue | XSyncCAEvents);
/* Start the first kind of alarm. This alarm assumes that the
counter does not wrap around along with the server time.
The protocol allows for more kinds of server behavior, but all
servers either implement the counter as one that wraps around
after Time ends, as defined here... */
if (scalar_value >= HalfMonth)
{
/* value exceeds HalfMonth. Wait for value to overflow back to
0. */
trigger.counter = counter;
trigger.test_type = XSyncNegativeComparison;
trigger.wait_value = current_value;
/* Set the trigger and ask for events. */
attributes.trigger = trigger;
attributes.events = True;
/* Create the alarm. */
alarm_a = XSyncCreateAlarm (compositor.display,
value_mask, &attributes);
}
else
{
/* value is not yet HalfMonth. Wait for value to exceed
HalfMonth - 1. */
trigger.counter = counter;
trigger.test_type = XSyncPositiveComparison;
ScalarToValue (HalfMonth, &trigger.wait_value);
/* Set the trigger and ask for events. */
attributes.trigger = trigger;
attributes.events = True;
/* Create the alarm. */
alarm_a = XSyncCreateAlarm (compositor.display,
value_mask, &attributes);
}
/* ...or the counter increases indefinitely, with its lower 32 bits
representing the server time, which this counter takes into
account. */
if ((scalar_value & MaxTime) >= HalfMonth)
{
/* The time exceeds HalfMonth. Wait for the value to overflow
time again. */
target = (scalar_value & ~MaxTime) + MaxTime + 1;
trigger.counter = counter;
trigger.test_type = XSyncPositiveComparison;
ScalarToValue (target, &trigger.wait_value);
/* Set the trigger and ask for events. */
attributes.trigger = trigger;
attributes.events = True;
/* Create the alarm. */
alarm_b = XSyncCreateAlarm (compositor.display,
value_mask, &attributes);
}
else
{
/* The time is not yet HalfMonth. Wait for the time to exceed
HalfMonth - 1. */
target = (scalar_value & ~MaxTime) + HalfMonth;
trigger.counter = counter;
trigger.test_type = XSyncPositiveComparison;
ScalarToValue (target, &trigger.wait_value);
/* Set the trigger and ask for events. */
attributes.trigger = trigger;
attributes.events = True;
/* Create the alarm. */
alarm_b = XSyncCreateAlarm (compositor.display,
value_mask, &attributes);
}
/* Now wait for alarm notifications to arrive. */
}
static Bool
HandleAlarmNotify (XSyncAlarmNotifyEvent *notify)
{
if (notify->alarm != alarm_a
|| notify->alarm != alarm_b)
/* We are not interested in this outdated or irrelevant alarm. */
return False;
/* First, synchronize our local time with the server time in the
notification. */
TimestampFromServerTime (notify->time);
/* Next, recreate the alarms for the new time. */
StartAlarms (counter, notify->counter_value);
return True;
}
Bool
HandleOneXEventForTime (XEvent *event)
{
if (event->type == xsync_event_base + XSyncAlarmNotify)
return HandleAlarmNotify ((XSyncAlarmNotifyEvent *) event);
return False;
}
void
InitTime (void)
{
XSyncValue value;
Bool supported;
int xsync_major, xsync_minor;
supported = XSyncQueryExtension (compositor.display,
&xsync_event_base,
&xsync_error_base);
if (supported)
supported = XSyncInitialize (compositor.display,
&xsync_major, &xsync_minor);
if (!supported)
{
fprintf (stderr, "A compatible version of the synchronization"
" extension was not found\n");
exit (1);
}
if (xsync_major < 3 || (xsync_major == 3 && xsync_minor < 1))
{
fprintf (stderr, "Sync fences are not supported by this X server\n");
exit (1);
}
/* Initialize server timestamp tracking. In order for server time
accounting to be absolutely reliable, we must receive an event
detailing each change every time it reaches HalfMonth and 0. Set
up multiple counter alarms to do that. */
counter = FindSystemCounter ("SERVERTIME");
if (!counter)
fprintf (stderr, "Server missing required system counter SERVERTIME\n");
else
{
/* Now, obtain the current value of the counter. This cannot
fail without calling the error (or IO error) handler. */
XSyncQueryCounter (compositor.display, counter, &value);
/* Start the alarms. */
StartAlarms (counter, value);
}
}