summaryrefslogtreecommitdiff
path: root/src/graphics/PBO.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/graphics/PBO.cpp')
-rw-r--r--src/graphics/PBO.cpp245
1 files changed, 245 insertions, 0 deletions
diff --git a/src/graphics/PBO.cpp b/src/graphics/PBO.cpp
new file mode 100644
index 0000000..64967de
--- /dev/null
+++ b/src/graphics/PBO.cpp
@@ -0,0 +1,245 @@
+//
+// 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 "PBO.h"
+#include "GLContext.h"
+
+#include "../base/Logger.h"
+#include "../base/Exception.h"
+#include "../base/ObjectCounter.h"
+
+#include <iostream>
+#include <cstring>
+
+using namespace std;
+using namespace boost;
+
+namespace avg {
+
+PBO::PBO(const IntPoint& size, PixelFormat pf, unsigned usage)
+ : TextureMover(size, pf),
+ m_Usage(usage)
+{
+ ObjectCounter::get()->incRef(&typeid(*this));
+ m_PBOID = GLContext::getCurrent()->getPBOCache().getBuffer();
+
+ unsigned target = getTarget();
+ glproc::BindBuffer(target, m_PBOID);
+ GLContext::checkError("PBO: BindBuffer()");
+ glproc::BufferData(target, getMemNeeded(), 0, usage);
+ GLContext::checkError("PBO: BufferData()");
+ glproc::BindBuffer(target, 0);
+}
+
+PBO::~PBO()
+{
+ glproc::BindBuffer(getTarget(), m_PBOID);
+ glproc::BufferData(getTarget(), 0, 0, m_Usage);
+ GLContext* pContext = GLContext::getCurrent();
+ if (pContext) {
+ pContext->getPBOCache().returnBuffer(m_PBOID);
+ }
+ glproc::BindBuffer(getTarget(), 0);
+ GLContext::checkError("PBO: DeleteBuffers()");
+ ObjectCounter::get()->decRef(&typeid(*this));
+}
+
+void PBO::activate()
+{
+ glproc::BindBuffer(getTarget(), m_PBOID);
+ GLContext::checkError("PBO::activate()");
+}
+
+int PBO::getID() const
+{
+ return m_PBOID;
+}
+
+void PBO::moveBmpToTexture(BitmapPtr pBmp, GLTexture& tex)
+{
+ AVG_ASSERT(pBmp->getSize() == tex.getSize());
+ AVG_ASSERT(getSize() == pBmp->getSize());
+ AVG_ASSERT(pBmp->getPixelFormat() == getPF());
+ AVG_ASSERT(tex.getPF() == getPF());
+ AVG_ASSERT(!isReadPBO());
+ glproc::BindBuffer(GL_PIXEL_UNPACK_BUFFER_EXT, m_PBOID);
+ GLContext::checkError("PBO::moveBmpToTexture BindBuffer()");
+ void * pPBOPixels = glproc::MapBuffer(GL_PIXEL_UNPACK_BUFFER_EXT, GL_WRITE_ONLY);
+ GLContext::checkError("PBO::moveBmpToTexture MapBuffer()");
+ Bitmap PBOBitmap(getSize(), getPF(), (unsigned char *)pPBOPixels, getStride(), false);
+ PBOBitmap.copyPixels(*pBmp);
+ glproc::UnmapBuffer(GL_PIXEL_UNPACK_BUFFER_EXT);
+ GLContext::checkError("PBO::setImage: UnmapBuffer()");
+
+ tex.setDirty();
+ moveToTexture(tex);
+}
+
+BitmapPtr PBO::moveTextureToBmp(GLTexture& tex, int mipmapLevel)
+{
+ moveTextureToPBO(tex, mipmapLevel);
+ return movePBOToBmp();
+}
+
+void PBO::moveTextureToPBO(GLTexture& tex, int mipmapLevel)
+{
+ AVG_ASSERT(isReadPBO());
+ AVG_ASSERT(getSize() == tex.getGLSize());
+ AVG_ASSERT(getPF() == tex.getPF());
+ glproc::BindBuffer(GL_PIXEL_PACK_BUFFER_EXT, m_PBOID);
+ GLContext::checkError("PBO::getImage BindBuffer()");
+
+ tex.activate(GL_TEXTURE0);
+
+ glGetTexImage(GL_TEXTURE_2D, mipmapLevel, GLTexture::getGLFormat(getPF()),
+ GLTexture::getGLType(getPF()), 0);
+ GLContext::checkError("PBO::getImage: glGetTexImage()");
+ if (mipmapLevel == 0) {
+ m_ActiveSize = tex.getSize();
+ m_BufferStride = tex.getGLSize().x;
+ } else {
+ m_ActiveSize = tex.getMipmapSize(mipmapLevel);
+ m_BufferStride = tex.getMipmapSize(mipmapLevel).x;
+ }
+}
+
+BitmapPtr PBO::movePBOToBmp() const
+{
+ AVG_ASSERT(isReadPBO());
+ glproc::BindBuffer(GL_PIXEL_PACK_BUFFER_EXT, m_PBOID);
+ GLContext::checkError("PBO::getImage BindBuffer()");
+ void * pPBOPixels = glproc::MapBuffer(GL_PIXEL_PACK_BUFFER_EXT, GL_READ_ONLY);
+ GLContext::checkError("PBO::getImage MapBuffer()");
+ Bitmap PBOBitmap(m_ActiveSize, getPF(), (unsigned char *)pPBOPixels,
+ m_BufferStride*getBytesPerPixel(getPF()), false);
+ BitmapPtr pBmp(new Bitmap(m_ActiveSize, getPF()));
+ pBmp->copyPixels(PBOBitmap);
+ glproc::UnmapBuffer(GL_PIXEL_PACK_BUFFER_EXT);
+ GLContext::checkError("PBO::getImage: UnmapBuffer()");
+ glproc::BindBuffer(GL_PIXEL_PACK_BUFFER_EXT, 0);
+
+ return pBmp;
+}
+
+BitmapPtr PBO::lock()
+{
+ AVG_ASSERT(!isReadPBO());
+ BitmapPtr pBmp;
+ glproc::BindBuffer(GL_PIXEL_UNPACK_BUFFER_EXT, m_PBOID);
+ GLContext::checkError("PBOTexture::lockBmp: glBindBuffer()");
+ glproc::BufferData(GL_PIXEL_UNPACK_BUFFER_EXT, getMemNeeded(), 0, m_Usage);
+ GLContext::checkError("PBOTexture::lockBmp: glBufferData()");
+ unsigned char * pBuffer = (unsigned char *)
+ glproc::MapBuffer(GL_PIXEL_UNPACK_BUFFER_EXT, GL_WRITE_ONLY);
+ GLContext::checkError("PBOTexture::lockBmp: glMapBuffer()");
+ glproc::BindBuffer(GL_PIXEL_UNPACK_BUFFER_EXT, 0);
+ GLContext::checkError("PBOTexture::lockBmp: glBindBuffer(0)");
+
+ pBmp = BitmapPtr(new Bitmap(getSize(), getPF(), pBuffer, getStride(), false));
+ return pBmp;
+}
+
+void PBO::unlock()
+{
+ AVG_ASSERT(!isReadPBO());
+ glproc::BindBuffer(GL_PIXEL_UNPACK_BUFFER_EXT, m_PBOID);
+ GLContext::checkError("PBOTexture::unlockBmp: glBindBuffer()");
+ glproc::UnmapBuffer(GL_PIXEL_UNPACK_BUFFER_EXT);
+ GLContext::checkError("PBOTexture::unlockBmp: glUnmapBuffer()");
+ glproc::BindBuffer(GL_PIXEL_UNPACK_BUFFER_EXT, 0);
+ GLContext::checkError("PBOTexture::unlockBmp: glBindBuffer(0)");
+}
+
+void PBO::moveToTexture(GLTexture& tex)
+{
+ AVG_ASSERT(!isReadPBO());
+ IntPoint size = tex.getSize();
+ if (size.x > getSize().x) {
+ size.x = getSize().x;
+ }
+ if (size.y > getSize().y) {
+ size.y = getSize().y;
+ }
+ glproc::BindBuffer(GL_PIXEL_UNPACK_BUFFER_EXT, m_PBOID);
+ GLContext::checkError("PBOTexture::lockBmp: glBindBuffer()");
+ tex.activate(GL_TEXTURE0);
+#ifdef __APPLE__
+ // See getStride()
+ if (getPF() == A8) {
+ glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
+ } else {
+ glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
+ }
+#endif
+ glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, size.x, size.y,
+ GLTexture::getGLFormat(getPF()), GLTexture::getGLType(getPF()), 0);
+ GLContext::checkError("PBO::setImage: glTexSubImage2D()");
+ glproc::BindBuffer(GL_PIXEL_UNPACK_BUFFER_EXT, 0);
+ tex.setDirty();
+ tex.generateMipmaps();
+}
+
+bool PBO::isReadPBO() const
+{
+ switch (m_Usage) {
+ case GL_STATIC_DRAW:
+ case GL_DYNAMIC_DRAW:
+ case GL_STREAM_DRAW:
+ return false;
+ case GL_STATIC_READ:
+ case GL_DYNAMIC_READ:
+ case GL_STREAM_READ:
+ return true;
+ default:
+ AVG_ASSERT(false);
+ return false;
+ }
+}
+
+unsigned PBO::getMemNeeded() const
+{
+ return getStride()*getSize().y;
+}
+
+unsigned PBO::getStride() const
+{
+ IntPoint size = getSize();
+ unsigned stride = Bitmap::getPreferredStride(size.x, getPF());
+#ifdef __APPLE__
+ if (getPF() == A8) {
+ // Workaround for apparent bug in Apple/NVidia drivers (Verified on OS X 10.6.8,
+ // MBP early 2011): GL_UNPACK_ALIGNMENT != 1 causes broken A8 textures.
+ stride = size.x;
+ }
+#endif
+ return stride;
+}
+
+unsigned PBO::getTarget() const
+{
+ if (isReadPBO()) {
+ return GL_PIXEL_PACK_BUFFER_EXT;
+ } else {
+ return GL_PIXEL_UNPACK_BUFFER_EXT;
+ }
+}
+
+}