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