docs/develop: a few notes about invalidation, view bitmaps, and overlays

Following some IRC discussions, it seems useful to have this written
down somewhere.

Change-Id: Ic02686948d989bff2fa671a3831ba5aed1515d25
Reviewed-on: https://review.haiku-os.org/c/haiku/+/6085
Tested-by: Automation <automation@haiku-os.org>
Reviewed-by: Adrien Destugues <pulkomandy@pulkomandy.tk>
This commit is contained in:
PulkoMandy 2023-02-25 09:46:40 +01:00 committed by Adrien Destugues
parent 53df6323c8
commit 2ab05dc925

View File

@ -1,6 +1,28 @@
Graphics
=========
Design overview
---------------
The app_server drawing system was designed in BeOS with the goal to provide low latency response
(it should look fast), making use of the quite powerful CPU, but somewhat limited RAM available
at the time.
As a result of these constraints, in BeOS the app_server operated with a single buffer framebuffer
(there was not enough RAM and especially not enough video RAM to enable double buffer). It is also
designed to use 2D acceleration whenever possible, to free up the CPU for other, more interesting
tasks. This is achived by the use of "accelerants", add-ons loaded into app-server that communicate
with the kernel part of graphics drivers. Usually the kernel part will be as minimal as possible,
providing low-level access to the video card (command ring buffers, memory mapping of registers,
DMA setup, that kind of things), and the accelerant will be doing the higher level work on top of
it. Note, however, that on modern hardware, the graphics card acceleration is often not that fast
for 2D work, compared to the power offered by multi-GigaHerz CPUs. So, Haiku does not currently use
most of these acceleration features, doing its drawing using the CPU instead.
The single buffer approach creates a problem: applications that are too slow to redraw things can
result in "glitches" on screen. These are of mainly two types: flickering and stamping. The
app_server in Haiku takes some care to avoid these.
Desktop Initialization
-----------------------
@ -133,6 +155,40 @@ action.
Screen Updates
--------------
Managing invalidation
.....................
The drawing is architectured around a single framebuffer, where all windows can draw.
In general, redrawing should be avoided when not necessary, and if possible, multiple drawing
requests should be combined together to avoid redrawing the same area over and over.
To achieve this, a protocol to decide which parts of the screen need to be redrawn is implemented.
When something needs to change, that region is marked as "invalidated" and the app_server will
ask the corresponding view to redraw itself. Invalidation can happen in two ways:
- Window management events (a window was resized or hidden, for example)
- The application asked to redraw something by calling Invalidate()
These two are handled slightly differently. When the event comes from window management, one of
the views involved will have parts of it newly exposed (previously they were hidden by another
window that is now hidden, or they were outside the window bounds, for example). In this case,
app_server will immediately fill the newly exposed area with the view color. This avoids one of
the two drawing problems when applications are too slow to redraw: stamping. For example, if one
windows is not redrawing fast enough, and another is moved above it, that movement will quickly
hide and re-expose parts of the bottom window. If the window does not redraw fast enough, and
nothing is done, we would be left with parts of the top window being partially drawn where they
shouldn't be anymore.
In the case of invalidation coming from the view itself, however, things are a bit different. We
can assume that the view had already drawn something at that place. If we cleared the area to the
view color, and the view takes a little time to redraw, this would result in flickering: the view
would be briefly visible with only its view color, and then redrawn with its content again. So,
in the case of invalidation, the app_server does nothing, and waits for the view to redraw itself.
Getting things drawn on screen
..............................
Screen updates are done entirely through the BView class or some
subclass thereof, hereafter referred to as a view. A view's drawing
commands will cause its window to store draw command messages in a
@ -147,6 +203,56 @@ such as StrokeRect, will involve the ServerWindow object calling the
appropriate command in the graphics module for the Layer corresponding
to the view which sent the command.
The commands are grouped together in a drawing session, that corresponds to a call to the
BView::Draw() method. In Haiku, the app_server uses double buffering, and all the drawing from
one session will be done on the backbuffer, and moved to the front buffer only when the session is
complete. The normal workflow is to trigger this by a request to draw something (either an
"expose" event because a part of the window has become visible, or a call to the Invalidate function
from the application side). However, it is also possible for views to send "unsollicited" drawing
commands outside of an update session. While this will work, the lack of a session means each
command will be handled separately, and immediately copied to the front buffer. As a result, there
will be more fickering and the drawing will be a lot slower.
When interpreting the drawing commands, app_server will prevent any drawing from happening outside
the area designated for a given view, including parts of it that could be hidden by other windows.
There is an exception to this, however: when using BDirectWindow, it is possible to access the
whole frame buffer. In this case, app_server provides the application with a BRegion it should
redraw, and it is up to the application to not draw ouside those bounds.
Offscreen views
...............
When a view does very complex drawing, that will take more than a frame to complete, the single
framebuffer design is not desirable, and will result in a lot of flickering as partially drawn
states of the view are shown on screen. To avoid this, the app_server provides the option for a
view to draw off-screen, into a BBitmap. When the bitmap is complete, it can then be put on-screen
using another view.
This can be done in two ways: either using DrawBitmap() or SetViewBitmap(). The latter is better,
since it simply lets app_server know that the view should show that bitmap, and then there is no
need to do anything to handle expose and invalidate events, the app_server can automatically draw
the bitmap instead of using the view color to fill the newly exposed or invalidated area.
Overlays
........
When view bitmaps are not enough, it is possible to go one step further: have the hardware insert
the picture inside a view, instead of app_server having to copy it in the framebuffer. This is
achieved using overlays. The API is similar to SetViewBitmap, but the bitmap is allocated directly
in video memory and managed by the video card. Unfortunately, not all video drivers currently
support this feature.
It is possible to mix overlays with normal drawing. The overlay is normally made visible only when
the framebuffer is a certain specific color(usually pure green or pure magenta, the specific
color is determined by the graphics driver and multiple colors may be used for multiple overlays
from different views, if the hardware can do that). The application can then simply let the view be
filled with that 'color key' (setting it as the view color), or it can draw other things that will
be displayed over the 'overlay' picture.
Depending on the graphics hardware, overlays can also be resized in hardware, and use a different
colorspace from other parts of the framebuffer (for example, a video overlay can be in YUV format
while the framebuffer is in RGB or even in a 256 color palette mode).
Cursor Management
-----------------
@ -170,3 +276,4 @@ machine for the remote_app_server, or drawing for a specific window can be grant
to the framebuffer on a specific display and video card, while other applications go through the
normal process of drawing only to their currently exposed region only.