/* * 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 #include #include #include #include #include #include #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) { return new SoftwareRenderer(view, opts); } struct RasBuf32 { int32 width, height, stride; int32 orgX, orgY; int32 *colors; RasBuf32(int32 width, int32 height, int32 stride, int32 orgX, int32 orgY, int32 *colors): width(width), height(height), stride(stride), orgX(orgX), orgY(orgY), colors(colors) {} RasBuf32(BBitmap *bmp) { width = bmp->Bounds().IntegerWidth() + 1; height = bmp->Bounds().IntegerHeight() + 1; stride = bmp->BytesPerRow()/4; orgX = 0; orgY = 0; colors = (int32*)bmp->Bits(); } RasBuf32(direct_buffer_info *info) { width = 0x7fffffff; height = 0x7fffffff; stride = info->bytes_per_row/4; orgX = 0; orgY = 0; colors = (int32*)info->bits; } void ClipSize(int32 x, int32 y, int32 w, int32 h) { if (x < 0) {w += x; x = 0;} if (y < 0) {h += y; y = 0;} if (x + w > width) {w = width - x;} if (y + h > height) {h = height - y;} if ((w > 0) && (h > 0)) { colors += y*stride + x; width = w; height = h; } else { width = 0; height = 0; colors = NULL; } if (x + orgX > 0) {orgX += x;} else {orgX = 0;} if (y + orgY > 0) {orgY += y;} else {orgY = 0;} } void ClipRect(int32 l, int32 t, int32 r, int32 b) { ClipSize(l, t, r - l, b - t); } void Shift(int32 dx, int32 dy) { orgX += dx; orgY += dy; } void Clear(int32 color) { RasBuf32 dst = *this; dst.stride -= dst.width; for (; dst.height > 0; dst.height--) { for (int32 i = dst.width; i > 0; i--) *dst.colors++ = color; dst.colors += dst.stride; } } void Blit(RasBuf32 src) { RasBuf32 dst = *this; int32 x, y; x = src.orgX - orgX; y = src.orgY - orgY; dst.ClipSize(x, y, src.width, src.height); src.ClipSize(-x, -y, width, height); for (; dst.height > 0; dst.height--) { memcpy(dst.colors, src.colors, 4*dst.width); dst.colors += dst.stride; src.colors += src.stride; } } }; SoftwareRenderer::SoftwareRenderer(BGLView *view, ulong options) : BGLRenderer(view, options), fDirectModeEnabled(false), fInfo(NULL), fInfoLocker("info locker"), fOptions(options), fColorSpace(B_NO_COLOR_SPACE) { CALLED(); // 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)); 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(); // Initialize the first "Haiku Software GL Pipe" context beg = time(NULL); fContextID = fContextObj->CreateContext(this); 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; } 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; } fContextObj->Validate(fWidth, fHeight); fColorSpace = cs; } // do not hold fInfoLocker here to avoid deadlock fContextObj->SetCurrentContext(true, fContextID); } void SoftwareRenderer::UnlockGL() { // CALLED(); if ((fOptions & BGL_DOUBLE) == 0) { SwapBuffers(); } fContextObj->SetCurrentContext(false, fContextID); BGLRenderer::UnlockGL(); } void SoftwareRenderer::Display(BBitmap *bitmap, BRect *updateRect) { // CALLED(); if (!fDirectModeEnabled) { // TODO: avoid timeout if (GLView()->LockLooperWithTimeout(1000) == B_OK) { GLView()->DrawBitmap(bitmap, B_ORIGIN); GLView()->UnlockLooper(); } } else { BAutolock lock(fInfoLocker); if (fInfo != NULL) { RasBuf32 srcBuf(bitmap); RasBuf32 dstBuf(fInfo); for (uint32 i = 0; i < fInfo->clip_list_count; i++) { clipping_rect *clip = &fInfo->clip_list[i]; RasBuf32 dstClip = dstBuf; dstClip.ClipRect(clip->left, clip->top, clip->right + 1, clip->bottom + 1); dstClip.Shift(-fInfo->window_bounds.left, -fInfo->window_bounds.top); dstClip.Blit(srcBuf); } } } } void SoftwareRenderer::SwapBuffers(bool vsync) { BScreen screen(GLView()->Window()); fContextObj->SwapBuffers(fContextID); fContextObj->Validate(fWidth, fHeight); if (vsync) screen.WaitForRetrace(); } void SoftwareRenderer::Draw(BRect updateRect) { // CALLED(); fContextObj->Draw(fContextID, updateRect); } status_t SoftwareRenderer::CopyPixelsOut(BPoint location, BBitmap *bitmap) { CALLED(); // TODO: implement return B_ERROR; } status_t SoftwareRenderer::CopyPixelsIn(BBitmap *bitmap, BPoint location) { CALLED(); // TODO: implement return B_ERROR; } 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; }