--- /dev/null
+/*
+ * 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, kallisti5@unixzen.com
+ */
+
+
+#include <kernel/image.h>
+#include "SoftwareRast.h"
+
+#include <Autolock.h>
+#include <interface/DirectWindowPrivate.h>
+#include <GraphicsDefs.h>
+#include <Screen.h>
+#include <stdio.h>
+#include <string.h>
+
+extern "C" {
+#include "extensions.h"
+#include "drivers/common/driverfuncs.h"
+#include "drivers/common/meta.h"
+#include "main/api_exec.h"
+#include "main/colormac.h"
+#include "main/cpuinfo.h"
+#include "main/buffers.h"
+#include "main/formats.h"
+#include "main/framebuffer.h"
+#include "main/renderbuffer.h"
+#include "main/version.h"
+#include "main/vtxfmt.h"
+#include "swrast/swrast.h"
+#include "swrast/s_renderbuffer.h"
+#include "swrast_setup/swrast_setup.h"
+#include "tnl/tnl.h"
+#include "tnl/t_context.h"
+#include "tnl/t_pipeline.h"
+#include "vbo/vbo.h"
+
+
+#ifdef DEBUG
+# define TRACE(x...) printf("MesaSoftwareRast: " x)
+# define CALLED() printf("MesaSoftwareRast: %s\n", __PRETTY_FUNCTION__)
+#else
+# define TRACE(x...)
+# define CALLED()
+#endif
+
+#define ERROR(x...) printf("MesaSoftwareRast: " x)
+}
+
+
+extern const char* color_space_name(color_space space);
+
+
+extern "C" _EXPORT BGLRenderer*
+instantiate_gl_renderer(BGLView* view, ulong options,
+ BGLDispatcher* dispatcher)
+{
+ return new MesaSoftwareRast(view, options, dispatcher);
+}
+
+
+MesaSoftwareRast::MesaSoftwareRast(BGLView* view, ulong options,
+ BGLDispatcher* dispatcher)
+ : BGLRenderer(view, options, dispatcher),
+ fBitmap(NULL),
+ fDirectModeEnabled(false),
+ fInfo(NULL),
+ fInfoLocker("info locker"),
+ fContext(NULL),
+ fVisual(NULL),
+ fFrameBuffer(NULL),
+ fFrontRenderBuffer(NULL),
+ fBackRenderBuffer(NULL),
+ fColorSpace(B_NO_COLOR_SPACE)
+{
+ CALLED();
+
+ fColorSpace = BScreen(GLView()->Window()).ColorSpace();
+
+ // We force single buffering for the time being
+ options &= ~BGL_DOUBLE;
+
+ const GLboolean rgbFlag = ((options & BGL_INDEX) == 0);
+ const GLboolean alphaFlag = ((options & BGL_ALPHA) == BGL_ALPHA);
+ const GLboolean dblFlag = ((options & BGL_DOUBLE) == BGL_DOUBLE);
+ const GLboolean stereoFlag = false;
+ const GLint depth = (options & BGL_DEPTH) ? 16 : 0;
+ const GLint stencil = (options & BGL_STENCIL) ? 8 : 0;
+ const GLint accum = (options & BGL_ACCUM) ? 16 : 0;
+ const GLint red = rgbFlag ? 8 : 0;
+ const GLint green = rgbFlag ? 8 : 0;
+ const GLint blue = rgbFlag ? 8 : 0;
+ const GLint alpha = alphaFlag ? 8 : 0;
+
+ fOptions = options; // | BGL_INDIRECT;
+ struct dd_function_table functions;
+
+ fVisual = _mesa_create_visual(dblFlag, stereoFlag, red, green,
+ blue, alpha, depth, stencil, accum, accum, accum,
+ alpha ? accum : 0, 1);
+
+ // Initialize device driver function table
+ _mesa_init_driver_functions(&functions);
+
+ functions.GetString = _GetString;
+ functions.UpdateState = _UpdateState;
+ functions.MapRenderbuffer = _RenderBufferMap;
+ functions.Flush = _Flush;
+
+ // create core context
+ fContext = _mesa_create_context(API_OPENGL_COMPAT, fVisual, NULL,
+ &functions);
+
+ if (!fContext) {
+ ERROR("%s: Failed to create Mesa context!\n", __func__);
+ _mesa_destroy_visual(fVisual);
+ return;
+ }
+
+ fContext->DriverCtx = (void*)this;
+
+ /* Initialize the software rasterizer and helper modules. */
+ _swrast_CreateContext(fContext);
+ _vbo_CreateContext(fContext);
+ _tnl_CreateContext(fContext);
+ _swsetup_CreateContext(fContext);
+ _swsetup_Wakeup(fContext);
+
+ // Use default TCL pipeline
+ TNL_CONTEXT(fContext)->Driver.RunPipeline = _tnl_run_pipeline;
+
+ _mesa_meta_init(fContext);
+ _mesa_enable_sw_extensions(fContext);
+
+ _mesa_compute_version(fContext);
+
+ _mesa_initialize_dispatch_tables(fContext);
+ _mesa_initialize_vbo_vtxfmt(fContext);
+
+ // create core framebuffer
+ fFrameBuffer = _mesa_create_framebuffer(fVisual);
+ if (fFrameBuffer == NULL) {
+ ERROR("%s: Unable to calloc GL FrameBuffer!\n", __func__);
+ _mesa_destroy_visual(fVisual);
+ return;
+ }
+
+ // Setup front render buffer
+ fFrontRenderBuffer = _NewRenderBuffer(true);
+ if (fFrontRenderBuffer == NULL) {
+ ERROR("%s: FrontRenderBuffer is requested but unallocated!\n",
+ __func__);
+ _mesa_destroy_visual(fVisual);
+ free(fFrameBuffer);
+ return;
+ }
+ _mesa_add_renderbuffer(fFrameBuffer, BUFFER_FRONT_LEFT,
+ &fFrontRenderBuffer->Base);
+
+ // Setup back render buffer (if requested)
+ if (fVisual->doubleBufferMode) {
+ fBackRenderBuffer = _NewRenderBuffer(false);
+ if (fBackRenderBuffer == NULL) {
+ ERROR("%s: BackRenderBuffer is requested but unallocated!\n",
+ __func__);
+ _mesa_destroy_visual(fVisual);
+ free(fFrameBuffer);
+ return;
+ }
+ _mesa_add_renderbuffer(fFrameBuffer, BUFFER_BACK_LEFT,
+ &fBackRenderBuffer->Base);
+ }
+
+ _swrast_add_soft_renderbuffers(fFrameBuffer, GL_FALSE,
+ fVisual->haveDepthBuffer, fVisual->haveStencilBuffer,
+ fVisual->haveAccumBuffer, alphaFlag, GL_FALSE);
+
+ BRect bounds = view->Bounds();
+ fWidth = (GLint)bounds.Width();
+ fHeight = (GLint)bounds.Height();
+
+ // some stupid applications (Quake2) don't even think about calling LockGL()
+ // before using glGetString and its glGet*() friends...
+ // so make sure there is at least a valid context.
+
+ if (!_mesa_get_current_context()) {
+ LockGL();
+ // not needed, we don't have a looper yet: UnlockLooper();
+ }
+}
+
+
+MesaSoftwareRast::~MesaSoftwareRast()
+{
+ CALLED();
+ _swsetup_DestroyContext(fContext);
+ _swrast_DestroyContext(fContext);
+ _tnl_DestroyContext(fContext);
+ _vbo_DestroyContext(fContext);
+ _mesa_destroy_visual(fVisual);
+ _mesa_destroy_framebuffer(fFrameBuffer);
+ _mesa_destroy_context(fContext);
+
+ free(fInfo);
+ free(fFrameBuffer);
+
+ delete fBitmap;
+}
+
+
+void
+MesaSoftwareRast::LockGL()
+{
+ CALLED();
+ BGLRenderer::LockGL();
+
+ _mesa_make_current(fContext, fFrameBuffer, fFrameBuffer);
+
+ color_space colorSpace = BScreen(GLView()->Window()).ColorSpace();
+
+ GLuint width = fWidth;
+ GLuint height = fHeight;
+
+ BAutolock lock(fInfoLocker);
+ if (fDirectModeEnabled && fInfo != NULL) {
+ width = fInfo->window_bounds.right
+ - fInfo->window_bounds.left + 1;
+ height = fInfo->window_bounds.bottom
+ - fInfo->window_bounds.top + 1;
+ }
+
+ if (fColorSpace != colorSpace) {
+ fColorSpace = colorSpace;
+ _SetupRenderBuffer(&fFrontRenderBuffer->Base, fColorSpace);
+ if (fVisual->doubleBufferMode)
+ _SetupRenderBuffer(&fBackRenderBuffer->Base, fColorSpace);
+ }
+
+ _CheckResize(width, height);
+}
+
+
+void
+MesaSoftwareRast::UnlockGL()
+{
+ CALLED();
+ _mesa_make_current(fContext, NULL, NULL);
+ BGLRenderer::UnlockGL();
+}
+
+
+void
+MesaSoftwareRast::SwapBuffers(bool VSync)
+{
+ CALLED();
+
+ if (!fBitmap)
+ return;
+
+ if (fVisual->doubleBufferMode)
+ _mesa_notifySwapBuffers(fContext);
+
+ if (!fDirectModeEnabled || fInfo == NULL) {
+ if (GLView()->LockLooperWithTimeout(1000) == B_OK) {
+ GLView()->DrawBitmap(fBitmap, B_ORIGIN);
+ GLView()->UnlockLooper();
+ }
+ } else {
+ // TODO: Here the BGLView needs to be drawlocked.
+ _CopyToDirect();
+ }
+
+ if (VSync) {
+ BScreen screen(GLView()->Window());
+ screen.WaitForRetrace();
+ }
+}
+
+
+void
+MesaSoftwareRast::Draw(BRect updateRect)
+{
+ CALLED();
+ if (fBitmap && (!fDirectModeEnabled || (fInfo == NULL)))
+ GLView()->DrawBitmap(fBitmap, updateRect, updateRect);
+}
+
+
+status_t
+MesaSoftwareRast::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)) {
+ fprintf(stderr, "CopyPixelsOut(): incompatible color space: %s != %s\n",
+ color_space_name(scs),
+ color_space_name(dcs));
+ return B_BAD_TYPE;
+ }
+
+ BRect sr = fBitmap->Bounds();
+ BRect dr = bitmap->Bounds();
+
+ sr = sr & dr.OffsetBySelf(location);
+ dr = sr.OffsetByCopy(-location.x, -location.y);
+
+ uint8* ps = (uint8*)fBitmap->Bits();
+ uint8* pd = (uint8*)bitmap->Bits();
+ uint32* s;
+ uint32* 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
+MesaSoftwareRast::CopyPixelsIn(BBitmap* bitmap, BPoint location)
+{
+ CALLED();
+ color_space scs = bitmap->ColorSpace();
+ color_space dcs = fBitmap->ColorSpace();
+
+ if (scs != dcs && (dcs != B_RGBA32 || scs != B_RGB32)) {
+ fprintf(stderr, "CopyPixelsIn(): incompatible color space: %s != %s\n",
+ color_space_name(scs),
+ color_space_name(dcs));
+ 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;
+ uint32* 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
+MesaSoftwareRast::EnableDirectMode(bool enabled)
+{
+ fDirectModeEnabled = enabled;
+}
+
+
+void
+MesaSoftwareRast::DirectConnected(direct_buffer_info* info)
+{
+ // TODO: I'm not sure we need to do this: BGLView already
+ // keeps a local copy of the direct_buffer_info passed by
+ // BDirectWindow::DirectConnected().
+ BAutolock lock(fInfoLocker);
+ if (info) {
+ if (!fInfo) {
+ fInfo = (direct_buffer_info*)malloc(DIRECT_BUFFER_INFO_AREA_SIZE);
+ if (!fInfo)
+ return;
+ }
+ memcpy(fInfo, info, DIRECT_BUFFER_INFO_AREA_SIZE);
+ } else if (fInfo) {
+ free(fInfo);
+ fInfo = NULL;
+ }
+}
+
+
+void
+MesaSoftwareRast::FrameResized(float width, float height)
+{
+ BAutolock lock(fInfoLocker);
+ _CheckResize((GLuint)width, (GLuint)height);
+}
+
+
+void
+MesaSoftwareRast::_CheckResize(GLuint newWidth, GLuint newHeight)
+{
+ CALLED();
+
+ if (fBitmap && newWidth == fWidth
+ && newHeight == fHeight) {
+ return;
+ }
+
+ _mesa_resize_framebuffer(fContext, fFrameBuffer, newWidth, newHeight);
+ fHeight = newHeight;
+ fWidth = newWidth;
+
+ _AllocateBitmap();
+}
+
+
+void
+MesaSoftwareRast::_AllocateBitmap()
+{
+ CALLED();
+
+ // allocate new size of back buffer bitmap
+ delete fBitmap;
+ fBitmap = NULL;
+
+ if (fWidth < 1 || fHeight < 1) {
+ TRACE("%s: Cannot allocate bitmap < 1x1!\n", __func__);
+ return;
+ }
+
+ BRect rect(0.0, 0.0, fWidth - 1, fHeight - 1);
+ fBitmap = new BBitmap(rect, fColorSpace);
+
+ #if 0
+ // Used for platform optimized drawing
+ for (uint i = 0; i < fHeight; i++) {
+ fRowAddr[fHeight - i - 1] = (GLvoid *)((GLubyte *)fBitmap->Bits()
+ + i * fBitmap->BytesPerRow());
+ }
+ #endif
+
+ fFrameBuffer->Width = fWidth;
+ fFrameBuffer->Height = fHeight;
+ TRACE("%s: Bitmap Size: %" B_PRIu32 "\n", __func__, fBitmap->BitsLength());
+
+ fFrontRenderBuffer->Buffer = (GLubyte*)fBitmap->Bits();
+}
+
+
+// #pragma mark - static
+
+
+const GLubyte*
+MesaSoftwareRast::_GetString(gl_context* ctx, GLenum name)
+{
+ switch (name) {
+ case GL_VENDOR:
+ return (const GLubyte*) "Mesa Project";
+ case GL_RENDERER:
+ return (const GLubyte*) "Software Rasterizer";
+ default:
+ // Let core library handle all other cases
+ return NULL;
+ }
+}
+
+
+void
+MesaSoftwareRast::_UpdateState(gl_context* ctx, GLuint new_state)
+{
+ if (!ctx)
+ return;
+
+ CALLED();
+ _swrast_InvalidateState(ctx, new_state);
+ _swsetup_InvalidateState(ctx, new_state);
+ _vbo_InvalidateState(ctx, new_state);
+ _tnl_InvalidateState(ctx, new_state);
+}
+
+
+GLboolean
+MesaSoftwareRast::_RenderBufferStorage(gl_context* ctx,
+ struct gl_renderbuffer* render, GLenum internalFormat,
+ GLuint width, GLuint height)
+{
+ CALLED();
+
+ render->Width = width;
+ render->Height = height;
+
+ struct swrast_renderbuffer *swRenderBuffer = swrast_renderbuffer(render);
+
+ swRenderBuffer->RowStride = width * _mesa_get_format_bytes(render->Format);
+
+ return GL_TRUE;
+}
+
+
+GLboolean
+MesaSoftwareRast::_RenderBufferStorageMalloc(gl_context* ctx,
+ struct gl_renderbuffer* render, GLenum internalFormat,
+ GLuint width, GLuint height)
+{
+ CALLED();
+
+ render->Width = width;
+ render->Height = height;
+
+ struct swrast_renderbuffer *swRenderBuffer = swrast_renderbuffer(render);
+
+ if (swRenderBuffer != NULL) {
+ free(swRenderBuffer->Buffer);
+ swRenderBuffer->RowStride
+ = width * _mesa_get_format_bytes(render->Format);
+
+ uint32 size = swRenderBuffer->RowStride * height;
+ TRACE("%s: Allocate %" B_PRIu32 " bytes for RenderBuffer\n",
+ __func__, size);
+ swRenderBuffer->Buffer = (GLubyte*)malloc(size);
+ if (!swRenderBuffer->Buffer) {
+ ERROR("%s: Memory allocation failure!\n", __func__);
+ return GL_FALSE;
+ }
+ } else {
+ ERROR("%s: Couldn't obtain software renderbuffer!\n",
+ __func__);
+ return GL_FALSE;
+ }
+
+ return GL_TRUE;
+}
+
+
+void
+MesaSoftwareRast::_Flush(gl_context* ctx)
+{
+ CALLED();
+ // TODO: We may want to add the void* DriverCtx back into mtypes.h for
+ // gl_context someday...
+ #if 0
+ MesaSoftwareRast* driverContext = (MesaSoftwareRast*)ctx->DriverCtx;
+ if ((driverContext->fOptions & BGL_DOUBLE) == 0) {
+ // TODO: SwapBuffers() can call _CopyToDirect(), which should
+ // be always called with with the BGLView drawlocked.
+ // This is not always the case if called from here.
+ driverContext->SwapBuffers();
+ }
+ #endif
+}
+
+
+struct swrast_renderbuffer*
+MesaSoftwareRast::_NewRenderBuffer(bool front)
+{
+ CALLED();
+ struct swrast_renderbuffer *swRenderBuffer
+ = (struct swrast_renderbuffer*)calloc(1, sizeof *swRenderBuffer);
+
+ if (!swRenderBuffer) {
+ ERROR("%s: Failed calloc RenderBuffer\n", __func__);
+ return NULL;
+ }
+
+ _mesa_init_renderbuffer(&swRenderBuffer->Base, 0);
+
+ swRenderBuffer->Base.ClassID = HAIKU_SWRAST_RENDERBUFFER_CLASS;
+ swRenderBuffer->Base.RefCount = 1;
+ swRenderBuffer->Base.Delete = _RenderBufferDelete;
+
+ if (!front)
+ swRenderBuffer->Base.AllocStorage = _RenderBufferStorageMalloc;
+ else
+ swRenderBuffer->Base.AllocStorage = _RenderBufferStorage;
+
+ if (_SetupRenderBuffer(&swRenderBuffer->Base, fColorSpace) != B_OK) {
+ free(swRenderBuffer);
+ return NULL;
+ }
+
+ return swRenderBuffer;
+}
+
+
+status_t
+MesaSoftwareRast::_SetupRenderBuffer(struct gl_renderbuffer* rb,
+ color_space colorSpace)
+{
+ CALLED();
+
+ rb->InternalFormat = GL_RGBA;
+
+ switch (colorSpace) {
+ case B_RGBA32:
+ rb->_BaseFormat = GL_RGBA;
+ rb->Format = MESA_FORMAT_ARGB8888;
+ break;
+ case B_RGB32:
+ rb->_BaseFormat = GL_RGB;
+ rb->Format = MESA_FORMAT_XRGB8888;
+ break;
+ case B_RGB24:
+ rb->_BaseFormat = GL_RGB;
+ rb->Format = MESA_FORMAT_RGB888;
+ break;
+ case B_RGB16:
+ rb->_BaseFormat = GL_RGB;
+ rb->Format = MESA_FORMAT_RGB565;
+ break;
+ case B_RGB15:
+ rb->_BaseFormat = GL_RGB;
+ rb->Format = MESA_FORMAT_ARGB1555;
+ break;
+ default:
+ fprintf(stderr, "Unsupported screen color space %s\n",
+ color_space_name(fColorSpace));
+ debugger("Unsupported OpenGL color space");
+ return B_ERROR;
+ }
+ return B_OK;
+}
+
+
+/*! Y inverted Map RenderBuffer function
+ We use a BBitmap for storage which has Y inverted.
+ If the Mesa provided Map function ever allows external
+ control of this we can omit this function.
+*/
+void
+MesaSoftwareRast::_RenderBufferMap(gl_context *ctx,
+ struct gl_renderbuffer *rb, GLuint x, GLuint y, GLuint w, GLuint h,
+ GLbitfield mode, GLubyte **mapOut, GLint *rowStrideOut)
+{
+ if (rb->ClassID == HAIKU_SWRAST_RENDERBUFFER_CLASS) {
+ struct swrast_renderbuffer *srb = swrast_renderbuffer(rb);
+ const GLuint bpp = _mesa_get_format_bytes(rb->Format);
+ GLint rowStride = rb->Width * bpp; // in Bytes
+
+ y = rb->Height - y - 1;
+
+ *rowStrideOut = -rowStride;
+ *mapOut = (GLubyte *) srb->Buffer + y * rowStride + x * bpp;
+ } else {
+ _swrast_map_soft_renderbuffer(ctx, rb, x, y, w, h, mode,
+ mapOut, rowStrideOut);
+ }
+}
+
+
+void
+MesaSoftwareRast::_RenderBufferDelete(struct gl_context *ctx,
+ struct gl_renderbuffer* rb)
+{
+ CALLED();
+ if (rb != NULL) {
+ struct swrast_renderbuffer *swRenderBuffer
+ = swrast_renderbuffer(rb);
+ if (swRenderBuffer != NULL)
+ free(swRenderBuffer->Buffer);
+ }
+ free(rb);
+}
+
+
+void
+MesaSoftwareRast::_CopyToDirect()
+{
+ BAutolock lock(fInfoLocker);
+
+ // 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())
+ return;
+
+ uint8 bytesPerPixel = fInfo->bits_per_pixel / 8;
+ uint32 bytesPerRow = fBitmap->BytesPerRow();
+ 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;
+ 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; y++) {
+ memcpy(p, b, bytesWidth);
+ p += fInfo->bytes_per_row;
+ b += bytesPerRow;
+ }
+ }
+}