summaryrefslogtreecommitdiff
path: root/common/gal/opengl/opengl_compositor.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'common/gal/opengl/opengl_compositor.cpp')
-rw-r--r--common/gal/opengl/opengl_compositor.cpp291
1 files changed, 291 insertions, 0 deletions
diff --git a/common/gal/opengl/opengl_compositor.cpp b/common/gal/opengl/opengl_compositor.cpp
new file mode 100644
index 0000000..9f35d9e
--- /dev/null
+++ b/common/gal/opengl/opengl_compositor.cpp
@@ -0,0 +1,291 @@
+/*
+ * This program source code file is part of KiCad, a free EDA CAD application.
+ *
+ * Copyright (C) 2013-2015 CERN
+ * @author Maciej Suminski <maciej.suminski@cern.ch>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, you may find one here:
+ * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
+ * or you may search the http://www.gnu.org website for the version 2 license,
+ * or you may write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+/**
+ * @file opengl_compositor.cpp
+ * @brief Class that handles multitarget rendering (i.e. to different textures/surfaces) and
+ * later compositing into a single image (OpenGL flavour).
+ */
+
+#include <gal/opengl/opengl_compositor.h>
+
+#include <stdexcept>
+#include <cassert>
+
+using namespace KIGFX;
+
+OPENGL_COMPOSITOR::OPENGL_COMPOSITOR() :
+ m_initialized( false ), m_current( 0 ), m_currentFbo( DIRECT_RENDERING )
+{
+ // Avoid not initialized members:
+ m_framebuffer = 0;
+ m_depthBuffer = 0;
+}
+
+
+OPENGL_COMPOSITOR::~OPENGL_COMPOSITOR()
+{
+ if( m_initialized )
+ clean();
+}
+
+
+void OPENGL_COMPOSITOR::Initialize()
+{
+ if( m_initialized )
+ return;
+
+ // We need framebuffer objects for drawing the screen contents
+ // Generate framebuffer and a depth buffer
+ glGenFramebuffersEXT( 1, &m_framebuffer );
+ glBindFramebufferEXT( GL_FRAMEBUFFER, m_framebuffer );
+ m_currentFbo = m_framebuffer;
+
+ // Allocate memory for the depth buffer
+ // Attach the depth buffer to the framebuffer
+ glGenRenderbuffersEXT( 1, &m_depthBuffer );
+ glBindRenderbufferEXT( GL_RENDERBUFFER_EXT, m_depthBuffer );
+
+ // Use here a size of 24 bits for the depth buffer, 8 bits for the stencil buffer
+ // this is required later for anti-aliasing
+ glRenderbufferStorageEXT( GL_RENDERBUFFER_EXT, GL_DEPTH_COMPONENT16, m_width, m_height );
+ glFramebufferRenderbufferEXT( GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT,
+ GL_RENDERBUFFER_EXT, m_depthBuffer );
+
+ // Unbind the framebuffer, so by default all the rendering goes directly to the display
+ glBindFramebufferEXT( GL_FRAMEBUFFER_EXT, DIRECT_RENDERING );
+ m_currentFbo = DIRECT_RENDERING;
+
+ m_initialized = true;
+}
+
+
+void OPENGL_COMPOSITOR::Resize( unsigned int aWidth, unsigned int aHeight )
+{
+ if( m_initialized )
+ clean();
+
+ m_width = aWidth;
+ m_height = aHeight;
+}
+
+
+unsigned int OPENGL_COMPOSITOR::CreateBuffer()
+{
+ assert( m_initialized );
+
+ unsigned int maxBuffers;
+
+ // Get the maximum number of buffers
+ glGetIntegerv( GL_MAX_COLOR_ATTACHMENTS, (GLint*) &maxBuffers );
+
+ if( usedBuffers() >= maxBuffers )
+ {
+ throw std::runtime_error("Cannot create more framebuffers. OpenGL rendering "
+ "backend requires at least 3 framebuffers. You may try to update/change "
+ "your graphic drivers.");
+ }
+
+ // GL_COLOR_ATTACHMENTn are consecutive integers
+ GLuint attachmentPoint = GL_COLOR_ATTACHMENT0 + usedBuffers();
+ GLuint textureTarget;
+
+ // Generate the texture for the pixel storage
+ glGenTextures( 1, &textureTarget );
+ glBindTexture( GL_TEXTURE_2D, textureTarget );
+
+ // Set texture parameters
+ glTexEnvf( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE );
+ glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA8, m_width, m_height, 0, GL_RGBA,
+ GL_UNSIGNED_BYTE, NULL );
+ glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST );
+ glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST );
+
+ // Bind the texture to the specific attachment point, clear and rebind the screen
+ glBindFramebufferEXT( GL_FRAMEBUFFER_EXT, m_framebuffer );
+ m_currentFbo = m_framebuffer;
+ glFramebufferTexture2DEXT( GL_FRAMEBUFFER_EXT, attachmentPoint,
+ GL_TEXTURE_2D, textureTarget, 0 );
+
+ // Check the status, exit if the framebuffer can't be created
+ GLenum status = glCheckFramebufferStatusEXT( GL_FRAMEBUFFER_EXT );
+
+ if( status != GL_FRAMEBUFFER_COMPLETE_EXT )
+ {
+ switch( status )
+ {
+ case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT_EXT:
+ throw std::runtime_error( "Cannot create the framebuffer." );
+ break;
+
+ case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT_EXT:
+ throw std::runtime_error( "The framebuffer attachment points are incomplete." );
+ break;
+
+ case GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER_EXT:
+ throw std::runtime_error( "The framebuffer does not have at least one "
+ "image attached to it." );
+ break;
+
+ case GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER_EXT:
+ throw std::runtime_error( "The framebuffer read buffer is incomplete." );
+ break;
+
+ case GL_FRAMEBUFFER_UNSUPPORTED_EXT:
+ throw std::runtime_error( "The combination of internal formats of the attached "
+ "images violates an implementation-dependent set of restrictions." );
+ break;
+
+ case GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE_EXT:
+ throw std::runtime_error( "GL_RENDERBUFFER_SAMPLES is not the same for "
+ "all attached renderbuffers" );
+ break;
+
+ case GL_FRAMEBUFFER_INCOMPLETE_LAYER_TARGETS_EXT:
+ throw std::runtime_error( "Framebuffer incomplete layer targets errors." );
+ break;
+
+ default:
+ throw std::runtime_error( "Cannot create the framebuffer." );
+ break;
+ }
+
+ return 0;
+ }
+
+ ClearBuffer();
+
+ // Return to direct rendering (we were asked only to create a buffer, not switch to one)
+ glBindFramebufferEXT( GL_FRAMEBUFFER_EXT, DIRECT_RENDERING );
+ m_currentFbo = DIRECT_RENDERING;
+
+ // Store the new buffer
+ OPENGL_BUFFER buffer = { textureTarget, attachmentPoint };
+ m_buffers.push_back( buffer );
+
+ return usedBuffers();
+}
+
+
+void OPENGL_COMPOSITOR::SetBuffer( unsigned int aBufferHandle )
+{
+ if( aBufferHandle > usedBuffers() )
+ return;
+
+ // Change the rendering destination to the selected attachment point
+ if( aBufferHandle == DIRECT_RENDERING )
+ {
+ m_currentFbo = DIRECT_RENDERING;
+ }
+ else if( m_currentFbo != m_framebuffer )
+ {
+ glBindFramebufferEXT( GL_FRAMEBUFFER_EXT, m_framebuffer );
+ m_currentFbo = m_framebuffer;
+ }
+
+ if( m_currentFbo != DIRECT_RENDERING )
+ {
+ m_current = aBufferHandle - 1;
+ glDrawBuffer( m_buffers[m_current].attachmentPoint );
+ }
+}
+
+
+void OPENGL_COMPOSITOR::ClearBuffer()
+{
+ assert( m_initialized );
+
+ glClearColor( 0.0f, 0.0f, 0.0f, 0.0f );
+ glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
+}
+
+
+void OPENGL_COMPOSITOR::DrawBuffer( unsigned int aBufferHandle )
+{
+ assert( m_initialized );
+ assert( aBufferHandle != 0 && aBufferHandle <= usedBuffers() );
+
+ // Switch to the main framebuffer and blit the scene
+ glBindFramebufferEXT( GL_FRAMEBUFFER, DIRECT_RENDERING );
+ m_currentFbo = DIRECT_RENDERING;
+
+ // Depth test has to be disabled to make transparency working
+ glDisable( GL_DEPTH_TEST );
+ glBlendFunc( GL_ONE, GL_ONE_MINUS_SRC_ALPHA );
+
+ // Enable texturing and bind the main texture
+ glEnable( GL_TEXTURE_2D );
+ glBindTexture( GL_TEXTURE_2D, m_buffers[aBufferHandle - 1].textureTarget );
+
+ // Draw a full screen quad with the texture
+ glMatrixMode( GL_MODELVIEW );
+ glPushMatrix();
+ glLoadIdentity();
+ glMatrixMode( GL_PROJECTION );
+ glPushMatrix();
+ glLoadIdentity();
+
+ glBegin( GL_TRIANGLES );
+ glTexCoord2f( 0.0f, 1.0f );
+ glVertex2f( -1.0f, -1.0f );
+ glTexCoord2f( 1.0f, 1.0f );
+ glVertex2f( 1.0f, -1.0f );
+ glTexCoord2f( 1.0f, 0.0f );
+ glVertex2f( 1.0f, 1.0f );
+
+ glTexCoord2f( 0.0f, 1.0f );
+ glVertex2f( -1.0f, -1.0f );
+ glTexCoord2f( 1.0f, 0.0f );
+ glVertex2f( 1.0f, 1.0f );
+ glTexCoord2f( 0.0f, 0.0f );
+ glVertex2f( -1.0f, 1.0f );
+ glEnd();
+
+ glPopMatrix();
+ glMatrixMode( GL_MODELVIEW );
+ glPopMatrix();
+}
+
+
+void OPENGL_COMPOSITOR::clean()
+{
+ assert( m_initialized );
+
+ glBindFramebufferEXT( GL_FRAMEBUFFER, DIRECT_RENDERING );
+ m_currentFbo = DIRECT_RENDERING;
+
+ OPENGL_BUFFERS::const_iterator it;
+
+ for( it = m_buffers.begin(); it != m_buffers.end(); ++it )
+ {
+ glDeleteTextures( 1, &it->textureTarget );
+ }
+
+ m_buffers.clear();
+
+ glDeleteFramebuffersEXT( 1, &m_framebuffer );
+ glDeleteRenderbuffersEXT( 1, &m_depthBuffer );
+
+ m_initialized = false;
+}