| /* |
| * Copyright 2006-2012, Haiku, Inc. All rights reserved. |
| * Distributed under the terms of the MIT License. |
| * |
| * Authors: |
| * Jérôme Duval, korli@users.berlios.de |
| * Philippe Houdoin, philippe.houdoin@free.fr |
| * Artur Wyszynski, harakash@gmail.com |
| * Alexander von Gluck IV, kallisti5@unixzen.com |
| */ |
| |
| |
| #include "SoftwareRenderer.h" |
| |
| #include <Autolock.h> |
| #include <interface/DirectWindowPrivate.h> |
| #include <GraphicsDefs.h> |
| #include <Screen.h> |
| #include <stdio.h> |
| #include <sys/time.h> |
| #include <new> |
| |
| |
| #ifdef DEBUG |
| # define TRACE(x...) printf("SoftwareRenderer: " x) |
| # define CALLED() TRACE("CALLED: %s\n", __PRETTY_FUNCTION__) |
| #else |
| # define TRACE(x...) |
| # define CALLED() |
| #endif |
| #define ERROR(x...) printf("SoftwareRenderer: " x) |
| |
| |
| extern const char* color_space_name(color_space space); |
| |
| |
| extern "C" _EXPORT BGLRenderer* |
| instantiate_gl_renderer(BGLView *view, ulong opts, BGLDispatcher *dispatcher) |
| { |
| return new SoftwareRenderer(view, opts, dispatcher); |
| } |
| |
| SoftwareRenderer::SoftwareRenderer(BGLView *view, ulong options, |
| BGLDispatcher* dispatcher) |
| : |
| BGLRenderer(view, options, dispatcher), |
| fBitmap(NULL), |
| fDirectModeEnabled(false), |
| fInfo(NULL), |
| fInfoLocker("info locker"), |
| fOptions(options), |
| fColorSpace(B_NO_COLOR_SPACE) |
| { |
| CALLED(); |
| |
| // Disable double buffer for the moment. |
| //options &= ~BGL_DOUBLE; |
| |
| // Initialize the "Haiku Software GL Pipe" |
| time_t beg; |
| time_t end; |
| beg = time(NULL); |
| fContextObj = new GalliumContext(options); |
| end = time(NULL); |
| TRACE("Haiku Software GL Pipe initialization time: %f.\n", |
| difftime(end, beg)); |
| |
| // Allocate a bitmap |
| BRect b = view->Bounds(); |
| fColorSpace = BScreen(view->Window()).ColorSpace(); |
| TRACE("%s: Colorspace:\t%s\n", __func__, color_space_name(fColorSpace)); |
| |
| fWidth = (GLint)b.IntegerWidth(); |
| fHeight = (GLint)b.IntegerHeight(); |
| |
| _AllocateBitmap(); |
| |
| // Initialize the first "Haiku Software GL Pipe" context |
| beg = time(NULL); |
| fContextID = fContextObj->CreateContext(fBitmap); |
| end = time(NULL); |
| |
| if (fContextID < 0) |
| ERROR("%s: There was an error creating the context!\n", __func__); |
| else { |
| TRACE("%s: Haiku Software GL Pipe context creation time: %f.\n", |
| __func__, difftime(end, beg)); |
| } |
| |
| if (!fContextObj->GetCurrentContext()) |
| LockGL(); |
| } |
| |
| |
| SoftwareRenderer::~SoftwareRenderer() |
| { |
| CALLED(); |
| |
| if (fContextObj) |
| delete fContextObj; |
| if (fBitmap) |
| delete fBitmap; |
| } |
| |
| |
| void |
| SoftwareRenderer::LockGL() |
| { |
| // CALLED(); |
| BGLRenderer::LockGL(); |
| |
| color_space cs = BScreen(GLView()->Window()).ColorSpace(); |
| |
| BAutolock lock(fInfoLocker); |
| if (fDirectModeEnabled && fInfo != NULL) { |
| fWidth = fInfo->window_bounds.right - fInfo->window_bounds.left; |
| fHeight = fInfo->window_bounds.bottom - fInfo->window_bounds.top; |
| } |
| |
| if (fBitmap && cs == fColorSpace && fContextObj->Validate(fWidth, fHeight)) { |
| fContextObj->SetCurrentContext(fBitmap, fContextID); |
| return; |
| } |
| |
| fColorSpace = cs; |
| |
| _AllocateBitmap(); |
| fContextObj->SetCurrentContext(fBitmap, fContextID); |
| } |
| |
| |
| void |
| SoftwareRenderer::UnlockGL() |
| { |
| // CALLED(); |
| if ((fOptions & BGL_DOUBLE) == 0) { |
| SwapBuffers(); |
| } |
| fContextObj->SetCurrentContext(NULL, fContextID); |
| BGLRenderer::UnlockGL(); |
| } |
| |
| |
| void |
| SoftwareRenderer::SwapBuffers(bool vsync) |
| { |
| // CALLED(); |
| if (!fBitmap) |
| return; |
| |
| BScreen screen(GLView()->Window()); |
| |
| fContextObj->SwapBuffers(fContextID); |
| |
| BAutolock lock(fInfoLocker); |
| |
| if (!fDirectModeEnabled || fInfo == NULL) { |
| if (GLView()->LockLooperWithTimeout(1000) == B_OK) { |
| GLView()->DrawBitmap(fBitmap, B_ORIGIN); |
| GLView()->UnlockLooper(); |
| if (vsync) |
| screen.WaitForRetrace(); |
| } |
| return; |
| } |
| |
| // check the bitmap size still matches the size |
| if (fInfo->window_bounds.bottom - fInfo->window_bounds.top |
| != fBitmap->Bounds().IntegerHeight() |
| || fInfo->window_bounds.right - fInfo->window_bounds.left |
| != fBitmap->Bounds().IntegerWidth()) { |
| ERROR("%s: Bitmap size doesn't match size!\n", __func__); |
| return; |
| } |
| |
| uint32 bytesPerRow = fBitmap->BytesPerRow(); |
| uint8 bytesPerPixel = bytesPerRow / fBitmap->Bounds().IntegerWidth(); |
| |
| for (uint32 i = 0; i < fInfo->clip_list_count; i++) { |
| clipping_rect *clip = &fInfo->clip_list[i]; |
| int32 height = clip->bottom - clip->top + 1; |
| int32 bytesWidth |
| = (clip->right - clip->left + 1) * bytesPerPixel; |
| bytesWidth -= bytesPerPixel; |
| uint8 *p = (uint8 *)fInfo->bits + clip->top |
| * fInfo->bytes_per_row + clip->left * bytesPerPixel; |
| uint8 *b = (uint8 *)fBitmap->Bits() |
| + (clip->top - fInfo->window_bounds.top) * bytesPerRow |
| + (clip->left - fInfo->window_bounds.left) * bytesPerPixel; |
| |
| for (int y = 0; y < height - 1; y++) { |
| memcpy(p, b, bytesWidth); |
| p += fInfo->bytes_per_row; |
| b += bytesPerRow; |
| } |
| } |
| |
| if (vsync) |
| screen.WaitForRetrace(); |
| } |
| |
| |
| void |
| SoftwareRenderer::Draw(BRect updateRect) |
| { |
| // CALLED(); |
| if ((!fDirectModeEnabled || fInfo == NULL) && fBitmap) |
| GLView()->DrawBitmap(fBitmap, updateRect, updateRect); |
| } |
| |
| |
| status_t |
| SoftwareRenderer::CopyPixelsOut(BPoint location, BBitmap *bitmap) |
| { |
| CALLED(); |
| color_space scs = fBitmap->ColorSpace(); |
| color_space dcs = bitmap->ColorSpace(); |
| |
| if (scs != dcs && (scs != B_RGBA32 || dcs != B_RGB32)) { |
| ERROR("%s::CopyPixelsOut(): incompatible color space: %s != %s\n", |
| __PRETTY_FUNCTION__, color_space_name(scs), color_space_name(dcs)); |
| return B_BAD_TYPE; |
| } |
| |
| BRect sr = fBitmap->Bounds(); |
| BRect dr = bitmap->Bounds(); |
| |
| // int32 w1 = sr.IntegerWidth(); |
| // int32 h1 = sr.IntegerHeight(); |
| // int32 w2 = dr.IntegerWidth(); |
| // int32 h2 = dr.IntegerHeight(); |
| |
| sr = sr & dr.OffsetBySelf(location); |
| dr = sr.OffsetByCopy(-location.x, -location.y); |
| |
| uint8 *ps = (uint8 *) fBitmap->Bits(); |
| uint8 *pd = (uint8 *) bitmap->Bits(); |
| uint32 *s, *d; |
| uint32 y; |
| for (y = (uint32) sr.top; y <= (uint32) sr.bottom; y++) { |
| s = (uint32 *)(ps + y * fBitmap->BytesPerRow()); |
| s += (uint32) sr.left; |
| |
| d = (uint32 *)(pd + (y + (uint32)(dr.top - sr.top)) |
| * bitmap->BytesPerRow()); |
| d += (uint32) dr.left; |
| memcpy(d, s, dr.IntegerWidth() * 4); |
| } |
| |
| return B_OK; |
| } |
| |
| |
| status_t |
| SoftwareRenderer::CopyPixelsIn(BBitmap *bitmap, BPoint location) |
| { |
| CALLED(); |
| |
| color_space sourceCS = bitmap->ColorSpace(); |
| color_space destinationCS = fBitmap->ColorSpace(); |
| |
| if (sourceCS != destinationCS |
| && (sourceCS != B_RGB32 || destinationCS != B_RGBA32)) { |
| ERROR("%s::CopyPixelsIn(): incompatible color space: %s != %s\n", |
| __PRETTY_FUNCTION__, color_space_name(sourceCS), |
| color_space_name(destinationCS)); |
| return B_BAD_TYPE; |
| } |
| |
| BRect sr = bitmap->Bounds(); |
| BRect dr = fBitmap->Bounds(); |
| |
| sr = sr & dr.OffsetBySelf(location); |
| dr = sr.OffsetByCopy(-location.x, -location.y); |
| |
| uint8 *ps = (uint8 *) bitmap->Bits(); |
| uint8 *pd = (uint8 *) fBitmap->Bits(); |
| uint32 *s, *d; |
| uint32 y; |
| |
| for (y = (uint32) sr.top; y <= (uint32) sr.bottom; y++) { |
| s = (uint32 *)(ps + y * bitmap->BytesPerRow()); |
| s += (uint32) sr.left; |
| |
| d = (uint32 *)(pd + (y + (uint32)(dr.top - sr.top)) |
| * fBitmap->BytesPerRow()); |
| d += (uint32) dr.left; |
| |
| memcpy(d, s, dr.IntegerWidth() * 4); |
| } |
| |
| return B_OK; |
| } |
| |
| |
| void |
| SoftwareRenderer::EnableDirectMode(bool enabled) |
| { |
| fDirectModeEnabled = enabled; |
| } |
| |
| |
| void |
| SoftwareRenderer::DirectConnected(direct_buffer_info *info) |
| { |
| // CALLED(); |
| BAutolock lock(fInfoLocker); |
| if (info) { |
| if (!fInfo) { |
| fInfo = (direct_buffer_info *)calloc(1, |
| DIRECT_BUFFER_INFO_AREA_SIZE); |
| } |
| memcpy(fInfo, info, DIRECT_BUFFER_INFO_AREA_SIZE); |
| } else if (fInfo) { |
| free(fInfo); |
| fInfo = NULL; |
| } |
| } |
| |
| |
| void |
| SoftwareRenderer::FrameResized(float width, float height) |
| { |
| TRACE("%s: %f x %f\n", __func__, width, height); |
| |
| BAutolock lock(fInfoLocker); |
| fWidth = (GLuint)width; |
| fHeight = (GLuint)height; |
| } |
| |
| |
| void |
| SoftwareRenderer::_AllocateBitmap() |
| { |
| // CALLED(); |
| |
| // allocate new size of back buffer bitmap |
| BAutolock lock(fInfoLocker); |
| if (fBitmap) |
| delete fBitmap; |
| |
| if (fWidth < 1 || fHeight < 1) { |
| TRACE("%s: Can't allocate bitmap of %dx%d\n", __func__, |
| fWidth, fHeight); |
| return; |
| } |
| BRect rect(0.0, 0.0, fWidth, fHeight); |
| fBitmap = new (std::nothrow) BBitmap(rect, fColorSpace); |
| if (fBitmap == NULL) { |
| TRACE("%s: Can't create bitmap!\n", __func__); |
| return; |
| } |
| |
| TRACE("%s: New bitmap size: %" B_PRId32 " x %" B_PRId32 "\n", __func__, |
| fBitmap->Bounds().IntegerWidth(), fBitmap->Bounds().IntegerHeight()); |
| |
| #if 0 |
| // debug.. |
| void *data = fBitmap->Bits(); |
| memset(data, 0xcc, fBitmap->BitsLength()); |
| #endif |
| } |