Commit 618a5f0e authored by Maciej Suminski's avatar Maciej Suminski
Browse files

OpenGL multitarget rendering (compositing).

parent a8f47913
Loading
Loading
Loading
Loading
+7 −1
Original line number Diff line number Diff line
@@ -29,11 +29,15 @@ add_custom_target(
)

set(GAL_SRCS
    # Common part
    drawpanel_gal.cpp
    painter.cpp
    gal/graphics_abstraction_layer.cpp
    gal/stroke_font.cpp
    gal/color4d.cpp
    view/wx_view_controls.cpp

    # OpenGL GAL
    gal/opengl/opengl_gal.cpp
    gal/opengl/shader.cpp
    gal/opengl/vertex_item.cpp
@@ -42,8 +46,10 @@ set(GAL_SRCS
    gal/opengl/noncached_container.cpp
    gal/opengl/vertex_manager.cpp
    gal/opengl/gpu_manager.cpp
    gal/opengl/opengl_compositor.cpp

    # Cairo GAL
    gal/cairo/cairo_gal.cpp
    view/wx_view_controls.cpp
    )

add_library(gal STATIC ${GAL_SRCS})
+6 −0
Original line number Diff line number Diff line
@@ -280,6 +280,12 @@ void CAIRO_GAL::RestoreScreen()
}


void CAIRO_GAL::SetTarget( RenderTarget aTarget )
{
    wxASSERT_MSG( false, wxT( "Not implemented yet" ) );
}


void CAIRO_GAL::DrawLine( const VECTOR2D& aStartPoint, const VECTOR2D& aEndPoint )
{
    cairo_move_to( cairoImage, aStartPoint.x, aStartPoint.y );
+2 −0
Original line number Diff line number Diff line
@@ -62,6 +62,8 @@ void GAL::DrawGrid()
    if( !gridVisibility )
        return;

    SetTarget( TARGET_NONCACHED );

    // The grid consists of lines
    // For the drawing the start points, end points and increments have to be calculated in world coordinates
    VECTOR2D    screenStartPoint( 0, 0 );
+5 −2
Original line number Diff line number Diff line
@@ -77,9 +77,11 @@ void GPU_MANAGER::SetShader( SHADER& aShader )

// Cached manager
GPU_CACHED_MANAGER::GPU_CACHED_MANAGER( VERTEX_CONTAINER* aContainer ) :
    GPU_MANAGER( aContainer ), m_buffersInitialized( false ), m_indicesPtr( NULL ),
    GPU_MANAGER( aContainer ), m_buffersInitialized( false ),
    m_indicesSize( 0 )
{
    // Allocate the biggest possible buffer for indices
    m_indices.reset( new GLuint[aContainer->GetSize()] );
}


@@ -252,7 +254,6 @@ void GPU_NONCACHED_MANAGER::EndDrawing()
    VERTEX*  vertices       = m_container->GetAllVertices();
    GLfloat* coordinates    = (GLfloat*) ( vertices );
    GLubyte* colors         = (GLubyte*) ( vertices ) + ColorOffset;
    GLfloat* shaders        = (GLfloat*) ( vertices ) + ShaderOffset / sizeof(GLfloat);

    // Prepare buffers
    glEnableClientState( GL_VERTEX_ARRAY );
@@ -263,6 +264,8 @@ void GPU_NONCACHED_MANAGER::EndDrawing()

    if( m_shader != NULL )    // Use shader if applicable
    {
        GLfloat* shaders = (GLfloat*) ( vertices ) + ShaderOffset / sizeof(GLfloat);

        m_shader->Use();
        glEnableVertexAttribArray( m_shaderAttrib );
        glVertexAttribPointer( m_shaderAttrib, ShaderStride, GL_FLOAT, GL_FALSE,
+242 −0
Original line number Diff line number Diff line
/*i
 * This program source code file is part of KiCad, a free EDA CAD application.
 *
 * Copyright (C) 2013 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:O//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 (ie. to different textures/surfaces) and
 * later compositing into a single image (OpenGL flavour).
 */

#include <gal/opengl/opengl_compositor.h>
#include <wx/log.h>

using namespace KiGfx;

OPENGL_COMPOSITOR::OPENGL_COMPOSITOR() :
    m_initialized( false ), m_current( 0 )
{
}


OPENGL_COMPOSITOR::~OPENGL_COMPOSITOR()
{
    if( m_initialized )
        clean();
}


void OPENGL_COMPOSITOR::Initialize()
{
    if( m_initialized )
        return;

    // Get the maximum number of buffers
    glGetIntegerv( GL_MAX_COLOR_ATTACHMENTS, (GLint*) &m_maxBuffers );

    // We need framebuffer objects for drawing the screen contents
    // Generate framebuffer and a depth buffer
    glGenFramebuffers( 1, &m_framebuffer );
    glBindFramebuffer( GL_FRAMEBUFFER, m_framebuffer );
    m_currentFbo = m_framebuffer;

    // Allocate memory for the depth buffer
    // Attach the depth buffer to the framebuffer
    glGenRenderbuffers( 1, &m_depthBuffer );
    glBindRenderbuffer( GL_RENDERBUFFER, 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
    glRenderbufferStorage( GL_RENDERBUFFER, GL_DEPTH_STENCIL, m_width, m_height );
    glFramebufferRenderbuffer( GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT,
                               GL_RENDERBUFFER, m_depthBuffer );
    glFramebufferRenderbuffer( GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT,
                               GL_RENDERBUFFER, m_depthBuffer );

    // Check the status, exit if the framebuffer can't be created
    GLenum status = glCheckFramebufferStatus( GL_FRAMEBUFFER );

    if( status != GL_FRAMEBUFFER_COMPLETE )
    {
        wxLogFatalError( wxT( "Cannot create the framebuffer." ) );
    }

    // Unbind the framebuffer, so by default all the rendering goes directly to the display
    glBindFramebuffer( GL_FRAMEBUFFER, 0 );
    m_currentFbo = 0;

    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::GetBuffer()
{
    wxASSERT( m_initialized );

    if( m_buffers.size() < m_maxBuffers )
    {
        // 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
        glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA, 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
        glBindFramebuffer( GL_FRAMEBUFFER, m_framebuffer );
        m_currentFbo = m_framebuffer;
        glFramebufferTexture2D( GL_FRAMEBUFFER, attachmentPoint, GL_TEXTURE_2D, textureTarget, 0 );
        ClearBuffer();
        glBindFramebuffer( GL_FRAMEBUFFER, 0 );
        m_currentFbo = 0;

        // Store the new buffer
        BUFFER_ITEM buffer = { textureTarget, attachmentPoint };
        m_buffers.push_back( buffer );

        return usedBuffers();
    }

    // Unfortunately we have no more buffers left
    return 0;
}


void OPENGL_COMPOSITOR::SetBuffer( unsigned int aBufferHandle )
{
    if( aBufferHandle <= usedBuffers() )
    {
        // Change the rendering destination to the selected attachment point
        if( m_currentFbo != m_framebuffer )
        {
            glBindFramebuffer( GL_FRAMEBUFFER, m_framebuffer );
            m_currentFbo = m_framebuffer;
        }

        if( m_current != aBufferHandle - 1 )
        {
            glDrawBuffer( m_buffers[m_current].attachmentPoint );
            m_current = aBufferHandle - 1;
        }
    }
}


void OPENGL_COMPOSITOR::ClearBuffer()
{
    wxASSERT( m_initialized );

    glClearColor( 0.0f, 0.0f, 0.0f, 0.0f );
    glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
}


void OPENGL_COMPOSITOR::BlitBuffer( unsigned int aBufferHandle )
{
    wxASSERT( m_initialized );

    wxASSERT_MSG( false, wxT( "Not implemented yet" ) );
}


void OPENGL_COMPOSITOR::DrawBuffer( unsigned int aBufferHandle, double aDepth )
{
    wxASSERT( m_initialized );

    // Switch to the main framebuffer and blit the scene
    glBindFramebuffer( GL_FRAMEBUFFER, 0 );
    m_currentFbo = 0;

    // 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 );
    glVertex3f(  -1.0f, -1.0f, aDepth );
    glTexCoord2f( 1.0f,  1.0f );
    glVertex3f(   1.0f, -1.0f, aDepth );
    glTexCoord2f( 1.0f,  0.0f );
    glVertex3f(   1.0f,  1.0f, aDepth );

    glTexCoord2f( 0.0f,  1.0f );
    glVertex3f(  -1.0f, -1.0f, aDepth );
    glTexCoord2f( 1.0f,  0.0f );
    glVertex3f(   1.0f,  1.0f, aDepth );
    glTexCoord2f( 0.0f,  0.0f );
    glVertex3f(  -1.0f,  1.0f, aDepth );
    glEnd();

    glPopMatrix();
    glMatrixMode( GL_MODELVIEW );
    glPopMatrix();
}


void OPENGL_COMPOSITOR::clean()
{
    wxASSERT( m_initialized );

    glDeleteFramebuffers( 1, &m_framebuffer );
    glDeleteRenderbuffers( 1, &m_depthBuffer );

    Buffers::const_iterator it;
    for( it = m_buffers.begin(); it != m_buffers.end(); ++it )
    {
        glDeleteTextures( 1, &it->textureTarget );
    }
    m_buffers.clear();

    m_initialized = false;
}

GLuint OPENGL_COMPOSITOR::m_currentFbo = 0;
Loading