KiCad PCB EDA Suite
antialiasing.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) 2016-2021 Kicad Developers, see AUTHORS.txt for contributors.
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License
8  * as published by the Free Software Foundation; either version 2
9  * of the License, or (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, you may find one here:
18  * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
19  * or you may search the http://www.gnu.org website for the version 2 license,
20  * or you may write to the Free Software Foundation, Inc.,
21  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
22  */
23 
26 #include <gal/opengl/utils.h>
27 #include <gal/color4d.h>
28 
29 #include <memory>
30 #include <tuple>
31 
32 #include "gl_builtin_shaders.h"
33 #include "SmaaAreaTex.h"
34 #include "SmaaSearchTex.h"
35 
36 using namespace KIGFX;
37 
38 // =========================
39 // ANTIALIASING_NONE
40 // =========================
41 
43  compositor( aCompositor )
44 {
45 }
46 
47 
49 {
50  // Nothing to initialize
51  return true;
52 }
53 
54 
56 {
57  return compositor->GetScreenSize();
58 }
59 
60 
61 void ANTIALIASING_NONE::DrawBuffer( GLuint buffer )
62 {
64 }
65 
66 
68 {
69  // Nothing to present, draw_buffer already drew to the screen
70 }
71 
72 
74 {
75  // Nothing to do
76 }
77 
78 
80 {
81  // Nothing to do
82 }
83 
84 
86 {
88 }
89 
90 
91 namespace
92 {
93 void draw_fullscreen_primitive()
94 {
95  glMatrixMode( GL_MODELVIEW );
96  glPushMatrix();
97  glLoadIdentity();
98  glMatrixMode( GL_PROJECTION );
99  glPushMatrix();
100  glLoadIdentity();
101 
102 
103  glBegin( GL_TRIANGLES );
104  glTexCoord2f( 0.0f, 1.0f );
105  glVertex2f( -1.0f, 1.0f );
106  glTexCoord2f( 0.0f, 0.0f );
107  glVertex2f( -1.0f, -1.0f );
108  glTexCoord2f( 1.0f, 1.0f );
109  glVertex2f( 1.0f, 1.0f );
110 
111  glTexCoord2f( 1.0f, 1.0f );
112  glVertex2f( 1.0f, 1.0f );
113  glTexCoord2f( 0.0f, 0.0f );
114  glVertex2f( -1.0f, -1.0f );
115  glTexCoord2f( 1.0f, 0.0f );
116  glVertex2f( 1.0f, -1.0f );
117  glEnd();
118 
119  glPopMatrix();
120  glMatrixMode( GL_MODELVIEW );
121  glPopMatrix();
122 }
123 
124 } // namespace
125 
126 // =========================
127 // ANTIALIASING_SUPERSAMPLING
128 // =========================
129 
131  compositor( aCompositor ),
132  ssaaMainBuffer( 0 ), areBuffersCreated( false ), areShadersCreated( false )
133 {
134 }
135 
136 
138 {
139  areShadersCreated = false;
140 
141  if( !areBuffersCreated )
142  {
144  glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
145  glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
146 
147  areBuffersCreated = true;
148  }
149 
150  return true;
151 }
152 
153 
155 {
156  return compositor->GetScreenSize() * 2;
157 }
158 
159 
161 {
164 }
165 
166 
168 {
169  compositor->DrawBuffer( aBuffer, ssaaMainBuffer );
170 }
171 
172 
174 {
175  glDisable( GL_BLEND );
176  glDisable( GL_DEPTH_TEST );
177  glActiveTexture( GL_TEXTURE0 );
178  glBindTexture( GL_TEXTURE_2D, compositor->GetBufferTexture( ssaaMainBuffer ) );
180 
181  draw_fullscreen_primitive();
182 }
183 
184 
186 {
187  areBuffersCreated = false;
188 }
189 
190 
192 {
194 }
195 
196 // ===============================
197 // ANTIALIASING_SMAA
198 // ===============================
199 
201  areBuffersInitialized( false ),
202  shadersLoaded( false ),
203  compositor( aCompositor )
204 {
205  smaaBaseBuffer = 0;
206  smaaEdgesBuffer = 0;
207  smaaBlendBuffer = 0;
208  smaaAreaTex = 0;
209  smaaSearchTex = 0;
210 
211  pass_1_metrics = 0;
212  pass_2_metrics = 0;
213  pass_3_metrics = 0;
214 }
215 
216 
218 {
219  return compositor->GetScreenSize();
220 }
221 
222 
224 {
225  // Load constant textures
226  glEnable( GL_TEXTURE_2D );
227  glActiveTexture( GL_TEXTURE0 );
228 
229  glGenTextures( 1, &smaaAreaTex );
230  glBindTexture( GL_TEXTURE_2D, smaaAreaTex );
231  glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE );
232  glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE );
233  glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
234  glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
235  glTexImage2D( GL_TEXTURE_2D, 0, GL_RG8, AREATEX_WIDTH, AREATEX_HEIGHT, 0, GL_RG,
236  GL_UNSIGNED_BYTE, areaTexBytes );
237  checkGlError( "loading smaa area tex", __FILE__, __LINE__ );
238 
239  glGenTextures( 1, &smaaSearchTex );
240  glBindTexture( GL_TEXTURE_2D, smaaSearchTex );
241  glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE );
242  glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE );
243  glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
244  glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
245  glTexImage2D( GL_TEXTURE_2D, 0, GL_R8, SEARCHTEX_WIDTH, SEARCHTEX_HEIGHT, 0, GL_RED,
246  GL_UNSIGNED_BYTE, searchTexBytes );
247  checkGlError( "loading smaa search tex", __FILE__, __LINE__ );
248 
249  // Quality settings:
250  // THRESHOLD: intended to exclude spurious edges in photorealistic game graphics
251  // but in a high-contrast CAD application, all edges are intentional
252  // should be set fairly low, so user color choices do not affect antialiasing
253  // MAX_SEARCH_STEPS: steps of 2px, searched in H/V direction to discover true angle of edges
254  // improves AA for lines close H/V but creates fuzzyness at junctions
255  // MAX_SEARCH_STEPS_DIAG: steps of 1px, searched in diagonal direction
256  // improves lines close to 45deg but turns small circles into octagons
257  // CORNER_ROUNDING: SMAA can distinguish actual corners from aliasing jaggies,
258  // we want to preserve those as much as possible
259  // Edge Detection: In Eeschema, when a single pixel line changes color, edge detection using
260  // color is too aggressive and leads to a white spot at the transition point
261  std::string quality_string;
262  const char* edge_detect_shader;
263 
264  // trades imperfect AA of shallow angles for a near artifact-free reproduction of fine features
265  // jaggies are smoothed over max 5px (original step + 2px in both directions)
266  quality_string = "#define SMAA_THRESHOLD 0.005\n"
267  "#define SMAA_MAX_SEARCH_STEPS 1\n"
268  "#define SMAA_MAX_SEARCH_STEPS_DIAG 2\n"
269  "#define SMAA_LOCAL_CONTRAST_ADAPTATION_FACTOR 1.5\n"
270  "#define SMAA_CORNER_ROUNDING 0\n";
272 
273  // set up shaders
274  std::string vert_preamble( R"SHADER(
275 #version 120
276 #define SMAA_GLSL_2_1
277 #define SMAA_INCLUDE_VS 1
278 #define SMAA_INCLUDE_PS 0
279 uniform vec4 SMAA_RT_METRICS;
280 )SHADER" );
281 
282  std::string frag_preamble( R"SHADER(
283 #version 120
284 #define SMAA_GLSL_2_1
285 #define SMAA_INCLUDE_VS 0
286 #define SMAA_INCLUDE_PS 1
287 uniform vec4 SMAA_RT_METRICS;
288 )SHADER" );
289 
290  std::string smaa_source = std::string( BUILTIN_SHADERS::smaa_base_shader_p1 )
291  + std::string( BUILTIN_SHADERS::smaa_base_shader_p2 )
292  + std::string( BUILTIN_SHADERS::smaa_base_shader_p3 )
293  + std::string( BUILTIN_SHADERS::smaa_base_shader_p4 );
294 
295  //
296  // Set up pass 1 Shader
297  //
298  pass_1_shader = std::make_unique<SHADER>();
299  pass_1_shader->LoadShaderFromStrings( KIGFX::SHADER_TYPE_VERTEX, vert_preamble, quality_string,
301  pass_1_shader->LoadShaderFromStrings( KIGFX::SHADER_TYPE_FRAGMENT, frag_preamble,
302  quality_string, smaa_source, edge_detect_shader );
303  pass_1_shader->Link();
304  checkGlError( "linking pass 1 shader", __FILE__, __LINE__ );
305 
306  GLint smaaColorTexParameter = pass_1_shader->AddParameter( "colorTex" );
307  checkGlError( "pass1: getting colorTex uniform", __FILE__, __LINE__ );
308  pass_1_metrics = pass_1_shader->AddParameter( "SMAA_RT_METRICS" );
309  checkGlError( "pass1: getting metrics uniform", __FILE__, __LINE__ );
310 
311  pass_1_shader->Use();
312  checkGlError( "pass1: using shader", __FILE__, __LINE__ );
313  pass_1_shader->SetParameter( smaaColorTexParameter, 0 );
314  checkGlError( "pass1: setting colorTex uniform", __FILE__, __LINE__ );
315  pass_1_shader->Deactivate();
316  checkGlError( "pass1: deactivating shader", __FILE__, __LINE__ );
317 
318  //
319  // set up pass 2 shader
320  //
321  pass_2_shader = std::make_unique<SHADER>();
322  pass_2_shader->LoadShaderFromStrings( KIGFX::SHADER_TYPE_VERTEX, vert_preamble, quality_string,
324  pass_2_shader->LoadShaderFromStrings( KIGFX::SHADER_TYPE_FRAGMENT, frag_preamble,
325  quality_string, smaa_source,
327  pass_2_shader->Link();
328  checkGlError( "linking pass 2 shader", __FILE__, __LINE__ );
329 
330  GLint smaaEdgesTexParameter = pass_2_shader->AddParameter( "edgesTex" );
331  checkGlError( "pass2: getting colorTex uniform", __FILE__, __LINE__ );
332  GLint smaaAreaTexParameter = pass_2_shader->AddParameter( "areaTex" );
333  checkGlError( "pass2: getting areaTex uniform", __FILE__, __LINE__ );
334  GLint smaaSearchTexParameter = pass_2_shader->AddParameter( "searchTex" );
335  checkGlError( "pass2: getting searchTex uniform", __FILE__, __LINE__ );
336  pass_2_metrics = pass_2_shader->AddParameter( "SMAA_RT_METRICS" );
337  checkGlError( "pass2: getting metrics uniform", __FILE__, __LINE__ );
338 
339  pass_2_shader->Use();
340  checkGlError( "pass2: using shader", __FILE__, __LINE__ );
341  pass_2_shader->SetParameter( smaaEdgesTexParameter, 0 );
342  checkGlError( "pass2: setting colorTex uniform", __FILE__, __LINE__ );
343  pass_2_shader->SetParameter( smaaAreaTexParameter, 1 );
344  checkGlError( "pass2: setting areaTex uniform", __FILE__, __LINE__ );
345  pass_2_shader->SetParameter( smaaSearchTexParameter, 3 );
346  checkGlError( "pass2: setting searchTex uniform", __FILE__, __LINE__ );
347  pass_2_shader->Deactivate();
348  checkGlError( "pass2: deactivating shader", __FILE__, __LINE__ );
349 
350  //
351  // set up pass 3 shader
352  //
353  pass_3_shader = std::make_unique<SHADER>();
354  pass_3_shader->LoadShaderFromStrings( KIGFX::SHADER_TYPE_VERTEX, vert_preamble, quality_string,
356  pass_3_shader->LoadShaderFromStrings( KIGFX::SHADER_TYPE_FRAGMENT, frag_preamble,
357  quality_string, smaa_source,
359  pass_3_shader->Link();
360 
361  GLint smaaP3ColorTexParameter = pass_3_shader->AddParameter( "colorTex" );
362  checkGlError( "pass3: getting colorTex uniform", __FILE__, __LINE__ );
363  GLint smaaBlendTexParameter = pass_3_shader->AddParameter( "blendTex" );
364  checkGlError( "pass3: getting blendTex uniform", __FILE__, __LINE__ );
365  pass_3_metrics = pass_3_shader->AddParameter( "SMAA_RT_METRICS" );
366  checkGlError( "pass3: getting metrics uniform", __FILE__, __LINE__ );
367 
368  pass_3_shader->Use();
369  checkGlError( "pass3: using shader", __FILE__, __LINE__ );
370  pass_3_shader->SetParameter( smaaP3ColorTexParameter, 0 );
371  checkGlError( "pass3: setting colorTex uniform", __FILE__, __LINE__ );
372  pass_3_shader->SetParameter( smaaBlendTexParameter, 1 );
373  checkGlError( "pass3: setting blendTex uniform", __FILE__, __LINE__ );
374  pass_3_shader->Deactivate();
375  checkGlError( "pass3: deactivating shader", __FILE__, __LINE__ );
376 
377  shadersLoaded = true;
378 }
379 
380 
382 {
383  auto dims = compositor->GetScreenSize();
384 
385  pass_1_shader->Use();
386  checkGlError( "pass1: using shader", __FILE__, __LINE__ );
387  pass_1_shader->SetParameter( pass_1_metrics, 1.f / float( dims.x ), 1.f / float( dims.y ),
388  float( dims.x ), float( dims.y ) );
389  checkGlError( "pass1: setting metrics uniform", __FILE__, __LINE__ );
390  pass_1_shader->Deactivate();
391  checkGlError( "pass1: deactivating shader", __FILE__, __LINE__ );
392 
393  pass_2_shader->Use();
394  checkGlError( "pass2: using shader", __FILE__, __LINE__ );
395  pass_2_shader->SetParameter( pass_2_metrics, 1.f / float( dims.x ), 1.f / float( dims.y ),
396  float( dims.x ), float( dims.y ) );
397  checkGlError( "pass2: setting metrics uniform", __FILE__, __LINE__ );
398  pass_2_shader->Deactivate();
399  checkGlError( "pass2: deactivating shader", __FILE__, __LINE__ );
400 
401  pass_3_shader->Use();
402  checkGlError( "pass3: using shader", __FILE__, __LINE__ );
403  pass_3_shader->SetParameter( pass_3_metrics, 1.f / float( dims.x ), 1.f / float( dims.y ),
404  float( dims.x ), float( dims.y ) );
405  checkGlError( "pass3: setting metrics uniform", __FILE__, __LINE__ );
406  pass_3_shader->Deactivate();
407  checkGlError( "pass3: deactivating shader", __FILE__, __LINE__ );
408 }
409 
410 
412 {
413  if( !shadersLoaded )
414  loadShaders();
415 
416  if( !areBuffersInitialized )
417  {
419  glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
420  glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
421 
423  glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
424  glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
425 
427  glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
428  glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
429 
430  updateUniforms();
431  areBuffersInitialized = true;
432  }
433 
434  // Nothing to initialize
435  return true;
436 }
437 
438 
440 {
441  areBuffersInitialized = false;
442 }
443 
444 
446 {
448 }
449 
450 
451 void ANTIALIASING_SMAA::DrawBuffer( GLuint buffer )
452 {
453  // draw to internal buffer
455 }
456 
457 
459 {
462 }
463 
464 
465 namespace
466 {
467 void draw_fullscreen_triangle()
468 {
469  glMatrixMode( GL_MODELVIEW );
470  glPushMatrix();
471  glLoadIdentity();
472  glMatrixMode( GL_PROJECTION );
473  glPushMatrix();
474  glLoadIdentity();
475 
476  glBegin( GL_TRIANGLES );
477  glTexCoord2f( 0.0f, 1.0f );
478  glVertex2f( -1.0f, 1.0f );
479  glTexCoord2f( 0.0f, -1.0f );
480  glVertex2f( -1.0f, -3.0f );
481  glTexCoord2f( 2.0f, 1.0f );
482  glVertex2f( 3.0f, 1.0f );
483  glEnd();
484 
485  glPopMatrix();
486  glMatrixMode( GL_MODELVIEW );
487  glPopMatrix();
488 }
489 } // namespace
490 
491 
493 {
494  auto sourceTexture = compositor->GetBufferTexture( smaaBaseBuffer );
495 
496  glDisable( GL_BLEND );
497  glDisable( GL_DEPTH_TEST );
498  glEnable( GL_TEXTURE_2D );
499 
500  //
501  // pass 1: main-buffer -> smaaEdgesBuffer
502  //
505 
506  glActiveTexture( GL_TEXTURE0 );
507  glBindTexture( GL_TEXTURE_2D, sourceTexture );
508  checkGlError( "binding colorTex", __FILE__, __LINE__ );
509  pass_1_shader->Use();
510  checkGlError( "using smaa pass 1 shader", __FILE__, __LINE__ );
511  draw_fullscreen_triangle();
512  pass_1_shader->Deactivate();
513 
514  //
515  // pass 2: smaaEdgesBuffer -> smaaBlendBuffer
516  //
519 
520  auto edgesTex = compositor->GetBufferTexture( smaaEdgesBuffer );
521 
522  glActiveTexture( GL_TEXTURE0 );
523  glBindTexture( GL_TEXTURE_2D, edgesTex );
524  glActiveTexture( GL_TEXTURE1 );
525  glBindTexture( GL_TEXTURE_2D, smaaAreaTex );
526  glActiveTexture( GL_TEXTURE3 );
527  glBindTexture( GL_TEXTURE_2D, smaaSearchTex );
528 
529  pass_2_shader->Use();
530  draw_fullscreen_triangle();
531  pass_2_shader->Deactivate();
532 
533  //
534  // pass 3: colorTex + BlendBuffer -> output
535  //
538  auto blendTex = compositor->GetBufferTexture( smaaBlendBuffer );
539 
540  glActiveTexture( GL_TEXTURE0 );
541  glBindTexture( GL_TEXTURE_2D, sourceTexture );
542  glActiveTexture( GL_TEXTURE1 );
543  glBindTexture( GL_TEXTURE_2D, blendTex );
544 
545  pass_3_shader->Use();
546  draw_fullscreen_triangle();
547  pass_3_shader->Deactivate();
548 }
VECTOR2U GetInternalBufferSize() override
void Present() override
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
ANTIALIASING_SUPERSAMPLING(OPENGL_COMPOSITOR *aCompositor)
ANTIALIASING_NONE(OPENGL_COMPOSITOR *aCompositor)
#define SEARCHTEX_WIDTH
Copyright (C) 2013 Jorge Jimenez ([email protected]) Copyright (C) 2013 Jose I.
Definition: SmaaSearchTex.h:33
#define AREATEX_HEIGHT
Definition: SmaaAreaTex.h:34
static const COLOR4D BLACK
Definition: color4d.h:387
const char smaa_pass_3_fragment_shader[]
unsigned int smaaEdgesBuffer
Definition: antialiasing.h:120
Fragment shader.
Definition: shader.h:47
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
VECTOR2U GetInternalBufferSize() override
unsigned int smaaSearchTex
Definition: antialiasing.h:125
void DrawBuffer(GLuint aBuffer) override
static const unsigned int DIRECT_RENDERING
Handle multitarget rendering (ie.
ANTIALIASING_SMAA(OPENGL_COMPOSITOR *aCompositor)
OPENGL_COMPOSITOR * compositor
Definition: antialiasing.h:70
VECTOR2U GetInternalBufferSize() override
void DrawBuffer(GLuint buffer) override
#define AREATEX_WIDTH
Copyright (C) 2013 Jorge Jimenez ([email protected]) Copyright (C) 2013 Jose I.
Definition: SmaaAreaTex.h:33
static const unsigned char searchTexBytes[]
Stored in R8 format.
Definition: SmaaSearchTex.h:43
const char smaa_base_shader_p4[]
void DrawBuffer(GLuint) override
#define SEARCHTEX_HEIGHT
Definition: SmaaSearchTex.h:34
virtual void ClearBuffer(const COLOR4D &aColor) override
Clear the selected buffer (set by the SetBuffer() function).
const unsigned char areaTexBytes[]
Stored in R8G8 format.
Definition: SmaaAreaTex.h:43
const char smaa_pass_2_fragment_shader[]
unsigned int smaaBlendBuffer
Definition: antialiasing.h:121
Vertex shader.
Definition: shader.h:46
virtual void SetBuffer(unsigned int aBufferHandle) override
Set the selected buffer as the rendering target.
OPENGL_COMPOSITOR * compositor
Definition: antialiasing.h:90
std::unique_ptr< SHADER > pass_2_shader
Definition: antialiasing.h:132
std::unique_ptr< SHADER > pass_3_shader
Definition: antialiasing.h:135
GLenum GetBufferTexture(unsigned int aBufferHandle)
const char smaa_base_shader_p3[]
unsigned int CreateBuffer() override
virtual unsigned int CreateBuffer() override
Prepare a new buffer that may be used as a rendering target.
std::unique_ptr< SHADER > pass_1_shader
Definition: antialiasing.h:129
OPENGL_COMPOSITOR * compositor
Definition: antialiasing.h:138
unsigned int CreateBuffer() override
unsigned int CreateBuffer() override
const char smaa_pass_1_fragment_shader_luma[]
void OnLostBuffers() override
void OnLostBuffers() override
unsigned int smaaBaseBuffer
Definition: antialiasing.h:119