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 <[email protected]>
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
44using namespace KIGFX;
45
46OPENGL_COMPOSITOR::OPENGL_COMPOSITOR() :
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
146void 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
164unsigned 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
265GLenum OPENGL_COMPOSITOR::GetBufferTexture( unsigned int aBufferHandle )
266{
267 wxASSERT( aBufferHandle > 0 && aBufferHandle <= usedBuffers() );
268 return m_buffers[aBufferHandle - 1].textureTarget;
269}
270
271
272void 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
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
318void OPENGL_COMPOSITOR::DrawBuffer( unsigned int aBufferHandle )
319{
320 m_antialiasing->DrawBuffer( aBufferHandle );
321}
322
323
324void 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
377void 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{
415 {
417 default: return 1;
418 }
419}
420
422{
424 {
425 case OPENGL_ANTIALIASING_MODE::SUPERSAMPLING: return VECTOR2D( 0.5, -0.5 );
426 default: return VECTOR2D( 0, 0 );
427 }
428}
A color representation with 4 components: red, green, blue, alpha.
Definition: color4d.h:104
double r
Red component.
Definition: color4d.h:390
double g
Green component.
Definition: color4d.h:391
static const COLOR4D BLACK
Definition: color4d.h:400
double b
Blue component.
Definition: color4d.h:392
unsigned int m_height
Height of the buffer (in pixels)
Definition: compositor.h:113
unsigned int m_width
Width of the buffer (in pixels)
Definition: compositor.h:112
VECTOR2D GetAntialiasRenderingOffset() const
OPENGL_ANTIALIASING_MODE m_currentAntialiasingMode
virtual void Begin() override
Call this at the beginning of each frame.
unsigned int usedBuffers()
Returns number of used buffers.
static const unsigned int DIRECT_RENDERING
OPENGL_ANTIALIASING_MODE GetAntialiasingMode() const
void SetAntialiasingMode(OPENGL_ANTIALIASING_MODE aMode)
void clean()
Perform freeing of resources.
virtual void Present() override
Call this to present the output buffer to the screen.
bool m_initialized
Initialization status flag.
void bindFb(unsigned int aFb)
Binds a specific Framebuffer Object.
GLuint m_curFbo
Store the used FBO name in case there was more than one compositor used.
virtual void Initialize() override
Perform primary initialization, necessary to use the object.
GLuint m_depthBuffer
Depth buffer handle.
virtual void ClearBuffer(const COLOR4D &aColor) override
Clear the selected buffer (set by the SetBuffer() function).
std::unique_ptr< OPENGL_PRESENTOR > m_antialiasing
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.
OPENGL_BUFFERS m_buffers
Stores information about initialized buffers.
virtual void DrawBuffer(unsigned int aBufferHandle) override
Draw the selected buffer to the output buffer.
GLuint m_mainFbo
Main FBO handle (storing all target textures)
int GetAntialiasSupersamplingFactor() const
unsigned int m_curBuffer
Currently used buffer handle.
GLenum GetBufferTexture(unsigned int aBufferHandle)
virtual void SetBuffer(unsigned int aBufferHandle) override
Set the selected buffer as the rendering target.
virtual unsigned int CreateBuffer() override
Prepare a new buffer that may be used as a rendering target.
@ NONE
Definition: kibis.h:53
The Cairo implementation of the graphics abstraction layer.
Definition: color4d.cpp:266
Handle multitarget rendering (ie.
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
VECTOR2< double > VECTOR2D
Definition: vector2d.h:617