OSDN Git Service

Implement a Radiance prototype.
[android-x86/external-swiftshader.git] / src / Radiance / libRAD / 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 // Tests for 2D texture sampling completeness. [OpenGL ES 2.0.24] section 3.8.2 page 85.\r
540 bool Texture2D::isSamplerComplete() const\r
541 {\r
542         if(!image[0])\r
543         {\r
544                 return false;\r
545         }\r
546 \r
547     GLsizei width = image[0]->getWidth();\r
548     GLsizei height = image[0]->getHeight();\r
549 \r
550     if(width <= 0 || height <= 0)\r
551     {\r
552         return false;\r
553     }\r
554 \r
555     if(isMipmapFiltered())\r
556     {\r
557         if(!isMipmapComplete())\r
558         {\r
559             return false;\r
560         }\r
561     }\r
562 \r
563     return true;\r
564 }\r
565 \r
566 // Tests for 2D texture (mipmap) completeness. [OpenGL ES 2.0.24] section 3.7.10 page 81.\r
567 bool Texture2D::isMipmapComplete() const\r
568 {\r
569     GLsizei width = image[0]->getWidth();\r
570     GLsizei height = image[0]->getHeight();\r
571 \r
572     int q = log2(std::max(width, height));\r
573 \r
574     for(int level = 1; level <= q; level++)\r
575     {\r
576                 if(!image[level])\r
577                 {\r
578                         return false;\r
579                 }\r
580 \r
581         if(image[level]->getFormat() != image[0]->getFormat())\r
582         {\r
583             return false;\r
584         }\r
585 \r
586         if(image[level]->getType() != image[0]->getType())\r
587         {\r
588             return false;\r
589         }\r
590 \r
591         if(image[level]->getWidth() != std::max(1, width >> level))\r
592         {\r
593             return false;\r
594         }\r
595 \r
596         if(image[level]->getHeight() != std::max(1, height >> level))\r
597         {\r
598             return false;\r
599         }\r
600     }\r
601 \r
602     return true;\r
603 }\r
604 \r
605 bool Texture2D::isCompressed(GLenum target, GLint level) const\r
606 {\r
607     return IsCompressed(getFormat(target, level));\r
608 }\r
609 \r
610 bool Texture2D::isDepth(GLenum target, GLint level) const\r
611 {\r
612     return IsDepthTexture(getFormat(target, level));\r
613 }\r
614 \r
615 void Texture2D::generateMipmaps()\r
616 {\r
617         if(!image[0])\r
618         {\r
619                 return;   // FIXME: error?\r
620         }\r
621 \r
622     unsigned int q = log2(std::max(image[0]->getWidth(), image[0]->getHeight()));\r
623     \r
624         for(unsigned int i = 1; i <= q; i++)\r
625     {\r
626                 if(image[i])\r
627                 {\r
628                         image[i]->unbind();\r
629                 }\r
630 \r
631                 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
632 \r
633                 if(!image[i])\r
634                 {\r
635                         return error(GL_OUT_OF_MEMORY);\r
636                 }\r
637 \r
638                 getDevice()->stretchRect(image[i - 1], 0, image[i], 0, true);\r
639     }\r
640 }\r
641 \r
642 egl::Image *Texture2D::getImage(unsigned int level)\r
643 {\r
644         return image[level];\r
645 }\r
646 \r
647 Renderbuffer *Texture2D::getRenderbuffer(GLenum target)\r
648 {\r
649     if(target != GL_TEXTURE_2D)\r
650     {\r
651         return error(GL_INVALID_OPERATION, (Renderbuffer *)NULL);\r
652     }\r
653 \r
654     if(mColorbufferProxy == NULL)\r
655     {\r
656         mColorbufferProxy = new Renderbuffer(id(), new RenderbufferTexture2D(this));\r
657     }\r
658 \r
659     return mColorbufferProxy;\r
660 }\r
661 \r
662 egl::Image *Texture2D::getRenderTarget(GLenum target, unsigned int level)\r
663 {\r
664     ASSERT(target == GL_TEXTURE_2D);\r
665         ASSERT(level < IMPLEMENTATION_MAX_TEXTURE_LEVELS);\r
666 \r
667         if(image[level])\r
668         {\r
669                 image[level]->addRef();\r
670         }\r
671 \r
672         return image[level];\r
673 }\r
674 \r
675 bool Texture2D::isShared(GLenum target, unsigned int level) const\r
676 {\r
677     ASSERT(target == GL_TEXTURE_2D);\r
678     ASSERT(level < IMPLEMENTATION_MAX_TEXTURE_LEVELS);\r
679 \r
680     if(mSurface)   // Bound to an EGLSurface\r
681     {\r
682         return true;\r
683     }\r
684 \r
685     if(!image[level])\r
686     {\r
687         return false;\r
688     }\r
689 \r
690     return image[level]->isShared();\r
691 }\r
692 \r
693 TextureCubeMap::TextureCubeMap(GLuint id) : Texture(id)\r
694 {\r
695         for(int f = 0; f < 6; f++)\r
696         {\r
697                 for(int i = 0; i < MIPMAP_LEVELS; i++)\r
698                 {\r
699                         image[f][i] = 0;\r
700                 }\r
701         }\r
702 \r
703         for(int f = 0; f < 6; f++)\r
704     {\r
705         mFaceProxies[f] = NULL;\r
706         mFaceProxyRefs[f] = 0;\r
707         }\r
708 }\r
709 \r
710 TextureCubeMap::~TextureCubeMap()\r
711 {\r
712         resource->lock(sw::DESTRUCT);\r
713 \r
714         for(int f = 0; f < 6; f++)\r
715         {\r
716                 for(int i = 0; i < MIPMAP_LEVELS; i++)\r
717                 {\r
718                         if(image[f][i])\r
719                         {\r
720                                 image[f][i]->unbind();\r
721                                 image[f][i] = 0;\r
722                         }\r
723                 }\r
724         }\r
725 \r
726         resource->unlock();\r
727 \r
728     for(int i = 0; i < 6; i++)\r
729     {\r
730         mFaceProxies[i] = NULL;\r
731     }\r
732 }\r
733 \r
734 // We need to maintain a count of references to renderbuffers acting as \r
735 // proxies for this texture, so that the texture is not deleted while \r
736 // proxy references still exist. If the reference count drops to zero,\r
737 // we set our proxy pointer NULL, so that a new attempt at referencing\r
738 // will cause recreation.\r
739 void TextureCubeMap::addProxyRef(const Renderbuffer *proxy)\r
740 {\r
741     for(int f = 0; f < 6; f++)\r
742     {\r
743         if(mFaceProxies[f] == proxy)\r
744         {\r
745                         mFaceProxyRefs[f]++;\r
746                 }\r
747         }\r
748 }\r
749 \r
750 void TextureCubeMap::releaseProxy(const Renderbuffer *proxy)\r
751 {\r
752     for(int f = 0; f < 6; f++)\r
753     {\r
754         if(mFaceProxies[f] == proxy)\r
755         {\r
756             if(mFaceProxyRefs[f] > 0)\r
757                         {\r
758                                 mFaceProxyRefs[f]--;\r
759                         }\r
760 \r
761             if(mFaceProxyRefs[f] == 0)\r
762                         {\r
763                                 mFaceProxies[f] = NULL;\r
764                         }\r
765                 }\r
766     }\r
767 }\r
768 \r
769 GLenum TextureCubeMap::getTarget() const\r
770 {\r
771     return GL_TEXTURE_CUBE_MAP;\r
772 }\r
773 \r
774 GLsizei TextureCubeMap::getWidth(GLenum target, GLint level) const\r
775 {\r
776         int face = CubeFaceIndex(target);\r
777     return image[face][level] ? image[face][level]->getWidth() : 0;\r
778 }\r
779 \r
780 GLsizei TextureCubeMap::getHeight(GLenum target, GLint level) const\r
781 {\r
782         int face = CubeFaceIndex(target);\r
783     return image[face][level] ? image[face][level]->getHeight() : 0;\r
784 }\r
785 \r
786 GLenum TextureCubeMap::getFormat(GLenum target, GLint level) const\r
787 {\r
788         int face = CubeFaceIndex(target);\r
789     return image[face][level] ? image[face][level]->getFormat() : 0;\r
790 }\r
791 \r
792 GLenum TextureCubeMap::getType(GLenum target, GLint level) const\r
793 {\r
794         int face = CubeFaceIndex(target);\r
795     return image[face][level] ? image[face][level]->getType() : 0;\r
796 }\r
797 \r
798 sw::Format TextureCubeMap::getInternalFormat(GLenum target, GLint level) const\r
799 {\r
800         int face = CubeFaceIndex(target);\r
801     return image[face][level] ? image[face][level]->getInternalFormat() : sw::FORMAT_NULL;\r
802 }\r
803 \r
804 int TextureCubeMap::getLevelCount() const\r
805 {\r
806         ASSERT(isSamplerComplete());\r
807         int levels = 0;\r
808 \r
809         while(levels < MIPMAP_LEVELS && image[0][levels])\r
810         {\r
811                 levels++;\r
812         }\r
813 \r
814         return levels;\r
815 }\r
816 \r
817 void TextureCubeMap::setCompressedImage(GLenum target, GLint level, GLenum format, GLsizei width, GLsizei height, GLsizei imageSize, const void *pixels)\r
818 {\r
819         int face = CubeFaceIndex(target);\r
820 \r
821         if(image[face][level])\r
822         {\r
823                 image[face][level]->unbind();\r
824         }\r
825 \r
826         image[face][level] = new Image(this, width, height, format, GL_UNSIGNED_BYTE);\r
827 \r
828         if(!image[face][level])\r
829         {\r
830                 return error(GL_OUT_OF_MEMORY);\r
831         }\r
832 \r
833     Texture::setCompressedImage(imageSize, pixels, image[face][level]);\r
834 }\r
835 \r
836 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
837 {\r
838     Texture::subImage(xoffset, yoffset, width, height, format, type, unpackAlignment, pixels, image[CubeFaceIndex(target)][level]);\r
839 }\r
840 \r
841 void TextureCubeMap::subImageCompressed(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void *pixels)\r
842 {\r
843     Texture::subImageCompressed(xoffset, yoffset, width, height, format, imageSize, pixels, image[CubeFaceIndex(target)][level]);\r
844 }\r
845 \r
846 // Tests for cube map sampling completeness. [OpenGL ES 2.0.24] section 3.8.2 page 86.\r
847 bool TextureCubeMap::isSamplerComplete() const\r
848 {\r
849         for(int face = 0; face < 6; face++)\r
850     {\r
851                 if(!image[face][0])\r
852                 {\r
853                         return false;\r
854                 }\r
855         }\r
856 \r
857     int size = image[0][0]->getWidth();\r
858 \r
859     if(size <= 0)\r
860     {\r
861         return false;\r
862     }\r
863 \r
864     if(!isMipmapFiltered())\r
865     {\r
866         if(!isCubeComplete())\r
867         {\r
868             return false;\r
869         }\r
870     }\r
871     else\r
872     {\r
873         if(!isMipmapCubeComplete())   // Also tests for isCubeComplete()\r
874         {\r
875             return false;\r
876         }\r
877     }\r
878 \r
879     return true;\r
880 }\r
881 \r
882 // Tests for cube texture completeness. [OpenGL ES 2.0.24] section 3.7.10 page 81.\r
883 bool TextureCubeMap::isCubeComplete() const\r
884 {\r
885     if(image[0][0]->getWidth() <= 0 || image[0][0]->getHeight() != image[0][0]->getWidth())\r
886     {\r
887         return false;\r
888     }\r
889 \r
890     for(unsigned int face = 1; face < 6; face++)\r
891     {\r
892         if(image[face][0]->getWidth()  != image[0][0]->getWidth() ||\r
893            image[face][0]->getWidth()  != image[0][0]->getHeight() ||\r
894            image[face][0]->getFormat() != image[0][0]->getFormat() ||\r
895            image[face][0]->getType()   != image[0][0]->getType())\r
896         {\r
897             return false;\r
898         }\r
899     }\r
900 \r
901     return true;\r
902 }\r
903 \r
904 bool TextureCubeMap::isMipmapCubeComplete() const\r
905 {\r
906     if(!isCubeComplete())\r
907     {\r
908         return false;\r
909     }\r
910 \r
911     GLsizei size = image[0][0]->getWidth();\r
912     int q = log2(size);\r
913 \r
914     for(int face = 0; face < 6; face++)\r
915     {\r
916         for(int level = 1; level <= q; level++)\r
917         {\r
918                         if(!image[face][level])\r
919                         {\r
920                                 return false;\r
921                         }\r
922 \r
923             if(image[face][level]->getFormat() != image[0][0]->getFormat())\r
924             {\r
925                 return false;\r
926             }\r
927 \r
928             if(image[face][level]->getType() != image[0][0]->getType())\r
929             {\r
930                 return false;\r
931             }\r
932 \r
933             if(image[face][level]->getWidth() != std::max(1, size >> level))\r
934             {\r
935                 return false;\r
936             }\r
937         }\r
938     }\r
939 \r
940     return true;\r
941 }\r
942 \r
943 bool TextureCubeMap::isCompressed(GLenum target, GLint level) const\r
944 {\r
945     return IsCompressed(getFormat(target, level));\r
946 }\r
947 \r
948 bool TextureCubeMap::isDepth(GLenum target, GLint level) const\r
949 {\r
950     return IsDepthTexture(getFormat(target, level));\r
951 }\r
952 \r
953 void TextureCubeMap::setImage(GLenum target, GLint level, GLsizei width, GLsizei height, GLenum format, GLenum type, GLint unpackAlignment, const void *pixels)\r
954 {\r
955         int face = CubeFaceIndex(target);\r
956 \r
957         if(image[face][level])\r
958         {\r
959                 image[face][level]->unbind();\r
960         }\r
961 \r
962         image[face][level] = new Image(this, width, height, format, type);\r
963 \r
964         if(!image[face][level])\r
965         {\r
966                 return error(GL_OUT_OF_MEMORY);\r
967         }\r
968 \r
969     Texture::setImage(format, type, unpackAlignment, pixels, image[face][level]);\r
970 }\r
971 \r
972 void TextureCubeMap::copyImage(GLenum target, GLint level, GLenum format, GLint x, GLint y, GLsizei width, GLsizei height, Framebuffer *source)\r
973 {\r
974         egl::Image *renderTarget = source->getRenderTarget();\r
975 \r
976     if(!renderTarget)\r
977     {\r
978         ERR("Failed to retrieve the render target.");\r
979         return error(GL_OUT_OF_MEMORY);\r
980     }\r
981 \r
982         int face = CubeFaceIndex(target);\r
983 \r
984         if(image[face][level])\r
985         {\r
986                 image[face][level]->unbind();\r
987         }\r
988 \r
989         image[face][level] = new Image(this, width, height, format, GL_UNSIGNED_BYTE);\r
990 \r
991         if(!image[face][level])\r
992         {\r
993                 return error(GL_OUT_OF_MEMORY);\r
994         }\r
995 \r
996     if(width != 0 && height != 0)\r
997     {\r
998                 sw::Rect sourceRect = {x, y, x + width, y + height};\r
999                 sourceRect.clip(0, 0, source->getColorbuffer()->getWidth(), source->getColorbuffer()->getHeight());\r
1000         \r
1001         copy(renderTarget, sourceRect, format, 0, 0, image[face][level]);\r
1002     }\r
1003 \r
1004         renderTarget->release();\r
1005 }\r
1006 \r
1007 Image *TextureCubeMap::getImage(int face, unsigned int level)\r
1008 {\r
1009         return image[face][level];\r
1010 }\r
1011 \r
1012 Image *TextureCubeMap::getImage(GLenum face, unsigned int level)\r
1013 {\r
1014     return image[CubeFaceIndex(face)][level];\r
1015 }\r
1016 \r
1017 void TextureCubeMap::copySubImage(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height, Framebuffer *source)\r
1018 {\r
1019         int face = CubeFaceIndex(target);\r
1020 \r
1021         if(!image[face][level])\r
1022         {\r
1023                 return error(GL_INVALID_OPERATION);\r
1024         }\r
1025 \r
1026     GLsizei size = image[face][level]->getWidth();\r
1027 \r
1028     if(xoffset + width > size || yoffset + height > size)\r
1029     {\r
1030         return error(GL_INVALID_VALUE);\r
1031     }\r
1032 \r
1033     egl::Image *renderTarget = source->getRenderTarget();\r
1034 \r
1035     if(!renderTarget)\r
1036     {\r
1037         ERR("Failed to retrieve the render target.");\r
1038         return error(GL_OUT_OF_MEMORY);\r
1039     }\r
1040 \r
1041         sw::Rect sourceRect = {x, y, x + width, y + height};\r
1042         sourceRect.clip(0, 0, source->getColorbuffer()->getWidth(), source->getColorbuffer()->getHeight());\r
1043 \r
1044         copy(renderTarget, sourceRect, image[face][level]->getFormat(), xoffset, yoffset, image[face][level]);\r
1045 \r
1046         renderTarget->release();\r
1047 }\r
1048 \r
1049 void TextureCubeMap::generateMipmaps()\r
1050 {\r
1051     if(!isCubeComplete())\r
1052     {\r
1053         return error(GL_INVALID_OPERATION);\r
1054     }\r
1055 \r
1056     unsigned int q = log2(image[0][0]->getWidth());\r
1057 \r
1058         for(unsigned int f = 0; f < 6; f++)\r
1059     {\r
1060                 for(unsigned int i = 1; i <= q; i++)\r
1061                 {\r
1062                         if(image[f][i])\r
1063                         {\r
1064                                 image[f][i]->unbind();\r
1065                         }\r
1066 \r
1067                         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
1068 \r
1069                         if(!image[f][i])\r
1070                         {\r
1071                                 return error(GL_OUT_OF_MEMORY);\r
1072                         }\r
1073 \r
1074                         getDevice()->stretchRect(image[f][i - 1], 0, image[f][i], 0, true);\r
1075                 }\r
1076         }\r
1077 }\r
1078 \r
1079 Renderbuffer *TextureCubeMap::getRenderbuffer(GLenum target)\r
1080 {\r
1081     if(!IsCubemapTextureTarget(target))\r
1082     {\r
1083         return error(GL_INVALID_OPERATION, (Renderbuffer *)NULL);\r
1084     }\r
1085 \r
1086     int face = CubeFaceIndex(target);\r
1087 \r
1088     if(mFaceProxies[face] == NULL)\r
1089     {\r
1090         mFaceProxies[face] = new Renderbuffer(id(), new RenderbufferTextureCubeMap(this, target));\r
1091     }\r
1092 \r
1093     return mFaceProxies[face];\r
1094 }\r
1095 \r
1096 Image *TextureCubeMap::getRenderTarget(GLenum target, unsigned int level)\r
1097 {\r
1098     ASSERT(IsCubemapTextureTarget(target));\r
1099     ASSERT(level < IMPLEMENTATION_MAX_TEXTURE_LEVELS);\r
1100     \r
1101         int face = CubeFaceIndex(target);\r
1102 \r
1103         if(image[face][level])\r
1104         {\r
1105                 image[face][level]->addRef();\r
1106         }\r
1107 \r
1108         return image[face][level];\r
1109 }\r
1110 \r
1111 bool TextureCubeMap::isShared(GLenum target, unsigned int level) const\r
1112 {\r
1113     ASSERT(IsCubemapTextureTarget(target));\r
1114     ASSERT(level < IMPLEMENTATION_MAX_TEXTURE_LEVELS);\r
1115 \r
1116     int face = CubeFaceIndex(target);\r
1117 \r
1118     if(!image[face][level])\r
1119     {\r
1120         return false;\r
1121     }\r
1122 \r
1123     return image[face][level]->isShared();\r
1124 }\r
1125 \r
1126 TextureExternal::TextureExternal(GLuint id) : Texture2D(id)\r
1127 {\r
1128     mMinFilter = GL_LINEAR;\r
1129     mMagFilter = GL_LINEAR;\r
1130     mWrapS = GL_CLAMP_TO_EDGE;\r
1131     mWrapT = GL_CLAMP_TO_EDGE;\r
1132 }\r
1133 \r
1134 TextureExternal::~TextureExternal()\r
1135 {\r
1136 }\r
1137 \r
1138 GLenum TextureExternal::getTarget() const\r
1139 {\r
1140     return GL_TEXTURE_EXTERNAL_OES;\r
1141 }\r
1142 \r
1143 void TextureExternal::setImage(Image *sharedImage)\r
1144 {\r
1145     if(image[0])\r
1146     {\r
1147         image[0]->release();\r
1148     }\r
1149 \r
1150     sharedImage->addRef();\r
1151     image[0] = sharedImage;\r
1152 }\r
1153 \r
1154 }\r
1155 \r
1156 // Exported functions for use by EGL\r
1157 extern "C"\r
1158 {\r
1159         es2::Image *createBackBuffer(int width, int height, const egl::Config *config)\r
1160         {\r
1161                 if(config)\r
1162                 {\r
1163                         return new es2::Image(0, width, height, config->mAlphaSize ? GL_RGBA : GL_RGB, GL_UNSIGNED_BYTE);\r
1164                 }\r
1165 \r
1166                 return 0;\r
1167         }\r
1168 \r
1169         es2::Image *createDepthStencil(unsigned int width, unsigned int height, sw::Format format, int multiSampleDepth, bool discard)\r
1170         {\r
1171                 if(width == 0 || height == 0 || height > OUTLINE_RESOLUTION)\r
1172                 {\r
1173                         ERR("Invalid parameters");\r
1174                         return 0;\r
1175                 }\r
1176                 \r
1177                 bool lockable = true;\r
1178 \r
1179                 switch(format)\r
1180                 {\r
1181         //      case sw::FORMAT_D15S1:\r
1182                 case sw::FORMAT_D24S8:\r
1183                 case sw::FORMAT_D24X8:\r
1184         //      case sw::FORMAT_D24X4S4:\r
1185                 case sw::FORMAT_D24FS8:\r
1186                 case sw::FORMAT_D32:\r
1187                 case sw::FORMAT_D16:\r
1188                         lockable = false;\r
1189                         break;\r
1190         //      case sw::FORMAT_S8_LOCKABLE:\r
1191         //      case sw::FORMAT_D16_LOCKABLE:\r
1192                 case sw::FORMAT_D32F_LOCKABLE:\r
1193         //      case sw::FORMAT_D32_LOCKABLE:\r
1194                 case sw::FORMAT_DF24S8:\r
1195                 case sw::FORMAT_DF16S8:\r
1196                         lockable = true;\r
1197                         break;\r
1198                 default:\r
1199                         UNREACHABLE();\r
1200                 }\r
1201 \r
1202                 es2::Image *surface = new es2::Image(0, width, height, format, multiSampleDepth, lockable, true);\r
1203 \r
1204                 if(!surface)\r
1205                 {\r
1206                         ERR("Out of memory");\r
1207                         return 0;\r
1208                 }\r
1209 \r
1210                 return surface;\r
1211         }\r
1212 }\r