@@ -544,6 +544,7 @@ void gl_compose(backend_t *base, void *image_data, int dst_x, int dst_y,
544
544
* Blur contents in a particular region.
545
545
*/
546
546
bool gl_kernel_blur (backend_t * base , double opacity , void * ctx , const rect_t * extent ,
547
+ const int width attr_unused , const int height attr_unused ,
547
548
const int nrects , const GLuint vao [2 ]) {
548
549
auto bctx = (struct gl_blur_context * )ctx ;
549
550
auto gd = (struct gl_data * )base ;
@@ -623,13 +624,119 @@ bool gl_kernel_blur(backend_t *base, double opacity, void *ctx, const rect_t *ex
623
624
return true;
624
625
}
625
626
626
- bool gl_dual_kawase_blur (backend_t * base , double opacity attr_unused , void * ctx ,
627
- const rect_t * extent attr_unused , const int nrects attr_unused ,
628
- const GLuint vao [2 ] attr_unused ) {
629
- auto bctx attr_unused = (struct gl_blur_context * )ctx ;
630
- auto gd attr_unused = (struct gl_data * )base ;
627
+ bool gl_dual_kawase_blur (backend_t * base , double opacity , void * ctx , const rect_t * extent ,
628
+ const int width , const int height , const int nrects ,
629
+ const GLuint vao [2 ]) {
630
+ auto bctx = (struct gl_blur_context * )ctx ;
631
+ auto gd = (struct gl_data * )base ;
632
+
633
+ int dst_y_screen_coord = gd -> height - extent -> y2 ,
634
+ dst_y_fb_coord = bctx -> fb_height - extent -> y2 ;
635
+
636
+ // Reduce number of iterations until the last one renders at least 1px in both
637
+ // dimensions
638
+ int iterations = bctx -> blur_texture_count ;
639
+ while (((width / (1 << iterations )) < 1 || (height / (1 << iterations )) < 1 ) &&
640
+ iterations > 0 ) {
641
+ -- iterations ;
642
+ }
643
+ assert (iterations < (sizeof (int ) * 8 ) && iterations >= 0 );
644
+
645
+ // Kawase downsample pass
646
+ const gl_blur_shader_t * down_pass = & bctx -> blur_shader [0 ];
647
+ assert (down_pass -> prog );
648
+ glUseProgram (down_pass -> prog );
649
+
650
+ // Downsample always renders with resize offset
651
+ glUniform2f (down_pass -> orig_loc , (GLfloat )bctx -> resize_width ,
652
+ - (GLfloat )bctx -> resize_height );
653
+
654
+ for (int i = 0 ; i < iterations ; ++ i ) {
655
+ GLuint src_texture ;
656
+ int tex_width , tex_height ;
657
+ int texorig_x , texorig_y ;
658
+
659
+ if (i == 0 ) {
660
+ // first pass: copy from back buffer
661
+ src_texture = gd -> back_texture ;
662
+ tex_width = gd -> width ;
663
+ tex_height = gd -> height ;
664
+
665
+ texorig_x = extent -> x1 ;
666
+ texorig_y = dst_y_screen_coord ;
667
+ } else {
668
+ // copy from previous pass
669
+ src_texture = bctx -> blur_textures [i - 1 ];
670
+ auto src_size = bctx -> texture_sizes [i - 1 ];
671
+ tex_width = src_size .width ;
672
+ tex_height = src_size .height ;
673
+
674
+ texorig_x = extent -> x1 + bctx -> resize_width ;
675
+ texorig_y = dst_y_fb_coord - bctx -> resize_height ;
676
+ }
677
+
678
+ assert (src_texture );
679
+ assert (bctx -> blur_fbos [i ]);
680
+
681
+ glBindTexture (GL_TEXTURE_2D , src_texture );
682
+ glBindVertexArray (vao [1 ]);
683
+ glBindFramebuffer (GL_DRAW_FRAMEBUFFER , bctx -> blur_fbos [i ]);
684
+ glDrawBuffer (GL_COLOR_ATTACHMENT0 );
685
+
686
+ glUniform2f (down_pass -> texorig_loc , (GLfloat )texorig_x , (GLfloat )texorig_y );
687
+ glUniform1f (down_pass -> scale_loc , (GLfloat )(1 << (i + 1 )));
688
+
689
+ glUniform2f (down_pass -> unifm_pixel_norm , 1.0f / (GLfloat )tex_width ,
690
+ 1.0f / (GLfloat )tex_height );
631
691
632
- // TODO: Blur with dual_kawase shaders
692
+ glDrawElements (GL_TRIANGLES , nrects * 6 , GL_UNSIGNED_INT , NULL );
693
+ }
694
+
695
+ // Kawase upsample pass
696
+ const gl_blur_shader_t * up_pass = & bctx -> blur_shader [1 ];
697
+ assert (up_pass -> prog );
698
+ glUseProgram (up_pass -> prog );
699
+
700
+ // Upsample always samples from textures with resize offset
701
+ glUniform2f (up_pass -> texorig_loc , (GLfloat )(extent -> x1 + bctx -> resize_width ),
702
+ (GLfloat )(dst_y_fb_coord - bctx -> resize_height ));
703
+
704
+ for (int i = iterations - 1 ; i >= 0 ; -- i ) {
705
+ const GLuint src_texture = bctx -> blur_textures [i ];
706
+ assert (src_texture );
707
+
708
+ // Calculate normalized half-width/-height of a src pixel
709
+ auto src_size = bctx -> texture_sizes [i ];
710
+ int tex_width = src_size .width ;
711
+ int tex_height = src_size .height ;
712
+
713
+ glBindTexture (GL_TEXTURE_2D , src_texture );
714
+ if (i > 0 ) {
715
+ assert (bctx -> blur_fbos [i - 1 ]);
716
+
717
+ // not last pass, draw into next framebuffer
718
+ glBindVertexArray (vao [1 ]);
719
+ glBindFramebuffer (GL_DRAW_FRAMEBUFFER , bctx -> blur_fbos [i - 1 ]);
720
+ glDrawBuffer (GL_COLOR_ATTACHMENT0 );
721
+
722
+ glUniform2f (up_pass -> orig_loc , (GLfloat )bctx -> resize_width ,
723
+ - (GLfloat )bctx -> resize_height );
724
+ glUniform1f (up_pass -> unifm_opacity , (GLfloat )1 );
725
+ } else {
726
+ // last pass, draw directly into the back buffer
727
+ glBindVertexArray (vao [0 ]);
728
+ glBindFramebuffer (GL_FRAMEBUFFER , gd -> back_fbo );
729
+
730
+ glUniform2f (up_pass -> orig_loc , (GLfloat )0 , (GLfloat )0 );
731
+ glUniform1f (up_pass -> unifm_opacity , (GLfloat )opacity );
732
+ }
733
+
734
+ glUniform1f (up_pass -> scale_loc , (GLfloat )(1 << i ));
735
+ glUniform2f (up_pass -> unifm_pixel_norm , 1.0f / (GLfloat )tex_width ,
736
+ 1.0f / (GLfloat )tex_height );
737
+
738
+ glDrawElements (GL_TRIANGLES , nrects * 6 , GL_UNSIGNED_INT , NULL );
739
+ }
633
740
634
741
return true;
635
742
}
@@ -651,7 +758,10 @@ bool gl_blur(backend_t *base, double opacity, void *ctx, const region_t *reg_blu
651
758
for (int i = 0 ; i < bctx -> blur_texture_count ; ++ i ) {
652
759
auto tex_size = bctx -> texture_sizes + i ;
653
760
if (bctx -> method == BLUR_METHOD_DUAL_KAWASE ) {
654
- // TODO: Use smaller textures for each iteration
761
+ // Use smaller textures for each iteration (quarter of the
762
+ // previous texture)
763
+ tex_size -> width = 1 + (bctx -> fb_width - 1 ) / (1 << (i + 1 ));
764
+ tex_size -> height = 1 + (bctx -> fb_height - 1 ) / (1 << (i + 1 ));
655
765
} else {
656
766
tex_size -> width = bctx -> fb_width ;
657
767
tex_size -> height = bctx -> fb_height ;
@@ -660,7 +770,22 @@ bool gl_blur(backend_t *base, double opacity, void *ctx, const region_t *reg_blu
660
770
glBindTexture (GL_TEXTURE_2D , bctx -> blur_textures [i ]);
661
771
glTexImage2D (GL_TEXTURE_2D , 0 , GL_RGBA8 , tex_size -> width ,
662
772
tex_size -> height , 0 , GL_BGRA , GL_UNSIGNED_BYTE , NULL );
773
+
774
+ if (bctx -> method == BLUR_METHOD_DUAL_KAWASE ) {
775
+ // Attach texture to FBO target
776
+ glBindFramebuffer (GL_DRAW_FRAMEBUFFER , bctx -> blur_fbos [i ]);
777
+ glFramebufferTexture2D (GL_DRAW_FRAMEBUFFER ,
778
+ GL_COLOR_ATTACHMENT0 , GL_TEXTURE_2D ,
779
+ bctx -> blur_textures [i ], 0 );
780
+ if (glCheckFramebufferStatus (GL_FRAMEBUFFER ) !=
781
+ GL_FRAMEBUFFER_COMPLETE ) {
782
+ log_error ("Framebuffer attachment failed." );
783
+ glBindFramebuffer (GL_FRAMEBUFFER , 0 );
784
+ return false;
785
+ }
786
+ }
663
787
}
788
+ glBindFramebuffer (GL_DRAW_FRAMEBUFFER , 0 );
664
789
}
665
790
666
791
// Remainder: regions are in Xorg coordinates
@@ -725,9 +850,11 @@ bool gl_blur(backend_t *base, double opacity, void *ctx, const region_t *reg_blu
725
850
sizeof (GLint ) * 4 , (void * )(sizeof (GLint ) * 2 ));
726
851
727
852
if (bctx -> method == BLUR_METHOD_DUAL_KAWASE ) {
728
- ret = gl_dual_kawase_blur (base , opacity , ctx , extent_resized , nrects , vao );
853
+ ret = gl_dual_kawase_blur (base , opacity , ctx , extent_resized , width ,
854
+ height , nrects , vao );
729
855
} else {
730
- ret = gl_kernel_blur (base , opacity , ctx , extent_resized , nrects , vao );
856
+ ret = gl_kernel_blur (base , opacity , ctx , extent_resized , width , height ,
857
+ nrects , vao );
731
858
}
732
859
733
860
glBindFramebuffer (GL_FRAMEBUFFER , 0 );
@@ -751,13 +878,14 @@ bool gl_blur(backend_t *base, double opacity, void *ctx, const region_t *reg_blu
751
878
// clang-format off
752
879
const char * vertex_shader = GLSL (330 ,
753
880
uniform mat4 projection ;
881
+ uniform float scale = 1.0 ;
754
882
uniform vec2 orig ;
755
883
uniform vec2 texorig ;
756
884
layout (location = 0 ) in vec2 coord ;
757
885
layout (location = 1 ) in vec2 in_texcoord ;
758
886
out vec2 texcoord ;
759
887
void main () {
760
- gl_Position = projection * vec4 (coord + orig , 0 , 1 );
888
+ gl_Position = projection * vec4 (coord + orig , 0 , scale );
761
889
texcoord = in_texcoord + texorig ;
762
890
}
763
891
);
@@ -1190,17 +1318,164 @@ bool gl_create_kernel_blur_context(void *blur_context, GLfloat *projection,
1190
1318
return success ;
1191
1319
}
1192
1320
1193
- bool gl_create_dual_kawase_blur_context (void * blur_context , GLfloat * projection attr_unused ,
1194
- enum blur_method method attr_unused ,
1195
- void * args attr_unused ) {
1321
+ bool gl_create_dual_kawase_blur_context (void * blur_context , GLfloat * projection ,
1322
+ enum blur_method method , void * args ) {
1196
1323
bool success = false;
1197
1324
auto ctx = (struct gl_blur_context * )blur_context ;
1198
1325
1199
- // TODO: Create and initialize down and upsample shader
1326
+ ctx -> method = method ;
1327
+
1328
+ auto blur_params = generate_dual_kawase_params (args );
1329
+
1330
+ // Specify required textures and FBOs
1331
+ ctx -> blur_texture_count = blur_params -> iterations ;
1332
+ ctx -> blur_fbo_count = blur_params -> iterations ;
1333
+
1334
+ ctx -> resize_width += blur_params -> expand ;
1335
+ ctx -> resize_height += blur_params -> expand ;
1336
+
1337
+ ctx -> npasses = 2 ;
1338
+ ctx -> blur_shader = ccalloc (ctx -> npasses , gl_blur_shader_t );
1339
+
1340
+ char * lc_numeric_old = strdup (setlocale (LC_NUMERIC , NULL ));
1341
+ // Enforce LC_NUMERIC locale "C" here to make sure decimal point is sane
1342
+ // Thanks to hiciu for reporting.
1343
+ setlocale (LC_NUMERIC , "C" );
1344
+
1345
+ // Dual-kawase downsample shader / program
1346
+ auto down_pass = ctx -> blur_shader ;
1347
+ {
1348
+ // clang-format off
1349
+ static const char * FRAG_SHADER_DOWN = GLSL (330 ,
1350
+ uniform sampler2D tex_src ;
1351
+ uniform float scale = 1.0 ;
1352
+ uniform vec2 pixel_norm ;
1353
+ in vec2 texcoord ;
1354
+ out vec4 out_color ;
1355
+ void main () {
1356
+ vec2 offset = %.7 g * pixel_norm ;
1357
+ vec2 uv = texcoord * pixel_norm * (2.0 / scale );
1358
+ vec4 sum = texture2D (tex_src , uv ) * 4.0 ;
1359
+ sum += texture2D (tex_src , uv - vec2 (0.5 , 0.5 ) * offset );
1360
+ sum += texture2D (tex_src , uv + vec2 (0.5 , 0.5 ) * offset );
1361
+ sum += texture2D (tex_src , uv + vec2 (0.5 , -0.5 ) * offset );
1362
+ sum += texture2D (tex_src , uv - vec2 (0.5 , -0.5 ) * offset );
1363
+ out_color = sum / 8.0 ;
1364
+ }
1365
+ );
1366
+ // clang-format on
1367
+
1368
+ // Build shader
1369
+ size_t shader_len =
1370
+ strlen (FRAG_SHADER_DOWN ) + 10 /* offset */ + 1 /* null terminator */ ;
1371
+ char * shader_str = ccalloc (shader_len , char );
1372
+ auto real_shader_len =
1373
+ snprintf (shader_str , shader_len , FRAG_SHADER_DOWN , blur_params -> offset );
1374
+ CHECK (real_shader_len >= 0 );
1375
+ CHECK ((size_t )real_shader_len < shader_len );
1376
+
1377
+ // Build program
1378
+ down_pass -> prog = gl_create_program_from_str (vertex_shader , shader_str );
1379
+ free (shader_str );
1380
+ if (!down_pass -> prog ) {
1381
+ log_error ("Failed to create GLSL program." );
1382
+ success = false;
1383
+ goto out ;
1384
+ }
1385
+ glBindFragDataLocation (down_pass -> prog , 0 , "out_color" );
1386
+
1387
+ // Get uniform addresses
1388
+ down_pass -> unifm_pixel_norm =
1389
+ glGetUniformLocationChecked (down_pass -> prog , "pixel_norm" );
1390
+ down_pass -> orig_loc =
1391
+ glGetUniformLocationChecked (down_pass -> prog , "orig" );
1392
+ down_pass -> texorig_loc =
1393
+ glGetUniformLocationChecked (down_pass -> prog , "texorig" );
1394
+ down_pass -> scale_loc =
1395
+ glGetUniformLocationChecked (down_pass -> prog , "scale" );
1396
+
1397
+ // Setup projection matrix
1398
+ glUseProgram (down_pass -> prog );
1399
+ int pml = glGetUniformLocationChecked (down_pass -> prog , "projection" );
1400
+ glUniformMatrix4fv (pml , 1 , false, projection );
1401
+ glUseProgram (0 );
1402
+ }
1403
+
1404
+ // Dual-kawase upsample shader / program
1405
+ auto up_pass = ctx -> blur_shader + 1 ;
1406
+ {
1407
+ // clang-format off
1408
+ static const char * FRAG_SHADER_UP = GLSL (330 ,
1409
+ uniform sampler2D tex_src ;
1410
+ uniform float scale = 1.0 ;
1411
+ uniform vec2 pixel_norm ;
1412
+ uniform float opacity ;
1413
+ in vec2 texcoord ;
1414
+ out vec4 out_color ;
1415
+ void main () {
1416
+ vec2 offset = %.7 g * pixel_norm ;
1417
+ vec2 uv = texcoord * pixel_norm / (2 * scale );
1418
+ vec4 sum = texture2D (tex_src , uv + vec2 (-1.0 , 0.0 ) * offset );
1419
+ sum += texture2D (tex_src , uv + vec2 (-0.5 , 0.5 ) * offset ) * 2.0 ;
1420
+ sum += texture2D (tex_src , uv + vec2 (0.0 , 1.0 ) * offset );
1421
+ sum += texture2D (tex_src , uv + vec2 (0.5 , 0.5 ) * offset ) * 2.0 ;
1422
+ sum += texture2D (tex_src , uv + vec2 (1.0 , 0.0 ) * offset );
1423
+ sum += texture2D (tex_src , uv + vec2 (0.5 , -0.5 ) * offset ) * 2.0 ;
1424
+ sum += texture2D (tex_src , uv + vec2 (0.0 , -1.0 ) * offset );
1425
+ sum += texture2D (tex_src , uv + vec2 (-0.5 , -0.5 ) * offset ) * 2.0 ;
1426
+ out_color = sum / 12.0 * opacity ;
1427
+ }
1428
+ );
1429
+ // clang-format on
1430
+
1431
+ // Build shader
1432
+ size_t shader_len =
1433
+ strlen (FRAG_SHADER_UP ) + 10 /* offset */ + 1 /* null terminator */ ;
1434
+ char * shader_str = ccalloc (shader_len , char );
1435
+ auto real_shader_len =
1436
+ snprintf (shader_str , shader_len , FRAG_SHADER_UP , blur_params -> offset );
1437
+ CHECK (real_shader_len >= 0 );
1438
+ CHECK ((size_t )real_shader_len < shader_len );
1439
+
1440
+ // Build program
1441
+ up_pass -> prog = gl_create_program_from_str (vertex_shader , shader_str );
1442
+ free (shader_str );
1443
+ if (!up_pass -> prog ) {
1444
+ log_error ("Failed to create GLSL program." );
1445
+ success = false;
1446
+ goto out ;
1447
+ }
1448
+ glBindFragDataLocation (up_pass -> prog , 0 , "out_color" );
1449
+
1450
+ // Get uniform addresses
1451
+ up_pass -> unifm_pixel_norm =
1452
+ glGetUniformLocationChecked (up_pass -> prog , "pixel_norm" );
1453
+ up_pass -> unifm_opacity =
1454
+ glGetUniformLocationChecked (up_pass -> prog , "opacity" );
1455
+ up_pass -> orig_loc = glGetUniformLocationChecked (up_pass -> prog , "orig" );
1456
+ up_pass -> texorig_loc =
1457
+ glGetUniformLocationChecked (up_pass -> prog , "texorig" );
1458
+ up_pass -> scale_loc = glGetUniformLocationChecked (up_pass -> prog , "scale" );
1459
+
1460
+ // Setup projection matrix
1461
+ glUseProgram (up_pass -> prog );
1462
+ int pml = glGetUniformLocationChecked (up_pass -> prog , "projection" );
1463
+ glUniformMatrix4fv (pml , 1 , false, projection );
1464
+ glUseProgram (0 );
1465
+ }
1200
1466
1201
- log_warn ("Blur method 'dual_kawase' is not yet implemented." );
1202
- ctx -> method = BLUR_METHOD_NONE ;
1203
1467
success = true;
1468
+ out :
1469
+ free (blur_params );
1470
+
1471
+ if (!success ) {
1472
+ ctx = NULL ;
1473
+ }
1474
+
1475
+ // Restore LC_NUMERIC
1476
+ setlocale (LC_NUMERIC , lc_numeric_old );
1477
+ free (lc_numeric_old );
1478
+
1204
1479
return success ;
1205
1480
}
1206
1481
0 commit comments