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 <[email protected]> Reviewed-by: Adrien Destugues <[email protected]>
This commit is contained in:
committed by
Adrien Destugues
parent
53df6323c8
commit
2ab05dc925
@@ -1,6 +1,28 @@
|
|||||||
Graphics
|
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
|
Desktop Initialization
|
||||||
-----------------------
|
-----------------------
|
||||||
|
|
||||||
@@ -133,6 +155,40 @@ action.
|
|||||||
Screen Updates
|
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
|
Screen updates are done entirely through the BView class or some
|
||||||
subclass thereof, hereafter referred to as a view. A view's drawing
|
subclass thereof, hereafter referred to as a view. A view's drawing
|
||||||
commands will cause its window to store draw command messages in a
|
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
|
appropriate command in the graphics module for the Layer corresponding
|
||||||
to the view which sent the command.
|
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
|
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
|
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.
|
normal process of drawing only to their currently exposed region only.
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user