OSDN Git Service

Added state query for immutable levels
[android-x86/external-swiftshader.git] / src / OpenGL / libGLESv2 / Texture.cpp
1 // SwiftShader Software Renderer\r
2 //\r
3 // Copyright(c) 2005-2013 TransGaming Inc.\r
4 //\r
5 // All rights reserved. No part of this software may be copied, distributed, transmitted,\r
6 // transcribed, stored in a retrieval system, translated into any human or computer\r
7 // language by any means, or disclosed to third parties without the explicit written\r
8 // agreement of TransGaming Inc. Without such an agreement, no rights or licenses, express\r
9 // or implied, including but not limited to any patent rights, are granted to you.\r
10 //\r
11 \r
12 // Texture.cpp: Implements the Texture class and its derived classes\r
13 // Texture2D, TextureCubeMap, Texture3D and Texture2DArray. Implements GL texture objects\r
14 // and related functionality. [OpenGL ES 2.0.24] section 3.7 page 63.\r
15 \r
16 #include "Texture.h"\r
17 \r
18 #include "main.h"\r
19 #include "mathutil.h"\r
20 #include "Framebuffer.h"\r
21 #include "Device.hpp"\r
22 #include "libEGL/Display.h"\r
23 #include "libEGL/Surface.h"\r
24 #include "common/debug.h"\r
25 \r
26 #include <algorithm>\r
27 \r
28 namespace es2\r
29 {\r
30 \r
31 Texture::Texture(GLuint name) : egl::Texture(name)\r
32 {\r
33     mMinFilter = GL_NEAREST_MIPMAP_LINEAR;\r
34     mMagFilter = GL_LINEAR;\r
35     mWrapS = GL_REPEAT;\r
36     mWrapT = GL_REPEAT;\r
37         mWrapR = GL_REPEAT;\r
38         mMaxAnisotropy = 1.0f;\r
39         mBaseLevel = 0;\r
40         mCompareFunc = GL_LEQUAL;\r
41         mCompareMode = GL_NONE;\r
42         mImmutableFormat = GL_FALSE;\r
43         mImmutableLevels = 0;\r
44         mMaxLevel = 1000;\r
45         mMaxLOD = 1000;\r
46         mMinLOD = -1000;\r
47         mSwizzleR = GL_RED;\r
48         mSwizzleG = GL_GREEN;\r
49         mSwizzleB = GL_BLUE;\r
50         mSwizzleA = GL_ALPHA;\r
51 \r
52         resource = new sw::Resource(0);\r
53 }\r
54 \r
55 Texture::~Texture()\r
56 {\r
57         resource->destruct();\r
58 }\r
59 \r
60 sw::Resource *Texture::getResource() const\r
61 {\r
62         return resource;\r
63 }\r
64 \r
65 // Returns true on successful filter state update (valid enum parameter)\r
66 bool Texture::setMinFilter(GLenum filter)\r
67 {\r
68     switch(filter)\r
69     {\r
70     case GL_NEAREST_MIPMAP_NEAREST:\r
71     case GL_LINEAR_MIPMAP_NEAREST:\r
72     case GL_NEAREST_MIPMAP_LINEAR:\r
73     case GL_LINEAR_MIPMAP_LINEAR:\r
74         if(getTarget() == GL_TEXTURE_EXTERNAL_OES)\r
75         {\r
76             return false;\r
77         }\r
78         // Fall through\r
79     case GL_NEAREST:\r
80     case GL_LINEAR:\r
81         mMinFilter = filter;\r
82         return true;\r
83     default:\r
84         return false;\r
85     }\r
86 }\r
87 \r
88 // Returns true on successful filter state update (valid enum parameter)\r
89 bool Texture::setMagFilter(GLenum filter)\r
90 {\r
91     switch(filter)\r
92     {\r
93     case GL_NEAREST:\r
94     case GL_LINEAR:\r
95         mMagFilter = filter;\r
96         return true;\r
97     default:\r
98         return false;\r
99     }\r
100 }\r
101 \r
102 // Returns true on successful wrap state update (valid enum parameter)\r
103 bool Texture::setWrapS(GLenum wrap)\r
104 {\r
105     switch(wrap)\r
106     {\r
107     case GL_REPEAT:\r
108     case GL_MIRRORED_REPEAT:\r
109         if(getTarget() == GL_TEXTURE_EXTERNAL_OES)\r
110         {\r
111             return false;\r
112         }\r
113         // Fall through\r
114     case GL_CLAMP_TO_EDGE:\r
115         mWrapS = wrap;\r
116         return true;\r
117     default:\r
118         return false;\r
119     }\r
120 }\r
121 \r
122 // Returns true on successful wrap state update (valid enum parameter)\r
123 bool Texture::setWrapT(GLenum wrap)\r
124 {\r
125         switch(wrap)\r
126         {\r
127         case GL_REPEAT:\r
128         case GL_MIRRORED_REPEAT:\r
129                 if(getTarget() == GL_TEXTURE_EXTERNAL_OES)\r
130                 {\r
131                         return false;\r
132                 }\r
133                 // Fall through\r
134         case GL_CLAMP_TO_EDGE:\r
135                 mWrapT = wrap;\r
136                 return true;\r
137         default:\r
138                 return false;\r
139         }\r
140 }\r
141 \r
142 // Returns true on successful wrap state update (valid enum parameter)\r
143 bool Texture::setWrapR(GLenum wrap)\r
144 {\r
145         switch(wrap)\r
146         {\r
147         case GL_REPEAT:\r
148         case GL_MIRRORED_REPEAT:\r
149                 if(getTarget() == GL_TEXTURE_EXTERNAL_OES)\r
150                 {\r
151                         return false;\r
152                 }\r
153                 // Fall through\r
154         case GL_CLAMP_TO_EDGE:\r
155                 mWrapR = wrap;\r
156                 return true;\r
157         default:\r
158                 return false;\r
159         }\r
160 }\r
161 \r
162 // Returns true on successful max anisotropy update (valid anisotropy value)\r
163 bool Texture::setMaxAnisotropy(float textureMaxAnisotropy)\r
164 {\r
165     textureMaxAnisotropy = std::min(textureMaxAnisotropy, MAX_TEXTURE_MAX_ANISOTROPY);\r
166 \r
167     if(textureMaxAnisotropy < 1.0f)\r
168     {\r
169         return false;\r
170     }\r
171 \r
172         if(mMaxAnisotropy != textureMaxAnisotropy)\r
173     {\r
174         mMaxAnisotropy = textureMaxAnisotropy;\r
175     }\r
176 \r
177     return true;\r
178 }\r
179 \r
180 bool Texture::setBaseLevel(GLint baseLevel)\r
181 {\r
182         mBaseLevel = baseLevel;\r
183         return true;\r
184 }\r
185 \r
186 bool Texture::setCompareFunc(GLenum compareFunc)\r
187 {\r
188         switch(compareFunc)\r
189         {\r
190         case GL_LEQUAL:\r
191         case GL_GEQUAL:\r
192         case GL_LESS:\r
193         case GL_GREATER:\r
194         case GL_EQUAL:\r
195         case GL_NOTEQUAL:\r
196         case GL_ALWAYS:\r
197         case GL_NEVER:\r
198                 mCompareFunc = compareFunc;\r
199                 return true;\r
200         default:\r
201                 return false;\r
202         }\r
203 }\r
204 \r
205 bool Texture::setCompareMode(GLenum compareMode)\r
206 {\r
207         switch(compareMode)\r
208         {\r
209         case GL_COMPARE_REF_TO_TEXTURE:\r
210         case GL_NONE:\r
211                 mCompareMode = compareMode;\r
212                 return true;\r
213         default:\r
214                 return false;\r
215         }\r
216 }\r
217 \r
218 void Texture::makeImmutable(GLsizei levels)\r
219 {\r
220         mImmutableFormat = GL_TRUE;\r
221         mImmutableLevels = levels;\r
222 }\r
223 \r
224 bool Texture::setMaxLevel(GLint maxLevel)\r
225 {\r
226         mMaxLevel = maxLevel;\r
227         return true;\r
228 }\r
229 \r
230 bool Texture::setMaxLOD(GLfloat maxLOD)\r
231 {\r
232         mMaxLOD = maxLOD;\r
233         return true;\r
234 }\r
235 \r
236 bool Texture::setMinLOD(GLfloat minLOD)\r
237 {\r
238         mMinLOD = minLOD;\r
239         return true;\r
240 }\r
241 \r
242 bool Texture::setSwizzleR(GLenum swizzleR)\r
243 {\r
244         switch(swizzleR)\r
245         {\r
246         case GL_RED:\r
247         case GL_GREEN:\r
248         case GL_BLUE:\r
249         case GL_ALPHA:\r
250         case GL_ZERO:\r
251         case GL_ONE:\r
252                 mSwizzleR = swizzleR;\r
253                 return true;\r
254         default:\r
255                 return false;\r
256         }\r
257 }\r
258 \r
259 bool Texture::setSwizzleG(GLenum swizzleG)\r
260 {\r
261         switch(swizzleG)\r
262         {\r
263         case GL_RED:\r
264         case GL_GREEN:\r
265         case GL_BLUE:\r
266         case GL_ALPHA:\r
267         case GL_ZERO:\r
268         case GL_ONE:\r
269                 mSwizzleG = swizzleG;\r
270                 return true;\r
271         default:\r
272                 return false;\r
273         }\r
274 }\r
275 \r
276 bool Texture::setSwizzleB(GLenum swizzleB)\r
277 {\r
278         switch(swizzleB)\r
279         {\r
280         case GL_RED:\r
281         case GL_GREEN:\r
282         case GL_BLUE:\r
283         case GL_ALPHA:\r
284         case GL_ZERO:\r
285         case GL_ONE:\r
286                 mSwizzleB = swizzleB;\r
287                 return true;\r
288         default:\r
289                 return false;\r
290         }\r
291 }\r
292 \r
293 bool Texture::setSwizzleA(GLenum swizzleA)\r
294 {\r
295         switch(swizzleA)\r
296         {\r
297         case GL_RED:\r
298         case GL_GREEN:\r
299         case GL_BLUE:\r
300         case GL_ALPHA:\r
301         case GL_ZERO:\r
302         case GL_ONE:\r
303                 mSwizzleA = swizzleA;\r
304                 return true;\r
305         default:\r
306                 return false;\r
307         }\r
308 }\r
309 \r
310 GLenum Texture::getMinFilter() const\r
311 {\r
312     return mMinFilter;\r
313 }\r
314 \r
315 GLenum Texture::getMagFilter() const\r
316 {\r
317     return mMagFilter;\r
318 }\r
319 \r
320 GLenum Texture::getWrapS() const\r
321 {\r
322     return mWrapS;\r
323 }\r
324 \r
325 GLenum Texture::getWrapT() const\r
326 {\r
327         return mWrapT;\r
328 }\r
329 \r
330 GLenum Texture::getWrapR() const\r
331 {\r
332         return mWrapR;\r
333 }\r
334 \r
335 GLfloat Texture::getMaxAnisotropy() const\r
336 {\r
337     return mMaxAnisotropy;\r
338 }\r
339 \r
340 GLint Texture::getBaseLevel() const\r
341 {\r
342         return mBaseLevel;\r
343 }\r
344 GLenum Texture::getCompareFunc() const\r
345 {\r
346         return mCompareFunc;\r
347 }\r
348 GLenum Texture::getCompareMode() const\r
349 {\r
350         return mCompareMode;\r
351 }\r
352 GLboolean Texture::getImmutableFormat() const\r
353 {\r
354         return mImmutableFormat;\r
355 }\r
356 GLsizei Texture::getImmutableLevels() const\r
357 {\r
358         return mImmutableLevels;\r
359 }\r
360 GLint Texture::getMaxLevel() const\r
361 {\r
362         return mMaxLevel;\r
363 }\r
364 GLfloat Texture::getMaxLOD() const\r
365 {\r
366         return mMaxLOD;\r
367 }\r
368 GLfloat Texture::getMinLOD() const\r
369 {\r
370         return mMinLOD;\r
371 }\r
372 GLenum Texture::getSwizzleR() const\r
373 {\r
374         return mSwizzleR;\r
375 }\r
376 GLenum Texture::getSwizzleG() const\r
377 {\r
378         return mSwizzleG;\r
379 }\r
380 GLenum Texture::getSwizzleB() const\r
381 {\r
382         return mSwizzleB;\r
383 }\r
384 GLenum Texture::getSwizzleA() const\r
385 {\r
386         return mSwizzleA;\r
387 }\r
388 \r
389 GLsizei Texture::getDepth(GLenum target, GLint level) const\r
390 {\r
391         return 1;\r
392 }\r
393 \r
394 egl::Image *Texture::createSharedImage(GLenum target, unsigned int level)\r
395 {\r
396     egl::Image *image = getRenderTarget(target, level);   // Increments reference count\r
397 \r
398     if(image)\r
399     {\r
400         image->markShared();\r
401     }\r
402 \r
403     return image;\r
404 }\r
405 \r
406 void Texture::setImage(GLenum format, GLenum type, const egl::Image::UnpackInfo& unpackInfo, const void *pixels, egl::Image *image)\r
407 {\r
408     if(pixels && image)\r
409     {\r
410                 GLsizei depth = (getTarget() == GL_TEXTURE_3D_OES || getTarget() == GL_TEXTURE_2D_ARRAY) ? image->getDepth() : 1;\r
411                 image->loadImageData(0, 0, 0, image->getWidth(), image->getHeight(), depth, format, type, unpackInfo, pixels);\r
412     }\r
413 }\r
414 \r
415 void Texture::setCompressedImage(GLsizei imageSize, const void *pixels, egl::Image *image)\r
416 {\r
417     if(pixels && image)\r
418     {\r
419                 GLsizei depth = (getTarget() == GL_TEXTURE_3D_OES || getTarget() == GL_TEXTURE_2D_ARRAY) ? image->getDepth() : 1;\r
420                 image->loadCompressedData(0, 0, 0, image->getWidth(), image->getHeight(), depth, imageSize, pixels);\r
421     }\r
422 }\r
423 \r
424 void Texture::subImage(GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const egl::Image::UnpackInfo& unpackInfo, const void *pixels, egl::Image *image)\r
425 {\r
426         if(!image)\r
427         {\r
428                 return error(GL_INVALID_OPERATION);\r
429         }\r
430 \r
431         if(width + xoffset > image->getWidth() || height + yoffset > image->getHeight() || depth + zoffset > image->getDepth())\r
432     {\r
433         return error(GL_INVALID_VALUE);\r
434     }\r
435 \r
436     if(IsCompressed(image->getFormat(), egl::getClientVersion()))\r
437     {\r
438         return error(GL_INVALID_OPERATION);\r
439     }\r
440 \r
441     if(format != image->getFormat())\r
442     {\r
443         return error(GL_INVALID_OPERATION);\r
444     }\r
445 \r
446     if(pixels)\r
447     {\r
448                 image->loadImageData(xoffset, yoffset, zoffset, width, height, depth, format, type, unpackInfo, pixels);\r
449     }\r
450 }\r
451 \r
452 void Texture::subImageCompressed(GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLsizei imageSize, const void *pixels, egl::Image *image)\r
453 {\r
454         if(!image)\r
455         {\r
456                 return error(GL_INVALID_OPERATION);\r
457         }\r
458 \r
459     if(width + xoffset > image->getWidth() || height + yoffset > image->getHeight() || depth + zoffset > image->getDepth())\r
460     {\r
461         return error(GL_INVALID_VALUE);\r
462     }\r
463 \r
464     if(format != image->getFormat())\r
465     {\r
466         return error(GL_INVALID_OPERATION);\r
467     }\r
468 \r
469     if(pixels)\r
470     {\r
471                 image->loadCompressedData(xoffset, yoffset, zoffset, width, height, depth, imageSize, pixels);\r
472     }\r
473 }\r
474 \r
475 bool Texture::copy(egl::Image *source, const sw::SliceRect &sourceRect, GLenum destFormat, GLint xoffset, GLint yoffset, GLint zoffset, egl::Image *dest)\r
476 {\r
477     Device *device = getDevice();\r
478 \r
479     sw::SliceRect destRect(xoffset, yoffset, xoffset + (sourceRect.x1 - sourceRect.x0), yoffset + (sourceRect.y1 - sourceRect.y0), zoffset);\r
480     bool success = device->stretchRect(source, &sourceRect, dest, &destRect, false);\r
481 \r
482     if(!success)\r
483     {\r
484         return error(GL_OUT_OF_MEMORY, false);\r
485     }\r
486 \r
487     return true;\r
488 }\r
489 \r
490 bool Texture::isMipmapFiltered() const\r
491 {\r
492         switch(mMinFilter)\r
493     {\r
494     case GL_NEAREST:\r
495     case GL_LINEAR:\r
496         return false;\r
497     case GL_NEAREST_MIPMAP_NEAREST:\r
498     case GL_LINEAR_MIPMAP_NEAREST:\r
499     case GL_NEAREST_MIPMAP_LINEAR:\r
500     case GL_LINEAR_MIPMAP_LINEAR:\r
501         return true;\r
502     default: UNREACHABLE(mMinFilter);\r
503     }\r
504 \r
505         return false;\r
506 }\r
507 \r
508 Texture2D::Texture2D(GLuint name) : Texture(name)\r
509 {\r
510         for(int i = 0; i < MIPMAP_LEVELS; i++)\r
511         {\r
512                 image[i] = nullptr;\r
513         }\r
514 \r
515     mSurface = nullptr;\r
516 \r
517         mColorbufferProxy = nullptr;\r
518         mProxyRefs = 0;\r
519 }\r
520 \r
521 Texture2D::~Texture2D()\r
522 {\r
523         resource->lock(sw::DESTRUCT);\r
524 \r
525         for(int i = 0; i < MIPMAP_LEVELS; i++)\r
526         {\r
527                 if(image[i])\r
528                 {\r
529                         image[i]->unbind(this);\r
530                         image[i] = nullptr;\r
531                 }\r
532         }\r
533 \r
534         resource->unlock();\r
535 \r
536     if(mSurface)\r
537     {\r
538         mSurface->setBoundTexture(nullptr);\r
539         mSurface = nullptr;\r
540     }\r
541 \r
542         mColorbufferProxy = nullptr;\r
543 }\r
544 \r
545 // We need to maintain a count of references to renderbuffers acting as\r
546 // proxies for this texture, so that we do not attempt to use a pointer\r
547 // to a renderbuffer proxy which has been deleted.\r
548 void Texture2D::addProxyRef(const Renderbuffer *proxy)\r
549 {\r
550     mProxyRefs++;\r
551 }\r
552 \r
553 void Texture2D::releaseProxy(const Renderbuffer *proxy)\r
554 {\r
555     if(mProxyRefs > 0)\r
556         {\r
557         mProxyRefs--;\r
558         }\r
559 \r
560     if(mProxyRefs == 0)\r
561         {\r
562                 mColorbufferProxy = nullptr;\r
563         }\r
564 }\r
565 \r
566 void Texture2D::sweep()\r
567 {\r
568         int imageCount = 0;\r
569 \r
570         for(int i = 0; i < MIPMAP_LEVELS; i++)\r
571         {\r
572                 if(image[i] && image[i]->isChildOf(this))\r
573                 {\r
574                         if(!image[i]->hasSingleReference())\r
575                         {\r
576                                 return;\r
577                         }\r
578 \r
579                         imageCount++;\r
580                 }\r
581         }\r
582 \r
583         if(imageCount == referenceCount)\r
584         {\r
585                 destroy();\r
586         }\r
587 }\r
588 \r
589 GLenum Texture2D::getTarget() const\r
590 {\r
591     return GL_TEXTURE_2D;\r
592 }\r
593 \r
594 GLsizei Texture2D::getWidth(GLenum target, GLint level) const\r
595 {\r
596         ASSERT(target == GL_TEXTURE_2D);\r
597     return image[level] ? image[level]->getWidth() : 0;\r
598 }\r
599 \r
600 GLsizei Texture2D::getHeight(GLenum target, GLint level) const\r
601 {\r
602         ASSERT(target == GL_TEXTURE_2D);\r
603     return image[level] ? image[level]->getHeight() : 0;\r
604 }\r
605 \r
606 GLenum Texture2D::getFormat(GLenum target, GLint level) const\r
607 {\r
608         ASSERT(target == GL_TEXTURE_2D);\r
609         return image[level] ? image[level]->getFormat() : GL_NONE;\r
610 }\r
611 \r
612 GLenum Texture2D::getType(GLenum target, GLint level) const\r
613 {\r
614         ASSERT(target == GL_TEXTURE_2D);\r
615         return image[level] ? image[level]->getType() : GL_NONE;\r
616 }\r
617 \r
618 sw::Format Texture2D::getInternalFormat(GLenum target, GLint level) const\r
619 {\r
620         ASSERT(target == GL_TEXTURE_2D);\r
621         return image[level] ? image[level]->getInternalFormat() : sw::FORMAT_NULL;\r
622 }\r
623 \r
624 int Texture2D::getLevelCount() const\r
625 {\r
626         ASSERT(isSamplerComplete());\r
627         int levels = 0;\r
628 \r
629         while(levels < MIPMAP_LEVELS && image[levels])\r
630         {\r
631                 levels++;\r
632         }\r
633 \r
634         return levels;\r
635 }\r
636 \r
637 void Texture2D::setImage(GLint level, GLsizei width, GLsizei height, GLenum format, GLenum type, const egl::Image::UnpackInfo& unpackInfo, const void *pixels)\r
638 {\r
639         if(image[level])\r
640         {\r
641                 image[level]->release();\r
642         }\r
643 \r
644         image[level] = new egl::Image(this, width, height, format, type);\r
645 \r
646         if(!image[level])\r
647         {\r
648                 return error(GL_OUT_OF_MEMORY);\r
649         }\r
650 \r
651         Texture::setImage(format, type, unpackInfo, pixels, image[level]);\r
652 }\r
653 \r
654 void Texture2D::bindTexImage(egl::Surface *surface)\r
655 {\r
656     GLenum format;\r
657 \r
658     switch(surface->getInternalFormat())\r
659     {\r
660     case sw::FORMAT_A8R8G8B8:\r
661                 format = GL_BGRA_EXT;\r
662         break;\r
663         case sw::FORMAT_A8B8G8R8:\r
664         format = GL_RGBA;\r
665         break;\r
666     case sw::FORMAT_X8B8G8R8:\r
667         case sw::FORMAT_X8R8G8B8:\r
668         format = GL_RGB;\r
669         break;\r
670     default:\r
671         UNIMPLEMENTED();\r
672         return;\r
673     }\r
674 \r
675         for(int level = 0; level < MIPMAP_LEVELS; level++)\r
676         {\r
677                 if(image[level])\r
678                 {\r
679                         image[level]->release();\r
680                         image[level] = nullptr;\r
681                 }\r
682         }\r
683 \r
684         image[0] = surface->getRenderTarget();\r
685 \r
686     mSurface = surface;\r
687     mSurface->setBoundTexture(this);\r
688 }\r
689 \r
690 void Texture2D::releaseTexImage()\r
691 {\r
692     for(int level = 0; level < MIPMAP_LEVELS; level++)\r
693         {\r
694                 if(image[level])\r
695                 {\r
696                         image[level]->release();\r
697                         image[level] = nullptr;\r
698                 }\r
699         }\r
700 }\r
701 \r
702 void Texture2D::setCompressedImage(GLint level, GLenum format, GLsizei width, GLsizei height, GLsizei imageSize, const void *pixels)\r
703 {\r
704         if(image[level])\r
705         {\r
706                 image[level]->release();\r
707         }\r
708 \r
709         GLenum sizedInternalFormat = GetSizedInternalFormat(format, GL_UNSIGNED_BYTE);\r
710         image[level] = new egl::Image(this, width, height, sizedInternalFormat, GL_UNSIGNED_BYTE);\r
711 \r
712         if(!image[level])\r
713         {\r
714                 return error(GL_OUT_OF_MEMORY);\r
715         }\r
716 \r
717     Texture::setCompressedImage(imageSize, pixels, image[level]);\r
718 }\r
719 \r
720 void Texture2D::subImage(GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const egl::Image::UnpackInfo& unpackInfo, const void *pixels)\r
721 {\r
722         Texture::subImage(xoffset, yoffset, 0, width, height, 1, format, type, unpackInfo, pixels, image[level]);\r
723 }\r
724 \r
725 void Texture2D::subImageCompressed(GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void *pixels)\r
726 {\r
727     Texture::subImageCompressed(xoffset, yoffset, 0, width, height, 1, format, imageSize, pixels, image[level]);\r
728 }\r
729 \r
730 void Texture2D::copyImage(GLint level, GLenum format, GLint x, GLint y, GLsizei width, GLsizei height, Framebuffer *source)\r
731 {\r
732     egl::Image *renderTarget = source->getRenderTarget(0);\r
733 \r
734     if(!renderTarget)\r
735     {\r
736         ERR("Failed to retrieve the render target.");\r
737         return error(GL_OUT_OF_MEMORY);\r
738     }\r
739 \r
740         if(image[level])\r
741         {\r
742                 image[level]->release();\r
743         }\r
744 \r
745         GLenum sizedInternalFormat = GetSizedInternalFormat(format, GL_UNSIGNED_BYTE);\r
746         image[level] = new egl::Image(this, width, height, sizedInternalFormat, GL_UNSIGNED_BYTE);\r
747 \r
748         if(!image[level])\r
749         {\r
750                 return error(GL_OUT_OF_MEMORY);\r
751         }\r
752 \r
753     if(width != 0 && height != 0)\r
754     {\r
755                 Renderbuffer* renderbuffer = source->getReadColorbuffer();\r
756 \r
757                 if(!renderbuffer)\r
758                 {\r
759                         ERR("Failed to retrieve the source colorbuffer.");\r
760                         return;\r
761                 }\r
762 \r
763                 sw::SliceRect sourceRect(x, y, x + width, y + height, 0);\r
764                 sourceRect.clip(0, 0, renderbuffer->getWidth(), renderbuffer->getHeight());\r
765 \r
766                 copy(renderTarget, sourceRect, sizedInternalFormat, 0, 0, 0, image[level]);\r
767     }\r
768 \r
769         renderTarget->release();\r
770 }\r
771 \r
772 void Texture2D::copySubImage(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLint x, GLint y, GLsizei width, GLsizei height, Framebuffer *source)\r
773 {\r
774         if(!image[level])\r
775         {\r
776                 return error(GL_INVALID_OPERATION);\r
777         }\r
778 \r
779     if(xoffset + width > image[level]->getWidth() || yoffset + height > image[level]->getHeight() || zoffset != 0)\r
780     {\r
781         return error(GL_INVALID_VALUE);\r
782     }\r
783 \r
784     egl::Image *renderTarget = source->getRenderTarget(0);\r
785 \r
786     if(!renderTarget)\r
787     {\r
788         ERR("Failed to retrieve the render target.");\r
789         return error(GL_OUT_OF_MEMORY);\r
790     }\r
791 \r
792         Renderbuffer* renderbuffer = source->getReadColorbuffer();\r
793 \r
794         if(!renderbuffer)\r
795         {\r
796                 ERR("Failed to retrieve the source colorbuffer.");\r
797                 return;\r
798         }\r
799 \r
800         sw::SliceRect sourceRect(x, y, x + width, y + height, 0);\r
801         sourceRect.clip(0, 0, renderbuffer->getWidth(), renderbuffer->getHeight());\r
802 \r
803         copy(renderTarget, sourceRect, image[level]->getFormat(), xoffset, yoffset, zoffset, image[level]);\r
804 \r
805         renderTarget->release();\r
806 }\r
807 \r
808 void Texture2D::setImage(egl::Image *sharedImage)\r
809 {\r
810         if (sharedImage == image[0])\r
811         {\r
812                 return;\r
813         }\r
814 \r
815         sharedImage->addRef();\r
816 \r
817     if(image[0])\r
818     {\r
819         image[0]->release();\r
820     }\r
821 \r
822     image[0] = sharedImage;\r
823 }\r
824 \r
825 // Tests for 2D texture sampling completeness. [OpenGL ES 2.0.24] section 3.8.2 page 85.\r
826 bool Texture2D::isSamplerComplete() const\r
827 {\r
828         if(!image[0])\r
829         {\r
830                 return false;\r
831         }\r
832 \r
833     GLsizei width = image[0]->getWidth();\r
834     GLsizei height = image[0]->getHeight();\r
835 \r
836     if(width <= 0 || height <= 0)\r
837     {\r
838         return false;\r
839     }\r
840 \r
841     if(isMipmapFiltered())\r
842     {\r
843         if(!isMipmapComplete())\r
844         {\r
845             return false;\r
846         }\r
847     }\r
848 \r
849     return true;\r
850 }\r
851 \r
852 // Tests for 2D texture (mipmap) completeness. [OpenGL ES 2.0.24] section 3.7.10 page 81.\r
853 bool Texture2D::isMipmapComplete() const\r
854 {\r
855     GLsizei width = image[mBaseLevel]->getWidth();\r
856     GLsizei height = image[mBaseLevel]->getHeight();\r
857 \r
858     int q = std::min(log2(std::max(width, height)), mMaxLevel);\r
859 \r
860     for(int level = mBaseLevel + 1; level <= q; level++)\r
861     {\r
862                 if(!image[level])\r
863                 {\r
864                         return false;\r
865                 }\r
866 \r
867         if(image[level]->getFormat() != image[0]->getFormat())\r
868         {\r
869             return false;\r
870         }\r
871 \r
872         if(image[level]->getType() != image[0]->getType())\r
873         {\r
874             return false;\r
875         }\r
876 \r
877         if(image[level]->getWidth() != std::max(1, width >> level))\r
878         {\r
879             return false;\r
880         }\r
881 \r
882         if(image[level]->getHeight() != std::max(1, height >> level))\r
883         {\r
884             return false;\r
885         }\r
886     }\r
887 \r
888     return true;\r
889 }\r
890 \r
891 bool Texture2D::isCompressed(GLenum target, GLint level) const\r
892 {\r
893     return IsCompressed(getFormat(target, level), egl::getClientVersion());\r
894 }\r
895 \r
896 bool Texture2D::isDepth(GLenum target, GLint level) const\r
897 {\r
898     return IsDepthTexture(getFormat(target, level));\r
899 }\r
900 \r
901 void Texture2D::generateMipmaps()\r
902 {\r
903         if(!image[0])\r
904         {\r
905                 return;   // FIXME: error?\r
906         }\r
907 \r
908     unsigned int q = log2(std::max(image[0]->getWidth(), image[0]->getHeight()));\r
909 \r
910         for(unsigned int i = 1; i <= q; i++)\r
911     {\r
912                 if(image[i])\r
913                 {\r
914                         image[i]->release();\r
915                 }\r
916 \r
917                 image[i] = new egl::Image(this, std::max(image[0]->getWidth() >> i, 1), std::max(image[0]->getHeight() >> i, 1), image[0]->getFormat(), image[0]->getType());\r
918 \r
919                 if(!image[i])\r
920                 {\r
921                         return error(GL_OUT_OF_MEMORY);\r
922                 }\r
923 \r
924                 getDevice()->stretchRect(image[i - 1], 0, image[i], 0, true);\r
925     }\r
926 }\r
927 \r
928 egl::Image *Texture2D::getImage(unsigned int level)\r
929 {\r
930         return image[level];\r
931 }\r
932 \r
933 Renderbuffer *Texture2D::getRenderbuffer(GLenum target, GLint level, GLint layer)\r
934 {\r
935     if(target != GL_TEXTURE_2D)\r
936     {\r
937         return error(GL_INVALID_OPERATION, (Renderbuffer*)nullptr);\r
938     }\r
939 \r
940     if(!mColorbufferProxy)\r
941     {\r
942         mColorbufferProxy = new Renderbuffer(name, new RenderbufferTexture2D(this, level));\r
943     }\r
944 \r
945     return mColorbufferProxy;\r
946 }\r
947 \r
948 egl::Image *Texture2D::getRenderTarget(GLenum target, unsigned int level)\r
949 {\r
950     ASSERT(target == GL_TEXTURE_2D);\r
951         ASSERT(level < IMPLEMENTATION_MAX_TEXTURE_LEVELS);\r
952 \r
953         if(image[level])\r
954         {\r
955                 image[level]->addRef();\r
956         }\r
957 \r
958         return image[level];\r
959 }\r
960 \r
961 bool Texture2D::isShared(GLenum target, unsigned int level) const\r
962 {\r
963     ASSERT(target == GL_TEXTURE_2D);\r
964     ASSERT(level < IMPLEMENTATION_MAX_TEXTURE_LEVELS);\r
965 \r
966     if(mSurface)   // Bound to an EGLSurface\r
967     {\r
968         return true;\r
969     }\r
970 \r
971     if(!image[level])\r
972     {\r
973         return false;\r
974     }\r
975 \r
976     return image[level]->isShared();\r
977 }\r
978 \r
979 TextureCubeMap::TextureCubeMap(GLuint name) : Texture(name)\r
980 {\r
981         for(int f = 0; f < 6; f++)\r
982         {\r
983                 for(int i = 0; i < MIPMAP_LEVELS; i++)\r
984                 {\r
985                         image[f][i] = nullptr;\r
986                 }\r
987         }\r
988 \r
989         for(int f = 0; f < 6; f++)\r
990     {\r
991         mFaceProxies[f] = nullptr;\r
992         mFaceProxyRefs[f] = 0;\r
993         }\r
994 }\r
995 \r
996 TextureCubeMap::~TextureCubeMap()\r
997 {\r
998         resource->lock(sw::DESTRUCT);\r
999 \r
1000         for(int f = 0; f < 6; f++)\r
1001         {\r
1002                 for(int i = 0; i < MIPMAP_LEVELS; i++)\r
1003                 {\r
1004                         if(image[f][i])\r
1005                         {\r
1006                                 image[f][i]->unbind(this);\r
1007                                 image[f][i] = nullptr;\r
1008                         }\r
1009                 }\r
1010         }\r
1011 \r
1012         resource->unlock();\r
1013 \r
1014     for(int i = 0; i < 6; i++)\r
1015     {\r
1016         mFaceProxies[i] = nullptr;\r
1017     }\r
1018 }\r
1019 \r
1020 // We need to maintain a count of references to renderbuffers acting as\r
1021 // proxies for this texture, so that the texture is not deleted while\r
1022 // proxy references still exist. If the reference count drops to zero,\r
1023 // we set our proxy pointer null, so that a new attempt at referencing\r
1024 // will cause recreation.\r
1025 void TextureCubeMap::addProxyRef(const Renderbuffer *proxy)\r
1026 {\r
1027     for(int f = 0; f < 6; f++)\r
1028     {\r
1029         if(mFaceProxies[f] == proxy)\r
1030         {\r
1031                         mFaceProxyRefs[f]++;\r
1032                 }\r
1033         }\r
1034 }\r
1035 \r
1036 void TextureCubeMap::releaseProxy(const Renderbuffer *proxy)\r
1037 {\r
1038     for(int f = 0; f < 6; f++)\r
1039     {\r
1040         if(mFaceProxies[f] == proxy)\r
1041         {\r
1042             if(mFaceProxyRefs[f] > 0)\r
1043                         {\r
1044                                 mFaceProxyRefs[f]--;\r
1045                         }\r
1046 \r
1047             if(mFaceProxyRefs[f] == 0)\r
1048                         {\r
1049                                 mFaceProxies[f] = nullptr;\r
1050                         }\r
1051                 }\r
1052     }\r
1053 }\r
1054 \r
1055 void TextureCubeMap::sweep()\r
1056 {\r
1057         int imageCount = 0;\r
1058 \r
1059         for(int f = 0; f < 6; f++)\r
1060         {\r
1061                 for(int i = 0; i < MIPMAP_LEVELS; i++)\r
1062                 {\r
1063                         if(image[f][i] && image[f][i]->isChildOf(this))\r
1064                         {\r
1065                                 if(!image[f][i]->hasSingleReference())\r
1066                                 {\r
1067                                         return;\r
1068                                 }\r
1069 \r
1070                                 imageCount++;\r
1071                         }\r
1072                 }\r
1073         }\r
1074 \r
1075         if(imageCount == referenceCount)\r
1076         {\r
1077                 destroy();\r
1078         }\r
1079 }\r
1080 \r
1081 GLenum TextureCubeMap::getTarget() const\r
1082 {\r
1083     return GL_TEXTURE_CUBE_MAP;\r
1084 }\r
1085 \r
1086 GLsizei TextureCubeMap::getWidth(GLenum target, GLint level) const\r
1087 {\r
1088         int face = CubeFaceIndex(target);\r
1089     return image[face][level] ? image[face][level]->getWidth() : 0;\r
1090 }\r
1091 \r
1092 GLsizei TextureCubeMap::getHeight(GLenum target, GLint level) const\r
1093 {\r
1094         int face = CubeFaceIndex(target);\r
1095     return image[face][level] ? image[face][level]->getHeight() : 0;\r
1096 }\r
1097 \r
1098 GLenum TextureCubeMap::getFormat(GLenum target, GLint level) const\r
1099 {\r
1100         int face = CubeFaceIndex(target);\r
1101     return image[face][level] ? image[face][level]->getFormat() : 0;\r
1102 }\r
1103 \r
1104 GLenum TextureCubeMap::getType(GLenum target, GLint level) const\r
1105 {\r
1106         int face = CubeFaceIndex(target);\r
1107     return image[face][level] ? image[face][level]->getType() : 0;\r
1108 }\r
1109 \r
1110 sw::Format TextureCubeMap::getInternalFormat(GLenum target, GLint level) const\r
1111 {\r
1112         int face = CubeFaceIndex(target);\r
1113     return image[face][level] ? image[face][level]->getInternalFormat() : sw::FORMAT_NULL;\r
1114 }\r
1115 \r
1116 int TextureCubeMap::getLevelCount() const\r
1117 {\r
1118         ASSERT(isSamplerComplete());\r
1119         int levels = 0;\r
1120 \r
1121         while(levels < MIPMAP_LEVELS && image[0][levels])\r
1122         {\r
1123                 levels++;\r
1124         }\r
1125 \r
1126         return levels;\r
1127 }\r
1128 \r
1129 void TextureCubeMap::setCompressedImage(GLenum target, GLint level, GLenum format, GLsizei width, GLsizei height, GLsizei imageSize, const void *pixels)\r
1130 {\r
1131         int face = CubeFaceIndex(target);\r
1132 \r
1133         if(image[face][level])\r
1134         {\r
1135                 image[face][level]->release();\r
1136         }\r
1137 \r
1138         GLenum sizedInternalFormat = GetSizedInternalFormat(format, GL_UNSIGNED_BYTE);\r
1139         image[face][level] = new egl::Image(this, width, height, sizedInternalFormat, GL_UNSIGNED_BYTE);\r
1140 \r
1141         if(!image[face][level])\r
1142         {\r
1143                 return error(GL_OUT_OF_MEMORY);\r
1144         }\r
1145 \r
1146     Texture::setCompressedImage(imageSize, pixels, image[face][level]);\r
1147 }\r
1148 \r
1149 void TextureCubeMap::subImage(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const egl::Image::UnpackInfo& unpackInfo, const void *pixels)\r
1150 {\r
1151         Texture::subImage(xoffset, yoffset, 0, width, height, 1, format, type, unpackInfo, pixels, image[CubeFaceIndex(target)][level]);\r
1152 }\r
1153 \r
1154 void TextureCubeMap::subImageCompressed(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void *pixels)\r
1155 {\r
1156     Texture::subImageCompressed(xoffset, yoffset, 0, width, height, 1, format, imageSize, pixels, image[CubeFaceIndex(target)][level]);\r
1157 }\r
1158 \r
1159 // Tests for cube map sampling completeness. [OpenGL ES 2.0.24] section 3.8.2 page 86.\r
1160 bool TextureCubeMap::isSamplerComplete() const\r
1161 {\r
1162         for(int face = 0; face < 6; face++)\r
1163     {\r
1164                 if(!image[face][0])\r
1165                 {\r
1166                         return false;\r
1167                 }\r
1168         }\r
1169 \r
1170     int size = image[0][0]->getWidth();\r
1171 \r
1172     if(size <= 0)\r
1173     {\r
1174         return false;\r
1175     }\r
1176 \r
1177     if(!isMipmapFiltered())\r
1178     {\r
1179         if(!isCubeComplete())\r
1180         {\r
1181             return false;\r
1182         }\r
1183     }\r
1184     else\r
1185     {\r
1186         if(!isMipmapCubeComplete())   // Also tests for isCubeComplete()\r
1187         {\r
1188             return false;\r
1189         }\r
1190     }\r
1191 \r
1192     return true;\r
1193 }\r
1194 \r
1195 // Tests for cube texture completeness. [OpenGL ES 2.0.24] section 3.7.10 page 81.\r
1196 bool TextureCubeMap::isCubeComplete() const\r
1197 {\r
1198     if(image[0][mBaseLevel]->getWidth() <= 0 || image[0][mBaseLevel]->getHeight() != image[0][mBaseLevel]->getWidth())\r
1199     {\r
1200         return false;\r
1201     }\r
1202 \r
1203     for(unsigned int face = 1; face < 6; face++)\r
1204     {\r
1205         if(image[face][mBaseLevel]->getWidth()  != image[0][mBaseLevel]->getWidth() ||\r
1206            image[face][mBaseLevel]->getWidth()  != image[0][mBaseLevel]->getHeight() ||\r
1207            image[face][mBaseLevel]->getFormat() != image[0][mBaseLevel]->getFormat() ||\r
1208            image[face][mBaseLevel]->getType()   != image[0][mBaseLevel]->getType())\r
1209         {\r
1210             return false;\r
1211         }\r
1212     }\r
1213 \r
1214     return true;\r
1215 }\r
1216 \r
1217 bool TextureCubeMap::isMipmapCubeComplete() const\r
1218 {\r
1219     if(!isCubeComplete())\r
1220     {\r
1221         return false;\r
1222     }\r
1223 \r
1224     GLsizei size = image[0][mBaseLevel]->getWidth();\r
1225     int q = std::min(log2(size), mMaxLevel);\r
1226 \r
1227     for(int face = 0; face < 6; face++)\r
1228     {\r
1229         for(int level = mBaseLevel + 1; level <= q; level++)\r
1230         {\r
1231                         if(!image[face][level])\r
1232                         {\r
1233                                 return false;\r
1234                         }\r
1235 \r
1236             if(image[face][level]->getFormat() != image[0][mBaseLevel]->getFormat())\r
1237             {\r
1238                 return false;\r
1239             }\r
1240 \r
1241             if(image[face][level]->getType() != image[0][mBaseLevel]->getType())\r
1242             {\r
1243                 return false;\r
1244             }\r
1245 \r
1246             if(image[face][level]->getWidth() != std::max(1, size >> level))\r
1247             {\r
1248                 return false;\r
1249             }\r
1250         }\r
1251     }\r
1252 \r
1253     return true;\r
1254 }\r
1255 \r
1256 bool TextureCubeMap::isCompressed(GLenum target, GLint level) const\r
1257 {\r
1258     return IsCompressed(getFormat(target, level), egl::getClientVersion());\r
1259 }\r
1260 \r
1261 bool TextureCubeMap::isDepth(GLenum target, GLint level) const\r
1262 {\r
1263     return IsDepthTexture(getFormat(target, level));\r
1264 }\r
1265 \r
1266 void TextureCubeMap::releaseTexImage()\r
1267 {\r
1268     UNREACHABLE(0);   // Cube maps cannot have an EGL surface bound as an image\r
1269 }\r
1270 \r
1271 void TextureCubeMap::setImage(GLenum target, GLint level, GLsizei width, GLsizei height, GLenum format, GLenum type, const egl::Image::UnpackInfo& unpackInfo, const void *pixels)\r
1272 {\r
1273         int face = CubeFaceIndex(target);\r
1274 \r
1275         if(image[face][level])\r
1276         {\r
1277                 image[face][level]->release();\r
1278         }\r
1279 \r
1280         image[face][level] = new egl::Image(this, width, height, format, type);\r
1281 \r
1282         if(!image[face][level])\r
1283         {\r
1284                 return error(GL_OUT_OF_MEMORY);\r
1285         }\r
1286 \r
1287         Texture::setImage(format, type, unpackInfo, pixels, image[face][level]);\r
1288 }\r
1289 \r
1290 void TextureCubeMap::copyImage(GLenum target, GLint level, GLenum format, GLint x, GLint y, GLsizei width, GLsizei height, Framebuffer *source)\r
1291 {\r
1292         egl::Image *renderTarget = source->getRenderTarget(0);\r
1293 \r
1294     if(!renderTarget)\r
1295     {\r
1296         ERR("Failed to retrieve the render target.");\r
1297         return error(GL_OUT_OF_MEMORY);\r
1298     }\r
1299 \r
1300         int face = CubeFaceIndex(target);\r
1301 \r
1302         if(image[face][level])\r
1303         {\r
1304                 image[face][level]->release();\r
1305         }\r
1306 \r
1307         GLenum sizedInternalFormat = GetSizedInternalFormat(format, GL_UNSIGNED_BYTE);\r
1308         image[face][level] = new egl::Image(this, width, height, sizedInternalFormat, GL_UNSIGNED_BYTE);\r
1309 \r
1310         if(!image[face][level])\r
1311         {\r
1312                 return error(GL_OUT_OF_MEMORY);\r
1313         }\r
1314 \r
1315     if(width != 0 && height != 0)\r
1316     {\r
1317                 Renderbuffer* renderbuffer = source->getReadColorbuffer();\r
1318 \r
1319                 if(!renderbuffer)\r
1320                 {\r
1321                         ERR("Failed to retrieve the source colorbuffer.");\r
1322                         return;\r
1323                 }\r
1324 \r
1325                 sw::SliceRect sourceRect(x, y, x + width, y + height, 0);\r
1326                 sourceRect.clip(0, 0, renderbuffer->getWidth(), renderbuffer->getHeight());\r
1327 \r
1328                 copy(renderTarget, sourceRect, sizedInternalFormat, 0, 0, 0, image[face][level]);\r
1329     }\r
1330 \r
1331         renderTarget->release();\r
1332 }\r
1333 \r
1334 egl::Image *TextureCubeMap::getImage(int face, unsigned int level)\r
1335 {\r
1336         return image[face][level];\r
1337 }\r
1338 \r
1339 egl::Image *TextureCubeMap::getImage(GLenum face, unsigned int level)\r
1340 {\r
1341     return image[CubeFaceIndex(face)][level];\r
1342 }\r
1343 \r
1344 void TextureCubeMap::copySubImage(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLint x, GLint y, GLsizei width, GLsizei height, Framebuffer *source)\r
1345 {\r
1346         int face = CubeFaceIndex(target);\r
1347 \r
1348         if(!image[face][level])\r
1349         {\r
1350                 return error(GL_INVALID_OPERATION);\r
1351         }\r
1352 \r
1353     GLsizei size = image[face][level]->getWidth();\r
1354 \r
1355     if(xoffset + width > size || yoffset + height > size || zoffset != 0)\r
1356     {\r
1357         return error(GL_INVALID_VALUE);\r
1358     }\r
1359 \r
1360     egl::Image *renderTarget = source->getRenderTarget(0);\r
1361 \r
1362     if(!renderTarget)\r
1363     {\r
1364         ERR("Failed to retrieve the render target.");\r
1365         return error(GL_OUT_OF_MEMORY);\r
1366     }\r
1367 \r
1368         Renderbuffer* renderbuffer = source->getReadColorbuffer();\r
1369 \r
1370         if(!renderbuffer)\r
1371         {\r
1372                 ERR("Failed to retrieve the source colorbuffer.");\r
1373                 return;\r
1374         }\r
1375 \r
1376         sw::SliceRect sourceRect(x, y, x + width, y + height, 0);\r
1377         sourceRect.clip(0, 0, renderbuffer->getWidth(), renderbuffer->getHeight());\r
1378 \r
1379         copy(renderTarget, sourceRect, image[face][level]->getFormat(), xoffset, yoffset, zoffset, image[face][level]);\r
1380 \r
1381         renderTarget->release();\r
1382 }\r
1383 \r
1384 void TextureCubeMap::generateMipmaps()\r
1385 {\r
1386     if(!isCubeComplete())\r
1387     {\r
1388         return error(GL_INVALID_OPERATION);\r
1389     }\r
1390 \r
1391     unsigned int q = log2(image[0][0]->getWidth());\r
1392 \r
1393         for(unsigned int f = 0; f < 6; f++)\r
1394     {\r
1395                 for(unsigned int i = 1; i <= q; i++)\r
1396                 {\r
1397                         if(image[f][i])\r
1398                         {\r
1399                                 image[f][i]->release();\r
1400                         }\r
1401 \r
1402                         image[f][i] = new egl::Image(this, std::max(image[0][0]->getWidth() >> i, 1), std::max(image[0][0]->getHeight() >> i, 1), image[0][0]->getFormat(), image[0][0]->getType());\r
1403 \r
1404                         if(!image[f][i])\r
1405                         {\r
1406                                 return error(GL_OUT_OF_MEMORY);\r
1407                         }\r
1408 \r
1409                         getDevice()->stretchRect(image[f][i - 1], 0, image[f][i], 0, true);\r
1410                 }\r
1411         }\r
1412 }\r
1413 \r
1414 Renderbuffer *TextureCubeMap::getRenderbuffer(GLenum target, GLint level, GLint layer)\r
1415 {\r
1416     if(!IsCubemapTextureTarget(target))\r
1417     {\r
1418         return error(GL_INVALID_OPERATION, (Renderbuffer*)nullptr);\r
1419     }\r
1420 \r
1421     int face = CubeFaceIndex(target);\r
1422 \r
1423     if(!mFaceProxies[face])\r
1424     {\r
1425         mFaceProxies[face] = new Renderbuffer(name, new RenderbufferTextureCubeMap(this, target, level));\r
1426     }\r
1427 \r
1428     return mFaceProxies[face];\r
1429 }\r
1430 \r
1431 egl::Image *TextureCubeMap::getRenderTarget(GLenum target, unsigned int level)\r
1432 {\r
1433     ASSERT(IsCubemapTextureTarget(target));\r
1434     ASSERT(level < IMPLEMENTATION_MAX_TEXTURE_LEVELS);\r
1435 \r
1436         int face = CubeFaceIndex(target);\r
1437 \r
1438         if(image[face][level])\r
1439         {\r
1440                 image[face][level]->addRef();\r
1441         }\r
1442 \r
1443         return image[face][level];\r
1444 }\r
1445 \r
1446 bool TextureCubeMap::isShared(GLenum target, unsigned int level) const\r
1447 {\r
1448     ASSERT(IsCubemapTextureTarget(target));\r
1449     ASSERT(level < IMPLEMENTATION_MAX_TEXTURE_LEVELS);\r
1450 \r
1451     int face = CubeFaceIndex(target);\r
1452 \r
1453     if(!image[face][level])\r
1454     {\r
1455         return false;\r
1456     }\r
1457 \r
1458     return image[face][level]->isShared();\r
1459 }\r
1460 \r
1461 Texture3D::Texture3D(GLuint name) : Texture(name)\r
1462 {\r
1463         for(int i = 0; i < MIPMAP_LEVELS; i++)\r
1464         {\r
1465                 image[i] = nullptr;\r
1466         }\r
1467 \r
1468         mSurface = nullptr;\r
1469 \r
1470         mColorbufferProxy = nullptr;\r
1471         mProxyRefs = 0;\r
1472 }\r
1473 \r
1474 Texture3D::~Texture3D()\r
1475 {\r
1476         resource->lock(sw::DESTRUCT);\r
1477 \r
1478         for(int i = 0; i < MIPMAP_LEVELS; i++)\r
1479         {\r
1480                 if(image[i])\r
1481                 {\r
1482                         image[i]->unbind(this);\r
1483                         image[i] = nullptr;\r
1484                 }\r
1485         }\r
1486 \r
1487         resource->unlock();\r
1488 \r
1489         if(mSurface)\r
1490         {\r
1491                 mSurface->setBoundTexture(nullptr);\r
1492                 mSurface = nullptr;\r
1493         }\r
1494 \r
1495         mColorbufferProxy = nullptr;\r
1496 }\r
1497 \r
1498 // We need to maintain a count of references to renderbuffers acting as\r
1499 // proxies for this texture, so that we do not attempt to use a pointer\r
1500 // to a renderbuffer proxy which has been deleted.\r
1501 void Texture3D::addProxyRef(const Renderbuffer *proxy)\r
1502 {\r
1503         mProxyRefs++;\r
1504 }\r
1505 \r
1506 void Texture3D::releaseProxy(const Renderbuffer *proxy)\r
1507 {\r
1508         if(mProxyRefs > 0)\r
1509         {\r
1510                 mProxyRefs--;\r
1511         }\r
1512 \r
1513         if(mProxyRefs == 0)\r
1514         {\r
1515                 mColorbufferProxy = nullptr;\r
1516         }\r
1517 }\r
1518 \r
1519 void Texture3D::sweep()\r
1520 {\r
1521         int imageCount = 0;\r
1522 \r
1523         for(int i = 0; i < MIPMAP_LEVELS; i++)\r
1524         {\r
1525                 if(image[i] && image[i]->isChildOf(this))\r
1526                 {\r
1527                         if(!image[i]->hasSingleReference())\r
1528                         {\r
1529                                 return;\r
1530                         }\r
1531 \r
1532                         imageCount++;\r
1533                 }\r
1534         }\r
1535 \r
1536         if(imageCount == referenceCount)\r
1537         {\r
1538                 destroy();\r
1539         }\r
1540 }\r
1541 \r
1542 GLenum Texture3D::getTarget() const\r
1543 {\r
1544         return GL_TEXTURE_3D_OES;\r
1545 }\r
1546 \r
1547 GLsizei Texture3D::getWidth(GLenum target, GLint level) const\r
1548 {\r
1549         ASSERT(target == getTarget());\r
1550         return image[level] ? image[level]->getWidth() : 0;\r
1551 }\r
1552 \r
1553 GLsizei Texture3D::getHeight(GLenum target, GLint level) const\r
1554 {\r
1555         ASSERT(target == getTarget());\r
1556         return image[level] ? image[level]->getHeight() : 0;\r
1557 }\r
1558 \r
1559 GLsizei Texture3D::getDepth(GLenum target, GLint level) const\r
1560 {\r
1561         ASSERT(target == getTarget());\r
1562         return image[level] ? image[level]->getDepth() : 0;\r
1563 }\r
1564 \r
1565 GLenum Texture3D::getFormat(GLenum target, GLint level) const\r
1566 {\r
1567         ASSERT(target == getTarget());\r
1568         return image[level] ? image[level]->getFormat() : GL_NONE;\r
1569 }\r
1570 \r
1571 GLenum Texture3D::getType(GLenum target, GLint level) const\r
1572 {\r
1573         ASSERT(target == getTarget());\r
1574         return image[level] ? image[level]->getType() : GL_NONE;\r
1575 }\r
1576 \r
1577 sw::Format Texture3D::getInternalFormat(GLenum target, GLint level) const\r
1578 {\r
1579         ASSERT(target == getTarget());\r
1580         return image[level] ? image[level]->getInternalFormat() : sw::FORMAT_NULL;\r
1581 }\r
1582 \r
1583 int Texture3D::getLevelCount() const\r
1584 {\r
1585         ASSERT(isSamplerComplete());\r
1586         int levels = 0;\r
1587 \r
1588         while(levels < MIPMAP_LEVELS && image[levels])\r
1589         {\r
1590                 levels++;\r
1591         }\r
1592 \r
1593         return levels;\r
1594 }\r
1595 \r
1596 void Texture3D::setImage(GLint level, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const egl::Image::UnpackInfo& unpackInfo, const void *pixels)\r
1597 {\r
1598         if(image[level])\r
1599         {\r
1600                 image[level]->release();\r
1601         }\r
1602 \r
1603         image[level] = new egl::Image(this, width, height, depth, format, type);\r
1604 \r
1605         if(!image[level])\r
1606         {\r
1607                 return error(GL_OUT_OF_MEMORY);\r
1608         }\r
1609 \r
1610         Texture::setImage(format, type, unpackInfo, pixels, image[level]);\r
1611 }\r
1612 \r
1613 void Texture3D::bindTexImage(egl::Surface *surface)\r
1614 {\r
1615         GLenum format;\r
1616 \r
1617         switch(surface->getInternalFormat())\r
1618         {\r
1619         case sw::FORMAT_A8R8G8B8:\r
1620                 format = GL_RGBA;\r
1621                 break;\r
1622         case sw::FORMAT_X8R8G8B8:\r
1623                 format = GL_RGB;\r
1624                 break;\r
1625         default:\r
1626                 UNIMPLEMENTED();\r
1627                 return;\r
1628         }\r
1629 \r
1630         for(int level = 0; level < MIPMAP_LEVELS; level++)\r
1631         {\r
1632                 if(image[level])\r
1633                 {\r
1634                         image[level]->release();\r
1635                         image[level] = nullptr;\r
1636                 }\r
1637         }\r
1638 \r
1639         image[0] = surface->getRenderTarget();\r
1640 \r
1641         mSurface = surface;\r
1642         mSurface->setBoundTexture(this);\r
1643 }\r
1644 \r
1645 void Texture3D::releaseTexImage()\r
1646 {\r
1647         for(int level = 0; level < MIPMAP_LEVELS; level++)\r
1648         {\r
1649                 if(image[level])\r
1650                 {\r
1651                         image[level]->release();\r
1652                         image[level] = nullptr;\r
1653                 }\r
1654         }\r
1655 }\r
1656 \r
1657 void Texture3D::setCompressedImage(GLint level, GLenum format, GLsizei width, GLsizei height, GLsizei depth, GLsizei imageSize, const void *pixels)\r
1658 {\r
1659         if(image[level])\r
1660         {\r
1661                 image[level]->release();\r
1662         }\r
1663 \r
1664         GLenum sizedInternalFormat = GetSizedInternalFormat(format, GL_UNSIGNED_BYTE);\r
1665         image[level] = new egl::Image(this, width, height, depth, sizedInternalFormat, GL_UNSIGNED_BYTE);\r
1666 \r
1667         if(!image[level])\r
1668         {\r
1669                 return error(GL_OUT_OF_MEMORY);\r
1670         }\r
1671 \r
1672         Texture::setCompressedImage(imageSize, pixels, image[level]);\r
1673 }\r
1674 \r
1675 void Texture3D::subImage(GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const egl::Image::UnpackInfo& unpackInfo, const void *pixels)\r
1676 {\r
1677         Texture::subImage(xoffset, yoffset, zoffset, width, height, depth, format, type, unpackInfo, pixels, image[level]);\r
1678 }\r
1679 \r
1680 void Texture3D::subImageCompressed(GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLsizei imageSize, const void *pixels)\r
1681 {\r
1682         Texture::subImageCompressed(xoffset, yoffset, zoffset, width, height, depth, format, imageSize, pixels, image[level]);\r
1683 }\r
1684 \r
1685 void Texture3D::copyImage(GLint level, GLenum format, GLint x, GLint y, GLint z, GLsizei width, GLsizei height, GLsizei depth, Framebuffer *source)\r
1686 {\r
1687         egl::Image *renderTarget = source->getRenderTarget(0);\r
1688 \r
1689         if(!renderTarget)\r
1690         {\r
1691                 ERR("Failed to retrieve the render target.");\r
1692                 return error(GL_OUT_OF_MEMORY);\r
1693         }\r
1694 \r
1695         if(image[level])\r
1696         {\r
1697                 image[level]->release();\r
1698         }\r
1699 \r
1700         GLenum sizedInternalFormat = GetSizedInternalFormat(format, GL_UNSIGNED_BYTE);\r
1701         image[level] = new egl::Image(this, width, height, depth, sizedInternalFormat, GL_UNSIGNED_BYTE);\r
1702 \r
1703         if(!image[level])\r
1704         {\r
1705                 return error(GL_OUT_OF_MEMORY);\r
1706         }\r
1707 \r
1708         if(width != 0 && height != 0 && depth != 0)\r
1709         {\r
1710                 Renderbuffer* renderbuffer = source->getReadColorbuffer();\r
1711 \r
1712                 if(!renderbuffer)\r
1713                 {\r
1714                         ERR("Failed to retrieve the source colorbuffer.");\r
1715                         return;\r
1716                 }\r
1717 \r
1718                 sw::SliceRect sourceRect(x, y, x + width, y + height, z);\r
1719                 sourceRect.clip(0, 0, renderbuffer->getWidth(), renderbuffer->getHeight());\r
1720                 for(GLint sliceZ = 0; sliceZ < depth; ++sliceZ, ++sourceRect.slice)\r
1721                 {\r
1722                         copy(renderTarget, sourceRect, sizedInternalFormat, 0, 0, sliceZ, image[level]);\r
1723                 }\r
1724         }\r
1725 \r
1726         renderTarget->release();\r
1727 }\r
1728 \r
1729 void Texture3D::copySubImage(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLint x, GLint y, GLsizei width, GLsizei height, Framebuffer *source)\r
1730 {\r
1731         if(!image[level])\r
1732         {\r
1733                 return error(GL_INVALID_OPERATION);\r
1734         }\r
1735 \r
1736         if(xoffset + width > image[level]->getWidth() || yoffset + height > image[level]->getHeight() || zoffset >= image[level]->getDepth())\r
1737         {\r
1738                 return error(GL_INVALID_VALUE);\r
1739         }\r
1740 \r
1741         egl::Image *renderTarget = source->getRenderTarget(0);\r
1742 \r
1743         if(!renderTarget)\r
1744         {\r
1745                 ERR("Failed to retrieve the render target.");\r
1746                 return error(GL_OUT_OF_MEMORY);\r
1747         }\r
1748 \r
1749         Renderbuffer* renderbuffer = source->getReadColorbuffer();\r
1750 \r
1751         if(!renderbuffer)\r
1752         {\r
1753                 ERR("Failed to retrieve the source colorbuffer.");\r
1754                 return;\r
1755         }\r
1756 \r
1757         sw::SliceRect sourceRect = {x, y, x + width, y + height, 0};\r
1758         sourceRect.clip(0, 0, renderbuffer->getWidth(), renderbuffer->getHeight());\r
1759 \r
1760         copy(renderTarget, sourceRect, image[level]->getFormat(), xoffset, yoffset, zoffset, image[level]);\r
1761 \r
1762         renderTarget->release();\r
1763 }\r
1764 \r
1765 void Texture3D::setImage(egl::Image *sharedImage)\r
1766 {\r
1767         sharedImage->addRef();\r
1768 \r
1769         if(image[0])\r
1770         {\r
1771                 image[0]->release();\r
1772         }\r
1773 \r
1774         image[0] = sharedImage;\r
1775 }\r
1776 \r
1777 // Tests for 3D texture sampling completeness. [OpenGL ES 2.0.24] section 3.8.2 page 85.\r
1778 bool Texture3D::isSamplerComplete() const\r
1779 {\r
1780         if(!image[0])\r
1781         {\r
1782                 return false;\r
1783         }\r
1784 \r
1785         GLsizei width = image[0]->getWidth();\r
1786         GLsizei height = image[0]->getHeight();\r
1787         GLsizei depth = image[0]->getDepth();\r
1788 \r
1789         if(width <= 0 || height <= 0 || depth <= 0)\r
1790         {\r
1791                 return false;\r
1792         }\r
1793 \r
1794         if(isMipmapFiltered())\r
1795         {\r
1796                 if(!isMipmapComplete())\r
1797                 {\r
1798                         return false;\r
1799                 }\r
1800         }\r
1801 \r
1802         return true;\r
1803 }\r
1804 \r
1805 // Tests for 3D texture (mipmap) completeness. [OpenGL ES 2.0.24] section 3.7.10 page 81.\r
1806 bool Texture3D::isMipmapComplete() const\r
1807 {\r
1808         GLsizei width = image[mBaseLevel]->getWidth();\r
1809         GLsizei height = image[mBaseLevel]->getHeight();\r
1810         GLsizei depth = image[mBaseLevel]->getDepth();\r
1811         bool isTexture2DArray = getTarget() == GL_TEXTURE_2D_ARRAY;\r
1812 \r
1813         int q = isTexture2DArray ? std::min(log2(std::max(width, height)), mMaxLevel) :\r
1814                 std::min(log2(std::max(std::max(width, height), depth)), mMaxLevel);\r
1815 \r
1816         for(int level = mBaseLevel + 1; level <= q; level++)\r
1817         {\r
1818                 if(!image[level])\r
1819                 {\r
1820                         return false;\r
1821                 }\r
1822 \r
1823                 if(image[level]->getFormat() != image[0]->getFormat())\r
1824                 {\r
1825                         return false;\r
1826                 }\r
1827 \r
1828                 if(image[level]->getType() != image[0]->getType())\r
1829                 {\r
1830                         return false;\r
1831                 }\r
1832 \r
1833                 if(image[level]->getWidth() != std::max(1, width >> level))\r
1834                 {\r
1835                         return false;\r
1836                 }\r
1837 \r
1838                 if(image[level]->getHeight() != std::max(1, height >> level))\r
1839                 {\r
1840                         return false;\r
1841                 }\r
1842 \r
1843                 int levelDepth = isTexture2DArray ? depth : std::max(1, depth >> level);\r
1844                 if(image[level]->getDepth() != levelDepth)\r
1845                 {\r
1846                         return false;\r
1847                 }\r
1848         }\r
1849 \r
1850         return true;\r
1851 }\r
1852 \r
1853 bool Texture3D::isCompressed(GLenum target, GLint level) const\r
1854 {\r
1855         return IsCompressed(getFormat(target, level), egl::getClientVersion());\r
1856 }\r
1857 \r
1858 bool Texture3D::isDepth(GLenum target, GLint level) const\r
1859 {\r
1860         return IsDepthTexture(getFormat(target, level));\r
1861 }\r
1862 \r
1863 void Texture3D::generateMipmaps()\r
1864 {\r
1865         if(!image[0])\r
1866         {\r
1867                 return;   // FIXME: error?\r
1868         }\r
1869 \r
1870         unsigned int q = log2(std::max(std::max(image[0]->getWidth(), image[0]->getHeight()), image[0]->getDepth()));\r
1871 \r
1872         for(unsigned int i = 1; i <= q; i++)\r
1873         {\r
1874                 if(image[i])\r
1875                 {\r
1876                         image[i]->release();\r
1877                 }\r
1878 \r
1879                 image[i] = new egl::Image(this, std::max(image[0]->getWidth() >> i, 1), std::max(image[0]->getHeight() >> i, 1), std::max(image[0]->getDepth() >> i, 1), image[0]->getFormat(), image[0]->getType());\r
1880 \r
1881                 if(!image[i])\r
1882                 {\r
1883                         return error(GL_OUT_OF_MEMORY);\r
1884                 }\r
1885 \r
1886                 getDevice()->stretchCube(image[i - 1], image[i]);\r
1887         }\r
1888 }\r
1889 \r
1890 egl::Image *Texture3D::getImage(unsigned int level)\r
1891 {\r
1892         return image[level];\r
1893 }\r
1894 \r
1895 Renderbuffer *Texture3D::getRenderbuffer(GLenum target, GLint level, GLint layer)\r
1896 {\r
1897         if(target != getTarget())\r
1898         {\r
1899                 return error(GL_INVALID_OPERATION, (Renderbuffer*)nullptr);\r
1900         }\r
1901 \r
1902         if(!mColorbufferProxy)\r
1903         {\r
1904                 mColorbufferProxy = new Renderbuffer(name, new RenderbufferTexture3D(this, level, layer));\r
1905         }\r
1906 \r
1907         return mColorbufferProxy;\r
1908 }\r
1909 \r
1910 egl::Image *Texture3D::getRenderTarget(GLenum target, unsigned int level)\r
1911 {\r
1912         ASSERT(target == getTarget());\r
1913         ASSERT(level < IMPLEMENTATION_MAX_TEXTURE_LEVELS);\r
1914 \r
1915         if(image[level])\r
1916         {\r
1917                 image[level]->addRef();\r
1918         }\r
1919 \r
1920         return image[level];\r
1921 }\r
1922 \r
1923 bool Texture3D::isShared(GLenum target, unsigned int level) const\r
1924 {\r
1925         ASSERT(target == getTarget());\r
1926         ASSERT(level < IMPLEMENTATION_MAX_TEXTURE_LEVELS);\r
1927 \r
1928         if(mSurface)   // Bound to an EGLSurface\r
1929         {\r
1930                 return true;\r
1931         }\r
1932 \r
1933         if(!image[level])\r
1934         {\r
1935                 return false;\r
1936         }\r
1937 \r
1938         return image[level]->isShared();\r
1939 }\r
1940 \r
1941 Texture2DArray::Texture2DArray(GLuint name) : Texture3D(name)\r
1942 {\r
1943 }\r
1944 \r
1945 Texture2DArray::~Texture2DArray()\r
1946 {\r
1947 }\r
1948 \r
1949 GLenum Texture2DArray::getTarget() const\r
1950 {\r
1951         return GL_TEXTURE_2D_ARRAY;\r
1952 }\r
1953 \r
1954 void Texture2DArray::generateMipmaps()\r
1955 {\r
1956         int depth = image[0] ? image[0]->getDepth() : 0;\r
1957         if(!depth)\r
1958         {\r
1959                 return;   // FIXME: error?\r
1960         }\r
1961 \r
1962         unsigned int q = log2(std::max(image[0]->getWidth(), image[0]->getHeight()));\r
1963 \r
1964         for(unsigned int i = 1; i <= q; i++)\r
1965         {\r
1966                 if(image[i])\r
1967                 {\r
1968                         image[i]->release();\r
1969                 }\r
1970 \r
1971                 GLsizei w = std::max(image[0]->getWidth() >> i, 1);\r
1972                 GLsizei h = std::max(image[0]->getHeight() >> i, 1);\r
1973                 image[i] = new egl::Image(this, w, h, depth, image[0]->getFormat(), image[0]->getType());\r
1974 \r
1975                 if(!image[i])\r
1976                 {\r
1977                         return error(GL_OUT_OF_MEMORY);\r
1978                 }\r
1979 \r
1980                 GLsizei srcw = image[i - 1]->getWidth();\r
1981                 GLsizei srch = image[i - 1]->getHeight();\r
1982                 for(int z = 0; z < depth; ++z)\r
1983                 {\r
1984                         sw::SliceRect srcRect(0, 0, srcw, srch, z);\r
1985                         sw::SliceRect dstRect(0, 0, w, h, z);\r
1986                         getDevice()->stretchRect(image[i - 1], &srcRect, image[i], &dstRect, true);\r
1987                 }\r
1988         }\r
1989 }\r
1990 \r
1991 TextureExternal::TextureExternal(GLuint name) : Texture2D(name)\r
1992 {\r
1993     mMinFilter = GL_LINEAR;\r
1994     mMagFilter = GL_LINEAR;\r
1995     mWrapS = GL_CLAMP_TO_EDGE;\r
1996         mWrapT = GL_CLAMP_TO_EDGE;\r
1997         mWrapR = GL_CLAMP_TO_EDGE;\r
1998 }\r
1999 \r
2000 TextureExternal::~TextureExternal()\r
2001 {\r
2002 }\r
2003 \r
2004 GLenum TextureExternal::getTarget() const\r
2005 {\r
2006     return GL_TEXTURE_EXTERNAL_OES;\r
2007 }\r
2008 \r
2009 }\r
2010 \r
2011 egl::Image *createBackBuffer(int width, int height, const egl::Config *config)\r
2012 {\r
2013         if(config)\r
2014         {\r
2015                 return new egl::Image(width, height, config->mRenderTargetFormat, config->mSamples, false);\r
2016         }\r
2017 \r
2018         return nullptr;\r
2019 }\r
2020 \r
2021 egl::Image *createDepthStencil(unsigned int width, unsigned int height, sw::Format format, int multiSampleDepth, bool discard)\r
2022 {\r
2023         if(width == 0 || height == 0 || height > OUTLINE_RESOLUTION)\r
2024         {\r
2025                 ERR("Invalid parameters: %dx%d", width, height);\r
2026                 return 0;\r
2027         }\r
2028 \r
2029         bool lockable = true;\r
2030 \r
2031         switch(format)\r
2032         {\r
2033 //      case sw::FORMAT_D15S1:\r
2034         case sw::FORMAT_D24S8:\r
2035         case sw::FORMAT_D24X8:\r
2036 //      case sw::FORMAT_D24X4S4:\r
2037         case sw::FORMAT_D24FS8:\r
2038         case sw::FORMAT_D32:\r
2039         case sw::FORMAT_D16:\r
2040                 lockable = false;\r
2041                 break;\r
2042 //      case sw::FORMAT_S8_LOCKABLE:\r
2043 //      case sw::FORMAT_D16_LOCKABLE:\r
2044         case sw::FORMAT_D32F_LOCKABLE:\r
2045 //      case sw::FORMAT_D32_LOCKABLE:\r
2046         case sw::FORMAT_DF24S8:\r
2047         case sw::FORMAT_DF16S8:\r
2048                 lockable = true;\r
2049                 break;\r
2050         default:\r
2051                 UNREACHABLE(format);\r
2052         }\r
2053 \r
2054         egl::Image *surface = new egl::Image(width, height, format, multiSampleDepth, lockable);\r
2055 \r
2056         if(!surface)\r
2057         {\r
2058                 ERR("Out of memory");\r
2059                 return nullptr;\r
2060         }\r
2061 \r
2062         return surface;\r
2063 }\r