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