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 <gal/color4d.h>
35 
36 #include <stdexcept>
37 #include <cassert>
38 
39 using namespace KIGFX;
40 
42  m_initialized( false ), m_curBuffer( 0 ),
43  m_mainFbo( 0 ), m_depthBuffer( 0 ), m_curFbo( DIRECT_RENDERING ),
44  m_currentAntialiasingMode( OPENGL_ANTIALIASING_MODE::NONE )
45 {
46  m_antialiasing.reset( new ANTIALIASING_NONE( this ) );
47 }
48 
49 
51 {
52  if( m_initialized )
53  clean();
54 }
55 
56 
58 {
60 
61  if( m_initialized )
62  clean();
63 }
64 
65 
67 {
69 }
70 
71 
73 {
74  if( m_initialized )
75  return;
76 
78  {
80  m_antialiasing.reset( new ANTIALIASING_NONE( this ) );
81  break;
84  break;
87  break;
90  break;
93  break;
94  }
95 
96  VECTOR2U dims = m_antialiasing->GetInternalBufferSize();
97  assert( dims.x != 0 && dims.y != 0 );
98 
99  GLint maxBufSize;
100  glGetIntegerv( GL_MAX_RENDERBUFFER_SIZE_EXT, &maxBufSize );
101 
102  // VECTOR2U is unsigned, so no need to check if < 0
103  if( dims.x > (unsigned) maxBufSize || dims.y >= (unsigned) maxBufSize )
104  throw std::runtime_error( "Requested render buffer size is not supported" );
105 
106  // We need framebuffer objects for drawing the screen contents
107  // Generate framebuffer and a depth buffer
108  glGenFramebuffersEXT( 1, &m_mainFbo );
109  checkGlError( "generating framebuffer" );
110  bindFb( m_mainFbo );
111 
112  // Allocate memory for the depth buffer
113  // Attach the depth buffer to the framebuffer
114  glGenRenderbuffersEXT( 1, &m_depthBuffer );
115  checkGlError( "generating renderbuffer" );
116  glBindRenderbufferEXT( GL_RENDERBUFFER_EXT, m_depthBuffer );
117  checkGlError( "binding renderbuffer" );
118 
119  glRenderbufferStorageEXT( GL_RENDERBUFFER_EXT, GL_DEPTH24_STENCIL8, dims.x, dims.y );
120  checkGlError( "creating renderbuffer storage" );
121  glFramebufferRenderbufferEXT( GL_FRAMEBUFFER_EXT, GL_DEPTH_STENCIL_ATTACHMENT,
122  GL_RENDERBUFFER_EXT, m_depthBuffer );
123  checkGlError( "attaching renderbuffer" );
124 
125  // Unbind the framebuffer, so by default all the rendering goes directly to the display
127 
128  m_initialized = true;
129 
130  m_antialiasing->Init();
131 }
132 
133 
134 void OPENGL_COMPOSITOR::Resize( unsigned int aWidth, unsigned int aHeight )
135 {
136  if( m_initialized )
137  clean();
138 
139  m_antialiasing->OnLostBuffers();
140 
141  m_width = aWidth;
142  m_height = aHeight;
143 }
144 
145 
147 {
148  return m_antialiasing->CreateBuffer();
149 }
150 
151 
152 unsigned int OPENGL_COMPOSITOR::CreateBuffer( VECTOR2U aDimensions )
153 {
154  assert( m_initialized );
155 
156  int maxBuffers, maxTextureSize;
157 
158  // Get the maximum number of buffers
159  glGetIntegerv( GL_MAX_COLOR_ATTACHMENTS, (GLint*) &maxBuffers );
160 
161  if( (int) usedBuffers() >= maxBuffers )
162  {
163  throw std::runtime_error( "Cannot create more framebuffers. OpenGL rendering "
164  "backend requires at least 3 framebuffers. You may try to update/change "
165  "your graphic drivers." );
166  }
167 
168  glGetIntegerv( GL_MAX_TEXTURE_SIZE, (GLint*) &maxTextureSize );
169 
170  if( maxTextureSize < (int) aDimensions.x || maxTextureSize < (int) aDimensions.y )
171  {
172  throw std::runtime_error( "Requested texture size is not supported. "
173  "Could not create a buffer." );
174  }
175 
176  // GL_COLOR_ATTACHMENTn are consecutive integers
177  GLuint attachmentPoint = GL_COLOR_ATTACHMENT0 + usedBuffers();
178  GLuint textureTarget;
179 
180  // Generate the texture for the pixel storage
181  glActiveTexture( GL_TEXTURE0 );
182  glGenTextures( 1, &textureTarget );
183  checkGlError( "generating framebuffer texture target" );
184  glBindTexture( GL_TEXTURE_2D, textureTarget );
185  checkGlError( "binding framebuffer texture target" );
186 
187  // Set texture parameters
188  glTexEnvf( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE );
189  glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA8, aDimensions.x, aDimensions.y, 0, GL_RGBA,
190  GL_UNSIGNED_BYTE, NULL );
191  checkGlError( "creating framebuffer texture" );
192  glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST );
193  glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST );
194 
195  // Bind the texture to the specific attachment point, clear and rebind the screen
196  bindFb( m_mainFbo );
197  glFramebufferTexture2DEXT( GL_FRAMEBUFFER_EXT, attachmentPoint,
198  GL_TEXTURE_2D, textureTarget, 0 );
199 
200  // Check the status, exit if the framebuffer can't be created
201  GLenum status = glCheckFramebufferStatusEXT( GL_FRAMEBUFFER_EXT );
202 
203  if( status != GL_FRAMEBUFFER_COMPLETE_EXT )
204  {
205  switch( status )
206  {
207  case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT_EXT:
208  throw std::runtime_error( "The framebuffer attachment points are incomplete." );
209  break;
210 
211  case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT_EXT:
212  throw std::runtime_error( "No images attached to the framebuffer." );
213  break;
214 
215  case GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER_EXT:
216  throw std::runtime_error( "The framebuffer does not have at least one "
217  "image attached to it." );
218  break;
219 
220  case GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER_EXT:
221  throw std::runtime_error( "The framebuffer read buffer is incomplete." );
222  break;
223 
224  case GL_FRAMEBUFFER_UNSUPPORTED_EXT:
225  throw std::runtime_error( "The combination of internal formats of the attached "
226  "images violates an implementation-dependent set of restrictions." );
227  break;
228 
229  case GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE_EXT:
230  throw std::runtime_error( "GL_RENDERBUFFER_SAMPLES is not the same for "
231  "all attached renderbuffers" );
232  break;
233 
234  case GL_FRAMEBUFFER_INCOMPLETE_LAYER_TARGETS_EXT:
235  throw std::runtime_error( "Framebuffer incomplete layer targets errors." );
236  break;
237 
238  case GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS_EXT:
239  throw std::runtime_error( "Framebuffer attachments have different dimensions" );
240  break;
241 
242  default:
243  throw std::runtime_error( "Unknown error occurred when creating the framebuffer." );
244  break;
245  }
246 
247  return 0;
248  }
249 
251 
252  // Return to direct rendering (we were asked only to create a buffer, not switch to one)
254 
255  // Store the new buffer
256  OPENGL_BUFFER buffer = { aDimensions, textureTarget, attachmentPoint };
257  m_buffers.push_back( buffer );
258 
259  return usedBuffers();
260 }
261 
262 
263 GLenum OPENGL_COMPOSITOR::GetBufferTexture( unsigned int aBufferHandle )
264 {
265  assert( aBufferHandle > 0 && aBufferHandle <= usedBuffers() );
266  return m_buffers[aBufferHandle - 1].textureTarget;
267 }
268 
269 
270 void OPENGL_COMPOSITOR::SetBuffer( unsigned int aBufferHandle )
271 {
272  assert( m_initialized );
273  assert( aBufferHandle <= usedBuffers() );
274 
275  // Either unbind the FBO for direct rendering, or bind the one with target textures
276  bindFb( aBufferHandle == DIRECT_RENDERING ? DIRECT_RENDERING : m_mainFbo );
277 
278  // Switch the target texture
279  if( m_curFbo != DIRECT_RENDERING )
280  {
281  m_curBuffer = aBufferHandle - 1;
282  glDrawBuffer( m_buffers[m_curBuffer].attachmentPoint );
283  checkGlError( "setting draw buffer" );
284 
285  glViewport( 0, 0,
286  m_buffers[m_curBuffer].dimensions.x, m_buffers[m_curBuffer].dimensions.y );
287  }
288  else
289  {
290  glViewport( 0, 0, GetScreenSize().x, GetScreenSize().y );
291  }
292 }
293 
294 
296 {
297  assert( m_initialized );
298 
299  glClearColor( aColor.r, aColor.g, aColor.b, 0.0f );
300  glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT );
301 }
302 
303 
305 {
306  return { m_width, m_height };
307 }
308 
309 
311 {
312  m_antialiasing->Begin();
313 }
314 
315 
316 void OPENGL_COMPOSITOR::DrawBuffer( unsigned int aBufferHandle )
317 {
318  m_antialiasing->DrawBuffer( aBufferHandle );
319 }
320 
321 
322 void OPENGL_COMPOSITOR::DrawBuffer( unsigned int aSourceHandle, unsigned int aDestHandle )
323 {
324  assert( m_initialized );
325  assert( aSourceHandle != 0 && aSourceHandle <= usedBuffers() );
326  assert( aDestHandle <= usedBuffers() );
327 
328  // Switch to the destination buffer and blit the scene
329  SetBuffer ( aDestHandle );
330 
331  // Depth test has to be disabled to make transparency working
332  glDisable( GL_DEPTH_TEST );
333  glBlendFunc( GL_ONE, GL_ONE_MINUS_SRC_ALPHA );
334 
335  // Enable texturing and bind the main texture
336  glEnable( GL_TEXTURE_2D );
337  glBindTexture( GL_TEXTURE_2D, m_buffers[aSourceHandle - 1].textureTarget );
338 
339  // Draw a full screen quad with the texture
340  glMatrixMode( GL_MODELVIEW );
341  glPushMatrix();
342  glLoadIdentity();
343  glMatrixMode( GL_PROJECTION );
344  glPushMatrix();
345  glLoadIdentity();
346 
347  glBegin( GL_TRIANGLES );
348  glTexCoord2f( 0.0f, 1.0f );
349  glVertex2f ( -1.0f, 1.0f );
350  glTexCoord2f( 0.0f, 0.0f );
351  glVertex2f ( -1.0f, -1.0f );
352  glTexCoord2f( 1.0f, 1.0f );
353  glVertex2f ( 1.0f, 1.0f );
354 
355  glTexCoord2f( 1.0f, 1.0f );
356  glVertex2f ( 1.0f, 1.0f );
357  glTexCoord2f( 0.0f, 0.0f );
358  glVertex2f ( -1.0f, -1.0f );
359  glTexCoord2f( 1.0f, 0.0f );
360  glVertex2f ( 1.0f, -1.0f );
361  glEnd();
362 
363  glPopMatrix();
364  glMatrixMode( GL_MODELVIEW );
365  glPopMatrix();
366 }
367 
368 
370 {
371  m_antialiasing->Present();
372 }
373 
374 
375 void OPENGL_COMPOSITOR::bindFb( unsigned int aFb )
376 {
377  // Currently there are only 2 valid FBOs
378  assert( aFb == DIRECT_RENDERING || aFb == m_mainFbo );
379 
380  if( m_curFbo != aFb )
381  {
382  glBindFramebufferEXT( GL_FRAMEBUFFER, aFb );
383  checkGlError( "switching framebuffer" );
384  m_curFbo = aFb;
385  }
386 }
387 
388 
390 {
391  assert( m_initialized );
392 
394 
395  for( OPENGL_BUFFERS::const_iterator it = m_buffers.begin(); it != m_buffers.end(); ++it )
396  {
397  glDeleteTextures( 1, &it->textureTarget );
398  }
399 
400  m_buffers.clear();
401 
402  glDeleteFramebuffersEXT( 1, &m_mainFbo );
403  glDeleteRenderbuffersEXT( 1, &m_depthBuffer );
404 
405  m_initialized = false;
406 }
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:57
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:119
void bindFb(unsigned int aFb)
Binds a specific Framebuffer Object.
static const COLOR4D BLACK
Definition: color4d.h:296
double g
Green component.
Definition: color4d.h:287
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
double b
Blue component.
Definition: color4d.h:288
OPENGL_ANTIALIASING_MODE m_currentAntialiasingMode
void SetAntialiasingMode(OPENGL_ANTIALIASING_MODE aMode)
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 ClearBuffer(const COLOR4D &aColor) override
Function ClearBuffer() clears the selected buffer (set by the SetBuffer() function).
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.
double r
Red component.
Definition: color4d.h:286
unsigned int m_height
Height of the buffer (in pixels)
Definition: compositor.h:120
Class COLOR4D is the color representation with 4 components: red, green, blue, alpha.
Definition: color4d.h:39