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