/* * 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 }