1af69d88dSmrg/*
2af69d88dSmrg * Copyright 2006-2012, Haiku, Inc. All rights reserved.
3af69d88dSmrg * Distributed under the terms of the MIT License.
4af69d88dSmrg *
5af69d88dSmrg * Authors:
6af69d88dSmrg *		Jérôme Duval, korli@users.berlios.de
7af69d88dSmrg *		Philippe Houdoin, philippe.houdoin@free.fr
8af69d88dSmrg *		Artur Wyszynski, harakash@gmail.com
9af69d88dSmrg *		Alexander von Gluck IV, kallisti5@unixzen.com
10af69d88dSmrg */
11af69d88dSmrg
12af69d88dSmrg
13af69d88dSmrg#include "SoftwareRenderer.h"
14af69d88dSmrg
15af69d88dSmrg#include <Autolock.h>
16af69d88dSmrg#include <interface/DirectWindowPrivate.h>
17af69d88dSmrg#include <GraphicsDefs.h>
18af69d88dSmrg#include <Screen.h>
19af69d88dSmrg#include <stdio.h>
20af69d88dSmrg#include <sys/time.h>
21af69d88dSmrg#include <new>
22af69d88dSmrg
23af69d88dSmrg
24af69d88dSmrg#ifdef DEBUG
25af69d88dSmrg#	define TRACE(x...) printf("SoftwareRenderer: " x)
26af69d88dSmrg#	define CALLED() TRACE("CALLED: %s\n", __PRETTY_FUNCTION__)
27af69d88dSmrg#else
28af69d88dSmrg#	define TRACE(x...)
29af69d88dSmrg#	define CALLED()
30af69d88dSmrg#endif
31af69d88dSmrg#define ERROR(x...)	printf("SoftwareRenderer: " x)
32af69d88dSmrg
33af69d88dSmrg
34af69d88dSmrgextern const char* color_space_name(color_space space);
35af69d88dSmrg
36af69d88dSmrg
37af69d88dSmrgextern "C" _EXPORT BGLRenderer*
387ec681f3Smrginstantiate_gl_renderer(BGLView *view, ulong opts)
39af69d88dSmrg{
407ec681f3Smrg	return new SoftwareRenderer(view, opts);
41af69d88dSmrg}
42af69d88dSmrg
437ec681f3Smrgstruct RasBuf32
447ec681f3Smrg{
457ec681f3Smrg	int32 width, height, stride;
467ec681f3Smrg	int32 orgX, orgY;
477ec681f3Smrg	int32 *colors;
487ec681f3Smrg
497ec681f3Smrg	RasBuf32(int32 width, int32 height, int32 stride, int32 orgX, int32 orgY, int32 *colors):
507ec681f3Smrg		width(width), height(height), stride(stride), orgX(orgX), orgY(orgY), colors(colors)
517ec681f3Smrg	{}
527ec681f3Smrg
537ec681f3Smrg	RasBuf32(BBitmap *bmp)
547ec681f3Smrg	{
557ec681f3Smrg		width  = bmp->Bounds().IntegerWidth()  + 1;
567ec681f3Smrg		height = bmp->Bounds().IntegerHeight() + 1;
577ec681f3Smrg		stride = bmp->BytesPerRow()/4;
587ec681f3Smrg		orgX   = 0;
597ec681f3Smrg		orgY   = 0;
607ec681f3Smrg		colors = (int32*)bmp->Bits();
617ec681f3Smrg	}
627ec681f3Smrg
637ec681f3Smrg	RasBuf32(direct_buffer_info *info)
647ec681f3Smrg	{
657ec681f3Smrg		width  = 0x7fffffff;
667ec681f3Smrg		height = 0x7fffffff;
677ec681f3Smrg		stride = info->bytes_per_row/4;
687ec681f3Smrg		orgX   = 0;
697ec681f3Smrg		orgY   = 0;
707ec681f3Smrg		colors = (int32*)info->bits;
717ec681f3Smrg	}
727ec681f3Smrg
737ec681f3Smrg	void ClipSize(int32 x, int32 y, int32 w, int32 h)
747ec681f3Smrg	{
757ec681f3Smrg		if (x < 0) {w += x; x = 0;}
767ec681f3Smrg		if (y < 0) {h += y; y = 0;}
777ec681f3Smrg		if (x + w >  width) {w = width  - x;}
787ec681f3Smrg		if (y + h > height) {h = height - y;}
797ec681f3Smrg		if ((w > 0) && (h > 0)) {
807ec681f3Smrg			colors += y*stride + x;
817ec681f3Smrg			width  = w;
827ec681f3Smrg			height = h;
837ec681f3Smrg		} else {
847ec681f3Smrg			width = 0; height = 0; colors = NULL;
857ec681f3Smrg		}
867ec681f3Smrg		if (x + orgX > 0) {orgX += x;} else {orgX = 0;}
877ec681f3Smrg		if (y + orgY > 0) {orgY += y;} else {orgY = 0;}
887ec681f3Smrg	}
897ec681f3Smrg
907ec681f3Smrg	void ClipRect(int32 l, int32 t, int32 r, int32 b)
917ec681f3Smrg	{
927ec681f3Smrg		ClipSize(l, t, r - l, b - t);
937ec681f3Smrg	}
947ec681f3Smrg
957ec681f3Smrg	void Shift(int32 dx, int32 dy)
967ec681f3Smrg	{
977ec681f3Smrg		orgX += dx;
987ec681f3Smrg		orgY += dy;
997ec681f3Smrg	}
1007ec681f3Smrg
1017ec681f3Smrg	void Clear(int32 color)
1027ec681f3Smrg	{
1037ec681f3Smrg		RasBuf32 dst = *this;
1047ec681f3Smrg		dst.stride -= dst.width;
1057ec681f3Smrg		for (; dst.height > 0; dst.height--) {
1067ec681f3Smrg			for (int32 i = dst.width; i > 0; i--)
1077ec681f3Smrg				*dst.colors++ = color;
1087ec681f3Smrg			dst.colors += dst.stride;
1097ec681f3Smrg		}
1107ec681f3Smrg	}
1117ec681f3Smrg
1127ec681f3Smrg	void Blit(RasBuf32 src)
1137ec681f3Smrg	{
1147ec681f3Smrg		RasBuf32 dst = *this;
1157ec681f3Smrg		int32 x, y;
1167ec681f3Smrg		x = src.orgX - orgX;
1177ec681f3Smrg		y = src.orgY - orgY;
1187ec681f3Smrg		dst.ClipSize(x, y, src.width, src.height);
1197ec681f3Smrg		src.ClipSize(-x, -y, width, height);
1207ec681f3Smrg		for (; dst.height > 0; dst.height--) {
1217ec681f3Smrg			memcpy(dst.colors, src.colors, 4*dst.width);
1227ec681f3Smrg			dst.colors += dst.stride;
1237ec681f3Smrg			src.colors += src.stride;
1247ec681f3Smrg		}
1257ec681f3Smrg	}
1267ec681f3Smrg};
1277ec681f3Smrg
1287ec681f3SmrgSoftwareRenderer::SoftwareRenderer(BGLView *view, ulong options)
129af69d88dSmrg	:
1307ec681f3Smrg	BGLRenderer(view, options),
131af69d88dSmrg	fDirectModeEnabled(false),
132af69d88dSmrg	fInfo(NULL),
133af69d88dSmrg	fInfoLocker("info locker"),
134af69d88dSmrg	fOptions(options),
135af69d88dSmrg	fColorSpace(B_NO_COLOR_SPACE)
136af69d88dSmrg{
137af69d88dSmrg	CALLED();
138af69d88dSmrg
139af69d88dSmrg	// Initialize the "Haiku Software GL Pipe"
140af69d88dSmrg	time_t beg;
141af69d88dSmrg	time_t end;
142af69d88dSmrg	beg = time(NULL);
143af69d88dSmrg	fContextObj = new GalliumContext(options);
144af69d88dSmrg	end = time(NULL);
145af69d88dSmrg	TRACE("Haiku Software GL Pipe initialization time: %f.\n",
146af69d88dSmrg		difftime(end, beg));
147af69d88dSmrg
148af69d88dSmrg	BRect b = view->Bounds();
149af69d88dSmrg	fColorSpace = BScreen(view->Window()).ColorSpace();
150af69d88dSmrg	TRACE("%s: Colorspace:\t%s\n", __func__, color_space_name(fColorSpace));
151af69d88dSmrg
152af69d88dSmrg	fWidth = (GLint)b.IntegerWidth();
153af69d88dSmrg	fHeight = (GLint)b.IntegerHeight();
154af69d88dSmrg
155af69d88dSmrg	// Initialize the first "Haiku Software GL Pipe" context
156af69d88dSmrg	beg = time(NULL);
1577ec681f3Smrg	fContextID = fContextObj->CreateContext(this);
158af69d88dSmrg	end = time(NULL);
159af69d88dSmrg
160af69d88dSmrg	if (fContextID < 0)
161af69d88dSmrg		ERROR("%s: There was an error creating the context!\n", __func__);
162af69d88dSmrg	else {
163af69d88dSmrg		TRACE("%s: Haiku Software GL Pipe context creation time: %f.\n",
164af69d88dSmrg			__func__, difftime(end, beg));
165af69d88dSmrg	}
166af69d88dSmrg
167af69d88dSmrg	if (!fContextObj->GetCurrentContext())
168af69d88dSmrg		LockGL();
169af69d88dSmrg}
170af69d88dSmrg
171af69d88dSmrg
172af69d88dSmrgSoftwareRenderer::~SoftwareRenderer()
173af69d88dSmrg{
174af69d88dSmrg	CALLED();
175af69d88dSmrg
176af69d88dSmrg	if (fContextObj)
177af69d88dSmrg		delete fContextObj;
178af69d88dSmrg}
179af69d88dSmrg
180af69d88dSmrg
181af69d88dSmrgvoid
182af69d88dSmrgSoftwareRenderer::LockGL()
183af69d88dSmrg{
184af69d88dSmrg//	CALLED();
185af69d88dSmrg	BGLRenderer::LockGL();
186af69d88dSmrg
187af69d88dSmrg	color_space cs = BScreen(GLView()->Window()).ColorSpace();
188af69d88dSmrg
1897ec681f3Smrg	{
1907ec681f3Smrg		BAutolock lock(fInfoLocker);
1917ec681f3Smrg		if (fDirectModeEnabled && fInfo != NULL) {
1927ec681f3Smrg			fWidth = fInfo->window_bounds.right - fInfo->window_bounds.left;
1937ec681f3Smrg			fHeight = fInfo->window_bounds.bottom - fInfo->window_bounds.top;
1947ec681f3Smrg		}
195af69d88dSmrg
1967ec681f3Smrg		fContextObj->Validate(fWidth, fHeight);
1977ec681f3Smrg		fColorSpace = cs;
198af69d88dSmrg	}
199af69d88dSmrg
2007ec681f3Smrg	// do not hold fInfoLocker here to avoid deadlock
2017ec681f3Smrg	fContextObj->SetCurrentContext(true, fContextID);
202af69d88dSmrg}
203af69d88dSmrg
204af69d88dSmrg
205af69d88dSmrgvoid
206af69d88dSmrgSoftwareRenderer::UnlockGL()
207af69d88dSmrg{
208af69d88dSmrg//	CALLED();
209af69d88dSmrg	if ((fOptions & BGL_DOUBLE) == 0) {
210af69d88dSmrg		SwapBuffers();
211af69d88dSmrg	}
2127ec681f3Smrg	fContextObj->SetCurrentContext(false, fContextID);
213af69d88dSmrg	BGLRenderer::UnlockGL();
214af69d88dSmrg}
215af69d88dSmrg
216af69d88dSmrg
217af69d88dSmrgvoid
2187ec681f3SmrgSoftwareRenderer::Display(BBitmap *bitmap, BRect *updateRect)
219af69d88dSmrg{
220af69d88dSmrg//	CALLED();
221af69d88dSmrg
2227ec681f3Smrg	if (!fDirectModeEnabled) {
2237ec681f3Smrg		// TODO: avoid timeout
224af69d88dSmrg		if (GLView()->LockLooperWithTimeout(1000) == B_OK) {
2257ec681f3Smrg			GLView()->DrawBitmap(bitmap, B_ORIGIN);
226af69d88dSmrg			GLView()->UnlockLooper();
227af69d88dSmrg		}
2287ec681f3Smrg	} else {
2297ec681f3Smrg		BAutolock lock(fInfoLocker);
2307ec681f3Smrg		if (fInfo != NULL) {
2317ec681f3Smrg			RasBuf32 srcBuf(bitmap);
2327ec681f3Smrg			RasBuf32 dstBuf(fInfo);
2337ec681f3Smrg			for (uint32 i = 0; i < fInfo->clip_list_count; i++) {
2347ec681f3Smrg				clipping_rect *clip = &fInfo->clip_list[i];
2357ec681f3Smrg				RasBuf32 dstClip = dstBuf;
2367ec681f3Smrg				dstClip.ClipRect(clip->left, clip->top, clip->right + 1, clip->bottom + 1);
2377ec681f3Smrg				dstClip.Shift(-fInfo->window_bounds.left, -fInfo->window_bounds.top);
2387ec681f3Smrg				dstClip.Blit(srcBuf);
2397ec681f3Smrg			}
240af69d88dSmrg		}
241af69d88dSmrg	}
2427ec681f3Smrg}
243af69d88dSmrg
2447ec681f3Smrg
2457ec681f3Smrgvoid
2467ec681f3SmrgSoftwareRenderer::SwapBuffers(bool vsync)
2477ec681f3Smrg{
2487ec681f3Smrg	BScreen screen(GLView()->Window());
2497ec681f3Smrg	fContextObj->SwapBuffers(fContextID);
2507ec681f3Smrg	fContextObj->Validate(fWidth, fHeight);
251af69d88dSmrg	if (vsync)
252af69d88dSmrg		screen.WaitForRetrace();
253af69d88dSmrg}
254af69d88dSmrg
255af69d88dSmrgvoid
256af69d88dSmrgSoftwareRenderer::Draw(BRect updateRect)
257af69d88dSmrg{
258af69d88dSmrg//	CALLED();
2597ec681f3Smrg	fContextObj->Draw(fContextID, updateRect);
260af69d88dSmrg}
261af69d88dSmrg
262af69d88dSmrg
263af69d88dSmrgstatus_t
264af69d88dSmrgSoftwareRenderer::CopyPixelsOut(BPoint location, BBitmap *bitmap)
265af69d88dSmrg{
266af69d88dSmrg	CALLED();
267af69d88dSmrg
2687ec681f3Smrg	// TODO: implement
2697ec681f3Smrg	return B_ERROR;
270af69d88dSmrg}
271af69d88dSmrg
272af69d88dSmrg
273af69d88dSmrgstatus_t
274af69d88dSmrgSoftwareRenderer::CopyPixelsIn(BBitmap *bitmap, BPoint location)
275af69d88dSmrg{
276af69d88dSmrg	CALLED();
277af69d88dSmrg
2787ec681f3Smrg	// TODO: implement
2797ec681f3Smrg	return B_ERROR;
280af69d88dSmrg}
281af69d88dSmrg
282af69d88dSmrg
283af69d88dSmrgvoid
284af69d88dSmrgSoftwareRenderer::EnableDirectMode(bool enabled)
285af69d88dSmrg{
286af69d88dSmrg	fDirectModeEnabled = enabled;
287af69d88dSmrg}
288af69d88dSmrg
289af69d88dSmrg
290af69d88dSmrgvoid
291af69d88dSmrgSoftwareRenderer::DirectConnected(direct_buffer_info *info)
292af69d88dSmrg{
293af69d88dSmrg//	CALLED();
294af69d88dSmrg	BAutolock lock(fInfoLocker);
295af69d88dSmrg	if (info) {
296af69d88dSmrg		if (!fInfo) {
297af69d88dSmrg			fInfo = (direct_buffer_info *)calloc(1,
298af69d88dSmrg				DIRECT_BUFFER_INFO_AREA_SIZE);
299af69d88dSmrg		}
300af69d88dSmrg		memcpy(fInfo, info, DIRECT_BUFFER_INFO_AREA_SIZE);
301af69d88dSmrg	} else if (fInfo) {
302af69d88dSmrg		free(fInfo);
303af69d88dSmrg		fInfo = NULL;
304af69d88dSmrg	}
305af69d88dSmrg}
306af69d88dSmrg
307af69d88dSmrg
308af69d88dSmrgvoid
309af69d88dSmrgSoftwareRenderer::FrameResized(float width, float height)
310af69d88dSmrg{
311af69d88dSmrg	TRACE("%s: %f x %f\n", __func__, width, height);
312af69d88dSmrg
313af69d88dSmrg	BAutolock lock(fInfoLocker);
31401e04c3fSmrg	fWidth = (GLuint)width;
31501e04c3fSmrg	fHeight = (GLuint)height;
316af69d88dSmrg}
317