• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 #include "gles2context.h"
2 
3 //#undef LOGD
4 //#define LOGD(...)
5 
6 #define API_ENTRY
7 #define CALL_GL_API(NAME,...) LOGD("?"#NAME); assert(0);
8 #define CALL_GL_API_RETURN(NAME,...) LOGD("?"#NAME); assert(0); return 0;
9 
AllocTexture()10 static inline GGLTexture * AllocTexture()
11 {
12    GGLTexture * tex = (GGLTexture *)calloc(1, sizeof(GGLTexture));
13    tex->minFilter = GGLTexture::GGL_LINEAR; // should be NEAREST_ MIPMAP_LINEAR
14    tex->magFilter = GGLTexture::GGL_LINEAR;
15    return tex;
16 }
17 
InitializeTextures()18 void GLES2Context::InitializeTextures()
19 {
20    tex.textures = std::map<GLuint, GGLTexture *>(); // the entire struct has been zeroed in constructor
21    tex.tex2D = AllocTexture();
22    tex.textures[GL_TEXTURE_2D] = tex.tex2D;
23    tex.texCube = AllocTexture();
24    tex.textures[GL_TEXTURE_CUBE_MAP] = tex.texCube;
25    for (unsigned i = 0; i < GGL_MAXCOMBINEDTEXTUREIMAGEUNITS; i++) {
26       tex.tmus[i] = NULL;
27       tex.sampler2tmu[i] = NULL;
28    }
29 
30    tex.active = 0;
31 
32    tex.free = max(GL_TEXTURE_2D, GL_TEXTURE_CUBE_MAP) + 1;
33 
34    tex.tex2D->format = GGL_PIXEL_FORMAT_RGBA_8888;
35    tex.tex2D->type = GL_TEXTURE_2D;
36    tex.tex2D->levelCount = 1;
37    tex.tex2D->wrapS = tex.tex2D->wrapT = GGLTexture::GGL_REPEAT;
38    tex.tex2D->minFilter = tex.tex2D->magFilter = GGLTexture::GGL_NEAREST;
39    tex.tex2D->width = tex.tex2D->height = 1;
40    tex.tex2D->levels = malloc(4);
41    *(unsigned *)tex.tex2D->levels = 0xff000000;
42 
43 
44    tex.texCube->format = GGL_PIXEL_FORMAT_RGBA_8888;
45    tex.texCube->type = GL_TEXTURE_CUBE_MAP;
46    tex.texCube->levelCount = 1;
47    tex.texCube->wrapS = tex.texCube->wrapT = GGLTexture::GGL_REPEAT;
48    tex.texCube->minFilter = tex.texCube->magFilter = GGLTexture::GGL_NEAREST;
49    tex.texCube->width = tex.texCube->height = 1;
50    tex.texCube->levels = malloc(4 * 6);
51    static unsigned texels [6] = {0xff0000ff, 0xff00ff00, 0xffff0000,
52                                  0xff00ffff, 0xffffff00, 0xffff00ff
53                                 };
54    memcpy(tex.texCube->levels, texels, sizeof texels);
55 
56    //texture.levelCount = GenerateMipmaps(texture.levels, texture.width, texture.height);
57 
58    //    static unsigned texels [6] = {0xff0000ff, 0xff00ff00, 0xffff0000,
59    //    0xff00ffff, 0xffffff00, 0xffff00ff};
60    //    memcpy(texture.levels[0], texels, sizeof texels);
61    //    texture.format = GGL_PIXEL_FORMAT_RGBA_8888;
62    //    texture.width = texture.height = 1;
63    //texture.height /= 6;
64    //texture.type = GL_TEXTURE_CUBE_MAP;
65 
66    tex.unpack = 4;
67 }
68 
UpdateSampler(GGLInterface * iface,unsigned tmu)69 void GLES2Context::TextureState::UpdateSampler(GGLInterface * iface, unsigned tmu)
70 {
71    for (unsigned i = 0; i < GGL_MAXCOMBINEDTEXTUREIMAGEUNITS; i++)
72       if (tmu == sampler2tmu[i])
73          iface->SetSampler(iface, i, tmus[tmu]);
74 }
75 
UninitializeTextures()76 void GLES2Context::UninitializeTextures()
77 {
78    for (std::map<GLuint, GGLTexture *>::iterator it = tex.textures.begin(); it != tex.textures.end(); it++) {
79       if (!it->second)
80          continue;
81       free(it->second->levels);
82       free(it->second);
83    }
84 }
85 
GetFormatAndBytesPerPixel(const GLenum format,unsigned * bytesPerPixel,GGLPixelFormat * texFormat)86 static inline void GetFormatAndBytesPerPixel(const GLenum format, unsigned * bytesPerPixel,
87       GGLPixelFormat * texFormat)
88 {
89    switch (format) {
90    case GL_ALPHA:
91       *texFormat = GGL_PIXEL_FORMAT_A_8;
92       *bytesPerPixel = 1;
93       break;
94    case GL_LUMINANCE:
95       *texFormat = GGL_PIXEL_FORMAT_L_8;
96       *bytesPerPixel = 1;
97       break;
98    case GL_LUMINANCE_ALPHA:
99       *texFormat = GGL_PIXEL_FORMAT_LA_88;
100       *bytesPerPixel = 2;
101       break;
102    case GL_RGB:
103       *texFormat = GGL_PIXEL_FORMAT_RGB_888;
104       *bytesPerPixel = 3;
105       break;
106    case GL_RGBA:
107       *texFormat = GGL_PIXEL_FORMAT_RGBA_8888;
108       *bytesPerPixel = 4;
109       break;
110 
111       // internal formats to avoid conversion
112    case GL_UNSIGNED_SHORT_5_6_5:
113       *texFormat = GGL_PIXEL_FORMAT_RGB_565;
114       *bytesPerPixel = 2;
115       break;
116 
117    default:
118       assert(0);
119       return;
120    }
121 }
122 
CopyTexture(char * dst,const char * src,const unsigned bytesPerPixel,const unsigned sx,const unsigned sy,const unsigned sw,const unsigned dx,const unsigned dy,const unsigned dw,const unsigned w,const unsigned h)123 static inline void CopyTexture(char * dst, const char * src, const unsigned bytesPerPixel,
124                                const unsigned sx, const unsigned sy,  const unsigned sw,
125                                const unsigned dx, const unsigned dy, const unsigned dw,
126                                const unsigned w, const unsigned h)
127 {
128    const unsigned bpp = bytesPerPixel;
129    if (dw == sw && dw == w && sx == 0 && dx == 0)
130       memcpy(dst + dy * dw * bpp, src + sy * sw * bpp, w * h * bpp);
131    else
132       for (unsigned y = 0; y < h; y++)
133          memcpy(dst + ((dy + y) * dw + dx) * bpp, src + ((sy + y) * sw + sx) * bpp, w * bpp);
134 }
135 
glActiveTexture(GLenum texture)136 void glActiveTexture(GLenum texture)
137 {
138    GLES2_GET_CONST_CONTEXT(ctx);
139    unsigned index = texture - GL_TEXTURE0;
140    assert(NELEM(ctx->tex.tmus) > index);
141 //   LOGD("agl2: glActiveTexture %u", index);
142    ctx->tex.active = index;
143 }
144 
glBindTexture(GLenum target,GLuint texture)145 void glBindTexture(GLenum target, GLuint texture)
146 {
147    GLES2_GET_CONST_CONTEXT(ctx);
148 //   LOGD("agl2: glBindTexture target=0x%.4X texture=%u active=%u", target, texture, ctx->tex.active);
149    std::map<GLuint, GGLTexture *>::iterator it = ctx->tex.textures.find(texture);
150    GGLTexture * tex = NULL;
151    if (it != ctx->tex.textures.end()) {
152       tex = it->second;
153       if (!tex) {
154          tex = AllocTexture();
155          tex->type = target;
156          it->second = tex;
157 //         LOGD("agl2: glBindTexture allocTexture");
158       }
159 //      else
160 //         LOGD("agl2: glBindTexture bind existing texture");
161       assert(target == tex->type);
162    } else if (0 == texture) {
163       if (GL_TEXTURE_2D == target)
164       {
165          tex = ctx->tex.tex2D;
166 //         LOGD("agl2: glBindTexture bind default tex2D");
167       }
168       else if (GL_TEXTURE_CUBE_MAP == target)
169       {
170          tex = ctx->tex.texCube;
171 //         LOGD("agl2: glBindTexture bind default texCube");
172       }
173       else
174          assert(0);
175    } else {
176       if (texture <= ctx->tex.free)
177          ctx->tex.free = texture + 1;
178       tex = AllocTexture();
179       tex->type = target;
180       ctx->tex.textures[texture] = tex;
181 //      LOGD("agl2: glBindTexture new texture=%u", texture);
182    }
183    ctx->tex.tmus[ctx->tex.active] = tex;
184 //   LOGD("agl2: glBindTexture format=0x%.2X w=%u h=%u levels=%p", tex->format,
185 //      tex->width, tex->height, tex->levels);
186    ctx->tex.UpdateSampler(ctx->iface, ctx->tex.active);
187 }
188 
API_ENTRY(glCompressedTexImage2D)189 void API_ENTRY(glCompressedTexImage2D)(GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLsizei imageSize, const GLvoid* data)
190 {
191    CALL_GL_API(glCompressedTexImage2D, target, level, internalformat, width, height, border, imageSize, data);
192 }
193 
API_ENTRY(glCompressedTexSubImage2D)194 void API_ENTRY(glCompressedTexSubImage2D)(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const GLvoid* data)
195 {
196    CALL_GL_API(glCompressedTexSubImage2D, target, level, xoffset, yoffset, width, height, format, imageSize, data);
197 }
198 
glCopyTexImage2D(GLenum target,GLint level,GLenum internalformat,GLint x,GLint y,GLsizei width,GLsizei height,GLint border)199 void glCopyTexImage2D(GLenum target, GLint level, GLenum internalformat,
200                       GLint x, GLint y, GLsizei width, GLsizei height, GLint border)
201 {
202    GLES2_GET_CONST_CONTEXT(ctx);
203 //   LOGD("agl2: glCopyTexImage2D target=0x%.4X internalformat=0x%.4X", target, internalformat);
204 //   LOGD("x=%d y=%d width=%d height=%d border=%d level=%d ", x, y, width, height, border, level);
205    assert(0 == border);
206    assert(0 == level);
207    unsigned bytesPerPixel = 0;
208    GGLPixelFormat texFormat = GGL_PIXEL_FORMAT_UNKNOWN;
209    GetFormatAndBytesPerPixel(internalformat, &bytesPerPixel, &texFormat);
210 
211    assert(texFormat == ctx->rasterizer.frameSurface.format);
212 //   LOGD("texFormat=0x%.2X bytesPerPixel=%d \n", texFormat, bytesPerPixel);
213    unsigned offset = 0, size = width * height * bytesPerPixel, totalSize = size;
214 
215    assert(ctx->tex.tmus[ctx->tex.active]);
216    assert(y + height <= ctx->rasterizer.frameSurface.height);
217    assert(x + width <= ctx->rasterizer.frameSurface.width);
218    GGLTexture & tex = *ctx->tex.tmus[ctx->tex.active];
219    tex.width = width;
220    tex.height = height;
221    tex.levelCount = 1;
222    tex.format = texFormat;
223    switch (target) {
224    case GL_TEXTURE_2D:
225       tex.levels = realloc(tex.levels, totalSize);
226       CopyTexture((char *)tex.levels, (const char *)ctx->rasterizer.frameSurface.data, bytesPerPixel,
227                   x, y, ctx->rasterizer.frameSurface.width, 0, 0, width, width, height);
228       break;
229    default:
230       assert(0);
231       return;
232    }
233    ctx->tex.UpdateSampler(ctx->iface, ctx->tex.active);
234 }
235 
glCopyTexSubImage2D(GLenum target,GLint level,GLint xoffset,GLint yoffset,GLint x,GLint y,GLsizei width,GLsizei height)236 void glCopyTexSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height)
237 {
238    // x, y are src offset
239    // xoffset and yoffset are dst offset
240    GLES2_GET_CONST_CONTEXT(ctx);
241 //   LOGD("agl2: glCopyTexSubImage2D target=0x%.4X level=%d", target, level);
242 //   LOGD("xoffset=%d yoffset=%d x=%d y=%d width=%d height=%d", xoffset, yoffset, x, y, width, height);
243    assert(0 == level);
244 
245    unsigned bytesPerPixel = 4;
246    unsigned offset = 0, size = width * height * bytesPerPixel, totalSize = size;
247 
248    assert(ctx->tex.tmus[ctx->tex.active]);
249    GGLTexture & tex = *ctx->tex.tmus[ctx->tex.active];
250 
251    assert(tex.format == ctx->rasterizer.frameSurface.format);
252    assert(GGL_PIXEL_FORMAT_RGBA_8888 == tex.format);
253 
254    const unsigned srcWidth = ctx->rasterizer.frameSurface.width;
255    const unsigned srcHeight = ctx->rasterizer.frameSurface.height;
256 
257    assert(x >= 0 && y >= 0);
258    assert(xoffset >= 0 && yoffset >= 0);
259    assert(x + width <= srcWidth);
260    assert(y + height <= srcHeight);
261    assert(xoffset + width <= tex.width);
262    assert(yoffset + height <= tex.height);
263 
264    switch (target) {
265    case GL_TEXTURE_2D:
266       CopyTexture((char *)tex.levels, (const char *)ctx->rasterizer.frameSurface.data, bytesPerPixel,
267                   x, y, srcWidth, xoffset, yoffset, tex.width, width, height);
268       break;
269    default:
270       assert(0);
271       return;
272    }
273    ctx->tex.UpdateSampler(ctx->iface, ctx->tex.active);
274 }
275 
glDeleteTextures(GLsizei n,const GLuint * textures)276 void glDeleteTextures(GLsizei n, const GLuint* textures)
277 {
278    GLES2_GET_CONST_CONTEXT(ctx);
279    for (unsigned i = 0; i < n; i++) {
280       std::map<GLuint, GGLTexture *>::iterator it = ctx->tex.textures.find(textures[i]);
281       if (it == ctx->tex.textures.end())
282          continue;
283       ctx->tex.free = min(ctx->tex.free, textures[i]);
284       for (unsigned i = 0; i <  GGL_MAXCOMBINEDTEXTUREIMAGEUNITS; i++)
285          if (ctx->tex.tmus[i] == it->second) {
286             if (GL_TEXTURE_2D == it->second->type)
287                ctx->tex.tmus[i] = ctx->tex.tex2D;
288             else if (GL_TEXTURE_CUBE_MAP == it->second->type)
289                ctx->tex.tmus[i] = ctx->tex.texCube;
290             else
291                assert(0);
292             ctx->tex.UpdateSampler(ctx->iface, i);
293          }
294       if (it->second) {
295          free(it->second->levels);
296          free(it->second);
297       }
298       ctx->tex.textures.erase(it);
299    }
300 }
301 
glGenTextures(GLsizei n,GLuint * textures)302 void glGenTextures(GLsizei n, GLuint* textures)
303 {
304    GLES2_GET_CONST_CONTEXT(ctx);
305    for (unsigned i = 0; i < n; i++) {
306       textures[i] = 0;
307       for (ctx->tex.free; ctx->tex.free < 0xffffffffu; ctx->tex.free++)
308          if (ctx->tex.textures.find(ctx->tex.free) == ctx->tex.textures.end()) {
309             ctx->tex.textures[ctx->tex.free] = NULL;
310             textures[i] = ctx->tex.free;
311             ctx->tex.free++;
312             break;
313          }
314       assert(textures[i]);
315    }
316 }
317 
API_ENTRY(glGetTexParameterfv)318 void API_ENTRY(glGetTexParameterfv)(GLenum target, GLenum pname, GLfloat* params)
319 {
320    CALL_GL_API(glGetTexParameterfv, target, pname, params);
321 }
API_ENTRY(glGetTexParameteriv)322 void API_ENTRY(glGetTexParameteriv)(GLenum target, GLenum pname, GLint* params)
323 {
324    CALL_GL_API(glGetTexParameteriv, target, pname, params);
325 }
326 
glIsTexture(GLuint texture)327 GLboolean glIsTexture(GLuint texture)
328 {
329    GLES2_GET_CONST_CONTEXT(ctx);
330    if (ctx->tex.textures.find(texture) == ctx->tex.textures.end())
331       return GL_FALSE;
332    else
333       return GL_TRUE;
334 }
335 
glPixelStorei(GLenum pname,GLint param)336 void glPixelStorei(GLenum pname, GLint param)
337 {
338    GLES2_GET_CONST_CONTEXT(ctx);
339    assert(GL_UNPACK_ALIGNMENT == pname);
340    assert(1 == param || 2 == param || 4 == param || 8 == param);
341 //   LOGD("\n*\n* agl2: glPixelStorei not implemented pname=0x%.4X param=%d \n*", pname, param);
342    ctx->tex.unpack = param;
343 //   CALL_GL_API(glPixelStorei, pname, param);
344 }
glTexImage2D(GLenum target,GLint level,GLint internalformat,GLsizei width,GLsizei height,GLint border,GLenum format,GLenum type,const GLvoid * pixels)345 void glTexImage2D(GLenum target, GLint level, GLint internalformat, GLsizei width,
346                   GLsizei height, GLint border, GLenum format, GLenum type, const GLvoid* pixels)
347 {
348    GLES2_GET_CONST_CONTEXT(ctx);
349 //   LOGD("agl2: glTexImage2D internalformat=0x%.4X format=0x%.4X type=0x%.4X \n", internalformat, format, type);
350 //   LOGD("width=%d height=%d border=%d level=%d pixels=%p \n", width, height, border, level, pixels);
351    switch (type) {
352    case GL_UNSIGNED_BYTE:
353       break;
354    case GL_UNSIGNED_SHORT_5_6_5:
355       internalformat = format = GL_UNSIGNED_SHORT_5_6_5;
356       assert(4 == ctx->tex.unpack);
357       break;
358    default:
359       assert(0);
360    }
361    assert(internalformat == format);
362    assert(0 == border);
363    if (0 != level) {
364       LOGD("agl2: glTexImage2D level=%d", level);
365       return;
366    }
367    unsigned bytesPerPixel = 0;
368    GGLPixelFormat texFormat = GGL_PIXEL_FORMAT_UNKNOWN;
369    GetFormatAndBytesPerPixel(format, &bytesPerPixel, &texFormat);
370 
371    assert(texFormat && bytesPerPixel);
372 //   LOGD("texFormat=0x%.2X bytesPerPixel=%d active=%u", texFormat, bytesPerPixel, ctx->tex.active);
373    unsigned offset = 0, size = width * height * bytesPerPixel, totalSize = size;
374 
375    assert(ctx->tex.tmus[ctx->tex.active]);
376 
377    GGLTexture & tex = *ctx->tex.tmus[ctx->tex.active];
378    tex.width = width;
379    tex.height = height;
380    tex.levelCount = 1;
381    tex.format = texFormat;
382 
383    switch (target) {
384    case GL_TEXTURE_2D:
385       assert(GL_TEXTURE_2D == ctx->tex.tmus[ctx->tex.active]->type);
386       offset = 0;
387       break;
388       break;
389    case GL_TEXTURE_CUBE_MAP_POSITIVE_X:
390    case GL_TEXTURE_CUBE_MAP_NEGATIVE_X:
391    case GL_TEXTURE_CUBE_MAP_POSITIVE_Y:
392    case GL_TEXTURE_CUBE_MAP_NEGATIVE_Y:
393    case GL_TEXTURE_CUBE_MAP_POSITIVE_Z:
394    case GL_TEXTURE_CUBE_MAP_NEGATIVE_Z:
395       assert(GL_TEXTURE_CUBE_MAP == ctx->tex.tmus[ctx->tex.active]->type);
396       assert(width == height);
397       offset = (target - GL_TEXTURE_CUBE_MAP_POSITIVE_X) * size;
398       totalSize = 6 * size;
399       break;
400    default:
401       assert(0);
402       return;
403    }
404 
405    tex.levels = realloc(tex.levels, totalSize);
406    if (pixels)
407       CopyTexture((char *)tex.levels, (const char *)pixels, bytesPerPixel, 0, 0, width, 0, 0, width, width, height);
408    ctx->tex.UpdateSampler(ctx->iface, ctx->tex.active);
409 }
410 
glTexParameterf(GLenum target,GLenum pname,GLfloat param)411 void glTexParameterf(GLenum target, GLenum pname, GLfloat param)
412 {
413 //   LOGD("agl2: glTexParameterf target=0x%.4X pname=0x%.4X param=%f", target, pname, param);
414    glTexParameteri(target, pname, param);
415 }
API_ENTRY(glTexParameterfv)416 void API_ENTRY(glTexParameterfv)(GLenum target, GLenum pname, const GLfloat* params)
417 {
418    CALL_GL_API(glTexParameterfv, target, pname, params);
419 }
glTexParameteri(GLenum target,GLenum pname,GLint param)420 void glTexParameteri(GLenum target, GLenum pname, GLint param)
421 {
422    GLES2_GET_CONST_CONTEXT(ctx);
423 //   LOGD("alg2: glTexParameteri target=0x%.0X pname=0x%.4X param=0x%.4X",
424 //        target, pname, param);
425    assert(ctx->tex.tmus[ctx->tex.active]);
426    assert(target == ctx->tex.tmus[ctx->tex.active]->type);
427    GGLTexture & tex = *ctx->tex.tmus[ctx->tex.active];
428    switch (pname) {
429    case GL_TEXTURE_WRAP_S:
430    case GL_TEXTURE_WRAP_T:
431       GGLTexture::GGLTextureWrap wrap;
432       switch (param) {
433       case GL_REPEAT:
434          wrap = GGLTexture::GGL_REPEAT;
435          break;
436       case GL_CLAMP_TO_EDGE:
437          wrap = GGLTexture::GGL_CLAMP_TO_EDGE;
438          break;
439       case GL_MIRRORED_REPEAT:
440          wrap = GGLTexture::GGL_MIRRORED_REPEAT;
441          break;
442       default:
443          assert(0);
444          return;
445       }
446       if (GL_TEXTURE_WRAP_S == pname)
447          tex.wrapS = wrap;
448       else
449          tex.wrapT = wrap;
450       break;
451    case GL_TEXTURE_MIN_FILTER:
452       switch (param) {
453       case GL_NEAREST:
454          tex.minFilter = GGLTexture::GGL_NEAREST;
455          break;
456       case GL_LINEAR:
457          tex.minFilter = GGLTexture::GGL_LINEAR;
458          break;
459       case GL_NEAREST_MIPMAP_NEAREST:
460 //         tex.minFilter = GGLTexture::GGL_NEAREST_MIPMAP_NEAREST;
461          break;
462       case GL_NEAREST_MIPMAP_LINEAR:
463 //         tex.minFilter = GGLTexture::GGL_NEAREST_MIPMAP_LINEAR;
464          break;
465       case GL_LINEAR_MIPMAP_NEAREST:
466 //         tex.minFilter = GGLTexture::GGL_LINEAR_MIPMAP_NEAREST;
467          break;
468       case GL_LINEAR_MIPMAP_LINEAR:
469 //         tex.minFilter = GGLTexture::GGL_LINEAR_MIPMAP_LINEAR;
470          break;
471       default:
472          assert(0);
473          return;
474       }
475       break;
476    case GL_TEXTURE_MAG_FILTER:
477       switch (param) {
478       case GL_NEAREST:
479          tex.minFilter = GGLTexture::GGL_NEAREST;
480          break;
481       case GL_LINEAR:
482          tex.minFilter = GGLTexture::GGL_LINEAR;
483          break;
484       default:
485          assert(0);
486          return;
487       }
488       break;
489    default:
490       assert(0);
491       return;
492    }
493    // implementation restriction
494    if (tex.magFilter != tex.minFilter)
495       tex.magFilter = tex.minFilter = GGLTexture::GGL_LINEAR;
496    ctx->tex.UpdateSampler(ctx->iface, ctx->tex.active);
497 }
API_ENTRY(glTexParameteriv)498 void API_ENTRY(glTexParameteriv)(GLenum target, GLenum pname, const GLint* params)
499 {
500    CALL_GL_API(glTexParameteriv, target, pname, params);
501 }
glTexSubImage2D(GLenum target,GLint level,GLint xoffset,GLint yoffset,GLsizei width,GLsizei height,GLenum format,GLenum type,const GLvoid * pixels)502 void glTexSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const GLvoid* pixels)
503 {
504    GLES2_GET_CONST_CONTEXT(ctx);
505 //   LOGD("agl2: glTexSubImage2D target=0x%.4X level=%d xoffset=%d yoffset=%d width=%d height=%d format=0x%.4X type=0x%.4X pixels=%p",
506 //        target, level, xoffset, yoffset, width, height, format, type, pixels);
507    assert(0 == level);
508    assert(target == ctx->tex.tmus[ctx->tex.active]->type);
509    switch (type) {
510    case GL_UNSIGNED_BYTE:
511       break;
512    case GL_UNSIGNED_SHORT_5_6_5:
513       format = GL_UNSIGNED_SHORT_5_6_5;
514       assert(4 == ctx->tex.unpack);
515       break;
516    default:
517       assert(0);
518    }
519    GGLTexture & tex = *ctx->tex.tmus[ctx->tex.active];
520    GGLPixelFormat texFormat = GGL_PIXEL_FORMAT_UNKNOWN;
521    unsigned bytesPerPixel = 0;
522    GetFormatAndBytesPerPixel(format, &bytesPerPixel, &texFormat);
523    assert(texFormat == tex.format);
524    assert(GL_UNSIGNED_BYTE == type);
525    switch (target) {
526    case GL_TEXTURE_2D:
527       CopyTexture((char *)tex.levels, (const char *)pixels, bytesPerPixel, 0, 0, width, xoffset,
528                   yoffset, tex.width, width, height);
529       break;
530    default:
531       assert(0);
532    }
533    ctx->tex.UpdateSampler(ctx->iface, ctx->tex.active);
534 }
535