app_server: clear background immediately on expose

Reduce stamping artifacts when application slowly responds to redraw requests.

This fixes and reintroduces logic previously removed in hrev53711.
Previous logic was incorrect as it didn't take the possibility of multiple
invalidations of different kinds (expose, update request) into account.
Now separate update and expose regions are maintained and only expose region
is cleared immediately.

Change-Id: I0fd98cb1b45ccec285154e8c0d8e3a1400d156d7
Reviewed-on: https://review.haiku-os.org/c/haiku/+/6067
Reviewed-by: Jérôme Duval <[email protected]>
Tested-by: Commit checker robot <[email protected]>
This commit is contained in:
X512
2023-12-13 11:31:27 +00:00
committed by Adrien Destugues
parent 9d660c7738
commit b5ba4badb3
9 changed files with 104 additions and 100 deletions
+23 -13
View File
@@ -1558,12 +1558,14 @@ Desktop::ResizeWindowBy(Window* window, float x, float y)
return;
}
// the dirty region for the inside of the window is
// constructed by the window itself in ResizeBy()
// The dirty region for the inside of the window is constructed by the window itself in
// ResizeBy()
BRegion newDirtyRegion;
// track the dirty region outside the window in case
// it is shrunk in "previouslyOccupiedRegion"
// Track the dirty region outside the window in case it is shrunk in "previouslyOccupiedRegion"
BRegion previouslyOccupiedRegion(window->VisibleRegion());
// Track the region that was drawn in previous update sessions, so we can compute the newly
// exposed areas by excluding this from the update region.
BRegion previousVisibleContentRegion(window->VisibleContentRegion());
// stop direct frame buffer access
bool direct = false;
@@ -1586,7 +1588,15 @@ Desktop::ResizeWindowBy(Window* window, float x, float y)
// ...because we do this ourselves
newDirtyRegion.Include(&previouslyOccupiedRegion);
MarkDirty(newDirtyRegion);
// calculate old expose region as window visible region difference
BRegion exposeRegion(previouslyOccupiedRegion);
exposeRegion.Exclude(&window->VisibleRegion());
// ...and new expose region as window content visible region difference
BRegion tmp(window->VisibleContentRegion());
tmp.Exclude(&previousVisibleContentRegion);
exposeRegion.Include(&tmp);
MarkDirty(newDirtyRegion, exposeRegion);
_SetBackground(background);
_WindowChanged(window);
@@ -2166,14 +2176,14 @@ Desktop::FindTarget(BMessenger& messenger)
void
Desktop::MarkDirty(BRegion& region)
Desktop::MarkDirty(BRegion& dirtyRegion, BRegion& exposeRegion)
{
if (region.CountRects() == 0)
if (dirtyRegion.CountRects() == 0)
return;
if (LockAllWindows()) {
// send redraw messages to all windows intersecting the dirty region
_TriggerWindowRedrawing(region);
_TriggerWindowRedrawing(dirtyRegion, exposeRegion);
UnlockAllWindows();
}
@@ -3455,14 +3465,14 @@ Desktop::_RebuildClippingForAllWindows(BRegion& stillAvailableOnScreen)
void
Desktop::_TriggerWindowRedrawing(BRegion& newDirtyRegion)
Desktop::_TriggerWindowRedrawing(BRegion& dirtyRegion, BRegion& exposeRegion)
{
// send redraw messages to all windows intersecting the dirty region
for (Window* window = CurrentWindows().LastWindow(); window != NULL;
window = window->PreviousWindow(fCurrentWorkspace)) {
if (!window->IsHidden()
&& newDirtyRegion.Intersects(window->VisibleRegion().Frame()))
window->ProcessDirtyRegion(newDirtyRegion);
&& dirtyRegion.Intersects(window->VisibleRegion().Frame()))
window->ProcessDirtyRegion(dirtyRegion, exposeRegion);
}
}
@@ -3531,7 +3541,7 @@ Desktop::RebuildAndRedrawAfterWindowChange(Window* changedWindow,
_SetBackground(stillAvailableOnScreen);
_WindowChanged(changedWindow);
_TriggerWindowRedrawing(dirty);
_TriggerWindowRedrawing(dirty, dirty);
}
@@ -3603,7 +3613,7 @@ Desktop::_ScreenChanged(Screen* screen)
// figure out dirty region
dirty.Exclude(&background);
_TriggerWindowRedrawing(dirty);
_TriggerWindowRedrawing(dirty, dirty);
// send B_SCREEN_CHANGED to windows on that screen
BMessage update(B_SCREEN_CHANGED);
+4 -2
View File
@@ -240,7 +240,9 @@ public:
team_id teamID);
EventTarget* FindTarget(BMessenger& messenger);
void MarkDirty(BRegion& region);
void MarkDirty(BRegion& dirtyRegion, BRegion& exposeRegion);
void MarkDirty(BRegion& region)
{ return MarkDirty(region, region); }
void Redraw();
void RedrawBackground();
@@ -312,7 +314,7 @@ private:
void _RebuildClippingForAllWindows(
BRegion& stillAvailableOnScreen);
void _TriggerWindowRedrawing(
BRegion& newDirtyRegion);
BRegion& dirtyRegion, BRegion& exposeRegion);
void _SetBackground(BRegion& background);
status_t _ActivateApp(team_id team);
+4 -4
View File
@@ -1378,7 +1378,7 @@ fDesktop->LockSingleWindow();
float offsetX = x - fCurrentView->Frame().left;
float offsetY = y - fCurrentView->Frame().top;
BRegion dirty;
BRegion dirty, expose;
fCurrentView->MoveBy(offsetX, offsetY, &dirty);
// TODO: think about how to avoid this hack:
@@ -1389,7 +1389,7 @@ fDesktop->LockSingleWindow();
if (View* parent = fCurrentView->Parent())
parent->RebuildClipping(false);
fWindow->MarkContentDirty(dirty);
fWindow->MarkContentDirty(dirty, expose);
break;
}
case AS_VIEW_RESIZE_TO:
@@ -1406,14 +1406,14 @@ fDesktop->LockSingleWindow();
float deltaWidth = newWidth - fCurrentView->Frame().Width();
float deltaHeight = newHeight - fCurrentView->Frame().Height();
BRegion dirty;
BRegion dirty, expose;
fCurrentView->ResizeBy(deltaWidth, deltaHeight, &dirty);
// TODO: see above
if (View* parent = fCurrentView->Parent())
parent->RebuildClipping(false);
fWindow->MarkContentDirty(dirty);
fWindow->MarkContentDirty(dirty, expose);
break;
}
case AS_VIEW_GET_COORD:
+7 -7
View File
@@ -905,7 +905,7 @@ View::CopyBits(IntRect src, IntRect dst, BRegion& windowContentClipping)
dirty->Exclude(copyRegion);
dirty->IntersectWith(screenAndUserClipping);
fWindow->MarkContentDirty(*dirty);
fWindow->MarkContentDirty(*dirty, *dirty);
fWindow->RecycleRegion(dirty);
fWindow->RecycleRegion(copyRegion);
@@ -1045,8 +1045,8 @@ View::BlendAllLayers()
void
View::Draw(DrawingEngine* drawingEngine, BRegion* effectiveClipping,
BRegion* windowContentClipping, bool deep)
View::Draw(DrawingEngine* drawingEngine, const BRegion* effectiveClipping,
const BRegion* windowContentClipping, bool deep)
{
if (!fVisible) {
// child views cannot be visible either
@@ -1245,9 +1245,9 @@ View::SetHidden(bool hidden)
IntRect clippedBounds = Bounds();
ConvertToVisibleInTopView(&clippedBounds);
BRegion dirty;
BRegion dirty, expose;
dirty.Set((clipping_rect)clippedBounds);
fWindow->MarkContentDirty(dirty);
fWindow->MarkContentDirty(dirty, expose);
}
}
}
@@ -1426,7 +1426,7 @@ View::RebuildClipping(bool deep)
BRegion&
View::ScreenAndUserClipping(BRegion* windowContentClipping, bool force) const
View::ScreenAndUserClipping(const BRegion* windowContentClipping, bool force) const
{
// no user clipping - return screen clipping directly
if (!fUserClipping.IsSet())
@@ -1477,7 +1477,7 @@ View::InvalidateScreenClipping()
BRegion&
View::_ScreenClipping(BRegion* windowContentClipping, bool force) const
View::_ScreenClipping(const BRegion* windowContentClipping, bool force) const
{
if (!fScreenClippingValid || force) {
fScreenClipping = fLocalClipping;
+4 -4
View File
@@ -170,8 +170,8 @@ public:
// for background clearing
virtual void Draw(DrawingEngine* drawingEngine,
BRegion* effectiveClipping,
BRegion* windowContentClipping,
const BRegion* effectiveClipping,
const BRegion* windowContentClipping,
bool deep = false);
virtual void MouseDown(BMessage* message, BPoint where);
@@ -204,7 +204,7 @@ public:
// clipping
void RebuildClipping(bool deep);
BRegion& ScreenAndUserClipping(
BRegion* windowContentClipping,
const BRegion* windowContentClipping,
bool force = false) const;
void InvalidateScreenClipping();
inline bool IsScreenClippingValid() const
@@ -228,7 +228,7 @@ protected:
virtual void _ScreenToLocalTransform(
SimpleTransform& transform) const;
BRegion& _ScreenClipping(BRegion* windowContentClipping,
BRegion& _ScreenClipping(const BRegion* windowContentClipping,
bool force = false) const;
void _MoveScreenClipping(int32 x, int32 y,
bool deep);
+35 -41
View File
@@ -81,7 +81,6 @@ Window::Window(const BRect& frame, const char *name,
fVisibleRegion(),
fVisibleContentRegion(),
fDirtyRegion(),
fDirtyCause(0),
fContentRegion(),
fEffectiveDrawingRegion(),
@@ -289,6 +288,7 @@ Window::MoveBy(int32 x, int32 y, bool moveStack)
// take along the dirty region which is not
// processed yet
fDirtyRegion.OffsetBy(x, y);
fExposeRegion.OffsetBy(x, y);
if (fContentRegionValid)
fContentRegion.OffsetBy(x, y);
@@ -737,7 +737,7 @@ Window::DrawingRegionChanged(View* view) const
void
Window::ProcessDirtyRegion(BRegion& region)
Window::ProcessDirtyRegion(const BRegion& dirtyRegion, const BRegion& exposeRegion)
{
// if this is executed in the desktop thread,
// it means that the window thread currently
@@ -760,8 +760,8 @@ Window::ProcessDirtyRegion(BRegion& region)
ServerWindow()->RequestRedraw();
}
fDirtyRegion.Include(&region);
fDirtyCause |= UPDATE_EXPOSE;
fDirtyRegion.Include(&dirtyRegion);
fExposeRegion.Include(&exposeRegion);
}
@@ -770,7 +770,7 @@ Window::RedrawDirtyRegion()
{
if (TopLayerStackWindow() != this) {
fDirtyRegion.MakeEmpty();
fDirtyCause = 0;
fExposeRegion.MakeEmpty();
return;
}
@@ -778,13 +778,15 @@ Window::RedrawDirtyRegion()
if (IsVisible()) {
_DrawBorder();
BRegion* dirtyContentRegion =
fRegionPool.GetRegion(VisibleContentRegion());
BRegion* dirtyContentRegion = fRegionPool.GetRegion(VisibleContentRegion());
BRegion* exposeContentRegion = fRegionPool.GetRegion(VisibleContentRegion());
dirtyContentRegion->IntersectWith(&fDirtyRegion);
exposeContentRegion->IntersectWith(&fExposeRegion);
_TriggerContentRedraw(*dirtyContentRegion);
_TriggerContentRedraw(*dirtyContentRegion, *exposeContentRegion);
fRegionPool.Recycle(dirtyContentRegion);
fRegionPool.Recycle(exposeContentRegion);
}
// reset the dirty region, since
@@ -795,7 +797,7 @@ Window::RedrawDirtyRegion()
// get write access, since we're holding
// the read lock for the whole time.
fDirtyRegion.MakeEmpty();
fDirtyCause = 0;
fExposeRegion.MakeEmpty();
}
@@ -812,7 +814,7 @@ Window::MarkDirty(BRegion& regionOnScreen)
void
Window::MarkContentDirty(BRegion& regionOnScreen)
Window::MarkContentDirty(BRegion& dirtyRegion, BRegion& exposeRegion)
{
// for triggering AS_REDRAW
// since this won't affect other windows, read locking
@@ -821,27 +823,26 @@ Window::MarkContentDirty(BRegion& regionOnScreen)
if (fHidden || IsOffscreenWindow())
return;
regionOnScreen.IntersectWith(&VisibleContentRegion());
fDirtyCause |= UPDATE_REQUEST;
_TriggerContentRedraw(regionOnScreen);
dirtyRegion.IntersectWith(&VisibleContentRegion());
exposeRegion.IntersectWith(&VisibleContentRegion());
_TriggerContentRedraw(dirtyRegion, exposeRegion);
}
void
Window::MarkContentDirtyAsync(BRegion& regionOnScreen)
Window::MarkContentDirtyAsync(BRegion& dirtyRegion)
{
// NOTE: see comments in ProcessDirtyRegion()
if (fHidden || IsOffscreenWindow())
return;
regionOnScreen.IntersectWith(&VisibleContentRegion());
dirtyRegion.IntersectWith(&VisibleContentRegion());
if (fDirtyRegion.CountRects() == 0) {
ServerWindow()->RequestRedraw();
}
fDirtyRegion.Include(&regionOnScreen);
fDirtyCause |= UPDATE_REQUEST;
fDirtyRegion.Include(&dirtyRegion);
}
@@ -860,7 +861,6 @@ Window::InvalidateView(View* view, BRegion& viewRegion)
//fDrawingEngine->FillRegion(viewRegion, rgb_color{ 0, 255, 0, 255 });
//snooze(10000);
fDirtyCause |= UPDATE_REQUEST;
_TriggerContentRedraw(viewRegion);
}
}
@@ -1767,16 +1767,27 @@ Window::_ShiftPartOfRegion(BRegion* region, BRegion* regionToShift,
void
Window::_TriggerContentRedraw(BRegion& dirtyContentRegion)
Window::_TriggerContentRedraw(BRegion& dirty, const BRegion& expose)
{
if (!IsVisible() || dirtyContentRegion.CountRects() == 0
|| (fFlags & kWindowScreenFlag) != 0)
if (!IsVisible() || dirty.CountRects() == 0 || (fFlags & kWindowScreenFlag) != 0)
return;
// put this into the pending dirty region
// to eventually trigger a client redraw
_TransferToUpdateSession(&dirty);
_TransferToUpdateSession(&dirtyContentRegion);
if (expose.CountRects() > 0) {
// draw exposed region background right now to avoid stamping artifacts
if (fDrawingEngine->LockParallelAccess()) {
bool copyToFrontEnabled = fDrawingEngine->CopyToFrontEnabled();
fDrawingEngine->SetCopyToFrontEnabled(true);
fDrawingEngine->SuspendAutoSync();
fTopView->Draw(fDrawingEngine.Get(), &expose, &fContentRegion, true);
fDrawingEngine->Sync();
fDrawingEngine->SetCopyToFrontEnabled(copyToFrontEnabled);
fDrawingEngine->UnlockParallelAccess();
}
}
}
@@ -1841,8 +1852,6 @@ Window::_TransferToUpdateSession(BRegion* contentDirtyRegion)
// add to pending
fPendingUpdateSession->SetUsed(true);
// if (!fPendingUpdateSession->IsExpose())
fPendingUpdateSession->AddCause(fDirtyCause);
fPendingUpdateSession->Include(contentDirtyRegion);
if (!fUpdateRequested) {
@@ -2058,13 +2067,7 @@ Window::_ObeySizeLimits()
Window::UpdateSession::UpdateSession()
:
fDirtyRegion(),
fInUse(false),
fCause(0)
{
}
Window::UpdateSession::~UpdateSession()
fInUse(false)
{
}
@@ -2094,17 +2097,8 @@ void
Window::UpdateSession::SetUsed(bool used)
{
fInUse = used;
if (!fInUse) {
if (!fInUse)
fDirtyRegion.MakeEmpty();
fCause = 0;
}
}
void
Window::UpdateSession::AddCause(uint8 cause)
{
fCause |= cause;
}
+21 -23
View File
@@ -81,11 +81,6 @@ class WorkspacesView;
// TODO: move this into a proper place
#define AS_REDRAW 'rdrw'
enum {
UPDATE_REQUEST = 0x01,
UPDATE_EXPOSE = 0x02,
};
class Window {
public:
@@ -149,15 +144,19 @@ public:
bool DrawingRegionChanged(View* view) const;
// generic version, used by the Desktop
void ProcessDirtyRegion(BRegion& regionOnScreen);
void ProcessDirtyRegion(const BRegion& dirtyRegion,
const BRegion& exposeRegion);
void ProcessDirtyRegion(const BRegion& exposeRegion)
{ ProcessDirtyRegion(exposeRegion, exposeRegion); }
void RedrawDirtyRegion();
// can be used from inside classes that don't
// need to know about Desktop (first version uses Desktop)
void MarkDirty(BRegion& regionOnScreen);
// these versions do not use the Desktop
void MarkContentDirty(BRegion& regionOnScreen);
void MarkContentDirtyAsync(BRegion& regionOnScreen);
void MarkContentDirty(BRegion& dirtyRegion,
BRegion& exposeRegion);
void MarkContentDirtyAsync(BRegion& dirtyRegion);
// shortcut for invalidating just one view
void InvalidateView(View* view, BRegion& viewRegion);
@@ -322,7 +321,8 @@ protected:
int32 yOffset);
// different types of drawing
void _TriggerContentRedraw(BRegion& dirty);
void _TriggerContentRedraw(BRegion& dirty,
const BRegion& expose = BRegion());
void _DrawBorder();
// handling update sessions
@@ -348,13 +348,19 @@ protected:
BRegion fVisibleRegion;
BRegion fVisibleContentRegion;
// our part of the "global" dirty region
// it is calculated from the desktop thread,
// but we can write to it when we read locked
// the clipping, since it is local and the desktop
// thread is blocked
// Our part of the "global" dirty region (what needs to be redrawn).
// It is calculated from the desktop thread, but we can write to it when we read locked
// the clipping, since it is local and the desktop thread is blocked.
BRegion fDirtyRegion;
uint32 fDirtyCause;
// Subset of the dirty region that is newly exposed. While the dirty region is merely
// showing out of date data on screen, this subset of it is showing remains of other
// windows. To avoid glitches, it must be set to a reasonable state as fast as possible,
// without waiting for a roundtrip to the window's Draw() methods. So it will be filled
// using background color and view bitmap, which can all be done without leaving
// app_server.
BRegion fExposeRegion;
// caching local regions
BRegion fContentRegion;
@@ -386,7 +392,6 @@ protected:
class UpdateSession {
public:
UpdateSession();
virtual ~UpdateSession();
void Include(BRegion* additionalDirty);
void Exclude(BRegion* dirtyInNextSession);
@@ -400,16 +405,9 @@ protected:
inline bool IsUsed() const
{ return fInUse; }
void AddCause(uint8 cause);
inline bool IsExpose() const
{ return fCause & UPDATE_EXPOSE; }
inline bool IsRequest() const
{ return fCause & UPDATE_REQUEST; }
private:
BRegion fDirtyRegion;
bool fInUse;
uint8 fCause;
};
UpdateSession fUpdateSessions[2];
+4 -4
View File
@@ -358,14 +358,14 @@ WorkspacesView::_Invalidate() const
BRect frame = Bounds();
LocalToScreenTransform().Apply(&frame);
BRegion region(frame);
Window()->MarkContentDirty(region);
BRegion region(frame), expose;
Window()->MarkContentDirty(region, expose);
}
void
WorkspacesView::Draw(DrawingEngine* drawingEngine, BRegion* effectiveClipping,
BRegion* windowContentClipping, bool deep)
WorkspacesView::Draw(DrawingEngine* drawingEngine, const BRegion* effectiveClipping,
const BRegion* windowContentClipping, bool deep)
{
// we can only draw within our own area
BRegion redraw(ScreenAndUserClipping(windowContentClipping));
+2 -2
View File
@@ -23,8 +23,8 @@ public:
virtual void DetachedFromWindow();
virtual void Draw(DrawingEngine* drawingEngine,
BRegion* effectiveClipping,
BRegion* windowContentClipping, bool deep = false);
const BRegion* effectiveClipping,
const BRegion* windowContentClipping, bool deep = false);
virtual void MouseDown(BMessage* message, BPoint where);
virtual void MouseUp(BMessage* message, BPoint where);