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 The 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( GAL_ANTIALIASING_MODE::AA_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 requires at "
175 "least 3 framebuffers. You may try to update/change your graphic drivers." );
176 }
177
178 glGetIntegerv( GL_MAX_TEXTURE_SIZE, (GLint*) &maxTextureSize );
179
180 if( maxTextureSize < (int) aDimensions.x || maxTextureSize < (int) aDimensions.y )
181 {
182 throw std::runtime_error( "Requested texture size is not supported. Could not create a buffer." );
183 }
184
185 // GL_COLOR_ATTACHMENTn are consecutive integers
186 GLuint attachmentPoint = GL_COLOR_ATTACHMENT0 + usedBuffers();
187 GLuint textureTarget;
188
189 // Generate the texture for the pixel storage
190 glActiveTexture( GL_TEXTURE0 );
191 glGenTextures( 1, &textureTarget );
192 checkGlError( "generating framebuffer texture target", __FILE__, __LINE__ );
193 glBindTexture( GL_TEXTURE_2D, textureTarget );
194 checkGlError( "binding framebuffer texture target", __FILE__, __LINE__ );
195
196 // Set texture parameters
197 glTexEnvf( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE );
198 glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA8, aDimensions.x, aDimensions.y, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr );
199 checkGlError( "creating framebuffer texture", __FILE__, __LINE__ );
200 glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST );
201 glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST );
202
203 // Bind the texture to the specific attachment point, clear and rebind the screen
204 bindFb( m_mainFbo );
205 glFramebufferTexture2DEXT( GL_FRAMEBUFFER_EXT, attachmentPoint, GL_TEXTURE_2D, textureTarget, 0 );
206
207 // Check the status, exit if the framebuffer can't be created
208 GLenum status = glCheckFramebufferStatusEXT( GL_FRAMEBUFFER_EXT );
209
210 if( status != GL_FRAMEBUFFER_COMPLETE_EXT )
211 {
212 switch( status )
213 {
214 case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT_EXT:
215 throw std::runtime_error( "The framebuffer attachment points are incomplete." );
216
217 case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT_EXT:
218 throw std::runtime_error( "No images attached to the framebuffer." );
219
220 case GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER_EXT:
221 throw std::runtime_error( "The framebuffer does not have at least one image attached to it." );
222
223 case GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER_EXT:
224 throw std::runtime_error( "The framebuffer read buffer is incomplete." );
225
226 case GL_FRAMEBUFFER_UNSUPPORTED_EXT:
227 throw std::runtime_error( "The combination of internal formats of the attached images violates "
228 "an implementation-dependent set of restrictions." );
229
230 case GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE_EXT:
231 throw std::runtime_error( "GL_RENDERBUFFER_SAMPLES is not the same for all attached renderbuffers" );
232
233 case GL_FRAMEBUFFER_INCOMPLETE_LAYER_TARGETS_EXT:
234 throw std::runtime_error( "Framebuffer incomplete layer targets errors." );
235
236 case GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS_EXT:
237 throw std::runtime_error( "Framebuffer attachments have different dimensions" );
238
239 default:
240 throw std::runtime_error( "Unknown error occurred when creating the framebuffer." );
241 }
242 }
243
245
246 // Return to direct rendering (we were asked only to create a buffer, not switch to one)
248
249 // Store the new buffer
250 OPENGL_BUFFER buffer = { aDimensions, textureTarget, attachmentPoint };
251 m_buffers.push_back( buffer );
252
253 return usedBuffers();
254}
255
256
257GLenum OPENGL_COMPOSITOR::GetBufferTexture( unsigned int aBufferHandle )
258{
259 wxCHECK( aBufferHandle > 0 && aBufferHandle <= usedBuffers(), 0 );
260 return m_buffers[aBufferHandle - 1].textureTarget;
261}
262
263
264void OPENGL_COMPOSITOR::SetBuffer( unsigned int aBufferHandle )
265{
266 wxCHECK( m_initialized && aBufferHandle <= usedBuffers(), /* void */ );
267
268 // Either unbind the FBO for direct rendering, or bind the one with target textures
269 bindFb( aBufferHandle == DIRECT_RENDERING ? DIRECT_RENDERING : m_mainFbo );
270
271 // Switch the target texture
273 {
274 m_curBuffer = aBufferHandle - 1;
275 glDrawBuffer( m_buffers[m_curBuffer].attachmentPoint );
276 checkGlError( "setting draw buffer", __FILE__, __LINE__ );
277
278 glViewport( 0, 0, m_buffers[m_curBuffer].dimensions.x, m_buffers[m_curBuffer].dimensions.y );
279 }
280 else
281 {
282 glViewport( 0, 0, GetScreenSize().x, GetScreenSize().y );
283 }
284}
285
286
288{
289 wxCHECK( m_initialized, /* void */ );
290
291 glClearColor( aColor.r, aColor.g, aColor.b, m_curFbo == DIRECT_RENDERING ? 1.0f : 0.0f );
292 glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT );
293}
294
295
297{
298 typedef VECTOR2I::coord_type coord_t;
299 wxASSERT( m_width <= static_cast<unsigned int>( std::numeric_limits<coord_t>::max() ) );
300 wxASSERT( m_height <= static_cast<unsigned int>( std::numeric_limits<coord_t>::max() ) );
301
302 return { static_cast<coord_t>( m_width ), static_cast<coord_t>( m_height ) };
303}
304
305
307{
308 m_antialiasing->Begin();
309}
310
311
312void OPENGL_COMPOSITOR::DrawBuffer( unsigned int aBufferHandle )
313{
314 m_antialiasing->DrawBuffer( aBufferHandle );
315}
316
317
318void OPENGL_COMPOSITOR::DrawBuffer( unsigned int aSourceHandle, unsigned int aDestHandle )
319{
320 wxCHECK( m_initialized && aSourceHandle != 0 && aSourceHandle <= usedBuffers(), /* void */ );
321 wxCHECK( aDestHandle <= usedBuffers(), /* void */ );
322
323 // Switch to the destination buffer and blit the scene
324 SetBuffer( aDestHandle );
325
326 // Depth test has to be disabled to make transparency working
327 glDisable( GL_DEPTH_TEST );
328 glBlendFunc( GL_ONE, GL_ONE_MINUS_SRC_ALPHA );
329
330 // Enable texturing and bind the main texture
331 glEnable( GL_TEXTURE_2D );
332 glBindTexture( GL_TEXTURE_2D, m_buffers[aSourceHandle - 1].textureTarget );
333
334 // Draw a full screen quad with the texture
335 glMatrixMode( GL_MODELVIEW );
336 glPushMatrix();
337 glLoadIdentity();
338 glMatrixMode( GL_PROJECTION );
339 glPushMatrix();
340 glLoadIdentity();
341
342 glBegin( GL_TRIANGLES );
343 glTexCoord2f( 0.0f, 1.0f );
344 glVertex2f( -1.0f, 1.0f );
345 glTexCoord2f( 0.0f, 0.0f );
346 glVertex2f( -1.0f, -1.0f );
347 glTexCoord2f( 1.0f, 1.0f );
348 glVertex2f( 1.0f, 1.0f );
349
350 glTexCoord2f( 1.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, 0.0f );
355 glVertex2f( 1.0f, -1.0f );
356 glEnd();
357
358 glPopMatrix();
359 glMatrixMode( GL_MODELVIEW );
360 glPopMatrix();
361}
362
363
365{
366 m_antialiasing->Present();
367}
368
369
370void OPENGL_COMPOSITOR::bindFb( unsigned int aFb )
371{
372 // Currently there are only 2 valid FBOs
373 wxASSERT( aFb == DIRECT_RENDERING || aFb == m_mainFbo );
374
375 if( m_curFbo != aFb )
376 {
377 glBindFramebufferEXT( GL_FRAMEBUFFER, aFb );
378 checkGlError( "switching framebuffer", __FILE__, __LINE__ );
379 m_curFbo = aFb;
380 }
381}
382
383
385{
386 wxCHECK( m_initialized, /* void */ );
387
389
390 for( const OPENGL_BUFFER& buffer : m_buffers )
391 glDeleteTextures( 1, &buffer.textureTarget );
392
393 m_buffers.clear();
394
395 if( glDeleteFramebuffersEXT )
396 glDeleteFramebuffersEXT( 1, &m_mainFbo );
397
398 if( glDeleteRenderbuffersEXT )
399 glDeleteRenderbuffersEXT( 1, &m_depthBuffer );
400
401 m_initialized = false;
402}
403
404
406{
408 {
410 default: return 1;
411 }
412}
413
415{
417 {
418 case GAL_ANTIALIASING_MODE::AA_HIGHQUALITY: return VECTOR2D( 0.5, -0.5 );
419 default: return VECTOR2D( 0, 0 );
420 }
421}
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
GAL_ANTIALIASING_MODE m_currentAntialiasingMode
VECTOR2D GetAntialiasRenderingOffset() const
virtual void Begin() override
Call this at the beginning of each frame.
unsigned int usedBuffers()
Returns number of used buffers.
void SetAntialiasingMode(GAL_ANTIALIASING_MODE aMode)
static const unsigned int DIRECT_RENDERING
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.
GAL_ANTIALIASING_MODE GetAntialiasingMode() const
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.
int32_t coord_type
Definition: vector2d.h:74
The Cairo implementation of the graphics abstraction layer.
Definition: eda_group.h:33
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:694