tests/alpha-blending: replace compare_float() with rgb_diff_stat

compare_float() was an ad hoc max error logger with optional debug
logging.

Now that we have rgb_diff_stat, we can get the same statistics and more
with less code. It looks like we would lose the pixel index x, but that
can be recovered from the dump file line number.

This patch takes care to keep the test condition exactly the same as it
was before. The statistics print-out has more details now.

The recorded dump position is the foreground color as that varies while
the background color is constant.

An example Octave function is included to show how to visualize the
rgb_diff_stat dump.

Signed-off-by: Pekka Paalanen <pekka.paalanen@collabora.com>
This commit is contained in:
Pekka Paalanen 2022-06-17 15:12:05 +03:00 committed by Pekka Paalanen
parent be281478dc
commit a0584e64cf
2 changed files with 120 additions and 72 deletions

View File

@ -27,6 +27,7 @@
#include "config.h"
#include <math.h>
#include <stdio.h>
#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;
}

View File

@ -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:');