OSDN Git Service

Support GL_TEXTURE_2D targets in glEGLImageTargetTexture2DOES.
[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 and TextureCubeMap. Implements GL texture objects and related\r
14 // 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 id) : RefCountObject(id)\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         mMaxAnisotropy = 1.0f;\r
38 \r
39         resource = new sw::Resource(0);\r
40 }\r
41 \r
42 Texture::~Texture()\r
43 {\r
44         resource->destruct();\r
45 }\r
46 \r
47 sw::Resource *Texture::getResource() const\r
48 {\r
49         return resource;\r
50 }\r
51 \r
52 // Returns true on successful filter state update (valid enum parameter)\r
53 bool Texture::setMinFilter(GLenum filter)\r
54 {\r
55     switch(filter)\r
56     {\r
57     case GL_NEAREST_MIPMAP_NEAREST:\r
58     case GL_LINEAR_MIPMAP_NEAREST:\r
59     case GL_NEAREST_MIPMAP_LINEAR:\r
60     case GL_LINEAR_MIPMAP_LINEAR:\r
61         if(getTarget() == GL_TEXTURE_EXTERNAL_OES)\r
62         {\r
63             return false;\r
64         }\r
65         // Fall through\r
66     case GL_NEAREST:\r
67     case GL_LINEAR:\r
68         mMinFilter = filter;\r
69         return true;\r
70     default:\r
71         return false;\r
72     }\r
73 }\r
74 \r
75 // Returns true on successful filter state update (valid enum parameter)\r
76 bool Texture::setMagFilter(GLenum filter)\r
77 {\r
78     switch(filter)\r
79     {\r
80     case GL_NEAREST:\r
81     case GL_LINEAR:\r
82         mMagFilter = filter;\r
83         return true;\r
84     default:\r
85         return false;\r
86     }\r
87 }\r
88 \r
89 // Returns true on successful wrap state update (valid enum parameter)\r
90 bool Texture::setWrapS(GLenum wrap)\r
91 {\r
92     switch(wrap)\r
93     {\r
94     case GL_REPEAT:\r
95     case GL_MIRRORED_REPEAT:\r
96         if(getTarget() == GL_TEXTURE_EXTERNAL_OES)\r
97         {\r
98             return false;\r
99         }\r
100         // Fall through\r
101     case GL_CLAMP_TO_EDGE:\r
102         mWrapS = wrap;\r
103         return true;\r
104     default:\r
105         return false;\r
106     }\r
107 }\r
108 \r
109 // Returns true on successful wrap state update (valid enum parameter)\r
110 bool Texture::setWrapT(GLenum wrap)\r
111 {\r
112     switch(wrap)\r
113     {\r
114     case GL_REPEAT:\r
115     case GL_MIRRORED_REPEAT:\r
116         if(getTarget() == GL_TEXTURE_EXTERNAL_OES)\r
117         {\r
118             return false;\r
119         }\r
120         // Fall through\r
121     case GL_CLAMP_TO_EDGE:\r
122          mWrapT = wrap;\r
123          return true;\r
124     default:\r
125         return false;\r
126     }\r
127 }\r
128 \r
129 // Returns true on successful max anisotropy update (valid anisotropy value)\r
130 bool Texture::setMaxAnisotropy(float textureMaxAnisotropy)\r
131 {\r
132     textureMaxAnisotropy = std::min(textureMaxAnisotropy, MAX_TEXTURE_MAX_ANISOTROPY);\r
133 \r
134     if(textureMaxAnisotropy < 1.0f)\r
135     {\r
136         return false;\r
137     }\r
138     \r
139         if(mMaxAnisotropy != textureMaxAnisotropy)\r
140     {\r
141         mMaxAnisotropy = textureMaxAnisotropy;\r
142     }\r
143 \r
144     return true;\r
145 }\r
146 \r
147 GLenum Texture::getMinFilter() const\r
148 {\r
149     return mMinFilter;\r
150 }\r
151 \r
152 GLenum Texture::getMagFilter() const\r
153 {\r
154     return mMagFilter;\r
155 }\r
156 \r
157 GLenum Texture::getWrapS() const\r
158 {\r
159     return mWrapS;\r
160 }\r
161 \r
162 GLenum Texture::getWrapT() const\r
163 {\r
164     return mWrapT;\r
165 }\r
166 \r
167 GLfloat Texture::getMaxAnisotropy() const\r
168 {\r
169     return mMaxAnisotropy;\r
170 }\r
171 \r
172 egl::Image *Texture::createSharedImage(GLenum target, unsigned int level)\r
173 {\r
174     egl::Image *image = getRenderTarget(target, level);   // Increments reference count\r
175 \r
176     if(image)\r
177     {\r
178         image->markShared();\r
179     }\r
180 \r
181     return image;\r
182 }\r
183 \r
184 void Texture::setImage(GLenum format, GLenum type, GLint unpackAlignment, const void *pixels,egl:: Image *image)\r
185 {\r
186     if(pixels && image)\r
187     {\r
188                 image->loadImageData(0, 0, image->getWidth(), image->getHeight(), format, type, unpackAlignment, pixels);\r
189     }\r
190 }\r
191 \r
192 void Texture::setCompressedImage(GLsizei imageSize, const void *pixels, egl::Image *image)\r
193 {\r
194     if(pixels && image)\r
195     {\r
196                 image->loadCompressedData(0, 0, image->getWidth(), image->getHeight(), imageSize, pixels);\r
197     }\r
198 }\r
199 \r
200 void Texture::subImage(GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, GLint unpackAlignment, const void *pixels, egl::Image *image)\r
201 {\r
202         if(!image)\r
203         {\r
204                 return error(GL_INVALID_OPERATION);\r
205         }\r
206 \r
207     if(width + xoffset > image->getWidth() || height + yoffset > image->getHeight())\r
208     {\r
209         return error(GL_INVALID_VALUE);\r
210     }\r
211 \r
212     if(IsCompressed(image->getFormat()))\r
213     {\r
214         return error(GL_INVALID_OPERATION);\r
215     }\r
216 \r
217     if(format != image->getFormat())\r
218     {\r
219         return error(GL_INVALID_OPERATION);\r
220     }\r
221 \r
222     if(pixels)\r
223     {\r
224         image->loadImageData(xoffset, yoffset, width, height, format, type, unpackAlignment, pixels);\r
225     }\r
226 }\r
227 \r
228 void Texture::subImageCompressed(GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void *pixels, egl::Image *image)\r
229 {\r
230         if(!image)\r
231         {\r
232                 return error(GL_INVALID_OPERATION);\r
233         }\r
234 \r
235     if(width + xoffset > image->getWidth() || height + yoffset > image->getHeight())\r
236     {\r
237         return error(GL_INVALID_VALUE);\r
238     }\r
239 \r
240     if(format != image->getFormat())\r
241     {\r
242         return error(GL_INVALID_OPERATION);\r
243     }\r
244 \r
245     if(pixels)\r
246     {\r
247                 image->loadCompressedData(xoffset, yoffset, width, height, imageSize, pixels);\r
248     }\r
249 }\r
250 \r
251 bool Texture::copy(egl::Image *source, const sw::Rect &sourceRect, GLenum destFormat, GLint xoffset, GLint yoffset, egl::Image *dest)\r
252 {\r
253     Device *device = getDevice();\r
254         \r
255     sw::Rect destRect = {xoffset, yoffset, xoffset + (sourceRect.x1 - sourceRect.x0), yoffset + (sourceRect.y1 - sourceRect.y0)};\r
256     bool success = device->stretchRect(source, &sourceRect, dest, &destRect, false);\r
257 \r
258     if(!success)\r
259     {\r
260         return error(GL_OUT_OF_MEMORY, false);\r
261     }\r
262 \r
263     return true;\r
264 }\r
265 \r
266 bool Texture::isMipmapFiltered() const\r
267 {\r
268         switch(mMinFilter)\r
269     {\r
270     case GL_NEAREST:\r
271     case GL_LINEAR:\r
272         return false;\r
273     case GL_NEAREST_MIPMAP_NEAREST:\r
274     case GL_LINEAR_MIPMAP_NEAREST:\r
275     case GL_NEAREST_MIPMAP_LINEAR:\r
276     case GL_LINEAR_MIPMAP_LINEAR:\r
277         return true;\r
278     default: UNREACHABLE();\r
279     }\r
280 \r
281         return false;\r
282 }\r
283 \r
284 Texture2D::Texture2D(GLuint id) : Texture(id)\r
285 {\r
286         for(int i = 0; i < MIPMAP_LEVELS; i++)\r
287         {\r
288                 image[i] = 0;\r
289         }\r
290 \r
291     mSurface = NULL;\r
292 \r
293         mColorbufferProxy = NULL;\r
294         mProxyRefs = 0;\r
295 }\r
296 \r
297 Texture2D::~Texture2D()\r
298 {\r
299         resource->lock(sw::DESTRUCT);\r
300 \r
301         for(int i = 0; i < MIPMAP_LEVELS; i++)\r
302         {\r
303                 if(image[i])\r
304                 {\r
305                         image[i]->unbind();\r
306                         image[i] = 0;\r
307                 }\r
308         }\r
309 \r
310         resource->unlock();\r
311 \r
312     if(mSurface)\r
313     {\r
314         mSurface->setBoundTexture(NULL);\r
315         mSurface = NULL;\r
316     }\r
317 \r
318         mColorbufferProxy = NULL;\r
319 }\r
320 \r
321 // We need to maintain a count of references to renderbuffers acting as \r
322 // proxies for this texture, so that we do not attempt to use a pointer \r
323 // to a renderbuffer proxy which has been deleted.\r
324 void Texture2D::addProxyRef(const Renderbuffer *proxy)\r
325 {\r
326     mProxyRefs++;\r
327 }\r
328 \r
329 void Texture2D::releaseProxy(const Renderbuffer *proxy)\r
330 {\r
331     if(mProxyRefs > 0)\r
332         {\r
333         mProxyRefs--;\r
334         }\r
335 \r
336     if(mProxyRefs == 0)\r
337         {\r
338                 mColorbufferProxy = NULL;\r
339         }\r
340 }\r
341 \r
342 GLenum Texture2D::getTarget() const\r
343 {\r
344     return GL_TEXTURE_2D;\r
345 }\r
346 \r
347 GLsizei Texture2D::getWidth(GLenum target, GLint level) const\r
348 {\r
349         ASSERT(target == GL_TEXTURE_2D);\r
350     return image[level] ? image[level]->getWidth() : 0;\r
351 }\r
352 \r
353 GLsizei Texture2D::getHeight(GLenum target, GLint level) const\r
354 {\r
355         ASSERT(target == GL_TEXTURE_2D);\r
356     return image[level] ? image[level]->getHeight() : 0;\r
357 }\r
358 \r
359 GLenum Texture2D::getFormat(GLenum target, GLint level) const\r
360 {\r
361         ASSERT(target == GL_TEXTURE_2D);\r
362     return image[level] ? image[level]->getFormat() : 0;\r
363 }\r
364 \r
365 GLenum Texture2D::getType(GLenum target, GLint level) const\r
366 {\r
367         ASSERT(target == GL_TEXTURE_2D);\r
368     return image[level] ? image[level]->getType() : 0;\r
369 }\r
370 \r
371 sw::Format Texture2D::getInternalFormat(GLenum target, GLint level) const\r
372 {\r
373         ASSERT(target == GL_TEXTURE_2D);\r
374         return image[level] ? image[level]->getInternalFormat() : sw::FORMAT_NULL;\r
375 }\r
376 \r
377 int Texture2D::getLevelCount() const\r
378 {\r
379         ASSERT(isSamplerComplete());\r
380         int levels = 0;\r
381 \r
382         while(levels < MIPMAP_LEVELS && image[levels])\r
383         {\r
384                 levels++;\r
385         }\r
386 \r
387         return levels;\r
388 }\r
389 \r
390 void Texture2D::setImage(GLint level, GLsizei width, GLsizei height, GLenum format, GLenum type, GLint unpackAlignment, const void *pixels)\r
391 {\r
392         if(image[level])\r
393         {\r
394                 image[level]->unbind();\r
395         }\r
396 \r
397         image[level] = new Image(this, width, height, format, type);\r
398 \r
399         if(!image[level])\r
400         {\r
401                 return error(GL_OUT_OF_MEMORY);\r
402         }\r
403 \r
404     Texture::setImage(format, type, unpackAlignment, pixels, image[level]);\r
405 }\r
406 \r
407 void Texture2D::bindTexImage(egl::Surface *surface)\r
408 {\r
409     GLenum format;\r
410 \r
411     switch(surface->getInternalFormat())\r
412     {\r
413     case sw::FORMAT_A8R8G8B8:\r
414         format = GL_RGBA;\r
415         break;\r
416     case sw::FORMAT_X8R8G8B8:\r
417         format = GL_RGB;\r
418         break;\r
419     default:\r
420         UNIMPLEMENTED();\r
421         return;\r
422     }\r
423 \r
424         for(int level = 0; level < MIPMAP_LEVELS; level++)\r
425         {\r
426                 if(image[level])\r
427                 {\r
428                         image[level]->unbind();\r
429                         image[level] = 0;\r
430                 }\r
431         }\r
432 \r
433         image[0] = surface->getRenderTarget();\r
434 \r
435     mSurface = surface;\r
436     mSurface->setBoundTexture(this);\r
437 }\r
438 \r
439 void Texture2D::releaseTexImage()\r
440 {\r
441     for(int level = 0; level < MIPMAP_LEVELS; level++)\r
442         {\r
443                 if(image[level])\r
444                 {\r
445                         image[level]->unbind();\r
446                         image[level] = 0;\r
447                 }\r
448         }\r
449 }\r
450 \r
451 void Texture2D::setCompressedImage(GLint level, GLenum format, GLsizei width, GLsizei height, GLsizei imageSize, const void *pixels)\r
452 {\r
453         if(image[level])\r
454         {\r
455                 image[level]->unbind();\r
456         }\r
457 \r
458         image[level] = new Image(this, width, height, format, GL_UNSIGNED_BYTE);\r
459 \r
460         if(!image[level])\r
461         {\r
462                 return error(GL_OUT_OF_MEMORY);\r
463         }\r
464 \r
465     Texture::setCompressedImage(imageSize, pixels, image[level]);\r
466 }\r
467 \r
468 void Texture2D::subImage(GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, GLint unpackAlignment, const void *pixels)\r
469 {\r
470         Texture::subImage(xoffset, yoffset, width, height, format, type, unpackAlignment, pixels, image[level]);\r
471 }\r
472 \r
473 void Texture2D::subImageCompressed(GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void *pixels)\r
474 {\r
475     Texture::subImageCompressed(xoffset, yoffset, width, height, format, imageSize, pixels, image[level]);\r
476 }\r
477 \r
478 void Texture2D::copyImage(GLint level, GLenum format, GLint x, GLint y, GLsizei width, GLsizei height, Framebuffer *source)\r
479 {\r
480     egl::Image *renderTarget = source->getRenderTarget();\r
481 \r
482     if(!renderTarget)\r
483     {\r
484         ERR("Failed to retrieve the render target.");\r
485         return error(GL_OUT_OF_MEMORY);\r
486     }\r
487 \r
488         if(image[level])\r
489         {\r
490                 image[level]->unbind();\r
491         }\r
492 \r
493         image[level] = new Image(this, width, height, format, GL_UNSIGNED_BYTE);\r
494 \r
495         if(!image[level])\r
496         {\r
497                 return error(GL_OUT_OF_MEMORY);\r
498         }\r
499 \r
500     if(width != 0 && height != 0)\r
501     {\r
502                 sw::Rect sourceRect = {x, y, x + width, y + height};\r
503                 sourceRect.clip(0, 0, source->getColorbuffer()->getWidth(), source->getColorbuffer()->getHeight());\r
504 \r
505         copy(renderTarget, sourceRect, format, 0, 0, image[level]);\r
506     }\r
507 \r
508         renderTarget->release();\r
509 }\r
510 \r
511 void Texture2D::copySubImage(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height, Framebuffer *source)\r
512 {\r
513         if(!image[level])\r
514         {\r
515                 return error(GL_INVALID_OPERATION);\r
516         }\r
517 \r
518     if(xoffset + width > image[level]->getWidth() || yoffset + height > image[level]->getHeight())\r
519     {\r
520         return error(GL_INVALID_VALUE);\r
521     }\r
522 \r
523         egl::Image *renderTarget = source->getRenderTarget();\r
524 \r
525     if(!renderTarget)\r
526     {\r
527         ERR("Failed to retrieve the render target.");\r
528         return error(GL_OUT_OF_MEMORY);\r
529     }\r
530 \r
531         sw::Rect sourceRect = {x, y, x + width, y + height};\r
532         sourceRect.clip(0, 0, source->getColorbuffer()->getWidth(), source->getColorbuffer()->getHeight());\r
533 \r
534         copy(renderTarget, sourceRect, image[level]->getFormat(), xoffset, yoffset, image[level]);\r
535 \r
536         renderTarget->release();\r
537 }\r
538 \r
539 void Texture2D::setImage(egl::Image *sharedImage)\r
540 {\r
541         sharedImage->addRef();\r
542 \r
543     if(image[0])\r
544     {\r
545         image[0]->unbind();\r
546     }\r
547 \r
548     image[0] = sharedImage;\r
549 }\r
550 \r
551 // Tests for 2D texture sampling completeness. [OpenGL ES 2.0.24] section 3.8.2 page 85.\r
552 bool Texture2D::isSamplerComplete() const\r
553 {\r
554         if(!image[0])\r
555         {\r
556                 return false;\r
557         }\r
558 \r
559     GLsizei width = image[0]->getWidth();\r
560     GLsizei height = image[0]->getHeight();\r
561 \r
562     if(width <= 0 || height <= 0)\r
563     {\r
564         return false;\r
565     }\r
566 \r
567     if(isMipmapFiltered())\r
568     {\r
569         if(!isMipmapComplete())\r
570         {\r
571             return false;\r
572         }\r
573     }\r
574 \r
575     return true;\r
576 }\r
577 \r
578 // Tests for 2D texture (mipmap) completeness. [OpenGL ES 2.0.24] section 3.7.10 page 81.\r
579 bool Texture2D::isMipmapComplete() const\r
580 {\r
581     GLsizei width = image[0]->getWidth();\r
582     GLsizei height = image[0]->getHeight();\r
583 \r
584     int q = log2(std::max(width, height));\r
585 \r
586     for(int level = 1; level <= q; level++)\r
587     {\r
588                 if(!image[level])\r
589                 {\r
590                         return false;\r
591                 }\r
592 \r
593         if(image[level]->getFormat() != image[0]->getFormat())\r
594         {\r
595             return false;\r
596         }\r
597 \r
598         if(image[level]->getType() != image[0]->getType())\r
599         {\r
600             return false;\r
601         }\r
602 \r
603         if(image[level]->getWidth() != std::max(1, width >> level))\r
604         {\r
605             return false;\r
606         }\r
607 \r
608         if(image[level]->getHeight() != std::max(1, height >> level))\r
609         {\r
610             return false;\r
611         }\r
612     }\r
613 \r
614     return true;\r
615 }\r
616 \r
617 bool Texture2D::isCompressed(GLenum target, GLint level) const\r
618 {\r
619     return IsCompressed(getFormat(target, level));\r
620 }\r
621 \r
622 bool Texture2D::isDepth(GLenum target, GLint level) const\r
623 {\r
624     return IsDepthTexture(getFormat(target, level));\r
625 }\r
626 \r
627 void Texture2D::generateMipmaps()\r
628 {\r
629         if(!image[0])\r
630         {\r
631                 return;   // FIXME: error?\r
632         }\r
633 \r
634     unsigned int q = log2(std::max(image[0]->getWidth(), image[0]->getHeight()));\r
635     \r
636         for(unsigned int i = 1; i <= q; i++)\r
637     {\r
638                 if(image[i])\r
639                 {\r
640                         image[i]->unbind();\r
641                 }\r
642 \r
643                 image[i] = new Image(this, std::max(image[0]->getWidth() >> i, 1), std::max(image[0]->getHeight() >> i, 1), image[0]->getFormat(), image[0]->getType());\r
644 \r
645                 if(!image[i])\r
646                 {\r
647                         return error(GL_OUT_OF_MEMORY);\r
648                 }\r
649 \r
650                 getDevice()->stretchRect(image[i - 1], 0, image[i], 0, true);\r
651     }\r
652 }\r
653 \r
654 egl::Image *Texture2D::getImage(unsigned int level)\r
655 {\r
656         return image[level];\r
657 }\r
658 \r
659 Renderbuffer *Texture2D::getRenderbuffer(GLenum target)\r
660 {\r
661     if(target != GL_TEXTURE_2D)\r
662     {\r
663         return error(GL_INVALID_OPERATION, (Renderbuffer *)NULL);\r
664     }\r
665 \r
666     if(mColorbufferProxy == NULL)\r
667     {\r
668         mColorbufferProxy = new Renderbuffer(id(), new RenderbufferTexture2D(this));\r
669     }\r
670 \r
671     return mColorbufferProxy;\r
672 }\r
673 \r
674 egl::Image *Texture2D::getRenderTarget(GLenum target, unsigned int level)\r
675 {\r
676     ASSERT(target == GL_TEXTURE_2D);\r
677         ASSERT(level < IMPLEMENTATION_MAX_TEXTURE_LEVELS);\r
678 \r
679         if(image[level])\r
680         {\r
681                 image[level]->addRef();\r
682         }\r
683 \r
684         return image[level];\r
685 }\r
686 \r
687 bool Texture2D::isShared(GLenum target, unsigned int level) const\r
688 {\r
689     ASSERT(target == GL_TEXTURE_2D);\r
690     ASSERT(level < IMPLEMENTATION_MAX_TEXTURE_LEVELS);\r
691 \r
692     if(mSurface)   // Bound to an EGLSurface\r
693     {\r
694         return true;\r
695     }\r
696 \r
697     if(!image[level])\r
698     {\r
699         return false;\r
700     }\r
701 \r
702     return image[level]->isShared();\r
703 }\r
704 \r
705 TextureCubeMap::TextureCubeMap(GLuint id) : Texture(id)\r
706 {\r
707         for(int f = 0; f < 6; f++)\r
708         {\r
709                 for(int i = 0; i < MIPMAP_LEVELS; i++)\r
710                 {\r
711                         image[f][i] = 0;\r
712                 }\r
713         }\r
714 \r
715         for(int f = 0; f < 6; f++)\r
716     {\r
717         mFaceProxies[f] = NULL;\r
718         mFaceProxyRefs[f] = 0;\r
719         }\r
720 }\r
721 \r
722 TextureCubeMap::~TextureCubeMap()\r
723 {\r
724         resource->lock(sw::DESTRUCT);\r
725 \r
726         for(int f = 0; f < 6; f++)\r
727         {\r
728                 for(int i = 0; i < MIPMAP_LEVELS; i++)\r
729                 {\r
730                         if(image[f][i])\r
731                         {\r
732                                 image[f][i]->unbind();\r
733                                 image[f][i] = 0;\r
734                         }\r
735                 }\r
736         }\r
737 \r
738         resource->unlock();\r
739 \r
740     for(int i = 0; i < 6; i++)\r
741     {\r
742         mFaceProxies[i] = NULL;\r
743     }\r
744 }\r
745 \r
746 // We need to maintain a count of references to renderbuffers acting as \r
747 // proxies for this texture, so that the texture is not deleted while \r
748 // proxy references still exist. If the reference count drops to zero,\r
749 // we set our proxy pointer NULL, so that a new attempt at referencing\r
750 // will cause recreation.\r
751 void TextureCubeMap::addProxyRef(const Renderbuffer *proxy)\r
752 {\r
753     for(int f = 0; f < 6; f++)\r
754     {\r
755         if(mFaceProxies[f] == proxy)\r
756         {\r
757                         mFaceProxyRefs[f]++;\r
758                 }\r
759         }\r
760 }\r
761 \r
762 void TextureCubeMap::releaseProxy(const Renderbuffer *proxy)\r
763 {\r
764     for(int f = 0; f < 6; f++)\r
765     {\r
766         if(mFaceProxies[f] == proxy)\r
767         {\r
768             if(mFaceProxyRefs[f] > 0)\r
769                         {\r
770                                 mFaceProxyRefs[f]--;\r
771                         }\r
772 \r
773             if(mFaceProxyRefs[f] == 0)\r
774                         {\r
775                                 mFaceProxies[f] = NULL;\r
776                         }\r
777                 }\r
778     }\r
779 }\r
780 \r
781 GLenum TextureCubeMap::getTarget() const\r
782 {\r
783     return GL_TEXTURE_CUBE_MAP;\r
784 }\r
785 \r
786 GLsizei TextureCubeMap::getWidth(GLenum target, GLint level) const\r
787 {\r
788         int face = CubeFaceIndex(target);\r
789     return image[face][level] ? image[face][level]->getWidth() : 0;\r
790 }\r
791 \r
792 GLsizei TextureCubeMap::getHeight(GLenum target, GLint level) const\r
793 {\r
794         int face = CubeFaceIndex(target);\r
795     return image[face][level] ? image[face][level]->getHeight() : 0;\r
796 }\r
797 \r
798 GLenum TextureCubeMap::getFormat(GLenum target, GLint level) const\r
799 {\r
800         int face = CubeFaceIndex(target);\r
801     return image[face][level] ? image[face][level]->getFormat() : 0;\r
802 }\r
803 \r
804 GLenum TextureCubeMap::getType(GLenum target, GLint level) const\r
805 {\r
806         int face = CubeFaceIndex(target);\r
807     return image[face][level] ? image[face][level]->getType() : 0;\r
808 }\r
809 \r
810 sw::Format TextureCubeMap::getInternalFormat(GLenum target, GLint level) const\r
811 {\r
812         int face = CubeFaceIndex(target);\r
813     return image[face][level] ? image[face][level]->getInternalFormat() : sw::FORMAT_NULL;\r
814 }\r
815 \r
816 int TextureCubeMap::getLevelCount() const\r
817 {\r
818         ASSERT(isSamplerComplete());\r
819         int levels = 0;\r
820 \r
821         while(levels < MIPMAP_LEVELS && image[0][levels])\r
822         {\r
823                 levels++;\r
824         }\r
825 \r
826         return levels;\r
827 }\r
828 \r
829 void TextureCubeMap::setCompressedImage(GLenum target, GLint level, GLenum format, GLsizei width, GLsizei height, GLsizei imageSize, const void *pixels)\r
830 {\r
831         int face = CubeFaceIndex(target);\r
832 \r
833         if(image[face][level])\r
834         {\r
835                 image[face][level]->unbind();\r
836         }\r
837 \r
838         image[face][level] = new Image(this, width, height, format, GL_UNSIGNED_BYTE);\r
839 \r
840         if(!image[face][level])\r
841         {\r
842                 return error(GL_OUT_OF_MEMORY);\r
843         }\r
844 \r
845     Texture::setCompressedImage(imageSize, pixels, image[face][level]);\r
846 }\r
847 \r
848 void TextureCubeMap::subImage(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, GLint unpackAlignment, const void *pixels)\r
849 {\r
850     Texture::subImage(xoffset, yoffset, width, height, format, type, unpackAlignment, pixels, image[CubeFaceIndex(target)][level]);\r
851 }\r
852 \r
853 void TextureCubeMap::subImageCompressed(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void *pixels)\r
854 {\r
855     Texture::subImageCompressed(xoffset, yoffset, width, height, format, imageSize, pixels, image[CubeFaceIndex(target)][level]);\r
856 }\r
857 \r
858 // Tests for cube map sampling completeness. [OpenGL ES 2.0.24] section 3.8.2 page 86.\r
859 bool TextureCubeMap::isSamplerComplete() const\r
860 {\r
861         for(int face = 0; face < 6; face++)\r
862     {\r
863                 if(!image[face][0])\r
864                 {\r
865                         return false;\r
866                 }\r
867         }\r
868 \r
869     int size = image[0][0]->getWidth();\r
870 \r
871     if(size <= 0)\r
872     {\r
873         return false;\r
874     }\r
875 \r
876     if(!isMipmapFiltered())\r
877     {\r
878         if(!isCubeComplete())\r
879         {\r
880             return false;\r
881         }\r
882     }\r
883     else\r
884     {\r
885         if(!isMipmapCubeComplete())   // Also tests for isCubeComplete()\r
886         {\r
887             return false;\r
888         }\r
889     }\r
890 \r
891     return true;\r
892 }\r
893 \r
894 // Tests for cube texture completeness. [OpenGL ES 2.0.24] section 3.7.10 page 81.\r
895 bool TextureCubeMap::isCubeComplete() const\r
896 {\r
897     if(image[0][0]->getWidth() <= 0 || image[0][0]->getHeight() != image[0][0]->getWidth())\r
898     {\r
899         return false;\r
900     }\r
901 \r
902     for(unsigned int face = 1; face < 6; face++)\r
903     {\r
904         if(image[face][0]->getWidth()  != image[0][0]->getWidth() ||\r
905            image[face][0]->getWidth()  != image[0][0]->getHeight() ||\r
906            image[face][0]->getFormat() != image[0][0]->getFormat() ||\r
907            image[face][0]->getType()   != image[0][0]->getType())\r
908         {\r
909             return false;\r
910         }\r
911     }\r
912 \r
913     return true;\r
914 }\r
915 \r
916 bool TextureCubeMap::isMipmapCubeComplete() const\r
917 {\r
918     if(!isCubeComplete())\r
919     {\r
920         return false;\r
921     }\r
922 \r
923     GLsizei size = image[0][0]->getWidth();\r
924     int q = log2(size);\r
925 \r
926     for(int face = 0; face < 6; face++)\r
927     {\r
928         for(int level = 1; level <= q; level++)\r
929         {\r
930                         if(!image[face][level])\r
931                         {\r
932                                 return false;\r
933                         }\r
934 \r
935             if(image[face][level]->getFormat() != image[0][0]->getFormat())\r
936             {\r
937                 return false;\r
938             }\r
939 \r
940             if(image[face][level]->getType() != image[0][0]->getType())\r
941             {\r
942                 return false;\r
943             }\r
944 \r
945             if(image[face][level]->getWidth() != std::max(1, size >> level))\r
946             {\r
947                 return false;\r
948             }\r
949         }\r
950     }\r
951 \r
952     return true;\r
953 }\r
954 \r
955 bool TextureCubeMap::isCompressed(GLenum target, GLint level) const\r
956 {\r
957     return IsCompressed(getFormat(target, level));\r
958 }\r
959 \r
960 bool TextureCubeMap::isDepth(GLenum target, GLint level) const\r
961 {\r
962     return IsDepthTexture(getFormat(target, level));\r
963 }\r
964 \r
965 void TextureCubeMap::setImage(GLenum target, GLint level, GLsizei width, GLsizei height, GLenum format, GLenum type, GLint unpackAlignment, const void *pixels)\r
966 {\r
967         int face = CubeFaceIndex(target);\r
968 \r
969         if(image[face][level])\r
970         {\r
971                 image[face][level]->unbind();\r
972         }\r
973 \r
974         image[face][level] = new Image(this, width, height, format, type);\r
975 \r
976         if(!image[face][level])\r
977         {\r
978                 return error(GL_OUT_OF_MEMORY);\r
979         }\r
980 \r
981     Texture::setImage(format, type, unpackAlignment, pixels, image[face][level]);\r
982 }\r
983 \r
984 void TextureCubeMap::copyImage(GLenum target, GLint level, GLenum format, GLint x, GLint y, GLsizei width, GLsizei height, Framebuffer *source)\r
985 {\r
986         egl::Image *renderTarget = source->getRenderTarget();\r
987 \r
988     if(!renderTarget)\r
989     {\r
990         ERR("Failed to retrieve the render target.");\r
991         return error(GL_OUT_OF_MEMORY);\r
992     }\r
993 \r
994         int face = CubeFaceIndex(target);\r
995 \r
996         if(image[face][level])\r
997         {\r
998                 image[face][level]->unbind();\r
999         }\r
1000 \r
1001         image[face][level] = new Image(this, width, height, format, GL_UNSIGNED_BYTE);\r
1002 \r
1003         if(!image[face][level])\r
1004         {\r
1005                 return error(GL_OUT_OF_MEMORY);\r
1006         }\r
1007 \r
1008     if(width != 0 && height != 0)\r
1009     {\r
1010                 sw::Rect sourceRect = {x, y, x + width, y + height};\r
1011                 sourceRect.clip(0, 0, source->getColorbuffer()->getWidth(), source->getColorbuffer()->getHeight());\r
1012         \r
1013         copy(renderTarget, sourceRect, format, 0, 0, image[face][level]);\r
1014     }\r
1015 \r
1016         renderTarget->release();\r
1017 }\r
1018 \r
1019 Image *TextureCubeMap::getImage(int face, unsigned int level)\r
1020 {\r
1021         return image[face][level];\r
1022 }\r
1023 \r
1024 Image *TextureCubeMap::getImage(GLenum face, unsigned int level)\r
1025 {\r
1026     return image[CubeFaceIndex(face)][level];\r
1027 }\r
1028 \r
1029 void TextureCubeMap::copySubImage(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height, Framebuffer *source)\r
1030 {\r
1031         int face = CubeFaceIndex(target);\r
1032 \r
1033         if(!image[face][level])\r
1034         {\r
1035                 return error(GL_INVALID_OPERATION);\r
1036         }\r
1037 \r
1038     GLsizei size = image[face][level]->getWidth();\r
1039 \r
1040     if(xoffset + width > size || yoffset + height > size)\r
1041     {\r
1042         return error(GL_INVALID_VALUE);\r
1043     }\r
1044 \r
1045     egl::Image *renderTarget = source->getRenderTarget();\r
1046 \r
1047     if(!renderTarget)\r
1048     {\r
1049         ERR("Failed to retrieve the render target.");\r
1050         return error(GL_OUT_OF_MEMORY);\r
1051     }\r
1052 \r
1053         sw::Rect sourceRect = {x, y, x + width, y + height};\r
1054         sourceRect.clip(0, 0, source->getColorbuffer()->getWidth(), source->getColorbuffer()->getHeight());\r
1055 \r
1056         copy(renderTarget, sourceRect, image[face][level]->getFormat(), xoffset, yoffset, image[face][level]);\r
1057 \r
1058         renderTarget->release();\r
1059 }\r
1060 \r
1061 void TextureCubeMap::generateMipmaps()\r
1062 {\r
1063     if(!isCubeComplete())\r
1064     {\r
1065         return error(GL_INVALID_OPERATION);\r
1066     }\r
1067 \r
1068     unsigned int q = log2(image[0][0]->getWidth());\r
1069 \r
1070         for(unsigned int f = 0; f < 6; f++)\r
1071     {\r
1072                 for(unsigned int i = 1; i <= q; i++)\r
1073                 {\r
1074                         if(image[f][i])\r
1075                         {\r
1076                                 image[f][i]->unbind();\r
1077                         }\r
1078 \r
1079                         image[f][i] = new 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
1080 \r
1081                         if(!image[f][i])\r
1082                         {\r
1083                                 return error(GL_OUT_OF_MEMORY);\r
1084                         }\r
1085 \r
1086                         getDevice()->stretchRect(image[f][i - 1], 0, image[f][i], 0, true);\r
1087                 }\r
1088         }\r
1089 }\r
1090 \r
1091 Renderbuffer *TextureCubeMap::getRenderbuffer(GLenum target)\r
1092 {\r
1093     if(!IsCubemapTextureTarget(target))\r
1094     {\r
1095         return error(GL_INVALID_OPERATION, (Renderbuffer *)NULL);\r
1096     }\r
1097 \r
1098     int face = CubeFaceIndex(target);\r
1099 \r
1100     if(mFaceProxies[face] == NULL)\r
1101     {\r
1102         mFaceProxies[face] = new Renderbuffer(id(), new RenderbufferTextureCubeMap(this, target));\r
1103     }\r
1104 \r
1105     return mFaceProxies[face];\r
1106 }\r
1107 \r
1108 Image *TextureCubeMap::getRenderTarget(GLenum target, unsigned int level)\r
1109 {\r
1110     ASSERT(IsCubemapTextureTarget(target));\r
1111     ASSERT(level < IMPLEMENTATION_MAX_TEXTURE_LEVELS);\r
1112     \r
1113         int face = CubeFaceIndex(target);\r
1114 \r
1115         if(image[face][level])\r
1116         {\r
1117                 image[face][level]->addRef();\r
1118         }\r
1119 \r
1120         return image[face][level];\r
1121 }\r
1122 \r
1123 bool TextureCubeMap::isShared(GLenum target, unsigned int level) const\r
1124 {\r
1125     ASSERT(IsCubemapTextureTarget(target));\r
1126     ASSERT(level < IMPLEMENTATION_MAX_TEXTURE_LEVELS);\r
1127 \r
1128     int face = CubeFaceIndex(target);\r
1129 \r
1130     if(!image[face][level])\r
1131     {\r
1132         return false;\r
1133     }\r
1134 \r
1135     return image[face][level]->isShared();\r
1136 }\r
1137 \r
1138 TextureExternal::TextureExternal(GLuint id) : Texture2D(id)\r
1139 {\r
1140     mMinFilter = GL_LINEAR;\r
1141     mMagFilter = GL_LINEAR;\r
1142     mWrapS = GL_CLAMP_TO_EDGE;\r
1143     mWrapT = GL_CLAMP_TO_EDGE;\r
1144 }\r
1145 \r
1146 TextureExternal::~TextureExternal()\r
1147 {\r
1148 }\r
1149 \r
1150 GLenum TextureExternal::getTarget() const\r
1151 {\r
1152     return GL_TEXTURE_EXTERNAL_OES;\r
1153 }\r
1154 \r
1155 }\r
1156 \r
1157 // Exported functions for use by EGL\r
1158 extern "C"\r
1159 {\r
1160         egl::Image *createBackBuffer(int width, int height, const egl::Config *config)\r
1161         {\r
1162                 if(config)\r
1163                 {\r
1164                         return new es2::Image(0, width, height, config->mAlphaSize ? GL_RGBA : GL_RGB, GL_UNSIGNED_BYTE);\r
1165                 }\r
1166 \r
1167                 return 0;\r
1168         }\r
1169 \r
1170         egl::Image *createDepthStencil(unsigned int width, unsigned int height, sw::Format format, int multiSampleDepth, bool discard)\r
1171         {\r
1172                 if(width == 0 || height == 0 || height > OUTLINE_RESOLUTION)\r
1173                 {\r
1174                         ERR("Invalid parameters");\r
1175                         return 0;\r
1176                 }\r
1177                 \r
1178                 bool lockable = true;\r
1179 \r
1180                 switch(format)\r
1181                 {\r
1182         //      case sw::FORMAT_D15S1:\r
1183                 case sw::FORMAT_D24S8:\r
1184                 case sw::FORMAT_D24X8:\r
1185         //      case sw::FORMAT_D24X4S4:\r
1186                 case sw::FORMAT_D24FS8:\r
1187                 case sw::FORMAT_D32:\r
1188                 case sw::FORMAT_D16:\r
1189                         lockable = false;\r
1190                         break;\r
1191         //      case sw::FORMAT_S8_LOCKABLE:\r
1192         //      case sw::FORMAT_D16_LOCKABLE:\r
1193                 case sw::FORMAT_D32F_LOCKABLE:\r
1194         //      case sw::FORMAT_D32_LOCKABLE:\r
1195                 case sw::FORMAT_DF24S8:\r
1196                 case sw::FORMAT_DF16S8:\r
1197                         lockable = true;\r
1198                         break;\r
1199                 default:\r
1200                         UNREACHABLE();\r
1201                 }\r
1202 \r
1203                 es2::Image *surface = new es2::Image(0, width, height, format, multiSampleDepth, lockable, true);\r
1204 \r
1205                 if(!surface)\r
1206                 {\r
1207                         ERR("Out of memory");\r
1208                         return 0;\r
1209                 }\r
1210 \r
1211                 return surface;\r
1212         }\r
1213 }\r