1 // SwiftShader Software Renderer
\r
3 // Copyright(c) 2005-2013 TransGaming Inc.
\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
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
16 #include "Texture.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
26 #include <algorithm>
\r
31 Texture::Texture(GLuint id) : RefCountObject(id)
\r
33 mMinFilter = GL_NEAREST_MIPMAP_LINEAR;
\r
34 mMagFilter = GL_LINEAR;
\r
37 mMaxAnisotropy = 1.0f;
\r
39 resource = new sw::Resource(0);
\r
44 resource->destruct();
\r
47 sw::Resource *Texture::getResource() const
\r
52 // Returns true on successful filter state update (valid enum parameter)
\r
53 bool Texture::setMinFilter(GLenum filter)
\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
68 mMinFilter = filter;
\r
75 // Returns true on successful filter state update (valid enum parameter)
\r
76 bool Texture::setMagFilter(GLenum filter)
\r
82 mMagFilter = filter;
\r
89 // Returns true on successful wrap state update (valid enum parameter)
\r
90 bool Texture::setWrapS(GLenum wrap)
\r
95 case GL_MIRRORED_REPEAT_OES:
\r
96 if(getTarget() == GL_TEXTURE_EXTERNAL_OES)
\r
101 case GL_CLAMP_TO_EDGE:
\r
109 // Returns true on successful wrap state update (valid enum parameter)
\r
110 bool Texture::setWrapT(GLenum wrap)
\r
115 case GL_MIRRORED_REPEAT_OES:
\r
116 if(getTarget() == GL_TEXTURE_EXTERNAL_OES)
\r
121 case GL_CLAMP_TO_EDGE:
\r
129 // Returns true on successful max anisotropy update (valid anisotropy value)
\r
130 bool Texture::setMaxAnisotropy(float textureMaxAnisotropy)
\r
132 textureMaxAnisotropy = std::min(textureMaxAnisotropy, MAX_TEXTURE_MAX_ANISOTROPY);
\r
134 if(textureMaxAnisotropy < 1.0f)
\r
139 if(mMaxAnisotropy != textureMaxAnisotropy)
\r
141 mMaxAnisotropy = textureMaxAnisotropy;
\r
147 GLenum Texture::getMinFilter() const
\r
152 GLenum Texture::getMagFilter() const
\r
157 GLenum Texture::getWrapS() const
\r
162 GLenum Texture::getWrapT() const
\r
167 GLfloat Texture::getMaxAnisotropy() const
\r
169 return mMaxAnisotropy;
\r
172 egl::Image *Texture::createSharedImage(GLenum target, unsigned int level)
\r
174 egl::Image *image = getRenderTarget(target, level); // Increments reference count
\r
178 image->markShared();
\r
184 void Texture::setImage(GLenum format, GLenum type, GLint unpackAlignment, const void *pixels, egl::Image *image)
\r
186 if(pixels && image)
\r
188 image->loadImageData(0, 0, image->getWidth(), image->getHeight(), format, type, unpackAlignment, pixels);
\r
192 void Texture::setCompressedImage(GLsizei imageSize, const void *pixels, egl::Image *image)
\r
194 if(pixels && image)
\r
196 image->loadCompressedData(0, 0, image->getWidth(), image->getHeight(), imageSize, pixels);
\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
204 return error(GL_INVALID_OPERATION);
\r
207 if(width + xoffset > image->getWidth() || height + yoffset > image->getHeight())
\r
209 return error(GL_INVALID_VALUE);
\r
212 if(IsCompressed(image->getFormat()))
\r
214 return error(GL_INVALID_OPERATION);
\r
217 if(format != image->getFormat())
\r
219 return error(GL_INVALID_OPERATION);
\r
224 image->loadImageData(xoffset, yoffset, width, height, format, type, unpackAlignment, pixels);
\r
228 void Texture::subImageCompressed(GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void *pixels, egl::Image *image)
\r
232 return error(GL_INVALID_OPERATION);
\r
235 if(width + xoffset > image->getWidth() || height + yoffset > image->getHeight())
\r
237 return error(GL_INVALID_VALUE);
\r
240 if(format != image->getFormat())
\r
242 return error(GL_INVALID_OPERATION);
\r
247 image->loadCompressedData(xoffset, yoffset, width, height, imageSize, pixels);
\r
251 bool Texture::copy(egl::Image *source, const sw::Rect &sourceRect, GLenum destFormat, GLint xoffset, GLint yoffset, egl::Image *dest)
\r
253 Device *device = getDevice();
\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
260 return error(GL_OUT_OF_MEMORY, false);
\r
266 bool Texture::isMipmapFiltered() const
\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
278 default: UNREACHABLE();
\r
284 Texture2D::Texture2D(GLuint id) : Texture(id)
\r
286 for(int i = 0; i < MIPMAP_LEVELS; i++)
\r
293 mColorbufferProxy = NULL;
\r
297 Texture2D::~Texture2D()
\r
299 resource->lock(sw::DESTRUCT);
\r
301 for(int i = 0; i < MIPMAP_LEVELS; i++)
\r
305 image[i]->unbind();
\r
310 resource->unlock();
\r
314 mSurface->setBoundTexture(NULL);
\r
318 mColorbufferProxy = NULL;
\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
329 void Texture2D::releaseProxy(const Renderbuffer *proxy)
\r
336 if(mProxyRefs == 0)
\r
338 mColorbufferProxy = NULL;
\r
342 GLenum Texture2D::getTarget() const
\r
344 return GL_TEXTURE_2D;
\r
347 GLsizei Texture2D::getWidth(GLenum target, GLint level) const
\r
349 ASSERT(target == GL_TEXTURE_2D);
\r
350 return image[level] ? image[level]->getWidth() : 0;
\r
353 GLsizei Texture2D::getHeight(GLenum target, GLint level) const
\r
355 ASSERT(target == GL_TEXTURE_2D);
\r
356 return image[level] ? image[level]->getHeight() : 0;
\r
359 GLenum Texture2D::getFormat(GLenum target, GLint level) const
\r
361 ASSERT(target == GL_TEXTURE_2D);
\r
362 return image[level] ? image[level]->getFormat() : 0;
\r
365 GLenum Texture2D::getType(GLenum target, GLint level) const
\r
367 ASSERT(target == GL_TEXTURE_2D);
\r
368 return image[level] ? image[level]->getType() : 0;
\r
371 sw::Format Texture2D::getInternalFormat(GLenum target, GLint level) const
\r
373 ASSERT(target == GL_TEXTURE_2D);
\r
374 return image[level] ? image[level]->getInternalFormat() : sw::FORMAT_NULL;
\r
377 int Texture2D::getLevelCount() const
\r
379 ASSERT(isSamplerComplete());
\r
382 while(levels < MIPMAP_LEVELS && image[levels])
\r
390 void Texture2D::setImage(GLint level, GLsizei width, GLsizei height, GLenum format, GLenum type, GLint unpackAlignment, const void *pixels)
\r
394 image[level]->unbind();
\r
397 image[level] = new Image(this, width, height, format, type);
\r
401 return error(GL_OUT_OF_MEMORY);
\r
404 Texture::setImage(format, type, unpackAlignment, pixels, image[level]);
\r
407 void Texture2D::bindTexImage(egl::Surface *surface)
\r
411 switch(surface->getInternalFormat())
\r
413 case sw::FORMAT_A8R8G8B8:
\r
416 case sw::FORMAT_X8R8G8B8:
\r
424 for(int level = 0; level < MIPMAP_LEVELS; level++)
\r
428 image[level]->unbind();
\r
433 image[0] = surface->getRenderTarget();
\r
435 mSurface = surface;
\r
436 mSurface->setBoundTexture(this);
\r
439 void Texture2D::releaseTexImage()
\r
441 for(int level = 0; level < MIPMAP_LEVELS; level++)
\r
445 image[level]->unbind();
\r
451 void Texture2D::setCompressedImage(GLint level, GLenum format, GLsizei width, GLsizei height, GLsizei imageSize, const void *pixels)
\r
455 image[level]->unbind();
\r
458 image[level] = new Image(this, width, height, format, GL_UNSIGNED_BYTE);
\r
462 return error(GL_OUT_OF_MEMORY);
\r
465 Texture::setCompressedImage(imageSize, pixels, image[level]);
\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
470 Texture::subImage(xoffset, yoffset, width, height, format, type, unpackAlignment, pixels, image[level]);
\r
473 void Texture2D::subImageCompressed(GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void *pixels)
\r
475 Texture::subImageCompressed(xoffset, yoffset, width, height, format, imageSize, pixels, image[level]);
\r
478 void Texture2D::copyImage(GLint level, GLenum format, GLint x, GLint y, GLsizei width, GLsizei height, Framebuffer *source)
\r
480 egl::Image *renderTarget = source->getRenderTarget();
\r
484 ERR("Failed to retrieve the render target.");
\r
485 return error(GL_OUT_OF_MEMORY);
\r
490 image[level]->unbind();
\r
493 image[level] = new Image(this, width, height, format, GL_UNSIGNED_BYTE);
\r
497 return error(GL_OUT_OF_MEMORY);
\r
500 if(width != 0 && height != 0)
\r
502 sw::Rect sourceRect = {x, y, x + width, y + height};
\r
503 sourceRect.clip(0, 0, source->getColorbuffer()->getWidth(), source->getColorbuffer()->getHeight());
\r
505 copy(renderTarget, sourceRect, format, 0, 0, image[level]);
\r
508 renderTarget->release();
\r
511 void Texture2D::copySubImage(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height, Framebuffer *source)
\r
515 return error(GL_INVALID_OPERATION);
\r
518 if(xoffset + width > image[level]->getWidth() || yoffset + height > image[level]->getHeight())
\r
520 return error(GL_INVALID_VALUE);
\r
523 egl::Image *renderTarget = source->getRenderTarget();
\r
527 ERR("Failed to retrieve the render target.");
\r
528 return error(GL_OUT_OF_MEMORY);
\r
531 sw::Rect sourceRect = {x, y, x + width, y + height};
\r
532 sourceRect.clip(0, 0, source->getColorbuffer()->getWidth(), source->getColorbuffer()->getHeight());
\r
534 copy(renderTarget, sourceRect, image[level]->getFormat(), xoffset, yoffset, image[level]);
\r
536 renderTarget->release();
\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
547 GLsizei width = image[0]->getWidth();
\r
548 GLsizei height = image[0]->getHeight();
\r
550 if(width <= 0 || height <= 0)
\r
555 if(isMipmapFiltered())
\r
557 if(!isMipmapComplete())
\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
569 GLsizei width = image[0]->getWidth();
\r
570 GLsizei height = image[0]->getHeight();
\r
572 int q = log2(std::max(width, height));
\r
574 for(int level = 1; level <= q; level++)
\r
581 if(image[level]->getFormat() != image[0]->getFormat())
\r
586 if(image[level]->getType() != image[0]->getType())
\r
591 if(image[level]->getWidth() != std::max(1, width >> level))
\r
596 if(image[level]->getHeight() != std::max(1, height >> level))
\r
605 bool Texture2D::isCompressed(GLenum target, GLint level) const
\r
607 return IsCompressed(getFormat(target, level));
\r
610 bool Texture2D::isDepth(GLenum target, GLint level) const
\r
612 return IsDepthTexture(getFormat(target, level));
\r
615 void Texture2D::generateMipmaps()
\r
619 return; // FIXME: error?
\r
622 unsigned int q = log2(std::max(image[0]->getWidth(), image[0]->getHeight()));
\r
624 for(unsigned int i = 1; i <= q; i++)
\r
628 image[i]->unbind();
\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
635 return error(GL_OUT_OF_MEMORY);
\r
638 getDevice()->stretchRect(image[i - 1], 0, image[i], 0, true);
\r
642 egl::Image *Texture2D::getImage(unsigned int level)
\r
644 return image[level];
\r
647 Renderbuffer *Texture2D::getRenderbuffer(GLenum target)
\r
649 if(target != GL_TEXTURE_2D)
\r
651 return error(GL_INVALID_OPERATION, (Renderbuffer *)NULL);
\r
654 if(mColorbufferProxy == NULL)
\r
656 mColorbufferProxy = new Renderbuffer(id(), new RenderbufferTexture2D(this));
\r
659 return mColorbufferProxy;
\r
662 egl::Image *Texture2D::getRenderTarget(GLenum target, unsigned int level)
\r
664 ASSERT(target == GL_TEXTURE_2D);
\r
665 ASSERT(level < IMPLEMENTATION_MAX_TEXTURE_LEVELS);
\r
669 image[level]->addRef();
\r
672 return image[level];
\r
675 bool Texture2D::isShared(GLenum target, unsigned int level) const
\r
677 ASSERT(target == GL_TEXTURE_2D);
\r
678 ASSERT(level < IMPLEMENTATION_MAX_TEXTURE_LEVELS);
\r
680 if(mSurface) // Bound to an EGLSurface
\r
690 return image[level]->isShared();
\r
693 TextureExternal::TextureExternal(GLuint id) : Texture2D(id)
\r
695 mMinFilter = GL_LINEAR;
\r
696 mMagFilter = GL_LINEAR;
\r
697 mWrapS = GL_CLAMP_TO_EDGE;
\r
698 mWrapT = GL_CLAMP_TO_EDGE;
\r
701 TextureExternal::~TextureExternal()
\r
705 GLenum TextureExternal::getTarget() const
\r
707 return GL_TEXTURE_EXTERNAL_OES;
\r
710 void TextureExternal::setImage(Image *sharedImage)
\r
714 image[0]->release();
\r
717 sharedImage->addRef();
\r
718 image[0] = sharedImage;
\r
723 // Exported functions for use by EGL
\r
726 es1::Image *createBackBuffer(int width, int height, const egl::Config *config)
\r
730 return new es1::Image(0, width, height, config->mAlphaSize ? GL_RGBA : GL_RGB, GL_UNSIGNED_BYTE);
\r
736 es1::Image *createDepthStencil(unsigned int width, unsigned int height, sw::Format format, int multiSampleDepth, bool discard)
\r
738 if(width == 0 || height == 0 || height > OUTLINE_RESOLUTION)
\r
740 ERR("Invalid parameters");
\r
744 bool lockable = true;
\r
748 // case sw::FORMAT_D15S1:
\r
749 case sw::FORMAT_D24S8:
\r
750 case sw::FORMAT_D24X8:
\r
751 // case sw::FORMAT_D24X4S4:
\r
752 case sw::FORMAT_D24FS8:
\r
753 case sw::FORMAT_D32:
\r
754 case sw::FORMAT_D16:
\r
757 // case sw::FORMAT_S8_LOCKABLE:
\r
758 // case sw::FORMAT_D16_LOCKABLE:
\r
759 case sw::FORMAT_D32F_LOCKABLE:
\r
760 // case sw::FORMAT_D32_LOCKABLE:
\r
761 case sw::FORMAT_DF24S8:
\r
762 case sw::FORMAT_DF16S8:
\r
769 es1::Image *surface = new es1::Image(0, width, height, format, multiSampleDepth, lockable, true);
\r
773 ERR("Out of memory");
\r