KiCad PCB EDA Suite
opengl_compositor.cpp
Go to the documentation of this file.
1 /*
2  * This program source code file is part of KiCad, a free EDA CAD application.
3  *
4  * Copyright (C) 2013-2017 CERN
5  * @author Maciej Suminski <maciej.suminski@cern.ch>
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License
9  * as published by the Free Software Foundation; either version 2
10  * of the License, or (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, you may find one here:
19  * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
20  * or you may search the http://www.gnu.org website for the version 2 license,
21  * or you may write to the Free Software Foundation, Inc.,
22  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
23  */
24 
32 #include <gal/opengl/utils.h>
33 
34 #include <stdexcept>
35 #include <cassert>
36 
37 using namespace KIGFX;
38 
40  m_initialized( false ), m_curBuffer( 0 ),
41  m_mainFbo( 0 ), m_depthBuffer( 0 ), m_curFbo( DIRECT_RENDERING ),
42  m_currentAntialiasingMode( OPENGL_ANTIALIASING_MODE::NONE )
43 {
44  m_antialiasing.reset( new ANTIALIASING_NONE( this ) );
45 }
46 
47 
49 {
50  if( m_initialized )
51  clean();
52 }
53 
54 
56 {
58 
59  if( m_initialized )
60  clean();
61 }
62 
63 
65 {
67 }
68 
69 
71 {
72  if( m_initialized )
73  return;
74 
76  {
78  m_antialiasing.reset( new ANTIALIASING_NONE( this ) );
79  break;
82  break;
85  break;
88  break;
91  break;
92  }
93 
94  VECTOR2U dims = m_antialiasing->GetInternalBufferSize();
95  assert( dims.x != 0 && dims.y != 0 );
96 
97  GLint maxBufSize;
98  glGetIntegerv( GL_MAX_RENDERBUFFER_SIZE_EXT, &maxBufSize );
99 
100  // VECTOR2U is unsigned, so no need to check if < 0
101  if( dims.x > (unsigned) maxBufSize || dims.y >= (unsigned) maxBufSize )
102  throw std::runtime_error( "Requested render buffer size is not supported" );
103 
104  // We need framebuffer objects for drawing the screen contents
105  // Generate framebuffer and a depth buffer
106  glGenFramebuffersEXT( 1, &m_mainFbo );
107  checkGlError( "generating framebuffer" );
108  bindFb( m_mainFbo );
109 
110  // Allocate memory for the depth buffer
111  // Attach the depth buffer to the framebuffer
112  glGenRenderbuffersEXT( 1, &m_depthBuffer );
113  checkGlError( "generating renderbuffer" );
114  glBindRenderbufferEXT( GL_RENDERBUFFER_EXT, m_depthBuffer );
115  checkGlError( "binding renderbuffer" );
116 
117  glRenderbufferStorageEXT( GL_RENDERBUFFER_EXT, GL_DEPTH24_STENCIL8, dims.x, dims.y );
118  checkGlError( "creating renderbuffer storage" );
119  glFramebufferRenderbufferEXT( GL_FRAMEBUFFER_EXT, GL_DEPTH_STENCIL_ATTACHMENT,
120  GL_RENDERBUFFER_EXT, m_depthBuffer );
121  checkGlError( "attaching renderbuffer" );
122 
123  // Unbind the framebuffer, so by default all the rendering goes directly to the display
125 
126  m_initialized = true;
127 
128  m_antialiasing->Init();
129 }
130 
131 
132 void OPENGL_COMPOSITOR::Resize( unsigned int aWidth, unsigned int aHeight )
133 {
134  if( m_initialized )
135  clean();
136 
137  m_antialiasing->OnLostBuffers();
138 
139  m_width = aWidth;
140  m_height = aHeight;
141 }
142 
143 
145 {
146  return m_antialiasing->CreateBuffer();
147 }
148 
149 
150 unsigned int OPENGL_COMPOSITOR::CreateBuffer( VECTOR2U aDimensions )
151 {
152  assert( m_initialized );
153 
154  int maxBuffers, maxTextureSize;
155 
156  // Get the maximum number of buffers
157  glGetIntegerv( GL_MAX_COLOR_ATTACHMENTS, (GLint*) &maxBuffers );
158 
159  if( (int) usedBuffers() >= maxBuffers )
160  {
161  throw std::runtime_error( "Cannot create more framebuffers. OpenGL rendering "
162  "backend requires at least 3 framebuffers. You may try to update/change "
163  "your graphic drivers." );
164  }
165 
166  glGetIntegerv( GL_MAX_TEXTURE_SIZE, (GLint*) &maxTextureSize );
167 
168  if( maxTextureSize < (int) aDimensions.x || maxTextureSize < (int) aDimensions.y )
169  {
170  throw std::runtime_error( "Requested texture size is not supported. "
171  "Could not create a buffer." );
172  }
173 
174  // GL_COLOR_ATTACHMENTn are consecutive integers
175  GLuint attachmentPoint = GL_COLOR_ATTACHMENT0 + usedBuffers();
176  GLuint textureTarget;
177 
178  // Generate the texture for the pixel storage
179  glActiveTexture( GL_TEXTURE0 );
180  glGenTextures( 1, &textureTarget );
181  checkGlError( "generating framebuffer texture target" );
182  glBindTexture( GL_TEXTURE_2D, textureTarget );
183  checkGlError( "binding framebuffer texture target" );
184 
185  // Set texture parameters
186  glTexEnvf( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE );
187  glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA8, aDimensions.x, aDimensions.y, 0, GL_RGBA,
188  GL_UNSIGNED_BYTE, NULL );
189  checkGlError( "creating framebuffer texture" );
190  glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST );
191  glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST );
192 
193  // Bind the texture to the specific attachment point, clear and rebind the screen
194  bindFb( m_mainFbo );
195  glFramebufferTexture2DEXT( GL_FRAMEBUFFER_EXT, attachmentPoint,
196  GL_TEXTURE_2D, textureTarget, 0 );
197 
198  // Check the status, exit if the framebuffer can't be created
199  GLenum status = glCheckFramebufferStatusEXT( GL_FRAMEBUFFER_EXT );
200 
201  if( status != GL_FRAMEBUFFER_COMPLETE_EXT )
202  {
203  switch( status )
204  {
205  case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT_EXT:
206  throw std::runtime_error( "The framebuffer attachment points are incomplete." );
207  break;
208 
209  case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT_EXT:
210  throw std::runtime_error( "No images attached to the framebuffer." );
211  break;
212 
213  case GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER_EXT:
214  throw std::runtime_error( "The framebuffer does not have at least one "
215  "image attached to it." );
216  break;
217 
218  case GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER_EXT:
219  throw std::runtime_error( "The framebuffer read buffer is incomplete." );
220  break;
221 
222  case GL_FRAMEBUFFER_UNSUPPORTED_EXT:
223  throw std::runtime_error( "The combination of internal formats of the attached "
224  "images violates an implementation-dependent set of restrictions." );
225  break;
226 
227  case GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE_EXT:
228  throw std::runtime_error( "GL_RENDERBUFFER_SAMPLES is not the same for "
229  "all attached renderbuffers" );
230  break;
231 
232  case GL_FRAMEBUFFER_INCOMPLETE_LAYER_TARGETS_EXT:
233  throw std::runtime_error( "Framebuffer incomplete layer targets errors." );
234  break;
235 
236  case GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS_EXT:
237  throw std::runtime_error( "Framebuffer attachments have different dimensions" );
238  break;
239 
240  default:
241  throw std::runtime_error( "Unknown error occurred when creating the framebuffer." );
242  break;
243  }
244 
245  return 0;
246  }
247 
248  ClearBuffer();
249 
250  // Return to direct rendering (we were asked only to create a buffer, not switch to one)
252 
253  // Store the new buffer
254  OPENGL_BUFFER buffer = { aDimensions, textureTarget, attachmentPoint };
255  m_buffers.push_back( buffer );
256 
257  return usedBuffers();
258 }
259 
260 
261 GLenum OPENGL_COMPOSITOR::GetBufferTexture( unsigned int aBufferHandle )
262 {
263  assert( aBufferHandle > 0 && aBufferHandle <= usedBuffers() );
264  return m_buffers[aBufferHandle - 1].textureTarget;
265 }
266 
267 
268 void OPENGL_COMPOSITOR::SetBuffer( unsigned int aBufferHandle )
269 {
270  assert( m_initialized );
271  assert( aBufferHandle <= usedBuffers() );
272 
273  // Either unbind the FBO for direct rendering, or bind the one with target textures
274  bindFb( aBufferHandle == DIRECT_RENDERING ? DIRECT_RENDERING : m_mainFbo );
275 
276  // Switch the target texture
277  if( m_curFbo != DIRECT_RENDERING )
278  {
279  m_curBuffer = aBufferHandle - 1;
280  glDrawBuffer( m_buffers[m_curBuffer].attachmentPoint );
281  checkGlError( "setting draw buffer" );
282 
283  glViewport( 0, 0,
284  m_buffers[m_curBuffer].dimensions.x, m_buffers[m_curBuffer].dimensions.y );
285  }
286  else
287  {
288  glViewport( 0, 0, GetScreenSize().x, GetScreenSize().y );
289  }
290 }
291 
292 
294 {
295  assert( m_initialized );
296 
297  glClearColor( 0.0f, 0.0f, 0.0f, 0.0f );
298  glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT );
299 }
300 
301 
303 {
304  return { m_width, m_height };
305 }
306 
307 
309 {
310  m_antialiasing->Begin();
311 }
312 
313 
314 void OPENGL_COMPOSITOR::DrawBuffer( unsigned int aBufferHandle )
315 {
316  m_antialiasing->DrawBuffer( aBufferHandle );
317 }
318 
319 
320 void OPENGL_COMPOSITOR::DrawBuffer( unsigned int aSourceHandle, unsigned int aDestHandle )
321 {
322  assert( m_initialized );
323  assert( aSourceHandle != 0 && aSourceHandle <= usedBuffers() );
324  assert( aDestHandle <= usedBuffers() );
325 
326  // Switch to the destination buffer and blit the scene
327  SetBuffer ( aDestHandle );
328 
329  // Depth test has to be disabled to make transparency working
330  glDisable( GL_DEPTH_TEST );
331  glBlendFunc( GL_ONE, GL_ONE_MINUS_SRC_ALPHA );
332 
333  // Enable texturing and bind the main texture
334  glEnable( GL_TEXTURE_2D );
335  glBindTexture( GL_TEXTURE_2D, m_buffers[aSourceHandle - 1].textureTarget );
336 
337  // Draw a full screen quad with the texture
338  glMatrixMode( GL_MODELVIEW );
339  glPushMatrix();
340  glLoadIdentity();
341  glMatrixMode( GL_PROJECTION );
342  glPushMatrix();
343  glLoadIdentity();
344 
345  glBegin( GL_TRIANGLES );
346  glTexCoord2f( 0.0f, 1.0f );
347  glVertex2f ( -1.0f, 1.0f );
348  glTexCoord2f( 0.0f, 0.0f );
349  glVertex2f ( -1.0f, -1.0f );
350  glTexCoord2f( 1.0f, 1.0f );
351  glVertex2f ( 1.0f, 1.0f );
352 
353  glTexCoord2f( 1.0f, 1.0f );
354  glVertex2f ( 1.0f, 1.0f );
355  glTexCoord2f( 0.0f, 0.0f );
356  glVertex2f ( -1.0f, -1.0f );
357  glTexCoord2f( 1.0f, 0.0f );
358  glVertex2f ( 1.0f, -1.0f );
359  glEnd();
360 
361  glPopMatrix();
362  glMatrixMode( GL_MODELVIEW );
363  glPopMatrix();
364 }
365 
366 
368 {
369  m_antialiasing->Present();
370 }
371 
372 
373 void OPENGL_COMPOSITOR::bindFb( unsigned int aFb )
374 {
375  // Currently there are only 2 valid FBOs
376  assert( aFb == DIRECT_RENDERING || aFb == m_mainFbo );
377 
378  if( m_curFbo != aFb )
379  {
380  glBindFramebufferEXT( GL_FRAMEBUFFER, aFb );
381  checkGlError( "switching framebuffer" );
382  m_curFbo = aFb;
383  }
384 }
385 
386 
388 {
389  assert( m_initialized );
390 
392 
393  for( OPENGL_BUFFERS::const_iterator it = m_buffers.begin(); it != m_buffers.end(); ++it )
394  {
395  glDeleteTextures( 1, &it->textureTarget );
396  }
397 
398  m_buffers.clear();
399 
400  glDeleteFramebuffersEXT( 1, &m_mainFbo );
401  glDeleteRenderbuffersEXT( 1, &m_depthBuffer );
402 
403  m_initialized = false;
404 }
unsigned int m_curBuffer
Currently used buffer handle.
virtual void DrawBuffer(unsigned int aBufferHandle) override
Function DrawBuffer() draws the selected buffer to the output buffer.
Class CAIRO_GAL is the cairo implementation of the graphics abstraction layer.
Definition: class_module.h:56
virtual void Begin() override
Function Begin() Call this at the beginning of each frame.
unsigned int m_width
Width of the buffer (in pixels)
Definition: compositor.h:117
void bindFb(unsigned int aFb)
Binds a specific Framebuffer Object.
unsigned int usedBuffers()
Returns number of used buffers.
int checkGlError(const std::string &aInfo, bool aThrow)
Checks if one of recent OpenGL operations has failed.
Definition: utils.cpp:30
bool m_initialized
Initialization status flag.
static const unsigned int DIRECT_RENDERING
Class that handles multitarget rendering (ie.
std::unique_ptr< OPENGL_PRESENTOR > m_antialiasing
OPENGL_ANTIALIASING_MODE m_currentAntialiasingMode
void SetAntialiasingMode(OPENGL_ANTIALIASING_MODE aMode)
virtual void ClearBuffer() override
Function ClearBuffer() clears the selected buffer (set by the SetBuffer() function).
void clean()
Function clean() performs freeing of resources.
virtual void Present() override
Function Present() Call this to present the output buffer to the screen.
virtual void Resize(unsigned int aWidth, unsigned int aHeight) override
Function Resize() clears the state of COMPOSITOR, so it has to be reinitialized again with the new di...
GLuint m_curFbo
Store the used FBO name in case there was more than one compositor used.
OPENGL_ANTIALIASING_MODE GetAntialiasingMode() const
virtual void SetBuffer(unsigned int aBufferHandle) override
Function SetBuffer() sets the selected buffer as the rendering target.
GLuint m_mainFbo
Main FBO handle (storing all target textures)
GLuint m_depthBuffer
Depth buffer handle.
virtual void Initialize() override
Function Reset() performs primary initialiation, necessary to use the object.
VECTOR2U GetScreenSize() const
OPENGL_BUFFERS m_buffers
Stores information about initialized buffers.
GLenum GetBufferTexture(unsigned int aBufferHandle)
virtual unsigned int CreateBuffer() override
Function CreateBuffer() prepares a new buffer that may be used as a rendering target.
unsigned int m_height
Height of the buffer (in pixels)
Definition: compositor.h:118