From 95e306dc84c4964bb5ef84ff4099ae20a7ac53f1 Mon Sep 17 00:00:00 2001 From: Alyssa Rosenzweig Date: Fri, 30 Jul 2021 18:43:48 -0400 Subject: [PATCH] panfrost: Add basic fixed-function blending tests Add unit tests for the fixed-function blending helpers in pan_blend.c. Each test consists of a Porter-Duff blend mode and the associated hardware state. In this commit, we add tests for the most common modes. For motivation, this code has NOT been properly tested in CI. True, functional correctness of the blend module as a whole is tested by dEQP-GLES3.functional.fragment_ops.blend.* among other integration tests. However, this testing is insufficient to check for regressions. Crucially, the following broken patch would clear CI: bool pan_can_fixed_function(...) { return false; } In that case, blend shaders are used 100% of the time, which will regress performance horribly but still pass dEQP. The only clue something went wrong would be some traces changing checksum due to the fixed-function blender producing slightly different output than equivalent blend shaders. By unit testing the fixed blend path, we ensure we always use the fixed-function path when we expect it to. Similarly, using incorrect values for the blend metadata may not affect functional correctness but will increase power consumption. Let's check all the data we export to drivers. Note: due to additive commutativity, there are many pairs of equivalent Mali blend modes. Unfortunately, the vendor is... inconsistent about how to resolve ambiguous modes. Our algorithm for computing modes is correct; the "preferred" values are left in comments since otherwise our tests fail despite correct code. I want to blame Bifrost for this, but Midgard was patient zero. Signed-off-by: Alyssa Rosenzweig Part-of: --- src/panfrost/lib/meson.build | 15 ++ src/panfrost/lib/tests/test-blend.c | 216 ++++++++++++++++++++++++++++ 2 files changed, 231 insertions(+) create mode 100644 src/panfrost/lib/tests/test-blend.c diff --git a/src/panfrost/lib/meson.build b/src/panfrost/lib/meson.build index 934440d3276..f01cb6c19b2 100644 --- a/src/panfrost/lib/meson.build +++ b/src/panfrost/lib/meson.build @@ -100,3 +100,18 @@ libpanfrost_dep = declare_dependency( include_directories: [inc_include, inc_src, inc_mapi, inc_mesa, inc_gallium, inc_gallium_aux, inc_panfrost_hw, inc_panfrost], dependencies: [dep_libdrm, idep_nir, idep_midgard_pack], ) + +if with_tests + test( + 'panfrost_blend', + executable( + 'panfrost_blend_test', + files('tests/test-blend.c'), + c_args : [c_msvc_compat_args, no_override_init_args], + gnu_symbol_visibility : 'hidden', + include_directories : [inc_include, inc_src, inc_mesa], + dependencies: [libpanfrost_dep], + ), + suite : ['panfrost'], + ) +endif diff --git a/src/panfrost/lib/tests/test-blend.c b/src/panfrost/lib/tests/test-blend.c new file mode 100644 index 00000000000..b088415f364 --- /dev/null +++ b/src/panfrost/lib/tests/test-blend.c @@ -0,0 +1,216 @@ +/* + * Copyright (C) 2021 Collabora, Ltd. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "pan_blend.h" + +/* A test consists of a given blend mode and its translated form */ +struct test { + const char *label; + struct pan_blend_equation eq; + unsigned constant_mask; + bool reads_dest; + bool opaque; + bool fixed_function; + uint32_t hardware; +}; + +#define RGBA(key, value) \ + .rgb_ ## key = value, \ + .alpha_ ## key = value + +static const struct test blend_tests[] = { + { + "Replace", + { + .blend_enable = false, + .color_mask = 0xF, + }, + .constant_mask = 0x0, + .reads_dest = false, + .opaque = true, + .fixed_function = true, + .hardware = 0xF0122122 + }, + { + "Alpha", + { + .blend_enable = true, + .color_mask = 0xF, + + RGBA(func, BLEND_FUNC_ADD), + RGBA(src_factor, BLEND_FACTOR_SRC_ALPHA), + RGBA(dst_factor, BLEND_FACTOR_SRC_ALPHA), + RGBA(invert_dst_factor, true), + }, + .constant_mask = 0x0, + .reads_dest = true, + .opaque = false, + .fixed_function = true, + .hardware = 0xF0503503 + }, + { + "Additive", + { + .blend_enable = true, + .color_mask = 0xF, + + RGBA(func, BLEND_FUNC_ADD), + RGBA(src_factor, BLEND_FACTOR_ZERO), + RGBA(dst_factor, BLEND_FACTOR_ZERO), + RGBA(invert_src_factor, true), + RGBA(invert_dst_factor, true), + }, + .constant_mask = 0x0, + .reads_dest = true, + .opaque = false, + .fixed_function = true, + .hardware = 0xF0932932 /* equivalently 0xF0923923 */ + }, + { + "Additive-Alpha", + { + .blend_enable = true, + .color_mask = 0xF, + + RGBA(func, BLEND_FUNC_ADD), + RGBA(src_factor, BLEND_FACTOR_SRC_ALPHA), + RGBA(dst_factor, BLEND_FACTOR_ZERO), + RGBA(invert_dst_factor, true), + }, + .constant_mask = 0x0, + .reads_dest = true, + .opaque = false, + .fixed_function = true, + .hardware = 0xF0523523 + }, + { + "Subtractive", + { + .blend_enable = true, + .color_mask = 0xF, + + RGBA(func, BLEND_FUNC_SUBTRACT), + RGBA(src_factor, BLEND_FACTOR_ZERO), + RGBA(dst_factor, BLEND_FACTOR_ZERO), + RGBA(invert_src_factor, true), + RGBA(invert_dst_factor, true), + }, + .constant_mask = 0x0, + .reads_dest = true, + .opaque = false, + .fixed_function = true, + .hardware = 0xF09B29B2 /* equivalently 0xF09A39A3 */ + }, + { + "Subtractive-Alpha", + { + .blend_enable = true, + .color_mask = 0xF, + + RGBA(func, BLEND_FUNC_SUBTRACT), + RGBA(src_factor, BLEND_FACTOR_SRC_ALPHA), + RGBA(dst_factor, BLEND_FACTOR_ZERO), + RGBA(invert_dst_factor, true), + }, + .constant_mask = 0x0, + .reads_dest = true, + .opaque = false, + .fixed_function = true, + .hardware = 0xF052B52b /* equivalently 0xF05A35A3 */ + }, + { + "Modulate", + { + .blend_enable = true, + .color_mask = 0xF, + + RGBA(func, BLEND_FUNC_ADD), + RGBA(src_factor, BLEND_FACTOR_ZERO), + RGBA(dst_factor, BLEND_FACTOR_SRC_COLOR), + }, + .constant_mask = 0x0, + .reads_dest = true, + .opaque = false, + .fixed_function = true, + .hardware = 0xF0231231 /* equivalently 0xF0321321 */ + }, + { + "Replace masked", + { + .blend_enable = false, + .color_mask = 0x3, + }, + .constant_mask = 0x0, + .reads_dest = true, + .opaque = false, + .fixed_function = true, + .hardware = 0x30122122 + }, + { + "Modulate masked", + { + .blend_enable = true, + .color_mask = 0xA, + + RGBA(func, BLEND_FUNC_ADD), + RGBA(src_factor, BLEND_FACTOR_ZERO), + RGBA(dst_factor, BLEND_FACTOR_SRC_COLOR), + }, + .constant_mask = 0x0, + .reads_dest = true, + .opaque = false, + .fixed_function = true, + .hardware = 0xA0231231 /* equivalently 0xA0321321 */ + }, + +}; + +#define ASSERT_EQ(x, y) do { \ + if (x == y) { \ + nr_pass++; \ + } else { \ + nr_fail++; \ + fprintf(stderr, "%s: Assertion failed %s (%x) != %s (%x)\n", \ + T.label, #x, x, #y, y); \ + } \ +} while(0) + +int main(int argc, const char **argv) +{ + unsigned nr_pass = 0, nr_fail = 0; + + for (unsigned i = 0; i < ARRAY_SIZE(blend_tests); ++i) { + struct test T = blend_tests[i]; + ASSERT_EQ(T.constant_mask, pan_blend_constant_mask(T.eq)); + ASSERT_EQ(T.reads_dest, pan_blend_reads_dest(T.eq)); + ASSERT_EQ(T.opaque, pan_blend_is_opaque(T.eq)); + ASSERT_EQ(T.fixed_function, pan_blend_can_fixed_function(T.eq)); + + if (pan_blend_can_fixed_function(T.eq)) { + ASSERT_EQ(T.hardware, pan_pack_blend(T.eq)); + } + } + + printf("Passed %u/%u\n", nr_pass, nr_pass + nr_fail); + return nr_fail ? 1 : 0; +}