diff --git a/tests/alpha-blending-test.c b/tests/alpha-blending-test.c index 2ec2b255..f5553b17 100644 --- a/tests/alpha-blending-test.c +++ b/tests/alpha-blending-test.c @@ -27,6 +27,7 @@ #include "config.h" #include +#include #include "weston-test-client-helper.h" #include "weston-test-fixture-compositor.h" @@ -119,76 +120,20 @@ fill_alpha_pattern(struct buffer *buf) } } -static bool -compare_float(float ref, float dst, int x, const char *chan, float *max_diff) -{ -#if 0 - /* - * This file can be loaded in Octave for visualization. - * - * S = load('compare_float_dump.txt'); - * - * rvec = S(S(:,1)==114, 2:3); - * gvec = S(S(:,1)==103, 2:3); - * bvec = S(S(:,1)==98, 2:3); - * - * figure - * subplot(3, 1, 1); - * plot(rvec(:,1), rvec(:,2) .* 255, 'r'); - * subplot(3, 1, 2); - * plot(gvec(:,1), gvec(:,2) .* 255, 'g'); - * subplot(3, 1, 3); - * plot(bvec(:,1), bvec(:,2) .* 255, 'b'); - */ - static FILE *fp = NULL; - - if (!fp) - fp = fopen("compare_float_dump.txt", "w"); - fprintf(fp, "%d %d %f\n", chan[0], x, dst - ref); - fflush(fp); -#endif - - float diff = fabsf(ref - dst); - - if (diff > *max_diff) - *max_diff = diff; - - /* - * Allow for +/- 1.5 code points of error in non-linear 8-bit channel - * value. This is necessary for the BLEND_LINEAR case. - * - * With llvmpipe, we could go as low as +/- 0.65 code points of error - * and still pass. - * - * AMD Polaris 11 would be ok with +/- 1.0 code points error threshold - * if not for one particular case of blending (a=254, r=0) into r=255, - * which results in error of 1.29 code points. - */ - if (diff < 1.5f / 255.f) - return true; - - testlog("x=%d %s: ref %f != dst %f, delta %f\n", - x, chan, ref, dst, dst - ref); - - return false; -} - enum blend_space { BLEND_NONLINEAR, BLEND_LINEAR, }; -static bool -verify_sRGB_blend_a8r8g8b8(uint32_t bg32, uint32_t fg32, uint32_t dst32, - int x, struct color_float *max_diff, - enum blend_space space) +static void +compare_sRGB_blend_a8r8g8b8(uint32_t bg32, uint32_t fg32, uint32_t dst32, + struct rgb_diff_stat *diffstat, + enum blend_space space) { - const char *const chan_name[COLOR_CHAN_NUM] = { "r", "g", "b" }; struct color_float bg = a8r8g8b8_to_float(bg32); struct color_float fg = a8r8g8b8_to_float(fg32); struct color_float dst = a8r8g8b8_to_float(dst32); struct color_float ref; - bool ok = true; int i; bg = color_float_unpremult(bg); @@ -206,12 +151,7 @@ verify_sRGB_blend_a8r8g8b8(uint32_t bg32, uint32_t fg32, uint32_t dst32, if (space == BLEND_LINEAR) sRGB_delinearize(&ref); - for (i = 0; i < COLOR_CHAN_NUM; i++) { - ok = compare_float(ref.rgb[i], dst.rgb[i], x, - chan_name[i], &max_diff->rgb[i]) && ok; - } - - return ok; + rgb_diff_stat_update(diffstat, &ref, &dst, &fg); } static uint8_t @@ -259,25 +199,56 @@ static bool check_blend_pattern(struct buffer *bg, struct buffer *fg, struct buffer *shot, enum blend_space space) { + FILE *dump = NULL; +#if 0 + /* + * This file can be loaded in Octave for visualization. Find the script + * in tests/visualization/weston_plot_rgb_diff_stat.m and call it with + * + * weston_plot_rgb_diff_stat('alpha_blend-f01-dump.txt', 255, 8) + */ + dump = fopen_dump_file("dump"); +#endif + + /* + * Allow for +/- 1.5 code points of error in non-linear 8-bit channel + * value. This is necessary for the BLEND_LINEAR case. + * + * With llvmpipe, we could go as low as +/- 0.65 code points of error + * and still pass. + * + * AMD Polaris 11 would be ok with +/- 1.0 code points error threshold + * if not for one particular case of blending (a=254, r=0) into r=255, + * which results in error of 1.29 code points. + */ + const float tolerance = 1.5f / 255.f; + uint32_t *bg_row = get_middle_row(bg); uint32_t *fg_row = get_middle_row(fg); uint32_t *shot_row = get_middle_row(shot); - struct color_float max_diff = { .rgb = { 0.0f, 0.0f, 0.0f } }; + struct rgb_diff_stat diffstat = { .dump = dump, }; bool ret = true; int x; + unsigned i; for (x = 0; x < BLOCK_WIDTH * ALPHA_STEPS - 1; x++) { if (!pixels_monotonic(shot_row, x)) ret = false; - if (!verify_sRGB_blend_a8r8g8b8(bg_row[x], fg_row[x], - shot_row[x], x, &max_diff, - space)) + compare_sRGB_blend_a8r8g8b8(bg_row[x], fg_row[x], shot_row[x], + &diffstat, space); + } + + for (i = 0; i < COLOR_CHAN_NUM; i++) { + if (diffstat.rgb[i].min <= -tolerance || + diffstat.rgb[i].max >= tolerance) ret = false; } - testlog("%s max diff: r=%f, g=%f, b=%f\n", - __func__, max_diff.r, max_diff.g, max_diff.b); + rgb_diff_stat_print(&diffstat, __func__, 8); + + if (dump) + fclose(dump); return ret; } diff --git a/tests/visualization/weston_plot_rgb_diff_stat.m b/tests/visualization/weston_plot_rgb_diff_stat.m new file mode 100644 index 00000000..b2546a31 --- /dev/null +++ b/tests/visualization/weston_plot_rgb_diff_stat.m @@ -0,0 +1,77 @@ +% -- weston_plot_rgb_diff_stat (fname) +% -- weston_plot_rgb_diff_stat (fname, scale) +% -- weston_plot_rgb_diff_stat (fname, scale, x_column) +% Plot an rgb_diff_stat dump file +% +% Creates a new figure and draws four sub-plots: R difference, +% G difference, B difference, and two-norm error. +% +% Scale defaults to 255. It is used to multiply both x and y values +% in all plots. Note that R, G and B plots will contain horizontal lines +% at y = +/- 0.5 to help you see the optimal rounding error range for +% the integer encoding [0, scale]. Two-norm plot contains a horizontal +% line at y = sqrt(0.75) which represents an error sphere with the radius +% equal to the two-norm of RGB error (0.5, 0.5, 0.5). +% +% By default, x-axis is sample index, not multiplied by scale. If +% x_column is given, it is a column index in the dump file to be used as +% the x-axis values, multiplied by scale. Indices start from 1, not 0. + +% SPDX-FileCopyrightText: 2022 Collabora, Ltd. +% SPDX-License-Identifier: MIT + +function weston_plot_rgb_diff_stat(fname, scale, x_column); + +S = load(fname); + +if nargin < 2 + scale = 255; +end +if nargin < 3 + x = 1:size(S, 1); +else + x = S(:, x_column) .* scale; +end + +x_lim = [min(x) max(x)]; + +evec = S(:, 1) .* scale; # two-norm error +rvec = S(:, 2) .* scale; # r diff +gvec = S(:, 3) .* scale; # g diff +bvec = S(:, 4) .* scale; # b diff + +figure + +subplot(4, 1, 1); +plot(x, rvec, 'r'); +plus_minus_half_lines(x_lim); +title(fname, "Interpreter", "none"); +ylabel('R diff'); +axis("tight"); + +subplot(4, 1, 2); +plot(x, gvec, 'g'); +plus_minus_half_lines(x_lim); +ylabel('G diff'); +axis("tight"); + +subplot(4, 1, 3); +plot(x, bvec, 'b'); +plus_minus_half_lines(x_lim); +ylabel('B diff'); +axis("tight"); + +subplot(4, 1, 4); +plot(x, evec, 'k'); +hold on; +plot(x_lim, [1 1] .* sqrt(0.75), 'k:'); +ylabel('Two-norm'); +axis("tight"); + +max_abs_err = [max(abs(rvec)) max(abs(gvec)) max(abs(bvec))] + +function plus_minus_half_lines(x_lim); + +hold on; +plot(x_lim, [0.5 -0.5; 0.5 -0.5], 'k:'); +