|
1 /* |
|
2 * Copyright (c) 2003 Nokia Corporation and/or its subsidiary(-ies). |
|
3 * All rights reserved. |
|
4 * This component and the accompanying materials are made available |
|
5 * under the terms of the License "Eclipse Public License v1.0" |
|
6 * which accompanies this distribution, and is available |
|
7 * at the URL "http://www.eclipse.org/legal/epl-v10.html". |
|
8 * |
|
9 * Initial Contributors: |
|
10 * Nokia Corporation - initial contribution. |
|
11 * |
|
12 * Contributors: |
|
13 * |
|
14 * Description: EGL rendering context management functions |
|
15 * |
|
16 */ |
|
17 |
|
18 |
|
19 /*! |
|
20 * \internal |
|
21 * \file |
|
22 * \brief EGL rendering context management functions |
|
23 */ |
|
24 |
|
25 #if defined(M3G_NGL_CONTEXT_API) |
|
26 # error This file is for the OES API only |
|
27 #endif |
|
28 |
|
29 #include <GLES/egl.h> |
|
30 #include "m3g_image.h" |
|
31 |
|
32 /*---------------------------------------------------------------------- |
|
33 * Private functions |
|
34 *--------------------------------------------------------------------*/ |
|
35 |
|
36 /*! |
|
37 * \internal |
|
38 * \brief Queries for an EGL configuration matching given M3G format |
|
39 * parameters |
|
40 */ |
|
41 static EGLConfig m3gQueryEGLConfig(M3Genum format, |
|
42 M3Gbitmask bufferBits, |
|
43 EGLint surfaceBits) |
|
44 { |
|
45 struct { int attrib, value; } attribs[9]; |
|
46 int samples; |
|
47 |
|
48 /* Determine color depth */ |
|
49 |
|
50 attribs[0].attrib = EGL_RED_SIZE; |
|
51 attribs[1].attrib = EGL_GREEN_SIZE; |
|
52 attribs[2].attrib = EGL_BLUE_SIZE; |
|
53 attribs[3].attrib = EGL_ALPHA_SIZE; |
|
54 |
|
55 switch (format) { |
|
56 case M3G_RGB4: |
|
57 attribs[0].value = 4; |
|
58 attribs[1].value = 4; |
|
59 attribs[2].value = 4; |
|
60 attribs[3].value = 0; |
|
61 break; |
|
62 case M3G_RGB565: |
|
63 attribs[0].value = 5; |
|
64 attribs[1].value = 6; |
|
65 attribs[2].value = 5; |
|
66 attribs[3].value = 0; |
|
67 break; |
|
68 case M3G_RGB8: |
|
69 case M3G_BGR8_32: |
|
70 attribs[0].value = 8; |
|
71 attribs[1].value = 8; |
|
72 attribs[2].value = 8; |
|
73 attribs[3].value = 0; |
|
74 break; |
|
75 case M3G_RGBA8: |
|
76 case M3G_BGRA8: |
|
77 attribs[0].value = 8; |
|
78 attribs[1].value = 8; |
|
79 attribs[2].value = 8; |
|
80 attribs[3].value = 8; |
|
81 break; |
|
82 default: |
|
83 return NULL; |
|
84 } |
|
85 |
|
86 /* Set up the depth buffer */ |
|
87 |
|
88 attribs[4].attrib = EGL_DEPTH_SIZE; |
|
89 attribs[4].value = (bufferBits & M3G_DEPTH_BUFFER_BIT) ? 16 : 0; |
|
90 |
|
91 /* Set target surface type mask */ |
|
92 |
|
93 attribs[5].attrib = EGL_SURFACE_TYPE; |
|
94 attribs[5].value = surfaceBits; |
|
95 |
|
96 /* Try to get multisampling if requested */ |
|
97 |
|
98 attribs[6].attrib = EGL_SAMPLE_BUFFERS; |
|
99 attribs[7].attrib = EGL_SAMPLES; |
|
100 attribs[8].attrib = EGL_NONE; |
|
101 |
|
102 /* Try 4 samples if multisampling enabled, then 2, then 1 */ |
|
103 |
|
104 samples = (bufferBits & M3G_MULTISAMPLE_BUFFER_BIT) ? 4 : 1; |
|
105 for ( ; samples > 0; samples >>= 1) { |
|
106 |
|
107 if (samples > 1) { |
|
108 attribs[6].value = 1; |
|
109 attribs[7].value = samples; |
|
110 } |
|
111 else { |
|
112 attribs[6].value = EGL_FALSE; |
|
113 attribs[7].value = 0; |
|
114 } |
|
115 |
|
116 /* Get the first matching config; according to EGL sorting |
|
117 * rules, this should be an accelerated one if possible */ |
|
118 { |
|
119 EGLConfig config; |
|
120 int numConfigs; |
|
121 eglChooseConfig(eglGetDisplay(EGL_DEFAULT_DISPLAY), |
|
122 (const int *) attribs, |
|
123 &config, 1, |
|
124 &numConfigs); |
|
125 |
|
126 M3G_ASSERT(eglGetError() == EGL_SUCCESS); |
|
127 |
|
128 /* If we got a config, return that; otherwise, drop the |
|
129 * number of multisampling samples and try again, or |
|
130 * return NULL for no config if we already have zero |
|
131 * samples */ |
|
132 |
|
133 if (numConfigs > 0) { |
|
134 M3G_LOG1(M3G_LOG_RENDERING, "Selected EGL config #%d\n", config); |
|
135 return config; |
|
136 } |
|
137 |
|
138 if (samples == 2) { |
|
139 M3G_LOG(M3G_LOG_WARNINGS, "Warning: multisampling not available\n"); |
|
140 } |
|
141 } |
|
142 } |
|
143 |
|
144 /* No matching configuration found */ |
|
145 |
|
146 return NULL; |
|
147 } |
|
148 |
|
149 /*! |
|
150 * \internal |
|
151 * \brief Initializes EGL |
|
152 */ |
|
153 static void m3gInitializeEGL(void) |
|
154 { |
|
155 M3G_LOG(M3G_LOG_INTERFACE, "Initializing EGL\n"); |
|
156 eglInitialize(eglGetDisplay(EGL_DEFAULT_DISPLAY), NULL, NULL); |
|
157 } |
|
158 |
|
159 /*! |
|
160 * \internal |
|
161 * \brief Terminates EGL |
|
162 */ |
|
163 static void m3gTerminateEGL(void) |
|
164 { |
|
165 M3G_LOG(M3G_LOG_INTERFACE, "Shutting down EGL\n"); |
|
166 eglTerminate(eglGetDisplay(EGL_DEFAULT_DISPLAY)); |
|
167 } |
|
168 |
|
169 /*! |
|
170 * \internal |
|
171 * \brief Creates a new EGL context |
|
172 */ |
|
173 static EGLContext m3gCreateGLContext(M3Genum format, |
|
174 M3Gbitmask bufferBits, |
|
175 M3Gbitmask reqSurfaceBits, |
|
176 EGLContext share, |
|
177 M3Gbitmask *outSurfaceBits) |
|
178 { |
|
179 EGLContext ctx; |
|
180 EGLConfig config; |
|
181 |
|
182 M3G_ASSERT((reqSurfaceBits & ~(EGL_PIXMAP_BIT|EGL_PBUFFER_BIT|EGL_WINDOW_BIT)) == 0); |
|
183 |
|
184 config = m3gQueryEGLConfig(format, bufferBits, reqSurfaceBits); |
|
185 |
|
186 if (!config || !eglGetConfigAttrib(eglGetDisplay(EGL_DEFAULT_DISPLAY), |
|
187 config, |
|
188 EGL_SURFACE_TYPE, |
|
189 (EGLint *) outSurfaceBits)) { |
|
190 return NULL; |
|
191 } |
|
192 |
|
193 ctx = eglCreateContext(eglGetDisplay(EGL_DEFAULT_DISPLAY), |
|
194 config, |
|
195 share, |
|
196 NULL); |
|
197 |
|
198 # if defined(M3G_DEBUG) |
|
199 { |
|
200 EGLint err = eglGetError(); |
|
201 M3G_ASSERT(err == EGL_SUCCESS || err == EGL_BAD_ALLOC); |
|
202 } |
|
203 # endif |
|
204 |
|
205 M3G_LOG1(M3G_LOG_OBJECTS, "New GL context 0x%08X\n", (unsigned) ctx); |
|
206 return ctx; |
|
207 } |
|
208 |
|
209 /*! |
|
210 * \internal |
|
211 * \brief Deletes an EGL context |
|
212 */ |
|
213 static void m3gDeleteGLContext(EGLContext ctx) |
|
214 { |
|
215 eglDestroyContext(eglGetDisplay(EGL_DEFAULT_DISPLAY), ctx); |
|
216 # if defined(M3G_DEBUG) |
|
217 { |
|
218 EGLint err = eglGetError(); |
|
219 if (err != EGL_SUCCESS) { |
|
220 M3G_LOG1(M3G_LOG_FATAL_ERRORS, "EGL error 0x%08X\n", (unsigned) err); |
|
221 } |
|
222 M3G_ASSERT(err == EGL_SUCCESS); |
|
223 } |
|
224 # endif |
|
225 M3G_LOG1(M3G_LOG_OBJECTS, "Destroyed GL context 0x%08X\n", |
|
226 (unsigned) ctx); |
|
227 } |
|
228 |
|
229 |
|
230 /*! |
|
231 * \internal |
|
232 * \brief Creates a new EGL window surface |
|
233 */ |
|
234 static EGLSurface m3gCreateWindowSurface(M3Genum format, |
|
235 M3Gbitmask bufferBits, |
|
236 M3GNativeWindow wnd) |
|
237 { |
|
238 EGLSurface surf; |
|
239 EGLConfig config = m3gQueryEGLConfig(format, bufferBits, EGL_WINDOW_BIT); |
|
240 |
|
241 if (!config) { |
|
242 return NULL; |
|
243 } |
|
244 |
|
245 surf = eglCreateWindowSurface(eglGetDisplay(EGL_DEFAULT_DISPLAY), |
|
246 config, |
|
247 (NativeWindowType) wnd, |
|
248 NULL); |
|
249 |
|
250 # if defined(M3G_DEBUG) |
|
251 { |
|
252 EGLint err = eglGetError(); |
|
253 M3G_ASSERT(err == EGL_SUCCESS || err == EGL_BAD_ALLOC); |
|
254 } |
|
255 # endif |
|
256 |
|
257 if (surf != EGL_NO_SURFACE) { |
|
258 M3G_LOG1(M3G_LOG_OBJECTS, "New GL window surface 0x%08X\n", |
|
259 (unsigned) surf); |
|
260 return surf; |
|
261 } |
|
262 return NULL; |
|
263 } |
|
264 |
|
265 |
|
266 /*! |
|
267 * \internal |
|
268 * \brief Creates a new EGL pixmap surface |
|
269 */ |
|
270 static EGLSurface m3gCreateBitmapSurface(M3Genum format, |
|
271 M3Gbitmask bufferBits, |
|
272 M3GNativeBitmap bmp) |
|
273 { |
|
274 EGLSurface surf; |
|
275 EGLConfig config = m3gQueryEGLConfig(format, bufferBits, EGL_PIXMAP_BIT); |
|
276 |
|
277 if (!config) { |
|
278 return NULL; |
|
279 } |
|
280 |
|
281 surf = eglCreatePixmapSurface(eglGetDisplay(EGL_DEFAULT_DISPLAY), |
|
282 config, |
|
283 (NativePixmapType) bmp, |
|
284 NULL); |
|
285 |
|
286 # if defined(M3G_DEBUG) |
|
287 { |
|
288 EGLint err = eglGetError(); |
|
289 M3G_ASSERT(err == EGL_SUCCESS || err == EGL_BAD_ALLOC); |
|
290 } |
|
291 # endif |
|
292 |
|
293 if (surf != EGL_NO_SURFACE) { |
|
294 M3G_LOG1(M3G_LOG_OBJECTS, "New GL pixmap surface 0x%08X\n", |
|
295 (unsigned) surf); |
|
296 return surf; |
|
297 } |
|
298 return NULL; |
|
299 } |
|
300 |
|
301 |
|
302 /*! |
|
303 * \internal |
|
304 * \brief Creates a new PBuffer |
|
305 */ |
|
306 static EGLSurface m3gCreatePBufferSurface(M3Genum format, |
|
307 M3Gbitmask bufferBits, |
|
308 M3Gint width, M3Gint height) |
|
309 { |
|
310 EGLSurface surf; |
|
311 EGLConfig config; |
|
312 EGLint attrib[5]; |
|
313 |
|
314 attrib[0] = EGL_WIDTH; |
|
315 attrib[1] = width; |
|
316 attrib[2] = EGL_HEIGHT; |
|
317 attrib[3] = height; |
|
318 attrib[4] = EGL_NONE; |
|
319 |
|
320 config = m3gQueryEGLConfig(format, bufferBits, EGL_PBUFFER_BIT); |
|
321 if (!config) { |
|
322 return NULL; |
|
323 } |
|
324 |
|
325 surf = eglCreatePbufferSurface(eglGetDisplay(EGL_DEFAULT_DISPLAY), |
|
326 config, |
|
327 attrib); |
|
328 # if defined(M3G_DEBUG) |
|
329 { |
|
330 EGLint err = eglGetError(); |
|
331 M3G_ASSERT(err == EGL_SUCCESS || err == EGL_BAD_ALLOC); |
|
332 } |
|
333 # endif |
|
334 |
|
335 if (surf != EGL_NO_SURFACE) { |
|
336 M3G_LOG1(M3G_LOG_OBJECTS, "New GL pbuffer surface 0x%08X\n", |
|
337 (unsigned) surf); |
|
338 return surf; |
|
339 } |
|
340 return NULL; |
|
341 } |
|
342 |
|
343 |
|
344 /*! |
|
345 * \internal |
|
346 * \brief Deletes an EGL surface |
|
347 */ |
|
348 static void m3gDeleteGLSurface(EGLSurface surface) |
|
349 { |
|
350 eglDestroySurface(eglGetDisplay(EGL_DEFAULT_DISPLAY), surface); |
|
351 |
|
352 # if defined(M3G_DEBUG) |
|
353 { |
|
354 EGLint err = eglGetError(); |
|
355 M3G_ASSERT(err == EGL_SUCCESS); |
|
356 } |
|
357 # endif |
|
358 |
|
359 M3G_LOG1(M3G_LOG_OBJECTS, "Destroyed GL surface 0x%08X\n", |
|
360 (unsigned) surface); |
|
361 } |
|
362 |
|
363 /*! |
|
364 * \brief Swap buffers on a rendering surface |
|
365 */ |
|
366 static M3Gbool m3gSwapBuffers(EGLSurface surface) |
|
367 { |
|
368 EGLBoolean success = eglSwapBuffers(eglGetDisplay(EGL_DEFAULT_DISPLAY), |
|
369 surface); |
|
370 |
|
371 # if defined(M3G_DEBUG) |
|
372 EGLint err = eglGetError(); |
|
373 M3G_ASSERT(err == EGL_SUCCESS); |
|
374 # endif |
|
375 |
|
376 return (M3Gbool) success; |
|
377 } |
|
378 |
|
379 /*! |
|
380 * \brief Does a sub-blit of a frame buffer blit operation |
|
381 */ |
|
382 static void m3gBlitFrameBufferPixels2(RenderContext *ctx, |
|
383 M3Gint xOffset, M3Gint yOffset, |
|
384 M3Gint width, M3Gint height, |
|
385 M3GPixelFormat internalFormat, |
|
386 M3Gsizei stride, |
|
387 const M3Gubyte *pixels) |
|
388 { |
|
389 # define MAX_TEMP_TEXTURES 8 |
|
390 GLuint glFormat; |
|
391 static const int MAX_TILE_SIZE = 256; /* -> 256 KB temp buffer(s) */ |
|
392 static const M3Gbyte tc[8] = { 0, 0, 0, 1, 1, 0, 1, 1 }; |
|
393 GLshort pos[8]; |
|
394 int tileWidth = MAX_TILE_SIZE, tileHeight = MAX_TILE_SIZE; |
|
395 M3Gbool mustConvert = M3G_FALSE; |
|
396 M3Gubyte *tempPixels = 0; /* initialize to avoid compiler warnings */ |
|
397 GLuint tempTexObj[MAX_TEMP_TEXTURES]; |
|
398 GLint tempTexCount; |
|
399 |
|
400 M3G_VALIDATE_OBJECT(ctx); |
|
401 M3G_ASSERT_GL; |
|
402 |
|
403 /* Analyze source and destination formats for possible conversion */ |
|
404 |
|
405 glFormat = m3gGetGLFormat(internalFormat); |
|
406 if (!glFormat) { |
|
407 M3G_ASSERT(M3G_FALSE); /* internal format not supported in GL */ |
|
408 return; |
|
409 } |
|
410 if (internalFormat == M3G_RGB8_32) { |
|
411 glFormat = GL_RGBA; |
|
412 } |
|
413 if (internalFormat == M3G_BGR8_32) { |
|
414 glFormat = GL_RGBA; |
|
415 mustConvert = M3G_TRUE; |
|
416 } |
|
417 |
|
418 /* Tweak tile size to avoid using excessive amounts of memory for |
|
419 * portions outside the blit area */ |
|
420 |
|
421 M3G_ASSERT((width > 0) && (height > 0)); |
|
422 |
|
423 while (tileWidth >= width * 2) { |
|
424 tileWidth >>= 1; |
|
425 tileHeight <<= 1; |
|
426 } |
|
427 while (tileHeight >= height * 2) { |
|
428 tileHeight >>= 1; |
|
429 } |
|
430 |
|
431 /* Allocate temp memory for conversion or adjust tile size for |
|
432 * optimal direct download to GL */ |
|
433 |
|
434 if (mustConvert) { |
|
435 tempPixels = m3gAllocTemp(M3G_INTERFACE(ctx), |
|
436 tileWidth * tileHeight * 4); |
|
437 if (!tempPixels) { |
|
438 return; /* out of memory */ |
|
439 } |
|
440 } |
|
441 else { |
|
442 |
|
443 /* Attempt to adjust the tile size so that we can copy |
|
444 * complete scanlines at a time -- this is because OpenGL ES |
|
445 * is missing PixelStore settings that could be used for |
|
446 * stride control during image uploading */ |
|
447 |
|
448 M3Gint maxWidth; |
|
449 glGetIntegerv(GL_MAX_TEXTURE_SIZE, &maxWidth); |
|
450 |
|
451 while (tileWidth < width && |
|
452 tileWidth < maxWidth && |
|
453 tileHeight > 1) { |
|
454 tileWidth <<= 1; |
|
455 tileHeight >>= 1; |
|
456 } |
|
457 } |
|
458 |
|
459 /* Load default images into the temp texture objects */ |
|
460 |
|
461 glActiveTexture(GL_TEXTURE0); |
|
462 glEnable(GL_TEXTURE_2D); |
|
463 { |
|
464 int ti; |
|
465 tempTexCount = ((width + tileWidth - 1) / tileWidth) |
|
466 * ((height + tileHeight - 1) / tileHeight); |
|
467 tempTexCount = m3gMinInt(tempTexCount, MAX_TEMP_TEXTURES); |
|
468 |
|
469 glGenTextures(tempTexCount, tempTexObj); |
|
470 |
|
471 for (ti = 0; ti < tempTexCount; ++ti) { |
|
472 glBindTexture(GL_TEXTURE_2D, tempTexObj[ti]); |
|
473 glTexEnvx(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); |
|
474 glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); |
|
475 glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); |
|
476 M3G_ASSERT_GL; |
|
477 |
|
478 glTexImage2D(GL_TEXTURE_2D, 0, |
|
479 glFormat, |
|
480 tileWidth, tileHeight, |
|
481 0, |
|
482 glFormat, |
|
483 GL_UNSIGNED_BYTE, NULL); |
|
484 |
|
485 /* Raise out-of-memory if OpenGL ran out of resources */ |
|
486 { |
|
487 GLint err = glGetError(); |
|
488 |
|
489 if (err == GL_OUT_OF_MEMORY) { |
|
490 m3gRaiseError(M3G_INTERFACE(ctx), M3G_OUT_OF_MEMORY); |
|
491 goto CleanUpAndExit; |
|
492 } |
|
493 else if (err != GL_NO_ERROR) { |
|
494 M3G_ASSERT(M3G_FALSE); |
|
495 } |
|
496 } |
|
497 } |
|
498 } |
|
499 |
|
500 /* Set up texture and vertex coordinate arrays for the image tiles */ |
|
501 |
|
502 glClientActiveTexture(GL_TEXTURE0); |
|
503 glTexCoordPointer(2, GL_BYTE, 0, tc); |
|
504 glEnableClientState(GL_TEXTURE_COORD_ARRAY); |
|
505 glVertexPointer(2, GL_SHORT, 0, pos); |
|
506 glEnableClientState(GL_VERTEX_ARRAY); |
|
507 glMatrixMode(GL_TEXTURE); |
|
508 glLoadIdentity(); |
|
509 glMatrixMode(GL_MODELVIEW); |
|
510 M3G_ASSERT_GL; |
|
511 |
|
512 /* Load each image tile into a texture and draw */ |
|
513 |
|
514 { |
|
515 M3Gint nextTexTile = 0; |
|
516 M3Gint x, y, bpp; |
|
517 bpp = m3gBytesPerPixel(internalFormat); |
|
518 if (stride == 0) { |
|
519 stride = bpp * width; |
|
520 } |
|
521 |
|
522 for (y = 0; y < height; y += tileHeight) { |
|
523 for (x = 0; x < width; x += tileWidth) { |
|
524 M3Gint w, h; |
|
525 |
|
526 w = M3G_MIN(tileWidth, width - x); |
|
527 h = M3G_MIN(tileHeight, height - y); |
|
528 |
|
529 glBindTexture(GL_TEXTURE_2D, tempTexObj[nextTexTile]); |
|
530 nextTexTile = (nextTexTile + 1) % MAX_TEMP_TEXTURES; |
|
531 |
|
532 if (mustConvert) { |
|
533 m3gConvertPixelRect(internalFormat, |
|
534 pixels + y * stride + x * bpp, |
|
535 stride, |
|
536 w, h, |
|
537 M3G_RGBA8, tempPixels, w * 4); |
|
538 glTexSubImage2D(GL_TEXTURE_2D, 0, |
|
539 0, 0, |
|
540 w, h, |
|
541 GL_RGBA, GL_UNSIGNED_BYTE, tempPixels); |
|
542 } |
|
543 else { |
|
544 if (w*bpp == stride) { |
|
545 glTexSubImage2D(GL_TEXTURE_2D, 0, |
|
546 0, 0, |
|
547 w, h, |
|
548 glFormat, |
|
549 GL_UNSIGNED_BYTE, |
|
550 pixels + y * stride + x * bpp); |
|
551 } |
|
552 else { |
|
553 int k; |
|
554 for (k = 0; k < h; ++k) { |
|
555 glTexSubImage2D(GL_TEXTURE_2D, 0, |
|
556 0, k, |
|
557 w, 1, |
|
558 glFormat, |
|
559 GL_UNSIGNED_BYTE, |
|
560 pixels + (y+k) * stride + x * bpp); |
|
561 } |
|
562 } |
|
563 } |
|
564 |
|
565 pos[0] = (GLshort)(x + xOffset); |
|
566 pos[1] = (GLshort)((height - y) + yOffset); |
|
567 pos[2] = pos[0]; |
|
568 pos[3] = (GLshort)((height - (y + tileHeight)) + yOffset); |
|
569 pos[4] = (GLshort)((x + tileWidth) + xOffset); |
|
570 pos[5] = pos[1]; |
|
571 pos[6] = pos[4]; |
|
572 pos[7] = pos[3]; |
|
573 glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); |
|
574 } |
|
575 } |
|
576 M3G_ASSERT_GL; |
|
577 } |
|
578 |
|
579 /* Restore required OpenGL state and release resources */ |
|
580 |
|
581 CleanUpAndExit: |
|
582 if (mustConvert) { |
|
583 m3gFreeTemp(M3G_INTERFACE(ctx)); |
|
584 } |
|
585 |
|
586 glDeleteTextures(tempTexCount, tempTexObj); |
|
587 |
|
588 M3G_ASSERT_GL; |
|
589 |
|
590 # undef MAX_TEMP_TEXTURES |
|
591 } |
|
592 |
|
593 /*---------------------------------------------------------------------- |
|
594 * Internal functions |
|
595 *--------------------------------------------------------------------*/ |
|
596 |
|
597 /* The frame buffer should be the first thing locked and the last one |
|
598 * released, so let's mandate that even though it has no real effect |
|
599 * with EGL */ |
|
600 #define m3gLockFrameBuffer(ctx) M3G_ASSERT_NO_LOCK(M3G_INTERFACE(ctx)) |
|
601 #define m3gReleaseFrameBuffer(ctx) M3G_ASSERT_NO_LOCK(M3G_INTERFACE(ctx)) |
|
602 |
|
603 /*! |
|
604 * \internal |
|
605 * \brief Alternate rendering function for two-sided lighting on buggy |
|
606 * hardware |
|
607 */ |
|
608 static M3Gbool m3gSplitDrawMesh(RenderContext *ctx, |
|
609 const VertexBuffer *vb, |
|
610 const IndexBuffer *ib, |
|
611 const Appearance *app, |
|
612 const M3GMatrix *modelTransform, |
|
613 M3Gint alphaFactor, |
|
614 M3Gint scope) |
|
615 { |
|
616 if (!ctx->inSplitDraw && m3gGetMaterial((M3GAppearance) app) && vb->normals) { |
|
617 PolygonMode *pm = m3gGetPolygonMode((M3GAppearance) app); |
|
618 if (pm && pm->enableTwoSidedLighting) { |
|
619 M3Gint originalCulling = m3gGetCulling(pm); |
|
620 if (originalCulling != M3G_CULL_BACK) { |
|
621 |
|
622 /* OK, we must render the back sides separately with |
|
623 * flipped normals */ |
|
624 |
|
625 Interface *m3g = M3G_INTERFACE(ctx); |
|
626 VertexArray *tempNormals; |
|
627 |
|
628 M3Gint normalCount = vb->vertexCount; |
|
629 M3Gint normalStride = vb->normals->stride; |
|
630 |
|
631 /* Duplicate the normal array */ |
|
632 |
|
633 m3gReleaseFrameBuffer(ctx); |
|
634 tempNormals = m3gCloneVertexArray(vb->normals); |
|
635 if (!tempNormals) { |
|
636 m3gLockFrameBuffer(ctx); |
|
637 return M3G_TRUE; /* automatic out-of-memory */ |
|
638 } |
|
639 |
|
640 /* Flip the signs of the temp normals */ |
|
641 |
|
642 if (tempNormals->elementType == GL_BYTE) { |
|
643 M3Gbyte *p = (M3Gbyte*) m3gMapObject(m3g, tempNormals->data); |
|
644 int i; |
|
645 for (i = 0; i < normalCount; ++i) { |
|
646 p[0] = (M3Gbyte) -m3gClampInt(p[0], -127, 127); |
|
647 p[1] = (M3Gbyte) -m3gClampInt(p[1], -127, 127); |
|
648 p[2] = (M3Gbyte) -m3gClampInt(p[2], -127, 127); |
|
649 p += normalStride; |
|
650 } |
|
651 } |
|
652 else { |
|
653 M3Gshort *p = (M3Gshort*) m3gMapObject(m3g, tempNormals->data); |
|
654 int i; |
|
655 for (i = 0; i < normalCount; ++i) { |
|
656 p[0] = (M3Gshort) -m3gClampInt(p[0], -32767, 32767); |
|
657 p[1] = (M3Gshort) -m3gClampInt(p[1], -32767, 32767); |
|
658 p[2] = (M3Gshort) -m3gClampInt(p[2], -32767, 32767); |
|
659 p += normalStride / 2; |
|
660 } |
|
661 } |
|
662 m3gUnmapObject(m3g, tempNormals->data); |
|
663 m3gLockFrameBuffer(ctx); |
|
664 |
|
665 ctx->inSplitDraw = M3G_TRUE; |
|
666 |
|
667 /* Set culling to front faces only and render with the |
|
668 * flipped normals */ |
|
669 { |
|
670 VertexArray *orgNormals = vb->normals; |
|
671 ((VertexBuffer*)vb)->normals = tempNormals; |
|
672 m3gSetCulling(pm, M3G_CULL_FRONT); |
|
673 m3gDrawMesh(ctx, |
|
674 vb, ib, app, |
|
675 modelTransform, |
|
676 alphaFactor, scope); |
|
677 ((VertexBuffer*)vb)->normals = orgNormals; |
|
678 } |
|
679 |
|
680 /* If no culling was enabled, render the front faces |
|
681 * with the original normals */ |
|
682 |
|
683 if (originalCulling == M3G_CULL_NONE) { |
|
684 m3gSetCulling(pm, M3G_CULL_BACK); |
|
685 m3gDrawMesh(ctx, |
|
686 vb, ib, app, |
|
687 modelTransform, |
|
688 alphaFactor, scope); |
|
689 } |
|
690 |
|
691 /* Restore original culling and free the temp normals */ |
|
692 |
|
693 m3gSetCulling(pm, originalCulling); |
|
694 |
|
695 m3gReleaseFrameBuffer(ctx); |
|
696 m3gDeleteObject((M3GObject) tempNormals); |
|
697 m3gLockFrameBuffer(ctx); |
|
698 |
|
699 ctx->inSplitDraw = M3G_FALSE; |
|
700 return M3G_TRUE; |
|
701 } |
|
702 } |
|
703 } |
|
704 return M3G_FALSE; |
|
705 } |
|
706 |
|
707 /*! |
|
708 * \internal |
|
709 * \brief Determines whether a format/mode combination can be directly |
|
710 * rendered |
|
711 */ |
|
712 static M3Gbool m3gCanDirectRender(const RenderContext *ctx) |
|
713 { |
|
714 M3GPixelFormat format = ctx->target.format; |
|
715 M3Gbitmask bufferBits = ctx->bufferBits; |
|
716 M3Gbitmask surfaceType = ctx->target.type; |
|
717 int i; |
|
718 |
|
719 /* Images always go via pbuffers; EGL surfaces can always be |
|
720 * rendered to */ |
|
721 |
|
722 if (surfaceType == SURFACE_IMAGE) { |
|
723 return M3G_FALSE; |
|
724 } |
|
725 if (surfaceType == SURFACE_EGL) { |
|
726 return M3G_TRUE; |
|
727 } |
|
728 |
|
729 /* First scan the context cache for a matching previously used |
|
730 * context; this should be faster than querying EGL */ |
|
731 |
|
732 for (i = 0; i < M3G_MAX_GL_CONTEXTS; ++i) { |
|
733 const GLContextRecord *rc = &ctx->glContext[i]; |
|
734 |
|
735 if ((rc->surfaceTypeBits & surfaceType) == surfaceType |
|
736 && rc->format == format |
|
737 && (rc->bufferBits & bufferBits) == bufferBits) { |
|
738 |
|
739 return M3G_TRUE; |
|
740 } |
|
741 } |
|
742 |
|
743 /* No dice; must resort to querying from EGL */ |
|
744 |
|
745 return (m3gQueryEGLConfig(format, bufferBits, (EGLint) surfaceType) != NULL); |
|
746 } |
|
747 |
|
748 /*! |
|
749 * \internal |
|
750 * \brief Ensures that a sufficient back buffer exists |
|
751 * |
|
752 * Creates a new PBuffer for the back buffer if required. |
|
753 */ |
|
754 static M3Gbool m3gValidateBackBuffer(RenderContext *ctx) |
|
755 { |
|
756 BackBuffer *bbuf = &ctx->backBuffer; |
|
757 int w = ctx->target.width; |
|
758 int h = ctx->target.height; |
|
759 |
|
760 /* NOTE the EGL specification is fuzzy on eglCopyBuffers when the |
|
761 * pbuffer is larger than the target, so we require an exact match |
|
762 * (can be relaxed by #undefining the flag, see m3g_defs.h) */ |
|
763 |
|
764 # if defined(M3G_GL_FORCE_PBUFFER_SIZE) |
|
765 if (bbuf->width != w || bbuf->height != h) { |
|
766 # else |
|
767 if (bbuf->width < w || bbuf->height < h) { |
|
768 # endif |
|
769 |
|
770 M3G_LOG(M3G_LOG_WARNINGS, |
|
771 "Warning (performance): Buffered rendering.\n"); |
|
772 |
|
773 if (bbuf->glSurface != NULL) { |
|
774 m3gDeleteGLSurface(bbuf->glSurface); |
|
775 } |
|
776 |
|
777 bbuf->glSurface = m3gCreatePBufferSurface( |
|
778 M3G_RGBA8, |
|
779 (M3Gbitmask)(M3G_COLOR_BUFFER_BIT|M3G_DEPTH_BUFFER_BIT|M3G_MULTISAMPLE_BUFFER_BIT), |
|
780 w, h); |
|
781 |
|
782 bbuf->width = w; |
|
783 bbuf->height = h; |
|
784 |
|
785 if (!bbuf->glSurface) { |
|
786 if (eglGetError() == EGL_BAD_ALLOC) { |
|
787 return M3G_FALSE; /* ouf of memory */ |
|
788 } |
|
789 else { |
|
790 M3G_ASSERT(M3G_FALSE); |
|
791 } |
|
792 } |
|
793 } |
|
794 return M3G_TRUE; |
|
795 } |
|
796 |
|
797 /*! |
|
798 * \internal |
|
799 * \brief Increment the rendering time stamp |
|
800 * |
|
801 * The time stamp is used to manage the various caches for the |
|
802 * context, so it needs to be updated often enough for the caches to |
|
803 * function optimally. |
|
804 * |
|
805 * In the rare case that the time stamp should wrap around(!), we |
|
806 * reset the time stamps dependent on it to avoid sub-optimal cache |
|
807 * performance. |
|
808 */ |
|
809 static void m3gIncrementRenderTimeStamp(RenderContext *ctx) |
|
810 { |
|
811 if (++ctx->cacheTimeStamp == 0) { |
|
812 int i; |
|
813 for (i = 0; i < M3G_MAX_GL_CONTEXTS; ++i) { |
|
814 ctx->glContext[i].lastUseTime = 0; |
|
815 } |
|
816 for (i = 0; i < M3G_MAX_GL_SURFACES; ++i) { |
|
817 ctx->glSurface[i].lastUseTime = 0; |
|
818 } |
|
819 } |
|
820 } |
|
821 |
|
822 /*! |
|
823 * \internal |
|
824 * \brief Draws a raw RGB or RGBA pixel rectangle of arbitrary size |
|
825 * into the frame buffer |
|
826 * |
|
827 * The offset only affects the position of the blitted rectangle in |
|
828 * the frame buffer. The source data is read starting at the given |
|
829 * pointer. |
|
830 * |
|
831 * \param ctx the rendering context |
|
832 * \param xOffset offset from the left edge of the frame buffer |
|
833 * \param yOffset offset from the bottom of the frame buffer |
|
834 * \param width width of the rectangle |
|
835 * \param height height of the rectangle |
|
836 * \param internalFormat format of the source pixels |
|
837 * \param stride stride of the source data |
|
838 * \param pixels pointer to the source pixels in top-to-bottom order |
|
839 */ |
|
840 static void m3gBlitFrameBufferPixels(RenderContext *ctx, |
|
841 M3Gint xOffset, M3Gint yOffset, |
|
842 M3Gint width, M3Gint height, |
|
843 M3GPixelFormat internalFormat, |
|
844 M3Gsizei stride, |
|
845 const M3Gubyte *pixels) |
|
846 { |
|
847 /* Skip this if nothing to copy */ |
|
848 |
|
849 if (width <= 0 || height <= 0) { |
|
850 return; |
|
851 } |
|
852 |
|
853 /* Set viewport, projection and modelview to map coordinates to |
|
854 * pixel boundaries */ |
|
855 |
|
856 glScissor(xOffset, yOffset, width, height); |
|
857 glViewport(0, 0, ctx->target.width, ctx->target.height); |
|
858 glMatrixMode(GL_PROJECTION); |
|
859 glLoadIdentity(); |
|
860 glOrthox(0, ctx->target.width << 16, |
|
861 0, ctx->target.height << 16, |
|
862 -1 << 16, 1 << 16); |
|
863 glMatrixMode(GL_MODELVIEW); |
|
864 glLoadIdentity(); |
|
865 |
|
866 /* Disable any stray state we don't want */ |
|
867 |
|
868 glDisable(GL_CULL_FACE); |
|
869 glDisable(GL_BLEND); |
|
870 glDisable(GL_ALPHA_TEST); |
|
871 glDisableClientState(GL_NORMAL_ARRAY); |
|
872 glDisableClientState(GL_COLOR_ARRAY); |
|
873 glDisable(GL_LIGHTING); |
|
874 glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); |
|
875 glDepthMask(GL_FALSE); |
|
876 glDepthFunc(GL_ALWAYS); |
|
877 m3gDisableTextures(); |
|
878 M3G_ASSERT_GL; |
|
879 |
|
880 /* Split the large blit operation into smaller chunks that are |
|
881 * efficiently taken care of using power-of-two textures */ |
|
882 { |
|
883 const int MAX_BLIT_SIZE = 256; /* should be power of two */ |
|
884 |
|
885 int xBlits = (width / MAX_BLIT_SIZE) + 1; |
|
886 int yBlits = (height / MAX_BLIT_SIZE) + 1; |
|
887 |
|
888 int xBlit, yBlit; |
|
889 |
|
890 for (yBlit = yBlits-1; yBlit >= 0; yBlit--) { |
|
891 for (xBlit = 0; xBlit < xBlits; ++xBlit) { |
|
892 |
|
893 M3Gint xStart = xOffset + xBlit * MAX_BLIT_SIZE; |
|
894 M3Gint yStart = yOffset + yBlit * MAX_BLIT_SIZE; |
|
895 M3Gint xSize = m3gMinInt(MAX_BLIT_SIZE, width - (xStart - xOffset)); |
|
896 M3Gint ySize = m3gMinInt(MAX_BLIT_SIZE, height - (yStart - yOffset)); |
|
897 |
|
898 M3Gint srcOffset = (height - (yStart - yOffset + ySize)) * stride + (xStart - xOffset) * m3gBytesPerPixel(ctx->target.format); |
|
899 |
|
900 m3gBlitFrameBufferPixels2(ctx, |
|
901 xStart, yStart, |
|
902 xSize, ySize, |
|
903 internalFormat, |
|
904 stride, |
|
905 pixels + srcOffset); |
|
906 } |
|
907 } |
|
908 } |
|
909 } |
|
910 |
|
911 /*! |
|
912 * \internal |
|
913 * \brief Synchronizes the contents of the back buffer with the target |
|
914 * buffer |
|
915 */ |
|
916 static void m3gUpdateBackBuffer(RenderContext *ctx) |
|
917 { |
|
918 if (ctx->target.type == SURFACE_IMAGE) { |
|
919 m3gDrawFrameBufferImage(ctx, (Image *) ctx->target.handle); |
|
920 } |
|
921 else if (ctx->target.type == SURFACE_BITMAP) { |
|
922 |
|
923 M3Gubyte *src; |
|
924 M3Gsizei stride; |
|
925 |
|
926 /* Obtain a pointer to the native bitmap and copy the data to |
|
927 * the backbuffer from there */ |
|
928 |
|
929 if (m3gglLockNativeBitmap((M3GNativeBitmap) ctx->target.handle, |
|
930 &src, &stride)) { |
|
931 |
|
932 M3Gint clipWidth = ctx->clip.x1 - ctx->clip.x0; |
|
933 M3Gint clipHeight = ctx->clip.y1 - ctx->clip.y0; |
|
934 M3Gint srcOffset = |
|
935 (ctx->target.height - ctx->clip.y1) * stride |
|
936 + ctx->clip.x0 * m3gBytesPerPixel(ctx->target.format); |
|
937 |
|
938 m3gBlitFrameBufferPixels( |
|
939 ctx, |
|
940 ctx->clip.x0, ctx->clip.y0, |
|
941 clipWidth, clipHeight, |
|
942 ctx->target.format, |
|
943 stride, |
|
944 src + srcOffset); |
|
945 |
|
946 m3gglReleaseNativeBitmap((M3GNativeBitmap) ctx->target.handle); |
|
947 } |
|
948 else { |
|
949 /* No dice! There's no way that we know of to copy the |
|
950 * data between the buffers */ |
|
951 M3G_ASSERT(M3G_FALSE); |
|
952 } |
|
953 } |
|
954 else { |
|
955 /* Buffered rendering is not supported for window and pbuffer |
|
956 * targets */ |
|
957 M3G_ASSERT(M3G_FALSE); |
|
958 } |
|
959 ctx->backBuffer.contentsValid = M3G_TRUE; |
|
960 } |
|
961 |
|
962 /*! |
|
963 * \internal |
|
964 * \brief Synchronizes the contents of the target buffer with the back |
|
965 * buffer |
|
966 */ |
|
967 static void m3gUpdateTargetBuffer(RenderContext *ctx) |
|
968 { |
|
969 if (ctx->target.type == SURFACE_IMAGE) { |
|
970 m3gCopyFrameBufferImage((Image *) ctx->target.handle); |
|
971 } |
|
972 else if (ctx->target.type == SURFACE_BITMAP) { |
|
973 |
|
974 /* We must copy the back buffer to a native bitmap: first |
|
975 * attempt a fast buffer-to-buffer copy using EGL, but if that |
|
976 * fails, obtain a pointer and do the copy ourselves */ |
|
977 |
|
978 /* We can only do the fast copy for the full buffer */ |
|
979 |
|
980 M3Gbool fullClip = (ctx->clip.x0 == 0) |
|
981 && (ctx->clip.y0 <= ctx->target.height - ctx->display.height) |
|
982 && (ctx->clip.x1 >= ctx->display.width) |
|
983 && (ctx->clip.y1 >= ctx->clip.y0 + ctx->display.height); |
|
984 |
|
985 if (!fullClip || !eglCopyBuffers(eglGetDisplay(EGL_DEFAULT_DISPLAY), |
|
986 ctx->backBuffer.glSurface, |
|
987 (NativePixmapType) ctx->target.handle)) { |
|
988 |
|
989 /* Fast copy failed, try the generic approach */ |
|
990 |
|
991 M3Gubyte *dst; |
|
992 M3Gsizei stride; |
|
993 |
|
994 if (m3gglLockNativeBitmap((M3GNativeBitmap) ctx->target.handle, |
|
995 &dst, &stride)) { |
|
996 |
|
997 /* OK, got the pointer; now, copy a scanline at a |
|
998 * time, and we can pretty much assume conversion |
|
999 * since the fast method didn't work */ |
|
1000 |
|
1001 M3GPixelFormat format = ctx->target.format; |
|
1002 M3Gint width = ctx->clip.x1 - ctx->clip.x0; |
|
1003 M3Gint height = ctx->clip.y1 - ctx->clip.y0; |
|
1004 M3Gint xOffset = ctx->clip.x0; |
|
1005 M3Gint yOffset = ctx->clip.y0; |
|
1006 M3Gint row; |
|
1007 |
|
1008 M3Gubyte *temp = m3gAllocTemp(M3G_INTERFACE(ctx), width * 4); |
|
1009 if (!temp) { |
|
1010 return; /* out of memory */ |
|
1011 } |
|
1012 |
|
1013 dst += (ctx->target.height - (yOffset + height)) * stride |
|
1014 + xOffset * m3gBytesPerPixel(format); |
|
1015 |
|
1016 for (row = 0; row < height; ++row) { |
|
1017 glReadPixels(xOffset, yOffset + height - row - 1, |
|
1018 width, 1, |
|
1019 GL_RGBA, GL_UNSIGNED_BYTE, |
|
1020 temp); |
|
1021 m3gConvertPixels(M3G_RGBA8, temp, format, dst, width); |
|
1022 dst += stride; |
|
1023 } |
|
1024 m3gFreeTemp(M3G_INTERFACE(ctx)); |
|
1025 |
|
1026 m3gglReleaseNativeBitmap((M3GNativeBitmap) ctx->target.handle); |
|
1027 } |
|
1028 else { |
|
1029 /* No dice! There's no way that we know of to copy the |
|
1030 * data between the buffers */ |
|
1031 M3G_ASSERT(M3G_FALSE); |
|
1032 } |
|
1033 } |
|
1034 } |
|
1035 else { |
|
1036 /* Buffered rendering is not supported for window and pbuffer |
|
1037 * targets */ |
|
1038 M3G_ASSERT(M3G_FALSE); |
|
1039 } |
|
1040 } |
|
1041 |
|
1042 /*! |
|
1043 * \internal |
|
1044 * \brief Selects a GL context matching a given GL surface and a set |
|
1045 * of rendering parameters |
|
1046 * |
|
1047 * If no existing context matches, a new one is created. Contexts are |
|
1048 * stored in a fixed-size cache and managed using a LRU policy. |
|
1049 */ |
|
1050 static EGLContext m3gSelectGLContext(RenderContext *ctx, |
|
1051 M3GPixelFormat format, |
|
1052 M3Gbitmask bufferBits, |
|
1053 M3Gbitmask surfaceTypeBits, |
|
1054 EGLSurface surface) |
|
1055 { |
|
1056 int i; |
|
1057 |
|
1058 /* Look for a matching cached context and attempt to make it |
|
1059 * current; on success, update the time in the context record and |
|
1060 * return the GL context handle */ |
|
1061 |
|
1062 for (i = 0; i < M3G_MAX_GL_CONTEXTS; ++i) { |
|
1063 GLContextRecord *rc = &ctx->glContext[i]; |
|
1064 |
|
1065 if ((rc->surfaceTypeBits & surfaceTypeBits) == surfaceTypeBits |
|
1066 && rc->format == format |
|
1067 && (rc->bufferBits & bufferBits) == bufferBits) { |
|
1068 |
|
1069 if (eglMakeCurrent(eglGetDisplay(EGL_DEFAULT_DISPLAY), |
|
1070 surface, surface, rc->handle)) { |
|
1071 rc->lastUseTime = ctx->cacheTimeStamp; |
|
1072 return rc->handle; |
|
1073 } |
|
1074 else { |
|
1075 /* NOTE we intentionally clear the error flag, since |
|
1076 * the MakeCurrent call above can fail in case of a |
|
1077 * context mismatch */ |
|
1078 eglGetError(); |
|
1079 } |
|
1080 } |
|
1081 } |
|
1082 |
|
1083 /* No match found, we must create a new context */ |
|
1084 { |
|
1085 GLContextRecord *lru = &ctx->glContext[0]; |
|
1086 EGLContext shareRc = lru->handle; |
|
1087 EGLContext glrc; |
|
1088 |
|
1089 /* Find the least recently used context entry */ |
|
1090 |
|
1091 for (i = 1; i < M3G_MAX_GL_CONTEXTS; ++i) { |
|
1092 GLContextRecord *rc = &ctx->glContext[i]; |
|
1093 if (rc->handle) { |
|
1094 shareRc = rc->handle; /* keep this for sharing */ |
|
1095 } |
|
1096 if (!rc->handle || rc->lastUseTime < lru->lastUseTime) { |
|
1097 lru = rc; |
|
1098 } |
|
1099 } |
|
1100 |
|
1101 /* Create a new GL context, then delete the LRU one. This is |
|
1102 * done in this order so that we don't lose any shared texture |
|
1103 * objects when deleting a context. */ |
|
1104 |
|
1105 if (surfaceTypeBits == SURFACE_EGL) { |
|
1106 EGLDisplay dpy = eglGetDisplay(EGL_DEFAULT_DISPLAY); |
|
1107 EGLint configID; |
|
1108 eglQuerySurface(dpy, |
|
1109 (EGLSurface) ctx->target.handle, |
|
1110 EGL_CONFIG_ID, |
|
1111 &configID); |
|
1112 glrc = eglCreateContext(dpy, (EGLConfig) configID, shareRc, NULL); |
|
1113 M3G_ASSERT(glrc); |
|
1114 } |
|
1115 else { |
|
1116 glrc = m3gCreateGLContext(format, |
|
1117 bufferBits, |
|
1118 surfaceTypeBits, |
|
1119 shareRc, |
|
1120 &lru->surfaceTypeBits); |
|
1121 } |
|
1122 |
|
1123 if (!glrc) { |
|
1124 m3gRaiseError(M3G_INTERFACE(ctx), M3G_OUT_OF_MEMORY); |
|
1125 return NULL; |
|
1126 } |
|
1127 if (lru->handle) { |
|
1128 m3gDeleteGLContext(lru->handle); |
|
1129 } |
|
1130 |
|
1131 /* Store the parameters for the new context and make it |
|
1132 * current */ |
|
1133 |
|
1134 lru->handle = glrc; |
|
1135 lru->surfaceTypeBits = surfaceTypeBits; |
|
1136 lru->format = format; |
|
1137 lru->bufferBits = bufferBits; |
|
1138 lru->modeBits = ctx->modeBits; |
|
1139 { |
|
1140 M3Gbool ok = eglMakeCurrent(eglGetDisplay(EGL_DEFAULT_DISPLAY), |
|
1141 surface, surface, glrc); |
|
1142 M3G_ASSERT(ok); |
|
1143 if (!ok) { |
|
1144 return NULL; |
|
1145 } |
|
1146 } |
|
1147 lru->lastUseTime = ctx->cacheTimeStamp; |
|
1148 m3gSetGLDefaults(); |
|
1149 return glrc; |
|
1150 } |
|
1151 } |
|
1152 |
|
1153 /*! |
|
1154 * \internal |
|
1155 * \brief Selects a GL surface suitable for rendering into the current |
|
1156 * target using the currently set rendering parameters |
|
1157 * |
|
1158 * If no existing surface matches, a new one is created. Surfaces are |
|
1159 * stored in a fixed-size LRU cache. |
|
1160 */ |
|
1161 static EGLSurface m3gSelectGLSurface(RenderContext *ctx) |
|
1162 { |
|
1163 int attempts = 0; |
|
1164 int i; |
|
1165 |
|
1166 /* Quick exit for EGL surfaces */ |
|
1167 |
|
1168 if (ctx->target.type == SURFACE_EGL) { |
|
1169 return (EGLSurface) ctx->target.handle; |
|
1170 } |
|
1171 |
|
1172 /* Buffered rendering is handled elsewhere! */ |
|
1173 |
|
1174 if (ctx->target.buffered) { |
|
1175 M3G_ASSERT(M3G_FALSE); |
|
1176 return NULL; |
|
1177 } |
|
1178 |
|
1179 /* Find the first matching surface and return it */ |
|
1180 |
|
1181 for (i = 0; i < M3G_MAX_GL_SURFACES; ++i) { |
|
1182 GLSurfaceRecord *surf = &ctx->glSurface[i]; |
|
1183 |
|
1184 if (surf->type == ctx->target.type |
|
1185 && surf->targetHandle == ctx->target.handle |
|
1186 && (ctx->bufferBits & surf->bufferBits) == ctx->bufferBits) { |
|
1187 |
|
1188 surf->lastUseTime = ctx->cacheTimeStamp; |
|
1189 return surf->handle; |
|
1190 } |
|
1191 } |
|
1192 |
|
1193 /* No matching surface found; must create a new one. If the cache |
|
1194 * is fully occupied, or if we run out of memory, one of the |
|
1195 * existing surfaces is swapped out */ |
|
1196 |
|
1197 while (attempts <= 1) { |
|
1198 |
|
1199 GLSurfaceRecord *lru = &ctx->glSurface[0]; |
|
1200 |
|
1201 /* Find the first entry without a GL surface handle, or the |
|
1202 * least recently used one if all are occupied. */ |
|
1203 |
|
1204 for (i = 1; lru->handle != NULL && i < M3G_MAX_GL_SURFACES; ++i) { |
|
1205 GLSurfaceRecord *surf = &ctx->glSurface[i]; |
|
1206 if (!surf->handle || surf->lastUseTime < lru->lastUseTime) { |
|
1207 lru = surf; |
|
1208 } |
|
1209 } |
|
1210 |
|
1211 /* Delete the existing surface if we hit an occupied slot */ |
|
1212 |
|
1213 if (lru->handle) { |
|
1214 m3gDeleteGLSurface(lru->handle); |
|
1215 } |
|
1216 |
|
1217 /* Create a new surface depending on the type of the current |
|
1218 * rendering target */ |
|
1219 |
|
1220 switch (ctx->target.type) { |
|
1221 case SURFACE_BITMAP: |
|
1222 lru->handle = |
|
1223 m3gCreateBitmapSurface(ctx->target.format, |
|
1224 ctx->bufferBits, |
|
1225 (M3GNativeBitmap) ctx->target.handle); |
|
1226 break; |
|
1227 case SURFACE_WINDOW: |
|
1228 lru->handle = |
|
1229 m3gCreateWindowSurface(ctx->target.format, |
|
1230 ctx->bufferBits, |
|
1231 (M3GNativeWindow) ctx->target.handle); |
|
1232 break; |
|
1233 default: |
|
1234 M3G_ASSERT(M3G_FALSE); |
|
1235 return NULL; |
|
1236 } |
|
1237 |
|
1238 /* Success, return the new surface */ |
|
1239 |
|
1240 if (lru->handle) { |
|
1241 lru->type = ctx->target.type; |
|
1242 lru->targetHandle = ctx->target.handle; |
|
1243 lru->bufferBits = ctx->bufferBits; |
|
1244 lru->lastUseTime = ctx->cacheTimeStamp; |
|
1245 return lru->handle; |
|
1246 } |
|
1247 |
|
1248 /* No surface created, likely due to running out of memory; |
|
1249 * delete all existing surfaces and try again */ |
|
1250 |
|
1251 if (!lru->handle) { |
|
1252 for (i = 0; i < M3G_MAX_GL_SURFACES; ++i) { |
|
1253 GLSurfaceRecord *surf = &ctx->glSurface[i]; |
|
1254 if (surf->handle) { |
|
1255 m3gDeleteGLSurface(surf->handle); |
|
1256 surf->handle = NULL; |
|
1257 surf->type = SURFACE_NONE; |
|
1258 } |
|
1259 } |
|
1260 ++attempts; |
|
1261 continue; |
|
1262 } |
|
1263 } |
|
1264 |
|
1265 /* Couldn't create a new surface; must return with an error */ |
|
1266 |
|
1267 m3gRaiseError(M3G_INTERFACE(ctx), M3G_OUT_OF_MEMORY); |
|
1268 return NULL; |
|
1269 } |
|
1270 |
|
1271 |
|
1272 /*! |
|
1273 * \internal |
|
1274 * \brief Deletes all native surfaces for a specific target |
|
1275 * |
|
1276 * \param ctx rendering context |
|
1277 * \param type bitmask of the types of surfaces to remove |
|
1278 * \param handle native target handle of the surfaces to remove |
|
1279 */ |
|
1280 static void m3gDeleteGLSurfaces(RenderContext *ctx, |
|
1281 M3Gbitmask type, |
|
1282 M3Guint handle) |
|
1283 { |
|
1284 int i; |
|
1285 M3G_VALIDATE_OBJECT(ctx); |
|
1286 |
|
1287 for (i = 0; i < M3G_MAX_GL_SURFACES; ++i) { |
|
1288 GLSurfaceRecord *surf = &ctx->glSurface[i]; |
|
1289 |
|
1290 if ((surf->type & type) != 0 && surf->targetHandle == handle) { |
|
1291 m3gDeleteGLSurface(surf->handle); |
|
1292 |
|
1293 surf->type = SURFACE_NONE; |
|
1294 surf->handle = NULL; |
|
1295 } |
|
1296 } |
|
1297 } |
|
1298 |
|
1299 /*! |
|
1300 * \internal |
|
1301 * \brief Makes an OpenGL context current to the current rendering target |
|
1302 */ |
|
1303 static void m3gMakeGLCurrent(RenderContext *ctx) |
|
1304 { |
|
1305 if (ctx != NULL) { |
|
1306 EGLContext eglCtx = NULL; |
|
1307 if (ctx->target.buffered) { |
|
1308 eglCtx = m3gSelectGLContext( |
|
1309 ctx, |
|
1310 M3G_RGBA8, |
|
1311 (M3Gbitmask) M3G_COLOR_BUFFER_BIT | |
|
1312 M3G_DEPTH_BUFFER_BIT | |
|
1313 M3G_MULTISAMPLE_BUFFER_BIT, |
|
1314 (M3Gbitmask) EGL_PBUFFER_BIT, |
|
1315 ctx->backBuffer.glSurface); |
|
1316 ctx->target.surface = ctx->backBuffer.glSurface; |
|
1317 } |
|
1318 else { |
|
1319 EGLSurface surface = m3gSelectGLSurface(ctx); |
|
1320 if (surface) { |
|
1321 eglCtx = m3gSelectGLContext(ctx, |
|
1322 ctx->target.format, |
|
1323 ctx->bufferBits, |
|
1324 ctx->target.type, |
|
1325 surface); |
|
1326 ctx->target.surface = surface; |
|
1327 } |
|
1328 } |
|
1329 |
|
1330 /* Update the current acceleration status */ |
|
1331 |
|
1332 if (eglCtx) { |
|
1333 EGLint param; |
|
1334 eglQueryContext(eglGetCurrentDisplay(), |
|
1335 eglCtx, EGL_CONFIG_ID, |
|
1336 ¶m); |
|
1337 eglGetConfigAttrib(eglGetCurrentDisplay(), |
|
1338 (EGLConfig) param, EGL_CONFIG_CAVEAT, |
|
1339 ¶m); |
|
1340 ctx->accelerated = (param == EGL_NONE); |
|
1341 } |
|
1342 } |
|
1343 else { |
|
1344 eglMakeCurrent(eglGetDisplay(EGL_DEFAULT_DISPLAY), NULL, NULL, NULL); |
|
1345 } |
|
1346 } |
|
1347 |
|
1348 |
|
1349 /*---------------------------------------------------------------------- |
|
1350 * Public API |
|
1351 *--------------------------------------------------------------------*/ |
|
1352 |
|
1353 /*! |
|
1354 * \brief |
|
1355 */ |
|
1356 void m3gBindBitmapTarget(M3GRenderContext hCtx, |
|
1357 M3GNativeBitmap hBitmap) |
|
1358 { |
|
1359 M3GPixelFormat format; |
|
1360 M3Gint width, height; |
|
1361 RenderContext *ctx = (RenderContext *) hCtx; |
|
1362 M3G_VALIDATE_OBJECT(ctx); |
|
1363 |
|
1364 M3G_LOG1(M3G_LOG_RENDERING, "Binding bitmap 0x%08X\n", (unsigned) hBitmap); |
|
1365 |
|
1366 if (!m3gglGetNativeBitmapParams(hBitmap, &format, &width, &height)) { |
|
1367 m3gRaiseError(M3G_INTERFACE(ctx), M3G_INVALID_OBJECT); |
|
1368 return; |
|
1369 } |
|
1370 |
|
1371 if (!m3gBindRenderTarget(ctx, |
|
1372 SURFACE_BITMAP, |
|
1373 width, height, |
|
1374 format, |
|
1375 hBitmap)) { |
|
1376 return; /* appropriate error raised automatically */ |
|
1377 } |
|
1378 |
|
1379 /* placeholder for bitmap target specific setup */ |
|
1380 } |
|
1381 |
|
1382 /*! |
|
1383 * \brief Binds an external EGL surface as a rendering target |
|
1384 * |
|
1385 * \param context the M3G rendering context |
|
1386 * \param surface an EGLSurface cast to M3GEGLSurface |
|
1387 */ |
|
1388 M3G_API void m3gBindEGLSurfaceTarget(M3GRenderContext context, |
|
1389 M3GEGLSurface surface) |
|
1390 { |
|
1391 RenderContext *ctx = (RenderContext*) context; |
|
1392 Interface *m3g = M3G_INTERFACE(ctx); |
|
1393 M3G_VALIDATE_OBJECT(ctx); |
|
1394 |
|
1395 M3G_LOG1(M3G_LOG_RENDERING, "Binding EGL surface 0x%08X\n", (unsigned) surface); |
|
1396 { |
|
1397 EGLDisplay dpy = eglGetDisplay(EGL_DEFAULT_DISPLAY); |
|
1398 EGLSurface surf = (EGLSurface) surface; |
|
1399 M3Gint width, height; |
|
1400 |
|
1401 if (!(eglQuerySurface(dpy, surf, EGL_WIDTH, &width) && |
|
1402 eglQuerySurface(dpy, surf, EGL_HEIGHT, &height))) { |
|
1403 m3gRaiseError(m3g, M3G_INVALID_OBJECT); |
|
1404 return; |
|
1405 } |
|
1406 |
|
1407 if (!m3gBindRenderTarget(ctx, |
|
1408 SURFACE_EGL, |
|
1409 width, height, |
|
1410 M3G_RGBA8, |
|
1411 surface)) { |
|
1412 return; /* error raised automatically */ |
|
1413 } |
|
1414 |
|
1415 /* placeholder for target type specific setup */ |
|
1416 } |
|
1417 } |
|
1418 |
|
1419 /*! |
|
1420 * \brief Unsupported with OpenGL ES |
|
1421 */ |
|
1422 /*@access EGLContext@*/ |
|
1423 M3G_API void m3gBindMemoryTarget(M3GRenderContext context, |
|
1424 /*@shared@*/ void *pixels, |
|
1425 M3Guint width, M3Guint height, |
|
1426 M3GPixelFormat format, |
|
1427 M3Guint stride, |
|
1428 M3Guint userHandle) |
|
1429 { |
|
1430 RenderContext *ctx = (RenderContext*) context; |
|
1431 Interface *m3g = M3G_INTERFACE(ctx); |
|
1432 M3G_VALIDATE_OBJECT(ctx); |
|
1433 |
|
1434 M3G_UNREF(pixels); |
|
1435 M3G_UNREF(width); |
|
1436 M3G_UNREF(height); |
|
1437 M3G_UNREF(format); |
|
1438 M3G_UNREF(stride); |
|
1439 M3G_UNREF(userHandle); |
|
1440 |
|
1441 m3gRaiseError(m3g, M3G_INVALID_OPERATION); |
|
1442 } |
|
1443 |
|
1444 /*! |
|
1445 * \brief |
|
1446 */ |
|
1447 M3G_API void m3gBindWindowTarget(M3GRenderContext hCtx, |
|
1448 M3GNativeWindow hWindow) |
|
1449 { |
|
1450 M3GPixelFormat format; |
|
1451 M3Gint width, height; |
|
1452 RenderContext *ctx = (RenderContext *) hCtx; |
|
1453 M3G_VALIDATE_OBJECT(ctx); |
|
1454 |
|
1455 M3G_LOG1(M3G_LOG_RENDERING, "Binding window 0x%08X\n", (unsigned) hWindow); |
|
1456 |
|
1457 if (!m3gglGetNativeWindowParams(hWindow, &format, &width, &height)) { |
|
1458 m3gRaiseError(M3G_INTERFACE(ctx), M3G_INVALID_OBJECT); |
|
1459 return; |
|
1460 } |
|
1461 |
|
1462 if (!m3gBindRenderTarget(ctx, |
|
1463 SURFACE_WINDOW, |
|
1464 width, height, |
|
1465 format, |
|
1466 hWindow)) { |
|
1467 return; /* appropriate error raised automatically */ |
|
1468 } |
|
1469 |
|
1470 /* placeholder for window target specific setup */ |
|
1471 } |
|
1472 |
|
1473 /*! |
|
1474 * \brief Invalidate a previously bound bitmap target |
|
1475 * |
|
1476 * This should be called prior to deleting a native bitmap that has |
|
1477 * been used as an M3G rendering target in the past. This erases the |
|
1478 * object from any internal caches and ensures it will not be accessed |
|
1479 * in the future. |
|
1480 * |
|
1481 * \param hCtx M3G rendering context |
|
1482 * \param hBitmap native handle of the bitmap object |
|
1483 */ |
|
1484 M3G_API void m3gInvalidateBitmapTarget(M3GRenderContext hCtx, |
|
1485 M3GNativeBitmap hBitmap) |
|
1486 { |
|
1487 RenderContext *ctx = (RenderContext *) hCtx; |
|
1488 M3G_VALIDATE_OBJECT(ctx); |
|
1489 |
|
1490 M3G_LOG1(M3G_LOG_RENDERING, "Invalidating bitmap 0x%08X\n", |
|
1491 (unsigned) hBitmap); |
|
1492 |
|
1493 m3gDeleteGLSurfaces(ctx, (M3Gbitmask) SURFACE_BITMAP, (M3Guint) hBitmap); |
|
1494 } |
|
1495 |
|
1496 /*! |
|
1497 * \brief Invalidate a previously bound window target |
|
1498 * |
|
1499 * This should be called prior to deleting a native window that has |
|
1500 * been used as an M3G rendering target in the past. This erases the |
|
1501 * object from any internal caches and ensures it will not be accessed |
|
1502 * in the future. |
|
1503 * |
|
1504 * \param hCtx M3G rendering context |
|
1505 * \param hWindow native handle of the bitmap object |
|
1506 */ |
|
1507 M3G_API void m3gInvalidateWindowTarget(M3GRenderContext hCtx, |
|
1508 M3GNativeWindow hWindow) |
|
1509 { |
|
1510 RenderContext *ctx = (RenderContext *) hCtx; |
|
1511 M3G_VALIDATE_OBJECT(ctx); |
|
1512 |
|
1513 M3G_LOG1(M3G_LOG_RENDERING, "Invalidating window 0x%08X\n", |
|
1514 (unsigned) hWindow); |
|
1515 |
|
1516 m3gDeleteGLSurfaces(ctx, (M3Gbitmask) SURFACE_WINDOW, (M3Guint) hWindow); |
|
1517 } |