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, BGLDispatcher *dispatcher)
39{
40	return new SoftwareRenderer(view, opts, dispatcher);
41}
42
43SoftwareRenderer::SoftwareRenderer(BGLView *view, ulong options,
44	BGLDispatcher* dispatcher)
45	:
46	BGLRenderer(view, options, dispatcher),
47	fBitmap(NULL),
48	fDirectModeEnabled(false),
49	fInfo(NULL),
50	fInfoLocker("info locker"),
51	fOptions(options),
52	fColorSpace(B_NO_COLOR_SPACE)
53{
54	CALLED();
55
56	// Disable double buffer for the moment.
57	//options &= ~BGL_DOUBLE;
58
59	// Initialize the "Haiku Software GL Pipe"
60	time_t beg;
61	time_t end;
62	beg = time(NULL);
63	fContextObj = new GalliumContext(options);
64	end = time(NULL);
65	TRACE("Haiku Software GL Pipe initialization time: %f.\n",
66		difftime(end, beg));
67
68	// Allocate a bitmap
69	BRect b = view->Bounds();
70	fColorSpace = BScreen(view->Window()).ColorSpace();
71	TRACE("%s: Colorspace:\t%s\n", __func__, color_space_name(fColorSpace));
72
73	fWidth = (GLint)b.IntegerWidth();
74	fHeight = (GLint)b.IntegerHeight();
75
76	_AllocateBitmap();
77
78	// Initialize the first "Haiku Software GL Pipe" context
79	beg = time(NULL);
80	fContextID = fContextObj->CreateContext(fBitmap);
81	end = time(NULL);
82
83	if (fContextID < 0)
84		ERROR("%s: There was an error creating the context!\n", __func__);
85	else {
86		TRACE("%s: Haiku Software GL Pipe context creation time: %f.\n",
87			__func__, difftime(end, beg));
88	}
89
90	if (!fContextObj->GetCurrentContext())
91		LockGL();
92}
93
94
95SoftwareRenderer::~SoftwareRenderer()
96{
97	CALLED();
98
99	if (fContextObj)
100		delete fContextObj;
101	if (fBitmap)
102		delete fBitmap;
103}
104
105
106void
107SoftwareRenderer::LockGL()
108{
109//	CALLED();
110	BGLRenderer::LockGL();
111
112	color_space cs = BScreen(GLView()->Window()).ColorSpace();
113
114	BAutolock lock(fInfoLocker);
115	if (fDirectModeEnabled && fInfo != NULL) {
116		fWidth = fInfo->window_bounds.right - fInfo->window_bounds.left;
117		fHeight = fInfo->window_bounds.bottom - fInfo->window_bounds.top;
118	}
119
120	if (fBitmap && cs == fColorSpace && fContextObj->Validate(fWidth, fHeight)) {
121		fContextObj->SetCurrentContext(fBitmap, fContextID);
122		return;
123	}
124
125	fColorSpace = cs;
126
127	_AllocateBitmap();
128	fContextObj->SetCurrentContext(fBitmap, fContextID);
129}
130
131
132void
133SoftwareRenderer::UnlockGL()
134{
135//	CALLED();
136	if ((fOptions & BGL_DOUBLE) == 0) {
137		SwapBuffers();
138	}
139	fContextObj->SetCurrentContext(NULL, fContextID);
140	BGLRenderer::UnlockGL();
141}
142
143
144void
145SoftwareRenderer::SwapBuffers(bool vsync)
146{
147//	CALLED();
148	if (!fBitmap)
149		return;
150
151	BScreen screen(GLView()->Window());
152
153	fContextObj->SwapBuffers(fContextID);
154
155	BAutolock lock(fInfoLocker);
156
157	if (!fDirectModeEnabled || fInfo == NULL) {
158		if (GLView()->LockLooperWithTimeout(1000) == B_OK) {
159			GLView()->DrawBitmap(fBitmap, B_ORIGIN);
160			GLView()->UnlockLooper();
161			if (vsync)
162				screen.WaitForRetrace();
163		}
164		return;
165	}
166
167	// check the bitmap size still matches the size
168	if (fInfo->window_bounds.bottom - fInfo->window_bounds.top
169			!= fBitmap->Bounds().IntegerHeight()
170			|| fInfo->window_bounds.right - fInfo->window_bounds.left
171			!= fBitmap->Bounds().IntegerWidth()) {
172		ERROR("%s: Bitmap size doesn't match size!\n", __func__);
173		return;
174	}
175
176	uint32 bytesPerRow = fBitmap->BytesPerRow();
177	uint8 bytesPerPixel = bytesPerRow / fBitmap->Bounds().IntegerWidth();
178
179	for (uint32 i = 0; i < fInfo->clip_list_count; i++) {
180		clipping_rect *clip = &fInfo->clip_list[i];
181		int32 height = clip->bottom - clip->top + 1;
182		int32 bytesWidth
183			= (clip->right - clip->left + 1) * bytesPerPixel;
184		bytesWidth -= bytesPerPixel;
185		uint8 *p = (uint8 *)fInfo->bits + clip->top
186			* fInfo->bytes_per_row + clip->left * bytesPerPixel;
187		uint8 *b = (uint8 *)fBitmap->Bits()
188			+ (clip->top - fInfo->window_bounds.top) * bytesPerRow
189			+ (clip->left - fInfo->window_bounds.left) * bytesPerPixel;
190
191		for (int y = 0; y < height - 1; y++) {
192			memcpy(p, b, bytesWidth);
193			p += fInfo->bytes_per_row;
194			b += bytesPerRow;
195		}
196	}
197
198	if (vsync)
199		screen.WaitForRetrace();
200}
201
202
203void
204SoftwareRenderer::Draw(BRect updateRect)
205{
206//	CALLED();
207	if ((!fDirectModeEnabled || fInfo == NULL) && fBitmap)
208		GLView()->DrawBitmap(fBitmap, updateRect, updateRect);
209}
210
211
212status_t
213SoftwareRenderer::CopyPixelsOut(BPoint location, BBitmap *bitmap)
214{
215	CALLED();
216	color_space scs = fBitmap->ColorSpace();
217	color_space dcs = bitmap->ColorSpace();
218
219	if (scs != dcs && (scs != B_RGBA32 || dcs != B_RGB32)) {
220		ERROR("%s::CopyPixelsOut(): incompatible color space: %s != %s\n",
221			__PRETTY_FUNCTION__, color_space_name(scs), color_space_name(dcs));
222		return B_BAD_TYPE;
223	}
224
225	BRect sr = fBitmap->Bounds();
226	BRect dr = bitmap->Bounds();
227
228//	int32 w1 = sr.IntegerWidth();
229//	int32 h1 = sr.IntegerHeight();
230//	int32 w2 = dr.IntegerWidth();
231//	int32 h2 = dr.IntegerHeight();
232
233	sr = sr & dr.OffsetBySelf(location);
234	dr = sr.OffsetByCopy(-location.x, -location.y);
235
236	uint8 *ps = (uint8 *) fBitmap->Bits();
237	uint8 *pd = (uint8 *) bitmap->Bits();
238	uint32 *s, *d;
239	uint32 y;
240	for (y = (uint32) sr.top; y <= (uint32) sr.bottom; y++) {
241		s = (uint32 *)(ps + y * fBitmap->BytesPerRow());
242		s += (uint32) sr.left;
243
244		d = (uint32 *)(pd + (y + (uint32)(dr.top - sr.top))
245			* bitmap->BytesPerRow());
246		d += (uint32) dr.left;
247		memcpy(d, s, dr.IntegerWidth() * 4);
248	}
249
250	return B_OK;
251}
252
253
254status_t
255SoftwareRenderer::CopyPixelsIn(BBitmap *bitmap, BPoint location)
256{
257	CALLED();
258
259	color_space sourceCS = bitmap->ColorSpace();
260	color_space destinationCS = fBitmap->ColorSpace();
261
262	if (sourceCS != destinationCS
263		&& (sourceCS != B_RGB32 || destinationCS != B_RGBA32)) {
264		ERROR("%s::CopyPixelsIn(): incompatible color space: %s != %s\n",
265			__PRETTY_FUNCTION__, color_space_name(sourceCS),
266			color_space_name(destinationCS));
267		return B_BAD_TYPE;
268	}
269
270	BRect sr = bitmap->Bounds();
271	BRect dr = fBitmap->Bounds();
272
273	sr = sr & dr.OffsetBySelf(location);
274	dr = sr.OffsetByCopy(-location.x, -location.y);
275
276	uint8 *ps = (uint8 *) bitmap->Bits();
277	uint8 *pd = (uint8 *) fBitmap->Bits();
278	uint32 *s, *d;
279	uint32 y;
280
281	for (y = (uint32) sr.top; y <= (uint32) sr.bottom; y++) {
282		s = (uint32 *)(ps + y * bitmap->BytesPerRow());
283		s += (uint32) sr.left;
284
285		d = (uint32 *)(pd + (y + (uint32)(dr.top - sr.top))
286			* fBitmap->BytesPerRow());
287		d += (uint32) dr.left;
288
289		memcpy(d, s, dr.IntegerWidth() * 4);
290	}
291
292	return B_OK;
293}
294
295
296void
297SoftwareRenderer::EnableDirectMode(bool enabled)
298{
299	fDirectModeEnabled = enabled;
300}
301
302
303void
304SoftwareRenderer::DirectConnected(direct_buffer_info *info)
305{
306//	CALLED();
307	BAutolock lock(fInfoLocker);
308	if (info) {
309		if (!fInfo) {
310			fInfo = (direct_buffer_info *)calloc(1,
311				DIRECT_BUFFER_INFO_AREA_SIZE);
312		}
313		memcpy(fInfo, info, DIRECT_BUFFER_INFO_AREA_SIZE);
314	} else if (fInfo) {
315		free(fInfo);
316		fInfo = NULL;
317	}
318}
319
320
321void
322SoftwareRenderer::FrameResized(float width, float height)
323{
324	TRACE("%s: %f x %f\n", __func__, width, height);
325
326	BAutolock lock(fInfoLocker);
327	fWidth = (GLuint)width;
328	fHeight = (GLuint)height;
329}
330
331
332void
333SoftwareRenderer::_AllocateBitmap()
334{
335//	CALLED();
336
337	// allocate new size of back buffer bitmap
338	BAutolock lock(fInfoLocker);
339	if (fBitmap)
340		delete fBitmap;
341
342	if (fWidth < 1 || fHeight < 1) {
343		TRACE("%s: Can't allocate bitmap of %dx%d\n", __func__,
344			fWidth, fHeight);
345		return;
346	}
347	BRect rect(0.0, 0.0, fWidth, fHeight);
348	fBitmap = new (std::nothrow) BBitmap(rect, fColorSpace);
349	if (fBitmap == NULL) {
350		TRACE("%s: Can't create bitmap!\n", __func__);
351		return;
352	}
353
354	TRACE("%s: New bitmap size: %" B_PRId32 " x %" B_PRId32 "\n", __func__,
355		fBitmap->Bounds().IntegerWidth(), fBitmap->Bounds().IntegerHeight());
356
357#if 0
358	// debug..
359	void *data = fBitmap->Bits();
360	memset(data, 0xcc, fBitmap->BitsLength());
361#endif
362}
363