1/*
2 * Copyright 2006-2012, Haiku, Inc. All rights reserved.
3 * Distributed under the terms of the MIT License.
4 *
5 * Authors:
6 *		Jérôme Duval, korli@users.berlios.de
7 *		Philippe Houdoin, philippe.houdoin@free.fr
8 *		Artur Wyszynski, harakash@gmail.com
9 *		Alexander von Gluck, kallisti5@unixzen.com
10 */
11
12
13#include "MesaSoftwareRenderer.h"
14
15#include <Autolock.h>
16#include <DirectWindowPrivate.h>
17#include <GraphicsDefs.h>
18#include <Screen.h>
19#include <stdio.h>
20#include <string.h>
21
22extern "C" {
23#include "extensions.h"
24#include "drivers/common/driverfuncs.h"
25#include "drivers/common/meta.h"
26#include "main/colormac.h"
27#include "main/cpuinfo.h"
28#include "main/buffers.h"
29#include "main/formats.h"
30#include "main/framebuffer.h"
31#include "main/renderbuffer.h"
32#include "swrast/swrast.h"
33#include "swrast/s_renderbuffer.h"
34#include "swrast_setup/swrast_setup.h"
35#include "tnl/tnl.h"
36#include "tnl/t_context.h"
37#include "tnl/t_pipeline.h"
38#include "vbo/vbo.h"
39
40
41//#define TRACE_SOFTGL
42#ifdef TRACE_SOFTGL
43#	define TRACE(x...) printf("MesaSoftwareRenderer: " x)
44#	define CALLED() printf("MesaSoftwareRenderer: %s\n", __PRETTY_FUNCTION__)
45#else
46#	define TRACE(x...)
47#	define CALLED()
48#endif
49
50#define ERROR(x...) printf("MesaSoftwareRenderer: " x)
51}
52
53
54extern const char* color_space_name(color_space space);
55
56
57extern "C" _EXPORT BGLRenderer*
58instantiate_gl_renderer(BGLView* view, ulong options,
59	BGLDispatcher* dispatcher)
60{
61	return new MesaSoftwareRenderer(view, options, dispatcher);
62}
63
64
65MesaSoftwareRenderer::MesaSoftwareRenderer(BGLView* view, ulong options,
66	BGLDispatcher* dispatcher)
67	: BGLRenderer(view, options, dispatcher),
68	fBitmap(NULL),
69	fDirectModeEnabled(false),
70	fInfo(NULL),
71	fInfoLocker("info locker"),
72	fContext(NULL),
73	fVisual(NULL),
74	fFrameBuffer(NULL),
75	fFrontRenderBuffer(NULL),
76	fBackRenderBuffer(NULL),
77	fColorSpace(B_NO_COLOR_SPACE)
78{
79	CALLED();
80
81	fColorSpace = BScreen(GLView()->Window()).ColorSpace();
82
83	// We force single buffering for the time being
84	options &= ~BGL_DOUBLE;
85
86	const GLboolean rgbFlag = ((options & BGL_INDEX) == 0);
87	const GLboolean alphaFlag = ((options & BGL_ALPHA) == BGL_ALPHA);
88	const GLboolean dblFlag = ((options & BGL_DOUBLE) == BGL_DOUBLE);
89	const GLboolean stereoFlag = false;
90	const GLint depth = (options & BGL_DEPTH) ? 16 : 0;
91	const GLint stencil = (options & BGL_STENCIL) ? 8 : 0;
92	const GLint accum = (options & BGL_ACCUM) ? 16 : 0;
93	const GLint red = rgbFlag ? 8 : 0;
94	const GLint green = rgbFlag ? 8 : 0;
95	const GLint blue = rgbFlag ? 8 : 0;
96	const GLint alpha = alphaFlag ? 8 : 0;
97
98	fOptions = options; // | BGL_INDIRECT;
99	struct dd_function_table functions;
100
101	fVisual = _mesa_create_visual(dblFlag, stereoFlag, red, green,
102		blue, alpha, depth, stencil, accum, accum, accum,
103		alpha ? accum : 0, 1);
104
105	// Initialize device driver function table
106	_mesa_init_driver_functions(&functions);
107
108	functions.GetString = _GetString;
109	functions.UpdateState = _UpdateState;
110	functions.MapRenderbuffer = _RenderBufferMap;
111	functions.GetBufferSize = NULL;
112	functions.Error = _Error;
113	functions.Flush = _Flush;
114
115	// create core context
116	fContext = _mesa_create_context(API_OPENGL, fVisual, NULL,
117		&functions, this);
118
119	if (!fContext) {
120		ERROR("%s: Failed to create Mesa context!\n", __func__);
121		_mesa_destroy_visual(fVisual);
122		return;
123	}
124
125	/* Initialize the software rasterizer and helper modules. */
126	_swrast_CreateContext(fContext);
127	_vbo_CreateContext(fContext);
128	_tnl_CreateContext(fContext);
129	_swsetup_CreateContext(fContext);
130	_swsetup_Wakeup(fContext);
131
132	// Use default TCL pipeline
133	TNL_CONTEXT(fContext)->Driver.RunPipeline = _tnl_run_pipeline;
134
135	_mesa_meta_init(fContext);
136	_mesa_enable_sw_extensions(fContext);
137	_mesa_enable_1_3_extensions(fContext);
138	_mesa_enable_1_4_extensions(fContext);
139	_mesa_enable_1_5_extensions(fContext);
140	_mesa_enable_2_0_extensions(fContext);
141	_mesa_enable_2_1_extensions(fContext);
142
143	// create core framebuffer
144	fFrameBuffer = _mesa_create_framebuffer(fVisual);
145	if (fFrameBuffer == NULL) {
146		ERROR("%s: Unable to calloc GL FrameBuffer!\n", __func__);
147		_mesa_destroy_visual(fVisual);
148		return;
149	}
150
151	// Setup front render buffer
152	fFrontRenderBuffer = _NewRenderBuffer(true);
153	if (fFrontRenderBuffer == NULL) {
154		ERROR("%s: FrontRenderBuffer is requested but unallocated!\n",
155			__func__);
156		_mesa_destroy_visual(fVisual);
157		free(fFrameBuffer);
158		return;
159	}
160	_mesa_add_renderbuffer(fFrameBuffer, BUFFER_FRONT_LEFT,
161		&fFrontRenderBuffer->Base);
162
163	// Setup back render buffer (if requested)
164	if (fVisual->doubleBufferMode) {
165		fBackRenderBuffer = _NewRenderBuffer(false);
166		if (fBackRenderBuffer == NULL) {
167			ERROR("%s: BackRenderBuffer is requested but unallocated!\n",
168				__func__);
169			_mesa_destroy_visual(fVisual);
170			free(fFrameBuffer);
171			return;
172		}
173		_mesa_add_renderbuffer(fFrameBuffer, BUFFER_BACK_LEFT,
174			&fBackRenderBuffer->Base);
175	}
176
177	_swrast_add_soft_renderbuffers(fFrameBuffer, GL_FALSE,
178		fVisual->haveDepthBuffer, fVisual->haveStencilBuffer,
179		fVisual->haveAccumBuffer, alphaFlag, GL_FALSE);
180
181	BRect bounds = view->Bounds();
182	fWidth = (GLint)bounds.Width();
183	fHeight = (GLint)bounds.Height();
184
185	// some stupid applications (Quake2) don't even think about calling LockGL()
186	// before using glGetString and its glGet*() friends...
187	// so make sure there is at least a valid context.
188
189	if (!_mesa_get_current_context()) {
190		LockGL();
191		// not needed, we don't have a looper yet: UnlockLooper();
192	}
193}
194
195
196MesaSoftwareRenderer::~MesaSoftwareRenderer()
197{
198	CALLED();
199	_swsetup_DestroyContext(fContext);
200	_swrast_DestroyContext(fContext);
201	_tnl_DestroyContext(fContext);
202	_vbo_DestroyContext(fContext);
203	_mesa_destroy_visual(fVisual);
204	_mesa_destroy_framebuffer(fFrameBuffer);
205	_mesa_destroy_context(fContext);
206
207	free(fInfo);
208	free(fFrameBuffer);
209
210	delete fBitmap;
211}
212
213
214void
215MesaSoftwareRenderer::LockGL()
216{
217	CALLED();
218	BGLRenderer::LockGL();
219
220	_mesa_make_current(fContext, fFrameBuffer, fFrameBuffer);
221
222	color_space colorSpace = BScreen(GLView()->Window()).ColorSpace();
223
224	GLuint width = fWidth;
225	GLuint height = fHeight;
226
227	BAutolock lock(fInfoLocker);
228	if (fDirectModeEnabled && fInfo != NULL) {
229		width = fInfo->window_bounds.right
230			- fInfo->window_bounds.left + 1;
231		height = fInfo->window_bounds.bottom
232			- fInfo->window_bounds.top + 1;
233	}
234
235	if (fColorSpace != colorSpace) {
236		fColorSpace = colorSpace;
237		_SetupRenderBuffer(&fFrontRenderBuffer->Base, fColorSpace);
238		if (fVisual->doubleBufferMode)
239			_SetupRenderBuffer(&fBackRenderBuffer->Base, fColorSpace);
240	}
241
242	_CheckResize(width, height);
243}
244
245
246void
247MesaSoftwareRenderer::UnlockGL()
248{
249	CALLED();
250	_mesa_make_current(fContext, NULL, NULL);
251	BGLRenderer::UnlockGL();
252}
253
254
255void
256MesaSoftwareRenderer::SwapBuffers(bool VSync)
257{
258	CALLED();
259
260	if (!fBitmap)
261		return;
262
263	if (fVisual->doubleBufferMode)
264		_mesa_notifySwapBuffers(fContext);
265
266	if (!fDirectModeEnabled || fInfo == NULL) {
267		if (GLView()->LockLooperWithTimeout(1000) == B_OK) {
268			GLView()->DrawBitmap(fBitmap, B_ORIGIN);
269			GLView()->UnlockLooper();
270		}
271	} else {
272		// TODO: Here the BGLView needs to be drawlocked.
273		_CopyToDirect();
274	}
275
276	if (VSync) {
277		BScreen screen(GLView()->Window());
278		screen.WaitForRetrace();
279	}
280}
281
282
283void
284MesaSoftwareRenderer::Draw(BRect updateRect)
285{
286	CALLED();
287	if (fBitmap && (!fDirectModeEnabled || (fInfo == NULL)))
288		GLView()->DrawBitmap(fBitmap, updateRect, updateRect);
289}
290
291
292status_t
293MesaSoftwareRenderer::CopyPixelsOut(BPoint location, BBitmap* bitmap)
294{
295	CALLED();
296	color_space scs = fBitmap->ColorSpace();
297	color_space dcs = bitmap->ColorSpace();
298
299	if (scs != dcs && (scs != B_RGBA32 || dcs != B_RGB32)) {
300		fprintf(stderr, "CopyPixelsOut(): incompatible color space: %s != %s\n",
301			color_space_name(scs),
302			color_space_name(dcs));
303		return B_BAD_TYPE;
304	}
305
306	BRect sr = fBitmap->Bounds();
307	BRect dr = bitmap->Bounds();
308
309	sr = sr & dr.OffsetBySelf(location);
310	dr = sr.OffsetByCopy(-location.x, -location.y);
311
312	uint8* ps = (uint8*)fBitmap->Bits();
313	uint8* pd = (uint8*)bitmap->Bits();
314	uint32* s;
315	uint32* d;
316	uint32 y;
317	for (y = (uint32)sr.top; y <= (uint32)sr.bottom; y++) {
318		s = (uint32*)(ps + y * fBitmap->BytesPerRow());
319		s += (uint32)sr.left;
320
321		d = (uint32*)(pd + (y + (uint32)(dr.top - sr.top))
322			* bitmap->BytesPerRow());
323		d += (uint32)dr.left;
324
325		memcpy(d, s, dr.IntegerWidth() * 4);
326	}
327	return B_OK;
328}
329
330
331status_t
332MesaSoftwareRenderer::CopyPixelsIn(BBitmap* bitmap, BPoint location)
333{
334	CALLED();
335	color_space scs = bitmap->ColorSpace();
336	color_space dcs = fBitmap->ColorSpace();
337
338	if (scs != dcs && (dcs != B_RGBA32 || scs != B_RGB32)) {
339		fprintf(stderr, "CopyPixelsIn(): incompatible color space: %s != %s\n",
340			color_space_name(scs),
341			color_space_name(dcs));
342		return B_BAD_TYPE;
343	}
344
345	BRect sr = bitmap->Bounds();
346	BRect dr = fBitmap->Bounds();
347
348	sr = sr & dr.OffsetBySelf(location);
349	dr = sr.OffsetByCopy(-location.x, -location.y);
350
351	uint8* ps = (uint8*)bitmap->Bits();
352	uint8* pd = (uint8*)fBitmap->Bits();
353	uint32* s;
354	uint32* d;
355	uint32 y;
356	for (y = (uint32)sr.top; y <= (uint32)sr.bottom; y++) {
357		s = (uint32*)(ps + y * bitmap->BytesPerRow());
358		s += (uint32)sr.left;
359
360		d = (uint32*)(pd + (y + (uint32)(dr.top - sr.top))
361			* fBitmap->BytesPerRow());
362		d += (uint32)dr.left;
363
364		memcpy(d, s, dr.IntegerWidth() * 4);
365	}
366	return B_OK;
367}
368
369
370void
371MesaSoftwareRenderer::EnableDirectMode(bool enabled)
372{
373	fDirectModeEnabled = enabled;
374}
375
376
377void
378MesaSoftwareRenderer::DirectConnected(direct_buffer_info* info)
379{
380	// TODO: I'm not sure we need to do this: BGLView already
381	// keeps a local copy of the direct_buffer_info passed by
382	// BDirectWindow::DirectConnected().
383	BAutolock lock(fInfoLocker);
384	if (info) {
385		if (!fInfo) {
386			fInfo = (direct_buffer_info*)malloc(DIRECT_BUFFER_INFO_AREA_SIZE);
387			if (!fInfo)
388				return;
389		}
390		memcpy(fInfo, info, DIRECT_BUFFER_INFO_AREA_SIZE);
391	} else if (fInfo) {
392		free(fInfo);
393		fInfo = NULL;
394	}
395}
396
397
398void
399MesaSoftwareRenderer::FrameResized(float width, float height)
400{
401	BAutolock lock(fInfoLocker);
402	_CheckResize((GLuint)width, (GLuint)height);
403}
404
405
406void
407MesaSoftwareRenderer::_CheckResize(GLuint newWidth, GLuint newHeight)
408{
409	CALLED();
410
411	if (fBitmap && newWidth == fWidth
412		&& newHeight == fHeight) {
413		return;
414	}
415
416	_mesa_resize_framebuffer(fContext, fFrameBuffer, newWidth, newHeight);
417	fHeight = newHeight;
418	fWidth = newWidth;
419
420	_AllocateBitmap();
421}
422
423
424void
425MesaSoftwareRenderer::_AllocateBitmap()
426{
427	CALLED();
428
429	// allocate new size of back buffer bitmap
430	delete fBitmap;
431	fBitmap = NULL;
432
433	if (fWidth < 1 || fHeight < 1) {
434		TRACE("%s: Cannot allocate bitmap < 1x1!\n", __func__);
435		return;
436	}
437
438	BRect rect(0.0, 0.0, fWidth - 1, fHeight - 1);
439	fBitmap = new BBitmap(rect, fColorSpace);
440
441	#if 0
442	// Used for platform optimized drawing
443	for (uint i = 0; i < fHeight; i++) {
444		fRowAddr[fHeight - i - 1] = (GLvoid *)((GLubyte *)fBitmap->Bits()
445			+ i * fBitmap->BytesPerRow());
446	}
447	#endif
448
449	fFrameBuffer->Width = fWidth;
450	fFrameBuffer->Height = fHeight;
451	TRACE("%s: Bitmap Size: %" B_PRIu32 "\n", __func__, fBitmap->BitsLength());
452
453	fFrontRenderBuffer->Buffer = (GLubyte*)fBitmap->Bits();
454}
455
456
457// #pragma mark - static
458
459
460void
461MesaSoftwareRenderer::_Error(gl_context* ctx)
462{
463	MesaSoftwareRenderer* mr = (MesaSoftwareRenderer*)ctx->DriverCtx;
464	if (mr && mr->GLView())
465		mr->GLView()->ErrorCallback((unsigned long)ctx->ErrorValue);
466}
467
468
469const GLubyte*
470MesaSoftwareRenderer::_GetString(gl_context* ctx, GLenum name)
471{
472	switch (name) {
473		case GL_VENDOR:
474			return (const GLubyte*) "Mesa Project";
475		case GL_RENDERER:
476			return (const GLubyte*) "Software Rasterizer";
477		default:
478			// Let core library handle all other cases
479			return NULL;
480	}
481}
482
483
484void
485MesaSoftwareRenderer::_UpdateState(gl_context* ctx, GLuint new_state)
486{
487	if (!ctx)
488		return;
489
490	CALLED();
491	_swrast_InvalidateState(ctx, new_state);
492	_swsetup_InvalidateState(ctx, new_state);
493	_vbo_InvalidateState(ctx, new_state);
494	_tnl_InvalidateState(ctx, new_state);
495}
496
497
498GLboolean
499MesaSoftwareRenderer::_RenderBufferStorage(gl_context* ctx,
500	struct gl_renderbuffer* render, GLenum internalFormat,
501	GLuint width, GLuint height)
502{
503	CALLED();
504
505	render->Width = width;
506	render->Height = height;
507
508	struct swrast_renderbuffer *swRenderBuffer = swrast_renderbuffer(render);
509
510	swRenderBuffer->RowStride = width * _mesa_get_format_bytes(render->Format);
511
512	return GL_TRUE;
513}
514
515
516GLboolean
517MesaSoftwareRenderer::_RenderBufferStorageMalloc(gl_context* ctx,
518	struct gl_renderbuffer* render, GLenum internalFormat,
519	GLuint width, GLuint height)
520{
521	CALLED();
522
523	render->Width = width;
524	render->Height = height;
525
526	struct swrast_renderbuffer *swRenderBuffer = swrast_renderbuffer(render);
527
528	if (swRenderBuffer != NULL) {
529		free(swRenderBuffer->Buffer);
530		swRenderBuffer->RowStride
531			= width * _mesa_get_format_bytes(render->Format);
532
533		uint32 size = swRenderBuffer->RowStride * height;
534		TRACE("%s: Allocate %" B_PRIu32 " bytes for RenderBuffer\n",
535			__func__, size);
536		swRenderBuffer->Buffer = (GLubyte*)malloc(size);
537		if (!swRenderBuffer->Buffer) {
538			ERROR("%s: Memory allocation failure!\n", __func__);
539			return GL_FALSE;
540		}
541	} else {
542		ERROR("%s: Couldn't obtain software renderbuffer!\n",
543			__func__);
544		return GL_FALSE;
545	}
546
547	return GL_TRUE;
548}
549
550
551void
552MesaSoftwareRenderer::_Flush(gl_context* ctx)
553{
554	CALLED();
555	MesaSoftwareRenderer* mr = (MesaSoftwareRenderer*)ctx->DriverCtx;
556	if ((mr->fOptions & BGL_DOUBLE) == 0) {
557		// TODO: SwapBuffers() can call _CopyToDirect(), which should
558		// be always called with with the BGLView drawlocked.
559		// This is not always the case if called from here.
560		mr->SwapBuffers();
561	}
562}
563
564
565struct swrast_renderbuffer*
566MesaSoftwareRenderer::_NewRenderBuffer(bool front)
567{
568	CALLED();
569	struct swrast_renderbuffer *swRenderBuffer
570		= (struct swrast_renderbuffer*)calloc(1, sizeof *swRenderBuffer);
571
572	if (!swRenderBuffer) {
573		ERROR("%s: Failed calloc RenderBuffer\n", __func__);
574		return NULL;
575	}
576
577	_mesa_init_renderbuffer(&swRenderBuffer->Base, 0);
578
579	swRenderBuffer->Base.ClassID = HAIKU_SWRAST_RENDERBUFFER_CLASS;
580	swRenderBuffer->Base.RefCount = 1;
581	swRenderBuffer->Base.Delete = _RenderBufferDelete;
582
583	if (!front)
584		swRenderBuffer->Base.AllocStorage = _RenderBufferStorageMalloc;
585	else
586		swRenderBuffer->Base.AllocStorage = _RenderBufferStorage;
587
588	if (_SetupRenderBuffer(&swRenderBuffer->Base, fColorSpace) != B_OK) {
589		free(swRenderBuffer);
590		return NULL;
591	}
592
593	return swRenderBuffer;
594}
595
596
597status_t
598MesaSoftwareRenderer::_SetupRenderBuffer(struct gl_renderbuffer* rb,
599	color_space colorSpace)
600{
601	CALLED();
602
603	rb->InternalFormat = GL_RGBA;
604
605	switch (colorSpace) {
606		case B_RGBA32:
607			rb->_BaseFormat = GL_RGBA;
608			rb->Format = MESA_FORMAT_ARGB8888;
609			break;
610		case B_RGB32:
611			rb->_BaseFormat = GL_RGB;
612			rb->Format = MESA_FORMAT_XRGB8888;
613			break;
614		case B_RGB24:
615			rb->_BaseFormat = GL_RGB;
616			rb->Format = MESA_FORMAT_RGB888;
617			break;
618		case B_RGB16:
619			rb->_BaseFormat = GL_RGB;
620			rb->Format = MESA_FORMAT_RGB565;
621			break;
622		case B_RGB15:
623			rb->_BaseFormat = GL_RGB;
624			rb->Format = MESA_FORMAT_ARGB1555;
625			break;
626		default:
627			fprintf(stderr, "Unsupported screen color space %s\n",
628				color_space_name(fColorSpace));
629			debugger("Unsupported OpenGL color space");
630			return B_ERROR;
631	}
632	return B_OK;
633}
634
635
636/*!	Y inverted Map RenderBuffer function
637	We use a BBitmap for storage which has Y inverted.
638	If the Mesa provided Map function ever allows external
639	control of this we can omit this function.
640*/
641void
642MesaSoftwareRenderer::_RenderBufferMap(gl_context *ctx,
643	struct gl_renderbuffer *rb, GLuint x, GLuint y, GLuint w, GLuint h,
644	GLbitfield mode, GLubyte **mapOut, GLint *rowStrideOut)
645{
646	if (rb->ClassID == HAIKU_SWRAST_RENDERBUFFER_CLASS) {
647		struct swrast_renderbuffer *srb = swrast_renderbuffer(rb);
648		const GLuint bpp = _mesa_get_format_bytes(rb->Format);
649		GLint rowStride = rb->Width * bpp; // in Bytes
650
651		y = rb->Height - y - 1;
652
653		*rowStrideOut = -rowStride;
654		*mapOut = (GLubyte *) srb->Buffer + y * rowStride + x * bpp;
655	} else {
656		_swrast_map_soft_renderbuffer(ctx, rb, x, y, w, h, mode,
657			mapOut, rowStrideOut);
658	}
659}
660
661
662void
663MesaSoftwareRenderer::_RenderBufferDelete(struct gl_renderbuffer* rb)
664{
665	CALLED();
666	if (rb != NULL) {
667		struct swrast_renderbuffer *swRenderBuffer
668			= swrast_renderbuffer(rb);
669		if (swRenderBuffer != NULL)
670			free(swRenderBuffer->Buffer);
671	}
672	free(rb);
673}
674
675
676void
677MesaSoftwareRenderer::_CopyToDirect()
678{
679	BAutolock lock(fInfoLocker);
680
681	// check the bitmap size still matches the size
682	if (fInfo->window_bounds.bottom - fInfo->window_bounds.top
683		!= fBitmap->Bounds().IntegerHeight()
684		|| fInfo->window_bounds.right - fInfo->window_bounds.left
685			!= fBitmap->Bounds().IntegerWidth())
686		return;
687
688	uint8 bytesPerPixel = fInfo->bits_per_pixel / 8;
689	uint32 bytesPerRow = fBitmap->BytesPerRow();
690	for (uint32 i = 0; i < fInfo->clip_list_count; i++) {
691		clipping_rect *clip = &fInfo->clip_list[i];
692		int32 height = clip->bottom - clip->top + 1;
693		int32 bytesWidth
694			= (clip->right - clip->left + 1) * bytesPerPixel;
695		uint8* p = (uint8*)fInfo->bits + clip->top
696			* fInfo->bytes_per_row + clip->left * bytesPerPixel;
697		uint8* b = (uint8*)fBitmap->Bits()
698			+ (clip->top - fInfo->window_bounds.top) * bytesPerRow
699			+ (clip->left - fInfo->window_bounds.left)
700				* bytesPerPixel;
701
702		for (int y = 0; y < height; y++) {
703			memcpy(p, b, bytesWidth);
704			p += fInfo->bytes_per_row;
705			b += bytesPerRow;
706		}
707	}
708}
709