diff options
Diffstat (limited to 'src/graphics/GLTexture.cpp')
-rw-r--r-- | src/graphics/GLTexture.cpp | 370 |
1 files changed, 370 insertions, 0 deletions
diff --git a/src/graphics/GLTexture.cpp b/src/graphics/GLTexture.cpp new file mode 100644 index 0000000..e19c0d4 --- /dev/null +++ b/src/graphics/GLTexture.cpp @@ -0,0 +1,370 @@ +// +// libavg - Media Playback Engine. +// Copyright (C) 2003-2014 Ulrich von Zadow +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// +// Current versions can be found at www.libavg.de +// + +#include "GLTexture.h" + +#include "../base/Exception.h" +#include "../base/StringHelper.h" +#include "../base/MathHelper.h" +#include "../base/ObjectCounter.h" + +#include "GLContext.h" +#include "TextureMover.h" +#ifndef AVG_ENABLE_EGL + #include "PBO.h" +#endif +#include "FBO.h" + +#include <string.h> +#include <iostream> + +namespace avg { + +using namespace std; + +// We assign our own texture ids and never reuse them instead of using glGenTextures. +// That works very well, except that other components (e.g. Ogre3d) with shared gl +// contexts don't know anything about our ids and thus use the same ones. +// Somewhat hackish solution: Assign ids starting with a very high id, so the id ranges +// don't overlap. +unsigned GLTexture::s_LastTexID = 10000000; + +GLTexture::GLTexture(const IntPoint& size, PixelFormat pf, bool bMipmap, + int potBorderColor, unsigned wrapSMode, unsigned wrapTMode, bool bForcePOT) + : m_Size(size), + m_pf(pf), + m_bMipmap(bMipmap), + m_bDeleteTex(true), + m_bIsDirty(true) +{ + m_pGLContext = GLContext::getCurrent(); + ObjectCounter::get()->incRef(&typeid(*this)); + m_bUsePOT = m_pGLContext->usePOTTextures() || bForcePOT; + if (m_pGLContext->isGLES() && bMipmap) { + m_bUsePOT = true; + } + if (m_bUsePOT) { + m_GLSize.x = nextpow2(m_Size.x); + m_GLSize.y = nextpow2(m_Size.y); + } else { + m_GLSize = m_Size; + } + + int maxTexSize = m_pGLContext->getMaxTexSize(); + if (m_Size.x > maxTexSize || m_Size.y > maxTexSize) { + throw Exception(AVG_ERR_VIDEO_GENERAL, "Texture too large (" + toString(m_Size) + + "). Maximum supported by graphics card is " + + toString(maxTexSize)); + } + if (getGLType(m_pf) == GL_FLOAT && !isFloatFormatSupported()) { + throw Exception(AVG_ERR_UNSUPPORTED, + "Float textures not supported by OpenGL configuration."); + } + + s_LastTexID++; + m_TexID = s_LastTexID; + m_pGLContext->bindTexture(GL_TEXTURE0, m_TexID); + + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, wrapSMode); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, wrapTMode); + glTexImage2D(GL_TEXTURE_2D, 0, getGLInternalFormat(), m_GLSize.x, m_GLSize.y, 0, + getGLFormat(m_pf), getGLType(m_pf), 0); + GLContext::checkError("GLTexture: glTexImage2D()"); + if (bMipmap) { + glproc::GenerateMipmap(GL_TEXTURE_2D); + GLContext::checkError("GLTexture::GLTexture generateMipmap()"); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); + } else { + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + } + + if (m_bUsePOT) { + // Make sure the texture is transparent and black before loading stuff + // into it to avoid garbage at the borders. + // In the case of UV textures, we set the border color to 128... + int TexMemNeeded = m_GLSize.x*m_GLSize.y*getBytesPerPixel(m_pf); + char * pPixels = new char[TexMemNeeded]; + memset(pPixels, potBorderColor, TexMemNeeded); + glTexImage2D(GL_TEXTURE_2D, 0, getGLInternalFormat(), m_GLSize.x, + m_GLSize.y, 0, getGLFormat(m_pf), getGLType(m_pf), + pPixels); + GLContext::checkError("PBOTexture::createTexture: glTexImage2D()"); + delete[] pPixels; + } +// dump(wrapSMode, wrapTMode); +} + +GLTexture::GLTexture(unsigned glTexID, const IntPoint& size, PixelFormat pf, bool bMipmap, + bool bDeleteTex) + : m_Size(size), + m_GLSize(size), + m_pf(pf), + m_bMipmap(bMipmap), + m_bDeleteTex(bDeleteTex), + m_bUsePOT(false), + m_TexID(glTexID), + m_bIsDirty(true) +{ + m_pGLContext = GLContext::getCurrent(); + ObjectCounter::get()->incRef(&typeid(*this)); +} + +GLTexture::~GLTexture() +{ + if (m_bDeleteTex) { + glDeleteTextures(1, &m_TexID); + GLContext::checkError("GLTexture: DeleteTextures()"); + } + ObjectCounter::get()->decRef(&typeid(*this)); +} + +void GLTexture::activate(int textureUnit) +{ + m_pGLContext->bindTexture(textureUnit, m_TexID); +} + +void GLTexture::generateMipmaps() +{ + if (m_bMipmap) { + activate(); + glproc::GenerateMipmap(GL_TEXTURE_2D); + GLContext::checkError("GLTexture::generateMipmap()"); + } +} + +void GLTexture::setWrapMode(unsigned wrapSMode, unsigned wrapTMode) +{ + activate(); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, wrapSMode); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, wrapTMode); +} + +void GLTexture::enableStreaming() +{ + m_pMover = TextureMover::create(m_Size, m_pf, GL_STREAM_DRAW); +} + +BitmapPtr GLTexture::lockStreamingBmp() +{ + AVG_ASSERT(m_pMover); + return m_pMover->lock(); +} + +void GLTexture::unlockStreamingBmp(bool bUpdated) +{ + AVG_ASSERT(m_pMover); + m_pMover->unlock(); + if (bUpdated) { + m_pMover->moveToTexture(*this); + m_bIsDirty = true; + } +} + +void GLTexture::moveBmpToTexture(BitmapPtr pBmp) +{ + TextureMoverPtr pMover = TextureMover::create(m_Size, m_pf, GL_DYNAMIC_DRAW); + pMover->moveBmpToTexture(pBmp, *this); + m_bIsDirty = true; +} + +BitmapPtr GLTexture::moveTextureToBmp(int mipmapLevel) +{ + TextureMoverPtr pMover = TextureMover::create(m_GLSize, m_pf, GL_DYNAMIC_READ); + return pMover->moveTextureToBmp(*this, mipmapLevel); +} + +const IntPoint& GLTexture::getSize() const +{ + return m_Size; +} + +const IntPoint& GLTexture::getGLSize() const +{ + return m_GLSize; +} + +PixelFormat GLTexture::getPF() const +{ + return m_pf; +} + +unsigned GLTexture::getID() const +{ + return m_TexID; +} + +IntPoint GLTexture::getMipmapSize(int level) const +{ + IntPoint size = m_Size; + for (int i=0; i<level; ++i) { + size.x = max(1, size.x >> 1); + size.y = max(1, size.y >> 1); + } + return size; +} + +bool GLTexture::isFloatFormatSupported() +{ + return queryOGLExtension("GL_ARB_texture_float"); +} + +int GLTexture::getGLFormat(PixelFormat pf) +{ + switch (pf) { + case I8: + case I32F: + return GL_LUMINANCE; + case A8: + return GL_ALPHA; + case R8G8B8A8: + case R8G8B8X8: + return GL_RGBA; + case B8G8R8A8: + case B8G8R8X8: + AVG_ASSERT(!GLContext::getMain()->isGLES()); + return GL_BGRA; +#ifndef AVG_ENABLE_EGL + case R32G32B32A32F: + return GL_BGRA; +#endif + case R8G8B8: + case B5G6R5: + return GL_RGB; + default: + AVG_ASSERT(false); + return 0; + } +} + +int GLTexture::getGLType(PixelFormat pf) +{ + switch (pf) { + case I8: + case A8: + return GL_UNSIGNED_BYTE; + case R8G8B8A8: + case R8G8B8X8: + case B8G8R8A8: + case B8G8R8X8: +#ifdef __APPLE__ + return GL_UNSIGNED_INT_8_8_8_8_REV; +#else + return GL_UNSIGNED_BYTE; +#endif + case R32G32B32A32F: + case I32F: + return GL_FLOAT; + case R8G8B8: + return GL_UNSIGNED_BYTE; + case B5G6R5: + return GL_UNSIGNED_SHORT_5_6_5; + default: + AVG_ASSERT(false); + return 0; + } +} + +int GLTexture::getGLInternalFormat() const +{ + switch (m_pf) { + case I8: + return GL_LUMINANCE; + case A8: + return GL_ALPHA; + case R8G8B8A8: + case R8G8B8X8: + return GL_RGBA; + case B8G8R8A8: + case B8G8R8X8: + AVG_ASSERT(!GLContext::getMain()->isGLES()); + return GL_RGBA; +#ifndef AVG_ENABLE_EGL + case R32G32B32A32F: + return GL_RGBA32F_ARB; + case I32F: + return GL_LUMINANCE32F_ARB; +#endif + case R8G8B8: + case B5G6R5: + return GL_RGB; + default: + AVG_ASSERT(false); + return 0; + } +} + +void GLTexture::setDirty() +{ + m_bIsDirty = true; +} + +bool GLTexture::isDirty() const +{ + return m_bIsDirty; +} + +void GLTexture::resetDirty() +{ + m_bIsDirty = false; +} + +const string wrapModeToStr(unsigned wrapMode) +{ + string sWrapMode; + switch (wrapMode) { + case GL_CLAMP_TO_EDGE: + sWrapMode = "CLAMP_TO_EDGE"; + break; +#ifndef AVG_ENABLE_EGL + case GL_CLAMP: + sWrapMode = "CLAMP"; + break; + case GL_CLAMP_TO_BORDER: + sWrapMode = "CLAMP_TO_BORDER"; + break; +#endif + case GL_REPEAT: + sWrapMode = "REPEAT"; + break; + case GL_MIRRORED_REPEAT: + sWrapMode = "MIRRORED_REPEAT"; + break; + default: + sWrapMode = "unknown"; + } + return sWrapMode; +} + +void GLTexture::dump(unsigned wrapSMode, unsigned wrapTMode) const +{ + cerr << "GLTexture" << endl; + cerr << "m_Size: " << m_Size << endl; + cerr << "m_GLSize: " << m_GLSize << endl; + cerr << "m_pf: " << m_pf << endl; + cerr << "m_bMipmap: " << m_bMipmap << endl; + if (wrapSMode != (unsigned)-1) { + cerr << "Wrap modes: " << \ + wrapModeToStr(wrapSMode) << ", " << wrapModeToStr(wrapTMode) << endl; + } +} + +} |