blob: 88e865124ec7eee0f5a08e546ab3755f649a4da3 [file] [log] [blame]
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001/* libs/opengles/texture.cpp
2**
3** Copyright 2006, The Android Open Source Project
4**
Mathias Agopian1473f462009-04-10 14:24:30 -07005** Licensed under the Apache License, Version 2.0 (the "License");
6** you may not use this file except in compliance with the License.
7** You may obtain a copy of the License at
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08008**
Mathias Agopian1473f462009-04-10 14:24:30 -07009** http://www.apache.org/licenses/LICENSE-2.0
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080010**
Mathias Agopian1473f462009-04-10 14:24:30 -070011** Unless required by applicable law or agreed to in writing, software
12** distributed under the License is distributed on an "AS IS" BASIS,
13** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14** See the License for the specific language governing permissions and
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080015** limitations under the License.
16*/
17
18#include <stdio.h>
19#include <stdlib.h>
20#include "context.h"
21#include "fp.h"
22#include "state.h"
23#include "texture.h"
24#include "TextureObjectManager.h"
25
Mathias Agopianb51e18d2009-05-05 18:21:32 -070026#include <private/ui/android_natives_priv.h>
Mathias Agopiand1f73a22010-02-01 18:24:52 -080027#include <ETC1/etc1.h>
Mathias Agopian1473f462009-04-10 14:24:30 -070028
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080029namespace android {
30
31// ----------------------------------------------------------------------------
32
33static void bindTextureTmu(
34 ogles_context_t* c, int tmu, GLuint texture, const sp<EGLTextureObject>& tex);
35
36static __attribute__((noinline))
37void generateMipmap(ogles_context_t* c, GLint level);
38
39// ----------------------------------------------------------------------------
40
41#if 0
42#pragma mark -
43#pragma mark Init
44#endif
45
46void ogles_init_texture(ogles_context_t* c)
47{
48 c->textures.packAlignment = 4;
49 c->textures.unpackAlignment = 4;
50
51 // each context has a default named (0) texture (not shared)
52 c->textures.defaultTexture = new EGLTextureObject();
53 c->textures.defaultTexture->incStrong(c);
Mathias Agopian1473f462009-04-10 14:24:30 -070054
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080055 // bind the default texture to each texture unit
56 for (int i=0; i<GGL_TEXTURE_UNIT_COUNT ; i++) {
57 bindTextureTmu(c, i, 0, c->textures.defaultTexture);
58 memset(c->current.texture[i].v, 0, sizeof(vec4_t));
59 c->current.texture[i].Q = 0x10000;
60 }
61}
62
63void ogles_uninit_texture(ogles_context_t* c)
64{
65 if (c->textures.ggl)
66 gglUninit(c->textures.ggl);
67 c->textures.defaultTexture->decStrong(c);
68 for (int i=0; i<GGL_TEXTURE_UNIT_COUNT ; i++) {
69 if (c->textures.tmu[i].texture)
70 c->textures.tmu[i].texture->decStrong(c);
71 }
72}
73
74static __attribute__((noinline))
75void validate_tmu(ogles_context_t* c, int i)
76{
77 texture_unit_t& u(c->textures.tmu[i]);
78 if (u.dirty) {
79 u.dirty = 0;
80 c->rasterizer.procs.activeTexture(c, i);
81 c->rasterizer.procs.bindTexture(c, &(u.texture->surface));
82 c->rasterizer.procs.texGeni(c, GGL_S,
83 GGL_TEXTURE_GEN_MODE, GGL_AUTOMATIC);
84 c->rasterizer.procs.texGeni(c, GGL_T,
85 GGL_TEXTURE_GEN_MODE, GGL_AUTOMATIC);
86 c->rasterizer.procs.texParameteri(c, GGL_TEXTURE_2D,
87 GGL_TEXTURE_WRAP_S, u.texture->wraps);
88 c->rasterizer.procs.texParameteri(c, GGL_TEXTURE_2D,
89 GGL_TEXTURE_WRAP_T, u.texture->wrapt);
90 c->rasterizer.procs.texParameteri(c, GGL_TEXTURE_2D,
91 GGL_TEXTURE_MIN_FILTER, u.texture->min_filter);
92 c->rasterizer.procs.texParameteri(c, GGL_TEXTURE_2D,
93 GGL_TEXTURE_MAG_FILTER, u.texture->mag_filter);
94
95 // disable this texture unit if it's not complete
96 if (!u.texture->isComplete()) {
97 c->rasterizer.procs.disable(c, GGL_TEXTURE_2D);
98 }
99 }
100}
101
Mathias Agopiandff8e582009-05-04 14:17:04 -0700102void ogles_validate_texture(ogles_context_t* c)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800103{
104 for (int i=0 ; i<GGL_TEXTURE_UNIT_COUNT ; i++) {
105 if (c->rasterizer.state.texture[i].enable)
106 validate_tmu(c, i);
107 }
108 c->rasterizer.procs.activeTexture(c, c->textures.active);
109}
110
111static
112void invalidate_texture(ogles_context_t* c, int tmu, uint8_t flags = 0xFF) {
113 c->textures.tmu[tmu].dirty = flags;
114}
115
Mathias Agopiandff8e582009-05-04 14:17:04 -0700116/*
117 * If the active textures are EGLImage, they need to be locked before
Jack Palevichfb5ea2e2009-09-10 17:13:28 -0700118 * they can be used.
119 *
Mathias Agopiandff8e582009-05-04 14:17:04 -0700120 * FIXME: code below is far from being optimal
Jack Palevichfb5ea2e2009-09-10 17:13:28 -0700121 *
Mathias Agopiandff8e582009-05-04 14:17:04 -0700122 */
123
124void ogles_lock_textures(ogles_context_t* c)
125{
126 for (int i=0 ; i<GGL_TEXTURE_UNIT_COUNT ; i++) {
127 if (c->rasterizer.state.texture[i].enable) {
128 texture_unit_t& u(c->textures.tmu[i]);
Iliyan Malchevb2a153a2011-05-01 11:33:26 -0700129 ANativeWindowBuffer* native_buffer = u.texture->buffer;
Mathias Agopiandff8e582009-05-04 14:17:04 -0700130 if (native_buffer) {
131 c->rasterizer.procs.activeTexture(c, i);
132 hw_module_t const* pModule;
133 if (hw_get_module(GRALLOC_HARDWARE_MODULE_ID, &pModule))
134 continue;
135
136 gralloc_module_t const* module =
137 reinterpret_cast<gralloc_module_t const*>(pModule);
Mathias Agopiane633f932009-05-05 00:59:23 -0700138
Mathias Agopian430f2ed2009-05-05 00:37:46 -0700139 void* vaddr;
Mathias Agopiane633f932009-05-05 00:59:23 -0700140 int err = module->lock(module, native_buffer->handle,
Mathias Agopiandff8e582009-05-04 14:17:04 -0700141 GRALLOC_USAGE_SW_READ_OFTEN,
142 0, 0, native_buffer->width, native_buffer->height,
Mathias Agopian430f2ed2009-05-05 00:37:46 -0700143 &vaddr);
Mathias Agopiandff8e582009-05-04 14:17:04 -0700144
Mathias Agopian430f2ed2009-05-05 00:37:46 -0700145 u.texture->setImageBits(vaddr);
Mathias Agopiandff8e582009-05-04 14:17:04 -0700146 c->rasterizer.procs.bindTexture(c, &(u.texture->surface));
147 }
148 }
149 }
150}
151
152void ogles_unlock_textures(ogles_context_t* c)
153{
154 for (int i=0 ; i<GGL_TEXTURE_UNIT_COUNT ; i++) {
155 if (c->rasterizer.state.texture[i].enable) {
156 texture_unit_t& u(c->textures.tmu[i]);
Iliyan Malchevb2a153a2011-05-01 11:33:26 -0700157 ANativeWindowBuffer* native_buffer = u.texture->buffer;
Mathias Agopiandff8e582009-05-04 14:17:04 -0700158 if (native_buffer) {
159 c->rasterizer.procs.activeTexture(c, i);
160 hw_module_t const* pModule;
161 if (hw_get_module(GRALLOC_HARDWARE_MODULE_ID, &pModule))
162 continue;
163
164 gralloc_module_t const* module =
165 reinterpret_cast<gralloc_module_t const*>(pModule);
Mathias Agopiane633f932009-05-05 00:59:23 -0700166
167 module->unlock(module, native_buffer->handle);
Mathias Agopiandff8e582009-05-04 14:17:04 -0700168 u.texture->setImageBits(NULL);
169 c->rasterizer.procs.bindTexture(c, &(u.texture->surface));
170 }
171 }
172 }
173 c->rasterizer.procs.activeTexture(c, c->textures.active);
174}
175
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800176// ----------------------------------------------------------------------------
177#if 0
178#pragma mark -
179#pragma mark Format conversion
180#endif
181
182static uint32_t gl2format_table[6][4] = {
183 // BYTE, 565, 4444, 5551
184 { GGL_PIXEL_FORMAT_A_8,
185 0, 0, 0 }, // GL_ALPHA
186 { GGL_PIXEL_FORMAT_RGB_888,
187 GGL_PIXEL_FORMAT_RGB_565,
188 0, 0 }, // GL_RGB
189 { GGL_PIXEL_FORMAT_RGBA_8888,
190 0,
191 GGL_PIXEL_FORMAT_RGBA_4444,
192 GGL_PIXEL_FORMAT_RGBA_5551 }, // GL_RGBA
193 { GGL_PIXEL_FORMAT_L_8,
194 0, 0, 0 }, // GL_LUMINANCE
195 { GGL_PIXEL_FORMAT_LA_88,
196 0, 0, 0 }, // GL_LUMINANCE_ALPHA
197};
198
199static int32_t convertGLPixelFormat(GLint format, GLenum type)
200{
201 int32_t fi = -1;
202 int32_t ti = -1;
203 switch (format) {
204 case GL_ALPHA: fi = 0; break;
205 case GL_RGB: fi = 1; break;
206 case GL_RGBA: fi = 2; break;
207 case GL_LUMINANCE: fi = 3; break;
208 case GL_LUMINANCE_ALPHA: fi = 4; break;
209 }
210 switch (type) {
211 case GL_UNSIGNED_BYTE: ti = 0; break;
212 case GL_UNSIGNED_SHORT_5_6_5: ti = 1; break;
213 case GL_UNSIGNED_SHORT_4_4_4_4: ti = 2; break;
214 case GL_UNSIGNED_SHORT_5_5_5_1: ti = 3; break;
215 }
216 if (fi==-1 || ti==-1)
217 return 0;
218 return gl2format_table[fi][ti];
219}
220
221// ----------------------------------------------------------------------------
222
223static GLenum validFormatType(ogles_context_t* c, GLenum format, GLenum type)
224{
225 GLenum error = 0;
226 if (format<GL_ALPHA || format>GL_LUMINANCE_ALPHA) {
227 error = GL_INVALID_ENUM;
228 }
229 if (type != GL_UNSIGNED_BYTE && type != GL_UNSIGNED_SHORT_4_4_4_4 &&
230 type != GL_UNSIGNED_SHORT_5_5_5_1 && type != GL_UNSIGNED_SHORT_5_6_5) {
231 error = GL_INVALID_ENUM;
232 }
233 if (type == GL_UNSIGNED_SHORT_5_6_5 && format != GL_RGB) {
234 error = GL_INVALID_OPERATION;
235 }
236 if ((type == GL_UNSIGNED_SHORT_4_4_4_4 ||
237 type == GL_UNSIGNED_SHORT_5_5_5_1) && format != GL_RGBA) {
238 error = GL_INVALID_OPERATION;
239 }
240 if (error) {
241 ogles_error(c, error);
242 }
243 return error;
244}
245
246// ----------------------------------------------------------------------------
247
248GGLContext* getRasterizer(ogles_context_t* c)
249{
250 GGLContext* ggl = c->textures.ggl;
251 if (ggl_unlikely(!ggl)) {
252 // this is quite heavy the first time...
253 gglInit(&ggl);
254 if (!ggl) {
255 return 0;
256 }
257 GGLfixed colors[4] = { 0, 0, 0, 0x10000 };
258 c->textures.ggl = ggl;
259 ggl->activeTexture(ggl, 0);
260 ggl->enable(ggl, GGL_TEXTURE_2D);
261 ggl->texEnvi(ggl, GGL_TEXTURE_ENV, GGL_TEXTURE_ENV_MODE, GGL_REPLACE);
262 ggl->disable(ggl, GGL_DITHER);
263 ggl->shadeModel(ggl, GGL_FLAT);
264 ggl->color4xv(ggl, colors);
265 }
266 return ggl;
267}
268
269static __attribute__((noinline))
270int copyPixels(
271 ogles_context_t* c,
272 const GGLSurface& dst,
273 GLint xoffset, GLint yoffset,
274 const GGLSurface& src,
275 GLint x, GLint y, GLsizei w, GLsizei h)
276{
277 if ((dst.format == src.format) &&
278 (dst.stride == src.stride) &&
279 (dst.width == src.width) &&
280 (dst.height == src.height) &&
281 (dst.stride > 0) &&
282 ((x|y) == 0) &&
283 ((xoffset|yoffset) == 0))
284 {
285 // this is a common case...
286 const GGLFormat& pixelFormat(c->rasterizer.formats[src.format]);
287 const size_t size = src.height * src.stride * pixelFormat.size;
288 memcpy(dst.data, src.data, size);
289 return 0;
290 }
291
292 // use pixel-flinger to handle all the conversions
293 GGLContext* ggl = getRasterizer(c);
294 if (!ggl) {
295 // the only reason this would fail is because we ran out of memory
296 return GL_OUT_OF_MEMORY;
297 }
298
299 ggl->colorBuffer(ggl, &dst);
300 ggl->bindTexture(ggl, &src);
301 ggl->texCoord2i(ggl, x-xoffset, y-yoffset);
302 ggl->recti(ggl, xoffset, yoffset, xoffset+w, yoffset+h);
303 return 0;
304}
305
306// ----------------------------------------------------------------------------
307
308static __attribute__((noinline))
309sp<EGLTextureObject> getAndBindActiveTextureObject(ogles_context_t* c)
310{
311 sp<EGLTextureObject> tex;
312 const int active = c->textures.active;
313 const GLuint name = c->textures.tmu[active].name;
314
315 // free the reference to the previously bound object
316 texture_unit_t& u(c->textures.tmu[active]);
317 if (u.texture)
318 u.texture->decStrong(c);
319
320 if (name == 0) {
Mathias Agopian1473f462009-04-10 14:24:30 -0700321 // 0 is our local texture object, not shared with anyone.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800322 // But it affects all bound TMUs immediately.
323 // (we need to invalidate all units bound to this texture object)
324 tex = c->textures.defaultTexture;
325 for (int i=0 ; i<GGL_TEXTURE_UNIT_COUNT ; i++) {
326 if (c->textures.tmu[i].texture == tex.get())
327 invalidate_texture(c, i);
328 }
329 } else {
330 // get a new texture object for that name
331 tex = c->surfaceManager->replaceTexture(name);
332 }
333
334 // bind this texture to the current active texture unit
335 // and add a reference to this texture object
336 u.texture = tex.get();
337 u.texture->incStrong(c);
338 u.name = name;
Mathias Agopian1473f462009-04-10 14:24:30 -0700339 invalidate_texture(c, active);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800340 return tex;
341}
342
343void bindTextureTmu(
344 ogles_context_t* c, int tmu, GLuint texture, const sp<EGLTextureObject>& tex)
345{
346 if (tex.get() == c->textures.tmu[tmu].texture)
347 return;
Mathias Agopian1473f462009-04-10 14:24:30 -0700348
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800349 // free the reference to the previously bound object
350 texture_unit_t& u(c->textures.tmu[tmu]);
351 if (u.texture)
352 u.texture->decStrong(c);
353
354 // bind this texture to the current active texture unit
355 // and add a reference to this texture object
356 u.texture = tex.get();
357 u.texture->incStrong(c);
358 u.name = texture;
359 invalidate_texture(c, tmu);
360}
361
362int createTextureSurface(ogles_context_t* c,
363 GGLSurface** outSurface, int32_t* outSize, GLint level,
364 GLenum format, GLenum type, GLsizei width, GLsizei height,
365 GLenum compressedFormat = 0)
366{
367 // find out which texture is bound to the current unit
368 const int active = c->textures.active;
369 const GLuint name = c->textures.tmu[active].name;
370
371 // convert the pixelformat to one we can handle
372 const int32_t formatIdx = convertGLPixelFormat(format, type);
373 if (formatIdx == 0) { // we don't know what to do with this
374 return GL_INVALID_OPERATION;
375 }
Mathias Agopian1473f462009-04-10 14:24:30 -0700376
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800377 // figure out the size we need as well as the stride
378 const GGLFormat& pixelFormat(c->rasterizer.formats[formatIdx]);
379 const int32_t align = c->textures.unpackAlignment-1;
380 const int32_t bpr = ((width * pixelFormat.size) + align) & ~align;
381 const size_t size = bpr * height;
382 const int32_t stride = bpr / pixelFormat.size;
383
384 if (level > 0) {
385 const int active = c->textures.active;
386 EGLTextureObject* tex = c->textures.tmu[active].texture;
387 status_t err = tex->reallocate(level,
388 width, height, stride, formatIdx, compressedFormat, bpr);
389 if (err != NO_ERROR)
390 return GL_OUT_OF_MEMORY;
391 GGLSurface& surface = tex->editMip(level);
392 *outSurface = &surface;
393 *outSize = size;
394 return 0;
395 }
396
397 sp<EGLTextureObject> tex = getAndBindActiveTextureObject(c);
398 status_t err = tex->reallocate(level,
399 width, height, stride, formatIdx, compressedFormat, bpr);
400 if (err != NO_ERROR)
401 return GL_OUT_OF_MEMORY;
402
403 tex->internalformat = format;
404 *outSurface = &tex->surface;
405 *outSize = size;
406 return 0;
407}
408
Jack Palevichfb5ea2e2009-09-10 17:13:28 -0700409static size_t dataSizePalette4(int numLevels, int width, int height, int format)
410{
411 int indexBits = 8;
412 int entrySize = 0;
413 switch (format) {
414 case GL_PALETTE4_RGB8_OES:
415 indexBits = 4;
416 /* FALLTHROUGH */
417 case GL_PALETTE8_RGB8_OES:
418 entrySize = 3;
419 break;
420
421 case GL_PALETTE4_RGBA8_OES:
422 indexBits = 4;
423 /* FALLTHROUGH */
424 case GL_PALETTE8_RGBA8_OES:
425 entrySize = 4;
426 break;
427
428 case GL_PALETTE4_R5_G6_B5_OES:
429 case GL_PALETTE4_RGBA4_OES:
430 case GL_PALETTE4_RGB5_A1_OES:
431 indexBits = 4;
432 /* FALLTHROUGH */
433 case GL_PALETTE8_R5_G6_B5_OES:
434 case GL_PALETTE8_RGBA4_OES:
435 case GL_PALETTE8_RGB5_A1_OES:
436 entrySize = 2;
437 break;
438 }
439
440 size_t size = (1 << indexBits) * entrySize; // palette size
441
442 for (int i=0 ; i< numLevels ; i++) {
443 int w = (width >> i) ? : 1;
444 int h = (height >> i) ? : 1;
445 int levelSize = h * ((w * indexBits) / 8) ? : 1;
446 size += levelSize;
447 }
448
449 return size;
450}
451
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800452static void decodePalette4(const GLvoid *data, int level, int width, int height,
453 void *surface, int stride, int format)
454
455{
456 int indexBits = 8;
457 int entrySize = 0;
458 switch (format) {
459 case GL_PALETTE4_RGB8_OES:
460 indexBits = 4;
461 /* FALLTHROUGH */
462 case GL_PALETTE8_RGB8_OES:
463 entrySize = 3;
464 break;
465
466 case GL_PALETTE4_RGBA8_OES:
467 indexBits = 4;
468 /* FALLTHROUGH */
469 case GL_PALETTE8_RGBA8_OES:
470 entrySize = 4;
471 break;
472
473 case GL_PALETTE4_R5_G6_B5_OES:
474 case GL_PALETTE4_RGBA4_OES:
475 case GL_PALETTE4_RGB5_A1_OES:
476 indexBits = 4;
477 /* FALLTHROUGH */
478 case GL_PALETTE8_R5_G6_B5_OES:
479 case GL_PALETTE8_RGBA4_OES:
480 case GL_PALETTE8_RGB5_A1_OES:
481 entrySize = 2;
482 break;
483 }
484
485 const int paletteSize = (1 << indexBits) * entrySize;
Jack Palevichfb5ea2e2009-09-10 17:13:28 -0700486
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800487 uint8_t const* pixels = (uint8_t *)data + paletteSize;
488 for (int i=0 ; i<level ; i++) {
489 int w = (width >> i) ? : 1;
490 int h = (height >> i) ? : 1;
491 pixels += h * ((w * indexBits) / 8);
492 }
493 width = (width >> level) ? : 1;
494 height = (height >> level) ? : 1;
495
496 if (entrySize == 2) {
497 uint8_t const* const palette = (uint8_t*)data;
498 for (int y=0 ; y<height ; y++) {
499 uint8_t* p = (uint8_t*)surface + y*stride*2;
500 if (indexBits == 8) {
501 for (int x=0 ; x<width ; x++) {
502 int index = 2 * (*pixels++);
503 *p++ = palette[index + 0];
504 *p++ = palette[index + 1];
505 }
506 } else {
507 for (int x=0 ; x<width ; x+=2) {
508 int v = *pixels++;
509 int index = 2 * (v >> 4);
510 *p++ = palette[index + 0];
511 *p++ = palette[index + 1];
512 if (x+1 < width) {
513 index = 2 * (v & 0xF);
514 *p++ = palette[index + 0];
515 *p++ = palette[index + 1];
516 }
517 }
518 }
519 }
520 } else if (entrySize == 3) {
521 uint8_t const* const palette = (uint8_t*)data;
522 for (int y=0 ; y<height ; y++) {
523 uint8_t* p = (uint8_t*)surface + y*stride*3;
524 if (indexBits == 8) {
525 for (int x=0 ; x<width ; x++) {
526 int index = 3 * (*pixels++);
527 *p++ = palette[index + 0];
528 *p++ = palette[index + 1];
529 *p++ = palette[index + 2];
530 }
531 } else {
532 for (int x=0 ; x<width ; x+=2) {
533 int v = *pixels++;
534 int index = 3 * (v >> 4);
535 *p++ = palette[index + 0];
536 *p++ = palette[index + 1];
537 *p++ = palette[index + 2];
538 if (x+1 < width) {
539 index = 3 * (v & 0xF);
540 *p++ = palette[index + 0];
541 *p++ = palette[index + 1];
542 *p++ = palette[index + 2];
543 }
544 }
545 }
546 }
547 } else if (entrySize == 4) {
548 uint8_t const* const palette = (uint8_t*)data;
549 for (int y=0 ; y<height ; y++) {
550 uint8_t* p = (uint8_t*)surface + y*stride*4;
551 if (indexBits == 8) {
552 for (int x=0 ; x<width ; x++) {
553 int index = 4 * (*pixels++);
554 *p++ = palette[index + 0];
555 *p++ = palette[index + 1];
556 *p++ = palette[index + 2];
557 *p++ = palette[index + 3];
558 }
559 } else {
560 for (int x=0 ; x<width ; x+=2) {
561 int v = *pixels++;
562 int index = 4 * (v >> 4);
563 *p++ = palette[index + 0];
564 *p++ = palette[index + 1];
565 *p++ = palette[index + 2];
566 *p++ = palette[index + 3];
567 if (x+1 < width) {
568 index = 4 * (v & 0xF);
569 *p++ = palette[index + 0];
570 *p++ = palette[index + 1];
571 *p++ = palette[index + 2];
572 *p++ = palette[index + 3];
573 }
574 }
575 }
576 }
577 }
578}
579
580
581
582static __attribute__((noinline))
Mathias Agopianac00ad12010-01-25 11:49:52 -0800583void set_depth_and_fog(ogles_context_t* c, GGLfixed z)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800584{
585 const uint32_t enables = c->rasterizer.state.enables;
586 // we need to compute Zw
587 int32_t iterators[3];
588 iterators[1] = iterators[2] = 0;
589 GGLfixed Zw;
590 GGLfixed n = gglFloatToFixed(c->transforms.vpt.zNear);
591 GGLfixed f = gglFloatToFixed(c->transforms.vpt.zFar);
Mathias Agopianac00ad12010-01-25 11:49:52 -0800592 if (z<=0) Zw = n;
593 else if (z>=0x10000) Zw = f;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800594 else Zw = gglMulAddx(z, (f-n), n);
595 if (enables & GGL_ENABLE_FOG) {
596 // set up fog if needed...
597 iterators[0] = c->fog.fog(c, Zw);
598 c->rasterizer.procs.fogGrad3xv(c, iterators);
599 }
600 if (enables & GGL_ENABLE_DEPTH_TEST) {
601 // set up z-test if needed...
602 int32_t z = (Zw & ~(Zw>>31));
603 if (z >= 0x10000)
604 z = 0xFFFF;
605 iterators[0] = (z << 16) | z;
606 c->rasterizer.procs.zGrad3xv(c, iterators);
607 }
608}
609
610// ----------------------------------------------------------------------------
611#if 0
612#pragma mark -
613#pragma mark Generate mimaps
614#endif
615
616extern status_t buildAPyramid(ogles_context_t* c, EGLTextureObject* tex);
617
618void generateMipmap(ogles_context_t* c, GLint level)
619{
620 if (level == 0) {
621 const int active = c->textures.active;
622 EGLTextureObject* tex = c->textures.tmu[active].texture;
623 if (tex->generate_mipmap) {
624 if (buildAPyramid(c, tex) != NO_ERROR) {
625 ogles_error(c, GL_OUT_OF_MEMORY);
626 return;
627 }
628 }
629 }
630}
631
632
633static void texParameterx(
634 GLenum target, GLenum pname, GLfixed param, ogles_context_t* c)
635{
Mathias Agopianf319e2f2011-08-18 16:26:21 -0700636 if (target != GL_TEXTURE_2D && target != GL_TEXTURE_EXTERNAL_OES) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800637 ogles_error(c, GL_INVALID_ENUM);
638 return;
639 }
Mathias Agopian1473f462009-04-10 14:24:30 -0700640
641 EGLTextureObject* textureObject = c->textures.tmu[c->textures.active].texture;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800642 switch (pname) {
643 case GL_TEXTURE_WRAP_S:
644 if ((param == GL_REPEAT) ||
645 (param == GL_CLAMP_TO_EDGE)) {
646 textureObject->wraps = param;
647 } else {
648 goto invalid_enum;
649 }
650 break;
651 case GL_TEXTURE_WRAP_T:
652 if ((param == GL_REPEAT) ||
653 (param == GL_CLAMP_TO_EDGE)) {
654 textureObject->wrapt = param;
655 } else {
656 goto invalid_enum;
657 }
658 break;
659 case GL_TEXTURE_MIN_FILTER:
660 if ((param == GL_NEAREST) ||
661 (param == GL_LINEAR) ||
662 (param == GL_NEAREST_MIPMAP_NEAREST) ||
663 (param == GL_LINEAR_MIPMAP_NEAREST) ||
664 (param == GL_NEAREST_MIPMAP_LINEAR) ||
665 (param == GL_LINEAR_MIPMAP_LINEAR)) {
666 textureObject->min_filter = param;
667 } else {
668 goto invalid_enum;
669 }
670 break;
671 case GL_TEXTURE_MAG_FILTER:
672 if ((param == GL_NEAREST) ||
673 (param == GL_LINEAR)) {
674 textureObject->mag_filter = param;
675 } else {
676 goto invalid_enum;
677 }
678 break;
679 case GL_GENERATE_MIPMAP:
680 textureObject->generate_mipmap = param;
681 break;
682 default:
683invalid_enum:
684 ogles_error(c, GL_INVALID_ENUM);
685 return;
686 }
687 invalidate_texture(c, c->textures.active);
688}
689
690
Mathias Agopian1473f462009-04-10 14:24:30 -0700691
692static void drawTexxOESImp(GLfixed x, GLfixed y, GLfixed z, GLfixed w, GLfixed h,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800693 ogles_context_t* c)
694{
Mathias Agopiandff8e582009-05-04 14:17:04 -0700695 ogles_lock_textures(c);
Jack Palevichfb5ea2e2009-09-10 17:13:28 -0700696
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800697 const GGLSurface& cbSurface = c->rasterizer.state.buffers.color.s;
698 y = gglIntToFixed(cbSurface.height) - (y + h);
699 w >>= FIXED_BITS;
700 h >>= FIXED_BITS;
701
702 // set up all texture units
703 for (int i=0 ; i<GGL_TEXTURE_UNIT_COUNT ; i++) {
704 if (!c->rasterizer.state.texture[i].enable)
705 continue;
706
707 int32_t texcoords[8];
708 texture_unit_t& u(c->textures.tmu[i]);
709
710 // validate this tmu (bind, wrap, filter)
711 validate_tmu(c, i);
712 // we CLAMP here, which works with premultiplied (s,t)
713 c->rasterizer.procs.texParameteri(c,
714 GGL_TEXTURE_2D, GGL_TEXTURE_WRAP_S, GGL_CLAMP);
715 c->rasterizer.procs.texParameteri(c,
716 GGL_TEXTURE_2D, GGL_TEXTURE_WRAP_T, GGL_CLAMP);
717 u.dirty = 0xFF; // XXX: should be more subtle
718
Mathias Agopian1473f462009-04-10 14:24:30 -0700719 EGLTextureObject* textureObject = u.texture;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800720 const GLint Ucr = textureObject->crop_rect[0] << 16;
721 const GLint Vcr = textureObject->crop_rect[1] << 16;
722 const GLint Wcr = textureObject->crop_rect[2] << 16;
723 const GLint Hcr = textureObject->crop_rect[3] << 16;
724
725 // computes texture coordinates (pre-multiplied)
726 int32_t dsdx = Wcr / w; // dsdx = ((Wcr/w)/Wt)*Wt
727 int32_t dtdy =-Hcr / h; // dtdy = -((Hcr/h)/Ht)*Ht
728 int32_t s0 = Ucr - gglMulx(dsdx, x); // s0 = Ucr - x * dsdx
729 int32_t t0 = (Vcr+Hcr) - gglMulx(dtdy, y); // t0 = (Vcr+Hcr) - y*dtdy
730 texcoords[0] = s0;
731 texcoords[1] = dsdx;
732 texcoords[2] = 0;
733 texcoords[3] = t0;
734 texcoords[4] = 0;
735 texcoords[5] = dtdy;
736 texcoords[6] = 0;
737 texcoords[7] = 0;
738 c->rasterizer.procs.texCoordGradScale8xv(c, i, texcoords);
739 }
740
741 const uint32_t enables = c->rasterizer.state.enables;
742 if (ggl_unlikely(enables & (GGL_ENABLE_DEPTH_TEST|GGL_ENABLE_FOG)))
743 set_depth_and_fog(c, z);
744
745 c->rasterizer.procs.activeTexture(c, c->textures.active);
746 c->rasterizer.procs.color4xv(c, c->currentColorClamped.v);
747 c->rasterizer.procs.disable(c, GGL_W_LERP);
748 c->rasterizer.procs.disable(c, GGL_AA);
749 c->rasterizer.procs.shadeModel(c, GL_FLAT);
Mathias Agopian1473f462009-04-10 14:24:30 -0700750 c->rasterizer.procs.recti(c,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800751 gglFixedToIntRound(x),
752 gglFixedToIntRound(y),
753 gglFixedToIntRound(x)+w,
754 gglFixedToIntRound(y)+h);
Mathias Agopiandff8e582009-05-04 14:17:04 -0700755
756 ogles_unlock_textures(c);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800757}
758
Mathias Agopian1473f462009-04-10 14:24:30 -0700759static void drawTexxOES(GLfixed x, GLfixed y, GLfixed z, GLfixed w, GLfixed h,
760 ogles_context_t* c)
761{
Mathias Agopian1473f462009-04-10 14:24:30 -0700762 // quickly reject empty rects
763 if ((w|h) <= 0)
764 return;
Mathias Agopianbd2de0e2010-07-29 23:28:03 -0700765
Mathias Agopian1473f462009-04-10 14:24:30 -0700766 drawTexxOESImp(x, y, z, w, h, c);
767}
768
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800769static void drawTexiOES(GLint x, GLint y, GLint z, GLint w, GLint h, ogles_context_t* c)
770{
771 // All coordinates are integer, so if we have only one
772 // texture unit active and no scaling is required
773 // THEN, we can use our special 1:1 mapping
774 // which is a lot faster.
775
776 if (ggl_likely(c->rasterizer.state.enabled_tmu == 1)) {
777 const int tmu = 0;
778 texture_unit_t& u(c->textures.tmu[tmu]);
Mathias Agopian1473f462009-04-10 14:24:30 -0700779 EGLTextureObject* textureObject = u.texture;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800780 const GLint Wcr = textureObject->crop_rect[2];
781 const GLint Hcr = textureObject->crop_rect[3];
782
783 if ((w == Wcr) && (h == -Hcr)) {
784 if ((w|h) <= 0) return; // quickly reject empty rects
785
786 if (u.dirty) {
787 c->rasterizer.procs.activeTexture(c, tmu);
788 c->rasterizer.procs.bindTexture(c, &(u.texture->surface));
789 c->rasterizer.procs.texParameteri(c, GGL_TEXTURE_2D,
790 GGL_TEXTURE_MIN_FILTER, u.texture->min_filter);
791 c->rasterizer.procs.texParameteri(c, GGL_TEXTURE_2D,
792 GGL_TEXTURE_MAG_FILTER, u.texture->mag_filter);
793 }
794 c->rasterizer.procs.texGeni(c, GGL_S,
795 GGL_TEXTURE_GEN_MODE, GGL_ONE_TO_ONE);
796 c->rasterizer.procs.texGeni(c, GGL_T,
797 GGL_TEXTURE_GEN_MODE, GGL_ONE_TO_ONE);
798 u.dirty = 0xFF; // XXX: should be more subtle
799 c->rasterizer.procs.activeTexture(c, c->textures.active);
Mathias Agopian1473f462009-04-10 14:24:30 -0700800
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800801 const GGLSurface& cbSurface = c->rasterizer.state.buffers.color.s;
802 y = cbSurface.height - (y + h);
803 const GLint Ucr = textureObject->crop_rect[0];
804 const GLint Vcr = textureObject->crop_rect[1];
805 const GLint s0 = Ucr - x;
806 const GLint t0 = (Vcr + Hcr) - y;
Mathias Agopian1473f462009-04-10 14:24:30 -0700807
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800808 const GLuint tw = textureObject->surface.width;
809 const GLuint th = textureObject->surface.height;
810 if ((uint32_t(s0+x+w) > tw) || (uint32_t(t0+y+h) > th)) {
811 // The GL spec is unclear about what should happen
812 // in this case, so we just use the slow case, which
813 // at least won't crash
814 goto slow_case;
Mathias Agopian1473f462009-04-10 14:24:30 -0700815 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800816
Mathias Agopiandff8e582009-05-04 14:17:04 -0700817 ogles_lock_textures(c);
818
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800819 c->rasterizer.procs.texCoord2i(c, s0, t0);
820 const uint32_t enables = c->rasterizer.state.enables;
821 if (ggl_unlikely(enables & (GGL_ENABLE_DEPTH_TEST|GGL_ENABLE_FOG)))
Mathias Agopianac00ad12010-01-25 11:49:52 -0800822 set_depth_and_fog(c, gglIntToFixed(z));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800823
824 c->rasterizer.procs.color4xv(c, c->currentColorClamped.v);
825 c->rasterizer.procs.disable(c, GGL_W_LERP);
826 c->rasterizer.procs.disable(c, GGL_AA);
827 c->rasterizer.procs.shadeModel(c, GL_FLAT);
828 c->rasterizer.procs.recti(c, x, y, x+w, y+h);
Jack Palevichfb5ea2e2009-09-10 17:13:28 -0700829
Mathias Agopiandff8e582009-05-04 14:17:04 -0700830 ogles_unlock_textures(c);
831
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800832 return;
833 }
834 }
835
836slow_case:
Mathias Agopian1473f462009-04-10 14:24:30 -0700837 drawTexxOESImp(
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800838 gglIntToFixed(x), gglIntToFixed(y), gglIntToFixed(z),
839 gglIntToFixed(w), gglIntToFixed(h),
840 c);
841}
842
843
844}; // namespace android
845// ----------------------------------------------------------------------------
846
847using namespace android;
848
849
850#if 0
851#pragma mark -
852#pragma mark Texture API
853#endif
854
855void glActiveTexture(GLenum texture)
856{
857 ogles_context_t* c = ogles_context_t::get();
858 if (uint32_t(texture-GL_TEXTURE0) > uint32_t(GGL_TEXTURE_UNIT_COUNT)) {
859 ogles_error(c, GL_INVALID_ENUM);
860 return;
861 }
862 c->textures.active = texture - GL_TEXTURE0;
863 c->rasterizer.procs.activeTexture(c, c->textures.active);
864}
865
866void glBindTexture(GLenum target, GLuint texture)
867{
868 ogles_context_t* c = ogles_context_t::get();
Mathias Agopianf319e2f2011-08-18 16:26:21 -0700869 if (target != GL_TEXTURE_2D && target != GL_TEXTURE_EXTERNAL_OES) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800870 ogles_error(c, GL_INVALID_ENUM);
871 return;
872 }
873
874 // Bind or create a texture
Mathias Agopian1473f462009-04-10 14:24:30 -0700875 sp<EGLTextureObject> tex;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800876 if (texture == 0) {
877 // 0 is our local texture object
878 tex = c->textures.defaultTexture;
879 } else {
880 tex = c->surfaceManager->texture(texture);
881 if (ggl_unlikely(tex == 0)) {
882 tex = c->surfaceManager->createTexture(texture);
883 if (tex == 0) {
884 ogles_error(c, GL_OUT_OF_MEMORY);
885 return;
886 }
887 }
888 }
889 bindTextureTmu(c, c->textures.active, texture, tex);
890}
891
892void glGenTextures(GLsizei n, GLuint *textures)
893{
894 ogles_context_t* c = ogles_context_t::get();
895 if (n<0) {
896 ogles_error(c, GL_INVALID_VALUE);
897 return;
898 }
899 // generate unique (shared) texture names
900 c->surfaceManager->getToken(n, textures);
901}
902
903void glDeleteTextures(GLsizei n, const GLuint *textures)
904{
905 ogles_context_t* c = ogles_context_t::get();
906 if (n<0) {
907 ogles_error(c, GL_INVALID_VALUE);
908 return;
909 }
910
911 // If deleting a bound texture, bind this unit to 0
912 for (int t=0 ; t<GGL_TEXTURE_UNIT_COUNT ; t++) {
913 if (c->textures.tmu[t].name == 0)
914 continue;
915 for (int i=0 ; i<n ; i++) {
916 if (textures[i] && (textures[i] == c->textures.tmu[t].name)) {
917 // bind this tmu to texture 0
918 sp<EGLTextureObject> tex(c->textures.defaultTexture);
919 bindTextureTmu(c, t, 0, tex);
920 }
921 }
922 }
923 c->surfaceManager->deleteTextures(n, textures);
924 c->surfaceManager->recycleTokens(n, textures);
925}
926
927void glMultiTexCoord4f(
928 GLenum target, GLfloat s, GLfloat t, GLfloat r, GLfloat q)
929{
930 ogles_context_t* c = ogles_context_t::get();
931 if (uint32_t(target-GL_TEXTURE0) > uint32_t(GGL_TEXTURE_UNIT_COUNT)) {
932 ogles_error(c, GL_INVALID_ENUM);
933 return;
934 }
935 const int tmu = target-GL_TEXTURE0;
936 c->current.texture[tmu].S = gglFloatToFixed(s);
937 c->current.texture[tmu].T = gglFloatToFixed(t);
938 c->current.texture[tmu].R = gglFloatToFixed(r);
939 c->current.texture[tmu].Q = gglFloatToFixed(q);
940}
941
942void glMultiTexCoord4x(
943 GLenum target, GLfixed s, GLfixed t, GLfixed r, GLfixed q)
944{
945 ogles_context_t* c = ogles_context_t::get();
946 if (uint32_t(target-GL_TEXTURE0) > uint32_t(GGL_TEXTURE_UNIT_COUNT)) {
947 ogles_error(c, GL_INVALID_ENUM);
948 return;
949 }
950 const int tmu = target-GL_TEXTURE0;
951 c->current.texture[tmu].S = s;
952 c->current.texture[tmu].T = t;
953 c->current.texture[tmu].R = r;
954 c->current.texture[tmu].Q = q;
955}
956
957void glPixelStorei(GLenum pname, GLint param)
958{
959 ogles_context_t* c = ogles_context_t::get();
960 if ((pname != GL_PACK_ALIGNMENT) && (pname != GL_UNPACK_ALIGNMENT)) {
961 ogles_error(c, GL_INVALID_ENUM);
962 return;
Mathias Agopian1473f462009-04-10 14:24:30 -0700963 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800964 if ((param<=0 || param>8) || (param & (param-1))) {
965 ogles_error(c, GL_INVALID_VALUE);
966 return;
967 }
968 if (pname == GL_PACK_ALIGNMENT)
969 c->textures.packAlignment = param;
970 if (pname == GL_UNPACK_ALIGNMENT)
971 c->textures.unpackAlignment = param;
972}
973
974void glTexEnvf(GLenum target, GLenum pname, GLfloat param)
975{
976 ogles_context_t* c = ogles_context_t::get();
977 c->rasterizer.procs.texEnvi(c, target, pname, GLint(param));
978}
979
980void glTexEnvfv(
981 GLenum target, GLenum pname, const GLfloat *params)
982{
983 ogles_context_t* c = ogles_context_t::get();
984 if (pname == GL_TEXTURE_ENV_MODE) {
985 c->rasterizer.procs.texEnvi(c, target, pname, GLint(*params));
986 return;
987 }
988 if (pname == GL_TEXTURE_ENV_COLOR) {
989 GGLfixed fixed[4];
990 for (int i=0 ; i<4 ; i++)
991 fixed[i] = gglFloatToFixed(params[i]);
992 c->rasterizer.procs.texEnvxv(c, target, pname, fixed);
993 return;
994 }
995 ogles_error(c, GL_INVALID_ENUM);
996}
997
998void glTexEnvx(GLenum target, GLenum pname, GLfixed param)
999{
1000 ogles_context_t* c = ogles_context_t::get();
1001 c->rasterizer.procs.texEnvi(c, target, pname, param);
1002}
1003
1004void glTexEnvxv(
1005 GLenum target, GLenum pname, const GLfixed *params)
1006{
1007 ogles_context_t* c = ogles_context_t::get();
1008 c->rasterizer.procs.texEnvxv(c, target, pname, params);
1009}
1010
1011void glTexParameteriv(
1012 GLenum target, GLenum pname, const GLint* params)
1013{
1014 ogles_context_t* c = ogles_context_t::get();
Mathias Agopianf319e2f2011-08-18 16:26:21 -07001015 if (target != GL_TEXTURE_2D && target != GL_TEXTURE_EXTERNAL_OES) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001016 ogles_error(c, GL_INVALID_ENUM);
1017 return;
1018 }
1019
1020 EGLTextureObject* textureObject = c->textures.tmu[c->textures.active].texture;
1021 switch (pname) {
1022 case GL_TEXTURE_CROP_RECT_OES:
1023 memcpy(textureObject->crop_rect, params, 4*sizeof(GLint));
1024 break;
1025 default:
Mathias Agopianaaf4b6b2009-06-22 18:04:45 -07001026 texParameterx(target, pname, GLfixed(params[0]), c);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001027 return;
1028 }
1029}
1030
1031void glTexParameterf(
1032 GLenum target, GLenum pname, GLfloat param)
1033{
1034 ogles_context_t* c = ogles_context_t::get();
1035 texParameterx(target, pname, GLfixed(param), c);
1036}
1037
1038void glTexParameterx(
1039 GLenum target, GLenum pname, GLfixed param)
1040{
1041 ogles_context_t* c = ogles_context_t::get();
1042 texParameterx(target, pname, param, c);
1043}
1044
Mathias Agopianaaf4b6b2009-06-22 18:04:45 -07001045void glTexParameteri(
1046 GLenum target, GLenum pname, GLint param)
1047{
1048 ogles_context_t* c = ogles_context_t::get();
1049 texParameterx(target, pname, GLfixed(param), c);
1050}
1051
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001052// ----------------------------------------------------------------------------
1053#if 0
1054#pragma mark -
1055#endif
1056
1057void glCompressedTexImage2D(
1058 GLenum target, GLint level, GLenum internalformat,
1059 GLsizei width, GLsizei height, GLint border,
1060 GLsizei imageSize, const GLvoid *data)
1061{
1062 ogles_context_t* c = ogles_context_t::get();
1063 if (target != GL_TEXTURE_2D) {
1064 ogles_error(c, GL_INVALID_ENUM);
1065 return;
1066 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001067 if (width<0 || height<0 || border!=0) {
1068 ogles_error(c, GL_INVALID_VALUE);
1069 return;
1070 }
1071
1072 // "uncompress" the texture since pixelflinger doesn't support
Mathias Agopian1473f462009-04-10 14:24:30 -07001073 // any compressed texture format natively.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001074 GLenum format;
1075 GLenum type;
1076 switch (internalformat) {
1077 case GL_PALETTE8_RGB8_OES:
1078 case GL_PALETTE4_RGB8_OES:
1079 format = GL_RGB;
1080 type = GL_UNSIGNED_BYTE;
1081 break;
1082 case GL_PALETTE8_RGBA8_OES:
1083 case GL_PALETTE4_RGBA8_OES:
1084 format = GL_RGBA;
1085 type = GL_UNSIGNED_BYTE;
1086 break;
1087 case GL_PALETTE8_R5_G6_B5_OES:
1088 case GL_PALETTE4_R5_G6_B5_OES:
1089 format = GL_RGB;
1090 type = GL_UNSIGNED_SHORT_5_6_5;
1091 break;
1092 case GL_PALETTE8_RGBA4_OES:
1093 case GL_PALETTE4_RGBA4_OES:
1094 format = GL_RGBA;
1095 type = GL_UNSIGNED_SHORT_4_4_4_4;
1096 break;
1097 case GL_PALETTE8_RGB5_A1_OES:
1098 case GL_PALETTE4_RGB5_A1_OES:
1099 format = GL_RGBA;
1100 type = GL_UNSIGNED_SHORT_5_5_5_1;
1101 break;
Mathias Agopiand1f73a22010-02-01 18:24:52 -08001102#ifdef GL_OES_compressed_ETC1_RGB8_texture
1103 case GL_ETC1_RGB8_OES:
1104 format = GL_RGB;
1105 type = GL_UNSIGNED_BYTE;
1106 break;
1107#endif
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001108 default:
1109 ogles_error(c, GL_INVALID_ENUM);
1110 return;
1111 }
1112
1113 if (!data || !width || !height) {
1114 // unclear if this is an error or not...
1115 return;
1116 }
1117
1118 int32_t size;
1119 GGLSurface* surface;
Mathias Agopiand1f73a22010-02-01 18:24:52 -08001120
1121#ifdef GL_OES_compressed_ETC1_RGB8_texture
1122 if (internalformat == GL_ETC1_RGB8_OES) {
1123 GLsizei compressedSize = etc1_get_encoded_data_size(width, height);
1124 if (compressedSize > imageSize) {
1125 ogles_error(c, GL_INVALID_VALUE);
1126 return;
1127 }
1128 int error = createTextureSurface(c, &surface, &size,
1129 level, format, type, width, height);
1130 if (error) {
1131 ogles_error(c, error);
1132 return;
1133 }
1134 if (etc1_decode_image(
1135 (const etc1_byte*)data,
1136 (etc1_byte*)surface->data,
Jack Palevich019fe732010-02-02 22:50:39 -08001137 width, height, 3, surface->stride*3) != 0) {
Mathias Agopiand1f73a22010-02-01 18:24:52 -08001138 ogles_error(c, GL_INVALID_OPERATION);
1139 }
1140 return;
1141 }
1142#endif
1143
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001144 // all mipmap levels are specified at once.
1145 const int numLevels = level<0 ? -level : 1;
Jack Palevichfb5ea2e2009-09-10 17:13:28 -07001146
1147 if (dataSizePalette4(numLevels, width, height, format) > imageSize) {
1148 ogles_error(c, GL_INVALID_VALUE);
1149 return;
1150 }
1151
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001152 for (int i=0 ; i<numLevels ; i++) {
1153 int lod_w = (width >> i) ? : 1;
1154 int lod_h = (height >> i) ? : 1;
1155 int error = createTextureSurface(c, &surface, &size,
1156 i, format, type, lod_w, lod_h);
1157 if (error) {
1158 ogles_error(c, error);
1159 return;
1160 }
1161 decodePalette4(data, i, width, height,
1162 surface->data, surface->stride, internalformat);
1163 }
1164}
1165
1166
1167void glTexImage2D(
1168 GLenum target, GLint level, GLint internalformat,
1169 GLsizei width, GLsizei height, GLint border,
1170 GLenum format, GLenum type, const GLvoid *pixels)
1171{
1172 ogles_context_t* c = ogles_context_t::get();
Mathias Agopian1473f462009-04-10 14:24:30 -07001173 if (target != GL_TEXTURE_2D) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001174 ogles_error(c, GL_INVALID_ENUM);
1175 return;
1176 }
1177 if (width<0 || height<0 || border!=0 || level < 0) {
1178 ogles_error(c, GL_INVALID_VALUE);
1179 return;
1180 }
Mathias Agopian1473f462009-04-10 14:24:30 -07001181 if (format != (GLenum)internalformat) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001182 ogles_error(c, GL_INVALID_OPERATION);
1183 return;
1184 }
1185 if (validFormatType(c, format, type)) {
1186 return;
1187 }
1188
1189 int32_t size = 0;
1190 GGLSurface* surface = 0;
Mathias Agopian1473f462009-04-10 14:24:30 -07001191 int error = createTextureSurface(c, &surface, &size,
1192 level, format, type, width, height);
1193 if (error) {
1194 ogles_error(c, error);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001195 return;
1196 }
1197
1198 if (pixels) {
1199 const int32_t formatIdx = convertGLPixelFormat(format, type);
1200 const GGLFormat& pixelFormat(c->rasterizer.formats[formatIdx]);
1201 const int32_t align = c->textures.unpackAlignment-1;
1202 const int32_t bpr = ((width * pixelFormat.size) + align) & ~align;
1203 const size_t size = bpr * height;
1204 const int32_t stride = bpr / pixelFormat.size;
1205
1206 GGLSurface userSurface;
1207 userSurface.version = sizeof(userSurface);
1208 userSurface.width = width;
1209 userSurface.height = height;
1210 userSurface.stride = stride;
1211 userSurface.format = formatIdx;
1212 userSurface.compressedFormat = 0;
1213 userSurface.data = (GLubyte*)pixels;
1214
Mathias Agopian1473f462009-04-10 14:24:30 -07001215 int err = copyPixels(c, *surface, 0, 0, userSurface, 0, 0, width, height);
1216 if (err) {
1217 ogles_error(c, err);
1218 return;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001219 }
Mathias Agopian1473f462009-04-10 14:24:30 -07001220 generateMipmap(c, level);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001221 }
1222}
1223
1224// ----------------------------------------------------------------------------
1225
1226void glCompressedTexSubImage2D(
1227 GLenum target, GLint level, GLint xoffset,
1228 GLint yoffset, GLsizei width, GLsizei height,
1229 GLenum format, GLsizei imageSize,
1230 const GLvoid *data)
1231{
1232 ogles_context_t* c = ogles_context_t::get();
1233 ogles_error(c, GL_INVALID_ENUM);
1234}
1235
1236void glTexSubImage2D(
1237 GLenum target, GLint level, GLint xoffset,
1238 GLint yoffset, GLsizei width, GLsizei height,
1239 GLenum format, GLenum type, const GLvoid *pixels)
1240{
1241 ogles_context_t* c = ogles_context_t::get();
1242 if (target != GL_TEXTURE_2D) {
1243 ogles_error(c, GL_INVALID_ENUM);
1244 return;
1245 }
1246 if (xoffset<0 || yoffset<0 || width<0 || height<0 || level<0) {
1247 ogles_error(c, GL_INVALID_VALUE);
1248 return;
1249 }
1250 if (validFormatType(c, format, type)) {
1251 return;
1252 }
1253
1254 // find out which texture is bound to the current unit
1255 const int active = c->textures.active;
1256 EGLTextureObject* tex = c->textures.tmu[active].texture;
1257 const GGLSurface& surface(tex->mip(level));
1258
1259 if (!tex->internalformat || tex->direct) {
1260 ogles_error(c, GL_INVALID_OPERATION);
1261 return;
1262 }
Mathias Agopian788119062009-10-16 18:34:31 -07001263
1264 if (format != tex->internalformat) {
1265 ogles_error(c, GL_INVALID_OPERATION);
1266 return;
1267 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001268 if ((xoffset + width > GLsizei(surface.width)) ||
1269 (yoffset + height > GLsizei(surface.height))) {
1270 ogles_error(c, GL_INVALID_VALUE);
1271 return;
1272 }
1273 if (!width || !height) {
1274 return; // okay, but no-op.
1275 }
1276
1277 // figure out the size we need as well as the stride
1278 const int32_t formatIdx = convertGLPixelFormat(format, type);
1279 if (formatIdx == 0) { // we don't know what to do with this
1280 ogles_error(c, GL_INVALID_OPERATION);
1281 return;
1282 }
1283
1284 const GGLFormat& pixelFormat(c->rasterizer.formats[formatIdx]);
1285 const int32_t align = c->textures.unpackAlignment-1;
1286 const int32_t bpr = ((width * pixelFormat.size) + align) & ~align;
1287 const size_t size = bpr * height;
1288 const int32_t stride = bpr / pixelFormat.size;
1289 GGLSurface userSurface;
1290 userSurface.version = sizeof(userSurface);
1291 userSurface.width = width;
1292 userSurface.height = height;
1293 userSurface.stride = stride;
1294 userSurface.format = formatIdx;
1295 userSurface.compressedFormat = 0;
1296 userSurface.data = (GLubyte*)pixels;
1297
1298 int err = copyPixels(c,
1299 surface, xoffset, yoffset,
Mathias Agopian1473f462009-04-10 14:24:30 -07001300 userSurface, 0, 0, width, height);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001301 if (err) {
1302 ogles_error(c, err);
1303 return;
1304 }
1305
1306 generateMipmap(c, level);
1307
1308 // since we only changed the content of the texture, we don't need
1309 // to call bindTexture on the main rasterizer.
1310}
1311
1312// ----------------------------------------------------------------------------
1313
1314void glCopyTexImage2D(
1315 GLenum target, GLint level, GLenum internalformat,
1316 GLint x, GLint y, GLsizei width, GLsizei height,
1317 GLint border)
1318{
1319 ogles_context_t* c = ogles_context_t::get();
1320 if (target != GL_TEXTURE_2D) {
1321 ogles_error(c, GL_INVALID_ENUM);
1322 return;
1323 }
1324 if (internalformat<GL_ALPHA || internalformat>GL_LUMINANCE_ALPHA) {
1325 ogles_error(c, GL_INVALID_ENUM);
1326 return;
1327 }
1328 if (width<0 || height<0 || border!=0 || level<0) {
1329 ogles_error(c, GL_INVALID_VALUE);
1330 return;
1331 }
1332
1333 GLenum format = 0;
1334 GLenum type = GL_UNSIGNED_BYTE;
1335 const GGLSurface& cbSurface = c->rasterizer.state.buffers.color.s;
1336 const int cbFormatIdx = cbSurface.format;
1337 switch (cbFormatIdx) {
1338 case GGL_PIXEL_FORMAT_RGB_565:
1339 type = GL_UNSIGNED_SHORT_5_6_5;
1340 break;
1341 case GGL_PIXEL_FORMAT_RGBA_5551:
1342 type = GL_UNSIGNED_SHORT_5_5_5_1;
1343 break;
1344 case GGL_PIXEL_FORMAT_RGBA_4444:
1345 type = GL_UNSIGNED_SHORT_4_4_4_4;
1346 break;
1347 }
1348 switch (internalformat) {
1349 case GL_ALPHA:
1350 case GL_LUMINANCE_ALPHA:
1351 case GL_LUMINANCE:
1352 type = GL_UNSIGNED_BYTE;
Mathias Agopian1473f462009-04-10 14:24:30 -07001353 break;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001354 }
1355
1356 // figure out the format to use for the new texture
1357 switch (cbFormatIdx) {
1358 case GGL_PIXEL_FORMAT_RGBA_8888:
1359 case GGL_PIXEL_FORMAT_A_8:
1360 case GGL_PIXEL_FORMAT_RGBA_5551:
1361 case GGL_PIXEL_FORMAT_RGBA_4444:
1362 format = internalformat;
Mathias Agopian1473f462009-04-10 14:24:30 -07001363 break;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001364 case GGL_PIXEL_FORMAT_RGBX_8888:
1365 case GGL_PIXEL_FORMAT_RGB_888:
1366 case GGL_PIXEL_FORMAT_RGB_565:
1367 case GGL_PIXEL_FORMAT_L_8:
1368 switch (internalformat) {
1369 case GL_LUMINANCE:
1370 case GL_RGB:
1371 format = internalformat;
Mathias Agopian1473f462009-04-10 14:24:30 -07001372 break;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001373 }
1374 break;
1375 }
1376
1377 if (format == 0) {
1378 // invalid combination
1379 ogles_error(c, GL_INVALID_ENUM);
1380 return;
1381 }
1382
1383 // create the new texture...
1384 int32_t size;
1385 GGLSurface* surface;
1386 int error = createTextureSurface(c, &surface, &size,
1387 level, format, type, width, height);
1388 if (error) {
1389 ogles_error(c, error);
1390 return;
1391 }
Mathias Agopian1473f462009-04-10 14:24:30 -07001392
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001393 // The bottom row is stored first in textures
1394 GGLSurface txSurface(*surface);
1395 txSurface.stride = -txSurface.stride;
1396
1397 // (x,y) is the lower-left corner of colorBuffer
1398 y = cbSurface.height - (y + height);
1399
Mathias Agopian9c847202010-02-01 13:45:08 -08001400 /* The GLES spec says:
1401 * If any of the pixels within the specified rectangle are outside
1402 * the framebuffer associated with the current rendering context,
1403 * then the values obtained for those pixels are undefined.
1404 */
1405 if (x+width > GLint(cbSurface.width))
1406 width = cbSurface.width - x;
1407
1408 if (y+height > GLint(cbSurface.height))
1409 height = cbSurface.height - y;
1410
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001411 int err = copyPixels(c,
1412 txSurface, 0, 0,
Mathias Agopian9c847202010-02-01 13:45:08 -08001413 cbSurface, x, y, width, height);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001414 if (err) {
1415 ogles_error(c, err);
1416 }
1417
1418 generateMipmap(c, level);
1419}
1420
1421void glCopyTexSubImage2D(
1422 GLenum target, GLint level, GLint xoffset, GLint yoffset,
1423 GLint x, GLint y, GLsizei width, GLsizei height)
1424{
1425 ogles_context_t* c = ogles_context_t::get();
1426 if (target != GL_TEXTURE_2D) {
1427 ogles_error(c, GL_INVALID_ENUM);
1428 return;
1429 }
1430 if (xoffset<0 || yoffset<0 || width<0 || height<0 || level<0) {
1431 ogles_error(c, GL_INVALID_VALUE);
1432 return;
1433 }
1434 if (!width || !height) {
1435 return; // okay, but no-op.
1436 }
1437
1438 // find out which texture is bound to the current unit
1439 const int active = c->textures.active;
1440 EGLTextureObject* tex = c->textures.tmu[active].texture;
1441 const GGLSurface& surface(tex->mip(level));
1442
1443 if (!tex->internalformat) {
1444 ogles_error(c, GL_INVALID_OPERATION);
1445 return;
1446 }
1447 if ((xoffset + width > GLsizei(surface.width)) ||
1448 (yoffset + height > GLsizei(surface.height))) {
1449 ogles_error(c, GL_INVALID_VALUE);
1450 return;
1451 }
1452
1453 // The bottom row is stored first in textures
1454 GGLSurface txSurface(surface);
1455 txSurface.stride = -txSurface.stride;
1456
1457 // (x,y) is the lower-left corner of colorBuffer
1458 const GGLSurface& cbSurface = c->rasterizer.state.buffers.color.s;
1459 y = cbSurface.height - (y + height);
1460
Mathias Agopian9c847202010-02-01 13:45:08 -08001461 /* The GLES spec says:
1462 * If any of the pixels within the specified rectangle are outside
1463 * the framebuffer associated with the current rendering context,
1464 * then the values obtained for those pixels are undefined.
1465 */
1466 if (x+width > GLint(cbSurface.width))
1467 width = cbSurface.width - x;
1468
1469 if (y+height > GLint(cbSurface.height))
1470 height = cbSurface.height - y;
1471
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001472 int err = copyPixels(c,
Jack Palevich3e5a5822010-03-12 17:11:34 -08001473 txSurface, xoffset, yoffset,
Mathias Agopian1473f462009-04-10 14:24:30 -07001474 cbSurface, x, y, width, height);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001475 if (err) {
1476 ogles_error(c, err);
1477 return;
1478 }
1479
1480 generateMipmap(c, level);
1481}
1482
1483void glReadPixels(
1484 GLint x, GLint y, GLsizei width, GLsizei height,
1485 GLenum format, GLenum type, GLvoid *pixels)
1486{
1487 ogles_context_t* c = ogles_context_t::get();
1488 if ((format != GL_RGBA) && (format != GL_RGB)) {
1489 ogles_error(c, GL_INVALID_ENUM);
1490 return;
1491 }
1492 if ((type != GL_UNSIGNED_BYTE) && (type != GL_UNSIGNED_SHORT_5_6_5)) {
1493 ogles_error(c, GL_INVALID_ENUM);
1494 return;
1495 }
1496 if (width<0 || height<0) {
1497 ogles_error(c, GL_INVALID_VALUE);
1498 return;
1499 }
Mike Playle81d42592010-01-29 09:52:22 +00001500 if (x<0 || y<0) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001501 ogles_error(c, GL_INVALID_VALUE);
1502 return;
1503 }
1504
1505 int32_t formatIdx = GGL_PIXEL_FORMAT_NONE;
1506 if ((format == GL_RGBA) && (type == GL_UNSIGNED_BYTE)) {
1507 formatIdx = GGL_PIXEL_FORMAT_RGBA_8888;
1508 } else if ((format == GL_RGB) && (type == GL_UNSIGNED_SHORT_5_6_5)) {
1509 formatIdx = GGL_PIXEL_FORMAT_RGB_565;
1510 } else {
1511 ogles_error(c, GL_INVALID_OPERATION);
1512 return;
1513 }
1514
1515 const GGLSurface& readSurface = c->rasterizer.state.buffers.read.s;
1516 if ((x+width > GLint(readSurface.width)) ||
1517 (y+height > GLint(readSurface.height))) {
1518 ogles_error(c, GL_INVALID_VALUE);
1519 return;
1520 }
1521
1522 const GGLFormat& pixelFormat(c->rasterizer.formats[formatIdx]);
1523 const int32_t align = c->textures.packAlignment-1;
1524 const int32_t bpr = ((width * pixelFormat.size) + align) & ~align;
1525 const int32_t stride = bpr / pixelFormat.size;
1526
1527 GGLSurface userSurface;
1528 userSurface.version = sizeof(userSurface);
1529 userSurface.width = width;
1530 userSurface.height = height;
1531 userSurface.stride = -stride; // bottom row is transfered first
1532 userSurface.format = formatIdx;
1533 userSurface.compressedFormat = 0;
1534 userSurface.data = (GLubyte*)pixels;
1535
1536 // use pixel-flinger to handle all the conversions
1537 GGLContext* ggl = getRasterizer(c);
1538 if (!ggl) {
1539 // the only reason this would fail is because we ran out of memory
1540 ogles_error(c, GL_OUT_OF_MEMORY);
1541 return;
1542 }
1543
Mathias Agopian1473f462009-04-10 14:24:30 -07001544 ggl->colorBuffer(ggl, &userSurface); // destination is user buffer
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001545 ggl->bindTexture(ggl, &readSurface); // source is read-buffer
1546 ggl->texCoord2i(ggl, x, readSurface.height - (y + height));
1547 ggl->recti(ggl, 0, 0, width, height);
1548}
1549
1550// ----------------------------------------------------------------------------
1551#if 0
1552#pragma mark -
1553#pragma mark DrawTexture Extension
1554#endif
1555
1556void glDrawTexsvOES(const GLshort* coords) {
1557 ogles_context_t* c = ogles_context_t::get();
1558 drawTexiOES(coords[0], coords[1], coords[2], coords[3], coords[4], c);
1559}
1560void glDrawTexivOES(const GLint* coords) {
1561 ogles_context_t* c = ogles_context_t::get();
1562 drawTexiOES(coords[0], coords[1], coords[2], coords[3], coords[4], c);
1563}
1564void glDrawTexsOES(GLshort x , GLshort y, GLshort z, GLshort w, GLshort h) {
1565 ogles_context_t* c = ogles_context_t::get();
1566 drawTexiOES(x, y, z, w, h, c);
1567}
1568void glDrawTexiOES(GLint x, GLint y, GLint z, GLint w, GLint h) {
1569 ogles_context_t* c = ogles_context_t::get();
1570 drawTexiOES(x, y, z, w, h, c);
1571}
1572
1573void glDrawTexfvOES(const GLfloat* coords) {
1574 ogles_context_t* c = ogles_context_t::get();
1575 drawTexxOES(
1576 gglFloatToFixed(coords[0]),
1577 gglFloatToFixed(coords[1]),
1578 gglFloatToFixed(coords[2]),
1579 gglFloatToFixed(coords[3]),
1580 gglFloatToFixed(coords[4]),
1581 c);
1582}
1583void glDrawTexxvOES(const GLfixed* coords) {
1584 ogles_context_t* c = ogles_context_t::get();
1585 drawTexxOES(coords[0], coords[1], coords[2], coords[3], coords[4], c);
1586}
1587void glDrawTexfOES(GLfloat x, GLfloat y, GLfloat z, GLfloat w, GLfloat h){
1588 ogles_context_t* c = ogles_context_t::get();
1589 drawTexxOES(
1590 gglFloatToFixed(x), gglFloatToFixed(y), gglFloatToFixed(z),
1591 gglFloatToFixed(w), gglFloatToFixed(h),
1592 c);
1593}
1594void glDrawTexxOES(GLfixed x, GLfixed y, GLfixed z, GLfixed w, GLfixed h) {
1595 ogles_context_t* c = ogles_context_t::get();
1596 drawTexxOES(x, y, z, w, h, c);
1597}
Mathias Agopian1473f462009-04-10 14:24:30 -07001598
1599// ----------------------------------------------------------------------------
1600#if 0
1601#pragma mark -
1602#pragma mark EGL Image Extension
1603#endif
1604
1605void glEGLImageTargetTexture2DOES(GLenum target, GLeglImageOES image)
1606{
1607 ogles_context_t* c = ogles_context_t::get();
Mathias Agopianf319e2f2011-08-18 16:26:21 -07001608 if (target != GL_TEXTURE_2D && target != GL_TEXTURE_EXTERNAL_OES) {
Mathias Agopian1473f462009-04-10 14:24:30 -07001609 ogles_error(c, GL_INVALID_ENUM);
1610 return;
1611 }
1612
Mathias Agopian42d99d22010-02-04 17:04:53 -08001613 if (image == EGL_NO_IMAGE_KHR) {
1614 ogles_error(c, GL_INVALID_VALUE);
1615 return;
1616 }
1617
Iliyan Malchevb2a153a2011-05-01 11:33:26 -07001618 ANativeWindowBuffer* native_buffer = (ANativeWindowBuffer*)image;
Mathias Agopian1473f462009-04-10 14:24:30 -07001619 if (native_buffer->common.magic != ANDROID_NATIVE_BUFFER_MAGIC) {
1620 ogles_error(c, GL_INVALID_VALUE);
1621 return;
1622 }
Iliyan Malchevb2a153a2011-05-01 11:33:26 -07001623 if (native_buffer->common.version != sizeof(ANativeWindowBuffer)) {
Mathias Agopian1473f462009-04-10 14:24:30 -07001624 ogles_error(c, GL_INVALID_VALUE);
1625 return;
1626 }
1627
Mathias Agopian1473f462009-04-10 14:24:30 -07001628 // bind it to the texture unit
1629 sp<EGLTextureObject> tex = getAndBindActiveTextureObject(c);
Mathias Agopiandff8e582009-05-04 14:17:04 -07001630 tex->setImage(native_buffer);
Mathias Agopian1473f462009-04-10 14:24:30 -07001631}
1632
1633void glEGLImageTargetRenderbufferStorageOES(GLenum target, GLeglImageOES image)
1634{
Mathias Agopian42d99d22010-02-04 17:04:53 -08001635 ogles_context_t* c = ogles_context_t::get();
1636 if (target != GL_RENDERBUFFER_OES) {
1637 ogles_error(c, GL_INVALID_ENUM);
1638 return;
1639 }
1640
1641 if (image == EGL_NO_IMAGE_KHR) {
1642 ogles_error(c, GL_INVALID_VALUE);
1643 return;
1644 }
1645
Iliyan Malchevb2a153a2011-05-01 11:33:26 -07001646 ANativeWindowBuffer* native_buffer = (ANativeWindowBuffer*)image;
Mathias Agopian42d99d22010-02-04 17:04:53 -08001647 if (native_buffer->common.magic != ANDROID_NATIVE_BUFFER_MAGIC) {
1648 ogles_error(c, GL_INVALID_VALUE);
1649 return;
1650 }
Iliyan Malchevb2a153a2011-05-01 11:33:26 -07001651 if (native_buffer->common.version != sizeof(ANativeWindowBuffer)) {
Mathias Agopian42d99d22010-02-04 17:04:53 -08001652 ogles_error(c, GL_INVALID_VALUE);
1653 return;
1654 }
1655
1656 // well, we're not supporting this extension anyways
Mathias Agopian1473f462009-04-10 14:24:30 -07001657}