Implemented a double buffered mode for the case that the screen is 32 bit.

It works by adjusting the display_mode to have twice the virtual height to
cause the graphics driver to allocate enough frame buffer. Since hardware
acceleration calls are based on geometry locations instead of memory locations,
accelerated calls can work in the offscreen buffer with this method.
Similarily, the new CopyBackToFront() method copies the back buffer into the
front buffer with hardware acceleration. The code is currently disabled, since
not all graphics drivers handle the virtual versus display size correctly,
for example the intel_extreme driver. It works for example with the radeon
driver, but another problem prevents me to judge the benefit of this method.
Most types of screen redraws are flicker free, though, which is the whole point
of the excercise. :-)
Another problem with the current code is the initial mode switch. It does
not try again to double the frame buffer in the fall back code paths. It could
also check the frame buffer memory at all, before even trying to save some
cycles.
To see it in action, uncomment the code at line 508.


git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@26545 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Stephan Aßmus 2008-07-21 12:50:32 +00:00
parent 576305a5d8
commit 1f855f9696
2 changed files with 308 additions and 204 deletions

View File

@ -134,6 +134,7 @@ AccelerantHWInterface::AccelerantHWInterface()
fBackBuffer(NULL),
fFrontBuffer(new (nothrow) AccelerantBuffer()),
fOffscreenBackBuffer(false),
fInitialModeSwitch(true),
@ -487,8 +488,7 @@ AccelerantHWInterface::SetMode(const display_mode& mode)
// error.
// prevent from doing the unnecessary
if (fModeCount > 0 && fBackBuffer && fFrontBuffer
&& fDisplayMode == mode) {
if (fModeCount > 0 && fFrontBuffer && fDisplayMode == mode) {
// TODO: better comparison of display modes
return B_OK;
}
@ -503,12 +503,32 @@ AccelerantHWInterface::SetMode(const display_mode& mode)
display_mode newMode = mode;
bool tryOffscreenBackBuffer = false;
fOffscreenBackBuffer = false;
#if 0
if (fVGADevice < 0 && (color_space)newMode.space == B_RGB32) {
// we should have an accelerated graphics driver, try
// to allocate a frame buffer large enough to contain
// the back buffer for double buffered drawing
newMode.virtual_height *= 2;
tryOffscreenBackBuffer = true;
}
#endif
status_t status = fAccSetDisplayMode(&newMode);
if (status != B_OK) {
ATRACE(("setting display mode failed\n"));
if (!fInitialModeSwitch)
return status;
// undo the offscreen backbuffer trick when trying the various
// fall back methods for the initial mode switch
// TODO: Do it even then, but it is more involved.
if (tryOffscreenBackBuffer) {
newMode.virtual_height /= 2;
tryOffscreenBackBuffer = false;
}
if (fModeList == NULL) {
status = _UpdateModeList();
if (status < B_OK)
@ -538,6 +558,15 @@ AccelerantHWInterface::SetMode(const display_mode& mode)
}
}
if (tryOffscreenBackBuffer) {
// The offscreen backbuffer was successfully allocated, since
// the mode switch succeeded! This should be handled transparently
// though and not be reflected in the mode structure, so that
// even real virtual screens should work eventually...
newMode.virtual_height /= 2;
fOffscreenBackBuffer = true;
}
fDisplayMode = newMode;
fInitialModeSwitch = false;
@ -552,19 +581,33 @@ AccelerantHWInterface::SetMode(const display_mode& mode)
// Update the frame buffer used by the on-screen KDL
#ifndef HAIKU_TARGET_PLATFORM_LIBBE_TEST
uint32 depth = (fFrameBufferConfig.bytes_per_row
/ fDisplayMode.virtual_width) << 3;
/ fFrontBuffer->Width()) << 3;
if (fDisplayMode.space == B_RGB15)
depth = 15;
_kern_frame_buffer_update(fFrameBufferConfig.frame_buffer,
fDisplayMode.virtual_width, fDisplayMode.virtual_height,
fFrontBuffer->Width(), fFrontBuffer->Height(),
depth, fFrameBufferConfig.bytes_per_row);
#endif
// update acceleration hooks
fAccFillRect = (fill_rectangle)fAccelerantHook(B_FILL_RECTANGLE,
(void *)&fDisplayMode);
fAccInvertRect = (invert_rectangle)fAccelerantHook(B_INVERT_RECTANGLE,
(void *)&fDisplayMode);
fAccScreenBlit = (screen_to_screen_blit)fAccelerantHook(
B_SCREEN_TO_SCREEN_BLIT, (void *)&fDisplayMode);
// in case there is no accelerated blit function, using
// an offscreen located backbuffer will not be beneficial!
if (fAccScreenBlit == NULL)
fOffscreenBackBuffer = false;
// update backbuffer if neccessary
if (!fBackBuffer || fBackBuffer->Width() != fDisplayMode.virtual_width
|| fBackBuffer->Height() != fDisplayMode.virtual_height
|| (fDisplayMode.space == B_RGB32 && fBackBuffer != NULL
if (!fBackBuffer || fBackBuffer->Width() != fFrontBuffer->Width()
|| fBackBuffer->Height() != fFrontBuffer->Height()
|| fOffscreenBackBuffer
|| (fFrontBuffer->ColorSpace() == B_RGB32 && fBackBuffer != NULL
&& !HWInterface::IsDoubleBuffered())) {
// NOTE: backbuffer is always B_RGBA32, this simplifies the
// drawing backend implementation tremendously for the time
@ -577,19 +620,25 @@ AccelerantHWInterface::SetMode(const display_mode& mode)
// -> fall back to double buffer for fDisplayMode.space != B_RGB32
// as intermediate solution...
bool doubleBuffered = HWInterface::IsDoubleBuffered();
if (((color_space)fDisplayMode.space != B_RGB32
&& (color_space)fDisplayMode.space != B_RGBA32)
|| fVGADevice > 0)
if ((fFrontBuffer->ColorSpace() != B_RGB32
&& fFrontBuffer->ColorSpace() != B_RGBA32)
|| fVGADevice >= 0 || fOffscreenBackBuffer)
doubleBuffered = true;
if (doubleBuffered) {
fBackBuffer = new(nothrow) MallocBuffer(fDisplayMode.virtual_width,
fDisplayMode.virtual_height);
if (fOffscreenBackBuffer) {
fBackBuffer = new(nothrow) AccelerantBuffer(*fFrontBuffer,
true);
} else {
fBackBuffer = new(nothrow) MallocBuffer(fFrontBuffer->Width(),
fFrontBuffer->Height());
}
status = fBackBuffer ? fBackBuffer->InitCheck() : B_NO_MEMORY;
if (status < B_OK) {
delete fBackBuffer;
fBackBuffer = NULL;
fOffscreenBackBuffer = false;
return status;
}
// clear out backbuffer, alpha is 255 this way
@ -597,19 +646,13 @@ AccelerantHWInterface::SetMode(const display_mode& mode)
}
}
// update color palette configuration if necessary
if (fDisplayMode.space == B_CMAP8)
_SetSystemPalette();
else if (fDisplayMode.space == B_GRAY8)
_SetGrayscalePalette();
// update acceleration hooks
fAccFillRect = (fill_rectangle)fAccelerantHook(B_FILL_RECTANGLE,
(void *)&fDisplayMode);
fAccInvertRect = (invert_rectangle)fAccelerantHook(B_INVERT_RECTANGLE,
(void *)&fDisplayMode);
fAccScreenBlit = (screen_to_screen_blit)fAccelerantHook(
B_SCREEN_TO_SCREEN_BLIT, (void *)&fDisplayMode);
// notify all listeners about the mode change
_NotifyFrameBufferChanged();
return status;
@ -764,11 +807,11 @@ AccelerantHWInterface::GetPreferredMode(display_mode* preferredMode)
{
status_t status = B_NOT_SUPPORTED;
if (fAccGetPreferredDisplayMode != NULL) {
status = fAccGetPreferredDisplayMode(preferredMode);
if (status == B_OK)
return B_OK;
}
// if (fAccGetPreferredDisplayMode != NULL) {
// status = fAccGetPreferredDisplayMode(preferredMode);
// if (status == B_OK)
// return B_OK;
// }
if (fAccGetEDIDInfo != NULL) {
edid1_info info;
@ -804,6 +847,7 @@ AccelerantHWInterface::GetPreferredMode(display_mode* preferredMode)
display_mode modeFound;
display_mode mode;
mode.timing.pixel_clock = timing.pixel_clock * 10;
mode.timing.h_display = timing.h_active;
mode.timing.h_sync_start = timing.h_active + timing.h_sync_off;
@ -815,15 +859,16 @@ AccelerantHWInterface::GetPreferredMode(display_mode* preferredMode)
mode.timing.v_sync_end = mode.timing.v_sync_start
+ timing.v_sync_width;
mode.timing.v_total = timing.v_active + timing.v_blank;
mode.space = B_RGB32;
mode.virtual_width = timing.h_active;
mode.virtual_height = timing.v_active;
mode.virtual_width = mode.timing.h_display;
mode.virtual_height = mode.timing.v_display;
// TODO: eventually ignore detailed modes for the preferred one
// if there are more than one usable?
int32 diff;
status = _FindBestMode(mode, aspectRatio, modeFound, &diff);
if (status == B_OK) {
if (_FindBestMode(mode, aspectRatio, modeFound, &diff) == B_OK) {
status = B_OK;
if (diff < bestDiff) {
bestMode = modeFound;
bestDiff = diff;
@ -1013,13 +1058,15 @@ AccelerantHWInterface::GetDriverPath(BString &string)
}
// AvailableHardwareAcceleration
// #pragma mark - acceleration
uint32
AccelerantHWInterface::AvailableHWAcceleration() const
{
uint32 flags = 0;
if (!IsDoubleBuffered()) {
if (!IsDoubleBuffered() || fOffscreenBackBuffer) {
if (fAccScreenBlit)
flags |= HW_ACC_COPY_REGION;
if (fAccFillRect)
@ -1032,6 +1079,72 @@ AccelerantHWInterface::AvailableHWAcceleration() const
}
void
AccelerantHWInterface::CopyRegion(const clipping_rect* sortedRectList,
uint32 count, int32 xOffset, int32 yOffset)
{
_CopyRegion(sortedRectList, count, xOffset, yOffset, fOffscreenBackBuffer);
}
void
AccelerantHWInterface::FillRegion(/*const*/ BRegion& region,
const rgb_color& color, bool autoSync)
{
if (fAccFillRect && fAccAcquireEngine) {
if (fAccAcquireEngine(B_2D_ACCELERATION, 0xff, &fSyncToken,
&fEngineToken) >= B_OK) {
// convert the region
uint32 count;
_RegionToRectParams(&region, &count);
// go
fAccFillRect(fEngineToken, _NativeColor(color), fRectParams, count);
// done
if (fAccReleaseEngine)
fAccReleaseEngine(fEngineToken, &fSyncToken);
// sync
if (autoSync && fAccSyncToToken)
fAccSyncToToken(&fSyncToken);
}
}
}
void
AccelerantHWInterface::InvertRegion(/*const*/ BRegion& region)
{
if (fAccInvertRect && fAccAcquireEngine) {
if (fAccAcquireEngine(B_2D_ACCELERATION, 0xff, &fSyncToken,
&fEngineToken) >= B_OK) {
// convert the region
uint32 count;
_RegionToRectParams(&region, &count);
fAccInvertRect(fEngineToken, fRectParams, count);
if (fAccReleaseEngine)
fAccReleaseEngine(fEngineToken, &fSyncToken);
if (fAccSyncToToken)
fAccSyncToToken(&fSyncToken);
}
}
}
void
AccelerantHWInterface::Sync()
{
if (fAccSyncToToken)
fAccSyncToToken(&fSyncToken);
}
// #pragma mark - overlays
overlay_token
AccelerantHWInterface::AcquireOverlayChannel()
{
@ -1156,15 +1269,151 @@ AccelerantHWInterface::HideOverlay(Overlay* overlay)
}
// CopyRegion
// #pragma mark - cursor
void
AccelerantHWInterface::CopyRegion(const clipping_rect* sortedRectList,
uint32 count, int32 xOffset, int32 yOffset)
AccelerantHWInterface::SetCursor(ServerCursor* cursor)
{
HWInterface::SetCursor(cursor);
// if (LockExclusiveAccess()) {
// TODO: implement setting the hard ware cursor
// NOTE: cursor should be always B_RGBA32
// NOTE: The HWInterface implementation should
// still be called, since it takes ownership of
// the cursor.
// UnlockExclusiveAccess();
// }
}
void
AccelerantHWInterface::SetCursorVisible(bool visible)
{
HWInterface::SetCursorVisible(visible);
// if (LockExclusiveAccess()) {
// TODO: update graphics hardware
// UnlockExclusiveAccess();
// }
}
void
AccelerantHWInterface::MoveCursorTo(const float& x, const float& y)
{
HWInterface::MoveCursorTo(x, y);
// if (LockExclusiveAccess()) {
// TODO: update graphics hardware
// UnlockExclusiveAccess();
// }
}
// #pragma mark - buffer access
RenderingBuffer *
AccelerantHWInterface::FrontBuffer() const
{
return fFrontBuffer;
}
RenderingBuffer *
AccelerantHWInterface::BackBuffer() const
{
return fBackBuffer;
}
bool
AccelerantHWInterface::IsDoubleBuffered() const
{
return fBackBuffer != NULL;
}
void
AccelerantHWInterface::CopyBackToFront(/*const*/ BRegion& region)
{
if (fOffscreenBackBuffer) {
int32 xOffset = 0;
int32 yOffset = -(int32)fFrontBuffer->Height();
int32 count = region.CountRects();
clipping_rect rects[count];
for (int32 i = 0; i < count; i++) {
rects[i] = region.RectAtInt(i);
rects[i].top -= yOffset;
rects[i].bottom -= yOffset;
}
_CopyRegion(rects, count, xOffset, yOffset, false);
return;
}
return HWInterface::CopyBackToFront(region);
}
// #pragma mark -
void
AccelerantHWInterface::_DrawCursor(IntRect area) const
{
// use the default implementation for now,
// until we have a hardware cursor
HWInterface::_DrawCursor(area);
// TODO: this would only be called, if we don't have
// a hardware cursor for some reason
}
void
AccelerantHWInterface::_RegionToRectParams(/*const*/ BRegion* region,
uint32* count) const
{
*count = region->CountRects();
// TODO: locking!!
if (fRectParamsCount < *count) {
fRectParamsCount = (*count / kDefaultParamsCount + 1)
* kDefaultParamsCount;
// NOTE: realloc() could be used instead...
fill_rect_params* params
= new (nothrow) fill_rect_params[fRectParamsCount];
if (params) {
delete[] fRectParams;
fRectParams = params;
} else {
*count = fRectParamsCount;
}
}
int32 srcOffsetY = fOffscreenBackBuffer ? fFrontBuffer->Height() : 0;
for (uint32 i = 0; i < *count; i++) {
clipping_rect r = region->RectAtInt(i);
fRectParams[i].left = (uint16)r.left;
fRectParams[i].top = (uint16)r.top + srcOffsetY;
fRectParams[i].right = (uint16)r.right;
fRectParams[i].bottom = (uint16)r.bottom + srcOffsetY;
}
}
void
AccelerantHWInterface::_CopyRegion(const clipping_rect* sortedRectList,
uint32 count, int32 xOffset, int32 yOffset, bool inBackBuffer)
{
if (fAccScreenBlit && fAccAcquireEngine) {
if (fAccAcquireEngine(B_2D_ACCELERATION, 0xff, &fSyncToken,
&fEngineToken) >= B_OK) {
// make sure the blit_params cache is large enough
// TODO: locking!!
if (fBlitParamsCount < count) {
fBlitParamsCount = (count / kDefaultParamsCount + 1)
* kDefaultParamsCount;
@ -1178,15 +1427,17 @@ AccelerantHWInterface::CopyRegion(const clipping_rect* sortedRectList,
count = fBlitParamsCount;
}
}
int32 srcOffsetY = inBackBuffer ? fFrontBuffer->Height() : 0;
// convert the rects
for (uint32 i = 0; i < count; i++) {
fBlitParams[i].src_left = (uint16)sortedRectList[i].left;
fBlitParams[i].src_top = (uint16)sortedRectList[i].top;
fBlitParams[i].src_top = (uint16)sortedRectList[i].top
+ srcOffsetY;
fBlitParams[i].dest_left = (uint16)sortedRectList[i].left
+ xOffset;
fBlitParams[i].dest_top = (uint16)sortedRectList[i].top
+ yOffset;
+ yOffset + srcOffsetY;
// NOTE: width and height are expressed as distance, not
// pixel count!
@ -1210,160 +1461,7 @@ AccelerantHWInterface::CopyRegion(const clipping_rect* sortedRectList,
}
}
// FillRegion
void
AccelerantHWInterface::FillRegion(/*const*/ BRegion& region,
const rgb_color& color, bool autoSync)
{
if (fAccFillRect && fAccAcquireEngine) {
if (fAccAcquireEngine(B_2D_ACCELERATION, 0xff, &fSyncToken,
&fEngineToken) >= B_OK) {
// convert the region
uint32 count;
_RegionToRectParams(&region, &count);
// go
fAccFillRect(fEngineToken, _NativeColor(color), fRectParams, count);
// done
if (fAccReleaseEngine)
fAccReleaseEngine(fEngineToken, &fSyncToken);
// sync
if (autoSync && fAccSyncToToken)
fAccSyncToToken(&fSyncToken);
}
}
}
// InvertRegion
void
AccelerantHWInterface::InvertRegion(/*const*/ BRegion& region)
{
if (fAccInvertRect && fAccAcquireEngine) {
if (fAccAcquireEngine(B_2D_ACCELERATION, 0xff, &fSyncToken,
&fEngineToken) >= B_OK) {
// convert the region
uint32 count;
_RegionToRectParams(&region, &count);
fAccInvertRect(fEngineToken, fRectParams, count);
if (fAccReleaseEngine)
fAccReleaseEngine(fEngineToken, &fSyncToken);
if (fAccSyncToToken)
fAccSyncToToken(&fSyncToken);
}
}
}
// Sync
void
AccelerantHWInterface::Sync()
{
if (fAccSyncToToken)
fAccSyncToToken(&fSyncToken);
}
// SetCursor
void
AccelerantHWInterface::SetCursor(ServerCursor* cursor)
{
HWInterface::SetCursor(cursor);
// if (LockExclusiveAccess()) {
// TODO: implement setting the hard ware cursor
// NOTE: cursor should be always B_RGBA32
// NOTE: The HWInterface implementation should
// still be called, since it takes ownership of
// the cursor.
// UnlockExclusiveAccess();
// }
}
// SetCursorVisible
void
AccelerantHWInterface::SetCursorVisible(bool visible)
{
HWInterface::SetCursorVisible(visible);
// if (LockExclusiveAccess()) {
// TODO: update graphics hardware
// UnlockExclusiveAccess();
// }
}
// MoveCursorTo
void
AccelerantHWInterface::MoveCursorTo(const float& x, const float& y)
{
HWInterface::MoveCursorTo(x, y);
// if (LockExclusiveAccess()) {
// TODO: update graphics hardware
// UnlockExclusiveAccess();
// }
}
// FrontBuffer
RenderingBuffer *
AccelerantHWInterface::FrontBuffer() const
{
return fFrontBuffer;
}
// BackBuffer
RenderingBuffer *
AccelerantHWInterface::BackBuffer() const
{
return fBackBuffer;
}
// IsDoubleBuffered
bool
AccelerantHWInterface::IsDoubleBuffered() const
{
return fBackBuffer != NULL;
}
// _DrawCursor
void
AccelerantHWInterface::_DrawCursor(IntRect area) const
{
// use the default implementation for now,
// until we have a hardware cursor
HWInterface::_DrawCursor(area);
// TODO: this would only be called, if we don't have
// a hardware cursor for some reason
}
// _RegionToRectParams
void
AccelerantHWInterface::_RegionToRectParams(/*const*/ BRegion* region,
uint32* count) const
{
*count = region->CountRects();
if (fRectParamsCount < *count) {
fRectParamsCount = (*count / kDefaultParamsCount + 1)
* kDefaultParamsCount;
// NOTE: realloc() could be used instead...
fill_rect_params* params
= new (nothrow) fill_rect_params[fRectParamsCount];
if (params) {
delete[] fRectParams;
fRectParams = params;
} else {
*count = fRectParamsCount;
}
}
for (uint32 i = 0; i < *count; i++) {
clipping_rect r = region->RectAtInt(i);
fRectParams[i].left = (uint16)r.left;
fRectParams[i].top = (uint16)r.top;
fRectParams[i].right = (uint16)r.right;
fRectParams[i].bottom = (uint16)r.bottom;
}
}
// _NativeColor
uint32
AccelerantHWInterface::_NativeColor(const rgb_color& color) const
{

View File

@ -15,8 +15,8 @@
#include <image.h>
#include <video_overlay.h>
class MallocBuffer;
class AccelerantBuffer;
class RenderingBuffer;
class AccelerantHWInterface : public HWInterface {
@ -54,9 +54,20 @@ public:
virtual status_t GetAccelerantPath(BString &path);
virtual status_t GetDriverPath(BString &path);
// query for available hardware accleration and perform it
// query for available hardware accleration
virtual uint32 AvailableHWAcceleration() const;
// accelerated drawing
virtual void CopyRegion(const clipping_rect* sortedRectList,
uint32 count,
int32 xOffset, int32 yOffset);
virtual void FillRegion(/*const*/ BRegion& region,
const rgb_color& color,
bool autoSync);
virtual void InvertRegion(/*const*/ BRegion& region);
virtual void Sync();
// overlay support
virtual overlay_token AcquireOverlayChannel();
virtual void ReleaseOverlayChannel(overlay_token token);
@ -72,17 +83,6 @@ public:
virtual void ConfigureOverlay(Overlay* overlay);
virtual void HideOverlay(Overlay* overlay);
// accelerated drawing
virtual void CopyRegion(const clipping_rect* sortedRectList,
uint32 count,
int32 xOffset, int32 yOffset);
virtual void FillRegion(/*const*/ BRegion& region,
const rgb_color& color,
bool autoSync);
virtual void InvertRegion(/*const*/ BRegion& region);
virtual void Sync();
// cursor handling
virtual void SetCursor(ServerCursor* cursor);
virtual void SetCursorVisible(bool visible);
@ -95,6 +95,8 @@ public:
virtual bool IsDoubleBuffered() const;
protected:
virtual void CopyBackToFront(/*const*/ BRegion& region);
virtual void _DrawCursor(IntRect area) const;
private:
@ -104,7 +106,10 @@ private:
status_t _UpdateModeList();
status_t _UpdateFrameBufferConfig();
void _RegionToRectParams(/*const*/ BRegion* region,
uint32* count) const;
uint32* count) const;
void _CopyRegion(const clipping_rect* sortedRectList,
uint32 count, int32 xOffset, int32 yOffset,
bool inBackBuffer);
uint32 _NativeColor(const rgb_color& color) const;
status_t _FindBestMode(const display_mode& compareMode,
float compareAspectRatio,
@ -164,8 +169,9 @@ private:
int fModeCount;
display_mode* fModeList;
MallocBuffer* fBackBuffer;
RenderingBuffer* fBackBuffer;
AccelerantBuffer* fFrontBuffer;
bool fOffscreenBackBuffer;
display_mode fDisplayMode;
bool fInitialModeSwitch;