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 IV, kallisti5@unixzen.com
10 */
11
12
13#include "SoftwareRenderer.h"
14
15#include <Autolock.h>
16#include <interface/DirectWindowPrivate.h>
17#include <GraphicsDefs.h>
18#include <Screen.h>
19#include <stdio.h>
20#include <sys/time.h>
21#include <new>
22
23
24#ifdef DEBUG
25#	define TRACE(x...) printf("SoftwareRenderer: " x)
26#	define CALLED() TRACE("CALLED: %s\n", __PRETTY_FUNCTION__)
27#else
28#	define TRACE(x...)
29#	define CALLED()
30#endif
31#define ERROR(x...)	printf("SoftwareRenderer: " x)
32
33
34extern const char* color_space_name(color_space space);
35
36
37extern "C" _EXPORT BGLRenderer*
38instantiate_gl_renderer(BGLView *view, ulong opts)
39{
40	return new SoftwareRenderer(view, opts);
41}
42
43struct RasBuf32
44{
45	int32 width, height, stride;
46	int32 orgX, orgY;
47	int32 *colors;
48
49	RasBuf32(int32 width, int32 height, int32 stride, int32 orgX, int32 orgY, int32 *colors):
50		width(width), height(height), stride(stride), orgX(orgX), orgY(orgY), colors(colors)
51	{}
52
53	RasBuf32(BBitmap *bmp)
54	{
55		width  = bmp->Bounds().IntegerWidth()  + 1;
56		height = bmp->Bounds().IntegerHeight() + 1;
57		stride = bmp->BytesPerRow()/4;
58		orgX   = 0;
59		orgY   = 0;
60		colors = (int32*)bmp->Bits();
61	}
62
63	RasBuf32(direct_buffer_info *info)
64	{
65		width  = 0x7fffffff;
66		height = 0x7fffffff;
67		stride = info->bytes_per_row/4;
68		orgX   = 0;
69		orgY   = 0;
70		colors = (int32*)info->bits;
71	}
72
73	void ClipSize(int32 x, int32 y, int32 w, int32 h)
74	{
75		if (x < 0) {w += x; x = 0;}
76		if (y < 0) {h += y; y = 0;}
77		if (x + w >  width) {w = width  - x;}
78		if (y + h > height) {h = height - y;}
79		if ((w > 0) && (h > 0)) {
80			colors += y*stride + x;
81			width  = w;
82			height = h;
83		} else {
84			width = 0; height = 0; colors = NULL;
85		}
86		if (x + orgX > 0) {orgX += x;} else {orgX = 0;}
87		if (y + orgY > 0) {orgY += y;} else {orgY = 0;}
88	}
89
90	void ClipRect(int32 l, int32 t, int32 r, int32 b)
91	{
92		ClipSize(l, t, r - l, b - t);
93	}
94
95	void Shift(int32 dx, int32 dy)
96	{
97		orgX += dx;
98		orgY += dy;
99	}
100
101	void Clear(int32 color)
102	{
103		RasBuf32 dst = *this;
104		dst.stride -= dst.width;
105		for (; dst.height > 0; dst.height--) {
106			for (int32 i = dst.width; i > 0; i--)
107				*dst.colors++ = color;
108			dst.colors += dst.stride;
109		}
110	}
111
112	void Blit(RasBuf32 src)
113	{
114		RasBuf32 dst = *this;
115		int32 x, y;
116		x = src.orgX - orgX;
117		y = src.orgY - orgY;
118		dst.ClipSize(x, y, src.width, src.height);
119		src.ClipSize(-x, -y, width, height);
120		for (; dst.height > 0; dst.height--) {
121			memcpy(dst.colors, src.colors, 4*dst.width);
122			dst.colors += dst.stride;
123			src.colors += src.stride;
124		}
125	}
126};
127
128SoftwareRenderer::SoftwareRenderer(BGLView *view, ulong options)
129	:
130	BGLRenderer(view, options),
131	fDirectModeEnabled(false),
132	fInfo(NULL),
133	fInfoLocker("info locker"),
134	fOptions(options),
135	fColorSpace(B_NO_COLOR_SPACE)
136{
137	CALLED();
138
139	// Initialize the "Haiku Software GL Pipe"
140	time_t beg;
141	time_t end;
142	beg = time(NULL);
143	fContextObj = new GalliumContext(options);
144	end = time(NULL);
145	TRACE("Haiku Software GL Pipe initialization time: %f.\n",
146		difftime(end, beg));
147
148	BRect b = view->Bounds();
149	fColorSpace = BScreen(view->Window()).ColorSpace();
150	TRACE("%s: Colorspace:\t%s\n", __func__, color_space_name(fColorSpace));
151
152	fWidth = (GLint)b.IntegerWidth();
153	fHeight = (GLint)b.IntegerHeight();
154
155	// Initialize the first "Haiku Software GL Pipe" context
156	beg = time(NULL);
157	fContextID = fContextObj->CreateContext(this);
158	end = time(NULL);
159
160	if (fContextID < 0)
161		ERROR("%s: There was an error creating the context!\n", __func__);
162	else {
163		TRACE("%s: Haiku Software GL Pipe context creation time: %f.\n",
164			__func__, difftime(end, beg));
165	}
166
167	if (!fContextObj->GetCurrentContext())
168		LockGL();
169}
170
171
172SoftwareRenderer::~SoftwareRenderer()
173{
174	CALLED();
175
176	if (fContextObj)
177		delete fContextObj;
178}
179
180
181void
182SoftwareRenderer::LockGL()
183{
184//	CALLED();
185	BGLRenderer::LockGL();
186
187	color_space cs = BScreen(GLView()->Window()).ColorSpace();
188
189	{
190		BAutolock lock(fInfoLocker);
191		if (fDirectModeEnabled && fInfo != NULL) {
192			fWidth = fInfo->window_bounds.right - fInfo->window_bounds.left;
193			fHeight = fInfo->window_bounds.bottom - fInfo->window_bounds.top;
194		}
195
196		fContextObj->Validate(fWidth, fHeight);
197		fColorSpace = cs;
198	}
199
200	// do not hold fInfoLocker here to avoid deadlock
201	fContextObj->SetCurrentContext(true, fContextID);
202}
203
204
205void
206SoftwareRenderer::UnlockGL()
207{
208//	CALLED();
209	if ((fOptions & BGL_DOUBLE) == 0) {
210		SwapBuffers();
211	}
212	fContextObj->SetCurrentContext(false, fContextID);
213	BGLRenderer::UnlockGL();
214}
215
216
217void
218SoftwareRenderer::Display(BBitmap *bitmap, BRect *updateRect)
219{
220//	CALLED();
221
222	if (!fDirectModeEnabled) {
223		// TODO: avoid timeout
224		if (GLView()->LockLooperWithTimeout(1000) == B_OK) {
225			GLView()->DrawBitmap(bitmap, B_ORIGIN);
226			GLView()->UnlockLooper();
227		}
228	} else {
229		BAutolock lock(fInfoLocker);
230		if (fInfo != NULL) {
231			RasBuf32 srcBuf(bitmap);
232			RasBuf32 dstBuf(fInfo);
233			for (uint32 i = 0; i < fInfo->clip_list_count; i++) {
234				clipping_rect *clip = &fInfo->clip_list[i];
235				RasBuf32 dstClip = dstBuf;
236				dstClip.ClipRect(clip->left, clip->top, clip->right + 1, clip->bottom + 1);
237				dstClip.Shift(-fInfo->window_bounds.left, -fInfo->window_bounds.top);
238				dstClip.Blit(srcBuf);
239			}
240		}
241	}
242}
243
244
245void
246SoftwareRenderer::SwapBuffers(bool vsync)
247{
248	BScreen screen(GLView()->Window());
249	fContextObj->SwapBuffers(fContextID);
250	fContextObj->Validate(fWidth, fHeight);
251	if (vsync)
252		screen.WaitForRetrace();
253}
254
255void
256SoftwareRenderer::Draw(BRect updateRect)
257{
258//	CALLED();
259	fContextObj->Draw(fContextID, updateRect);
260}
261
262
263status_t
264SoftwareRenderer::CopyPixelsOut(BPoint location, BBitmap *bitmap)
265{
266	CALLED();
267
268	// TODO: implement
269	return B_ERROR;
270}
271
272
273status_t
274SoftwareRenderer::CopyPixelsIn(BBitmap *bitmap, BPoint location)
275{
276	CALLED();
277
278	// TODO: implement
279	return B_ERROR;
280}
281
282
283void
284SoftwareRenderer::EnableDirectMode(bool enabled)
285{
286	fDirectModeEnabled = enabled;
287}
288
289
290void
291SoftwareRenderer::DirectConnected(direct_buffer_info *info)
292{
293//	CALLED();
294	BAutolock lock(fInfoLocker);
295	if (info) {
296		if (!fInfo) {
297			fInfo = (direct_buffer_info *)calloc(1,
298				DIRECT_BUFFER_INFO_AREA_SIZE);
299		}
300		memcpy(fInfo, info, DIRECT_BUFFER_INFO_AREA_SIZE);
301	} else if (fInfo) {
302		free(fInfo);
303		fInfo = NULL;
304	}
305}
306
307
308void
309SoftwareRenderer::FrameResized(float width, float height)
310{
311	TRACE("%s: %f x %f\n", __func__, width, height);
312
313	BAutolock lock(fInfoLocker);
314	fWidth = (GLuint)width;
315	fHeight = (GLuint)height;
316}
317