KiCad PCB EDA Suite
Loading...
Searching...
No Matches
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
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 VECTOR2I 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 if( dims.x < 0 || dims.y < 0 || dims.x > maxBufSize || dims.y >= maxBufSize )
115 throw std::runtime_error( "Requested render buffer size is not supported" );
116
117 // We need framebuffer objects for drawing the screen contents
118 // Generate framebuffer and a depth buffer
119 glGenFramebuffersEXT( 1, &m_mainFbo );
120 checkGlError( "generating framebuffer", __FILE__, __LINE__ );
121 bindFb( m_mainFbo );
122
123 // Allocate memory for the depth buffer
124 // Attach the depth buffer to the framebuffer
125 glGenRenderbuffersEXT( 1, &m_depthBuffer );
126 checkGlError( "generating renderbuffer", __FILE__, __LINE__ );
127 glBindRenderbufferEXT( GL_RENDERBUFFER_EXT, m_depthBuffer );
128 checkGlError( "binding renderbuffer", __FILE__, __LINE__ );
129
130 glRenderbufferStorageEXT( GL_RENDERBUFFER_EXT, GL_DEPTH24_STENCIL8, dims.x, dims.y );
131 checkGlError( "creating renderbuffer storage", __FILE__, __LINE__ );
132 glFramebufferRenderbufferEXT( GL_FRAMEBUFFER_EXT, GL_DEPTH_STENCIL_ATTACHMENT,
133 GL_RENDERBUFFER_EXT, m_depthBuffer );
134 checkGlError( "attaching renderbuffer", __FILE__, __LINE__ );
135
136 // Unbind the framebuffer, so by default all the rendering goes directly to the display
138
139 m_initialized = true;
140
141 m_antialiasing->Init();
142}
143
144
145void OPENGL_COMPOSITOR::Resize( unsigned int aWidth, unsigned int aHeight )
146{
147 if( m_initialized )
148 clean();
149
150 m_antialiasing->OnLostBuffers();
151
152 m_width = aWidth;
153 m_height = aHeight;
154}
155
156
158{
159 return m_antialiasing->CreateBuffer();
160}
161
162
163unsigned int OPENGL_COMPOSITOR::CreateBuffer( VECTOR2I aDimensions )
164{
165 assert( m_initialized );
166
167 int maxBuffers, maxTextureSize;
168
169 // Get the maximum number of buffers
170 glGetIntegerv( GL_MAX_COLOR_ATTACHMENTS, (GLint*) &maxBuffers );
171
172 if( (int) usedBuffers() >= maxBuffers )
173 {
174 throw std::runtime_error( "Cannot create more framebuffers. OpenGL rendering backend "
175 "requires at least 3 framebuffers. You may try to update/change "
176 "your graphic drivers." );
177 }
178
179 glGetIntegerv( GL_MAX_TEXTURE_SIZE, (GLint*) &maxTextureSize );
180
181 if( maxTextureSize < (int) aDimensions.x || maxTextureSize < (int) aDimensions.y )
182 {
183 throw std::runtime_error( "Requested texture size is not supported. "
184 "Could not create a buffer." );
185 }
186
187 // GL_COLOR_ATTACHMENTn are consecutive integers
188 GLuint attachmentPoint = GL_COLOR_ATTACHMENT0 + usedBuffers();
189 GLuint textureTarget;
190
191 // Generate the texture for the pixel storage
192 glActiveTexture( GL_TEXTURE0 );
193 glGenTextures( 1, &textureTarget );
194 checkGlError( "generating framebuffer texture target", __FILE__, __LINE__ );
195 glBindTexture( GL_TEXTURE_2D, textureTarget );
196 checkGlError( "binding framebuffer texture target", __FILE__, __LINE__ );
197
198 // Set texture parameters
199 glTexEnvf( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE );
200 glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA8, aDimensions.x, aDimensions.y, 0, GL_RGBA,
201 GL_UNSIGNED_BYTE, nullptr );
202 checkGlError( "creating framebuffer texture", __FILE__, __LINE__ );
203 glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST );
204 glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST );
205
206 // Bind the texture to the specific attachment point, clear and rebind the screen
207 bindFb( m_mainFbo );
208 glFramebufferTexture2DEXT( GL_FRAMEBUFFER_EXT, attachmentPoint, GL_TEXTURE_2D, textureTarget,
209 0 );
210
211 // Check the status, exit if the framebuffer can't be created
212 GLenum status = glCheckFramebufferStatusEXT( GL_FRAMEBUFFER_EXT );
213
214 if( status != GL_FRAMEBUFFER_COMPLETE_EXT )
215 {
216 switch( status )
217 {
218 case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT_EXT:
219 throw std::runtime_error( "The framebuffer attachment points are incomplete." );
220
221 case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT_EXT:
222 throw std::runtime_error( "No images attached to the framebuffer." );
223
224 case GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER_EXT:
225 throw std::runtime_error( "The framebuffer does not have at least one "
226 "image attached to it." );
227
228 case GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER_EXT:
229 throw std::runtime_error( "The framebuffer read buffer is incomplete." );
230
231 case GL_FRAMEBUFFER_UNSUPPORTED_EXT:
232 throw std::runtime_error( "The combination of internal formats of the attached "
233 "images violates an implementation-dependent set of "
234 "restrictions." );
235
236 case GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE_EXT:
237 throw std::runtime_error( "GL_RENDERBUFFER_SAMPLES is not the same for "
238 "all attached renderbuffers" );
239
240 case GL_FRAMEBUFFER_INCOMPLETE_LAYER_TARGETS_EXT:
241 throw std::runtime_error( "Framebuffer incomplete layer targets errors." );
242
243 case GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS_EXT:
244 throw std::runtime_error( "Framebuffer attachments have different dimensions" );
245
246 default:
247 throw std::runtime_error( "Unknown error occurred when creating the framebuffer." );
248 }
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
264GLenum OPENGL_COMPOSITOR::GetBufferTexture( unsigned int aBufferHandle )
265{
266 wxASSERT( aBufferHandle > 0 && aBufferHandle <= usedBuffers() );
267 return m_buffers[aBufferHandle - 1].textureTarget;
268}
269
270
271void OPENGL_COMPOSITOR::SetBuffer( unsigned int aBufferHandle )
272{
273 wxASSERT( m_initialized );
274 wxASSERT( 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
281 {
282 m_curBuffer = aBufferHandle - 1;
283 glDrawBuffer( m_buffers[m_curBuffer].attachmentPoint );
284 checkGlError( "setting draw buffer", __FILE__, __LINE__ );
285
286 glViewport( 0, 0, m_buffers[m_curBuffer].dimensions.x,
287 m_buffers[m_curBuffer].dimensions.y );
288 }
289 else
290 {
291 glViewport( 0, 0, GetScreenSize().x, GetScreenSize().y );
292 }
293}
294
295
297{
298 wxASSERT( m_initialized );
299
300 glClearColor( aColor.r, aColor.g, aColor.b, m_curFbo == DIRECT_RENDERING ? 1.0f : 0.0f );
301 glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT );
302}
303
304
306{
307 typedef VECTOR2I::coord_type coord_t;
308 wxASSERT( m_width <= static_cast<unsigned int>( std::numeric_limits<coord_t>::max() ) );
309 wxASSERT( m_height <= static_cast<unsigned int>( std::numeric_limits<coord_t>::max() ) );
310
311 return { static_cast<coord_t>( m_width ), static_cast<coord_t>( m_height ) };
312}
313
314
316{
317 m_antialiasing->Begin();
318}
319
320
321void OPENGL_COMPOSITOR::DrawBuffer( unsigned int aBufferHandle )
322{
323 m_antialiasing->DrawBuffer( aBufferHandle );
324}
325
326
327void OPENGL_COMPOSITOR::DrawBuffer( unsigned int aSourceHandle, unsigned int aDestHandle )
328{
329 wxASSERT( m_initialized );
330 wxASSERT( aSourceHandle != 0 && aSourceHandle <= usedBuffers() );
331 wxASSERT( aDestHandle <= usedBuffers() );
332
333 // Switch to the destination buffer and blit the scene
334 SetBuffer( aDestHandle );
335
336 // Depth test has to be disabled to make transparency working
337 glDisable( GL_DEPTH_TEST );
338 glBlendFunc( GL_ONE, GL_ONE_MINUS_SRC_ALPHA );
339
340 // Enable texturing and bind the main texture
341 glEnable( GL_TEXTURE_2D );
342 glBindTexture( GL_TEXTURE_2D, m_buffers[aSourceHandle - 1].textureTarget );
343
344 // Draw a full screen quad with the texture
345 glMatrixMode( GL_MODELVIEW );
346 glPushMatrix();
347 glLoadIdentity();
348 glMatrixMode( GL_PROJECTION );
349 glPushMatrix();
350 glLoadIdentity();
351
352 glBegin( GL_TRIANGLES );
353 glTexCoord2f( 0.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, 1.0f );
358 glVertex2f( 1.0f, 1.0f );
359
360 glTexCoord2f( 1.0f, 1.0f );
361 glVertex2f( 1.0f, 1.0f );
362 glTexCoord2f( 0.0f, 0.0f );
363 glVertex2f( -1.0f, -1.0f );
364 glTexCoord2f( 1.0f, 0.0f );
365 glVertex2f( 1.0f, -1.0f );
366 glEnd();
367
368 glPopMatrix();
369 glMatrixMode( GL_MODELVIEW );
370 glPopMatrix();
371}
372
373
375{
376 m_antialiasing->Present();
377}
378
379
380void OPENGL_COMPOSITOR::bindFb( unsigned int aFb )
381{
382 // Currently there are only 2 valid FBOs
383 wxASSERT( aFb == DIRECT_RENDERING || aFb == m_mainFbo );
384
385 if( m_curFbo != aFb )
386 {
387 glBindFramebufferEXT( GL_FRAMEBUFFER, aFb );
388 checkGlError( "switching framebuffer", __FILE__, __LINE__ );
389 m_curFbo = aFb;
390 }
391}
392
393
395{
396 wxASSERT( m_initialized );
397
399
400 for( const OPENGL_BUFFER& buffer : m_buffers )
401 glDeleteTextures( 1, &buffer.textureTarget );
402
403 m_buffers.clear();
404
405 if( glDeleteFramebuffersEXT )
406 glDeleteFramebuffersEXT( 1, &m_mainFbo );
407
408 if( glDeleteRenderbuffersEXT )
409 glDeleteRenderbuffersEXT( 1, &m_depthBuffer );
410
411 m_initialized = false;
412}
413
414
416{
418 {
420 default: return 1;
421 }
422}
423
425{
427 {
428 case OPENGL_ANTIALIASING_MODE::SUPERSAMPLING: return VECTOR2D( 0.5, -0.5 );
429 default: return VECTOR2D( 0, 0 );
430 }
431}
A color representation with 4 components: red, green, blue, alpha.
Definition: color4d.h:104
double r
Red component.
Definition: color4d.h:392
double g
Green component.
Definition: color4d.h:393
static const COLOR4D BLACK
Definition: color4d.h:402
double b
Blue component.
Definition: color4d.h:394
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.
int coord_type
Definition: vector2d.h:73
@ NONE
Definition: kibis.h:54
The Cairo implementation of the graphics abstraction layer.
Definition: color4d.cpp:247
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:587