mirror of
https://gitlab.freedesktop.org/mesa/mesa.git
synced 2025-12-21 15:50:11 +01:00
The key change is to use a builder to write the expected shader result
and compare that. To make this less error prone, a few helper functions
were added
- a way to allocate VGRFs from both shaders in parallel, that way the
same brw_reg can be used in both of them;
- assertions that a pass will make progress or not, and proper output
when the unexpected happens;
- use a common brw_shader_pass_test class so to collect some of the helpers;
- make some helpers work directly with builder.
The idea is to improve the signal in tests, so that the disasm comments
are not necessary anymore. For example
```
TEST_F(saturate_propagation_test, basic)
{
brw_reg dst1 = bld.vgrf(BRW_TYPE_F);
brw_reg src0 = bld.vgrf(BRW_TYPE_F);
brw_reg src1 = bld.vgrf(BRW_TYPE_F);
brw_reg dst0 = bld.ADD(src0, src1);
set_saturate(true, bld.MOV(dst1, dst0));
/* = Before =
*
* 0: add(16) dst0 src0 src1
* 1: mov.sat(16) dst1 dst0
*
* = After =
* 0: add.sat(16) dst0 src0 src1
* 1: mov(16) dst1 dst0
*/
brw_calculate_cfg(*v);
bblock_t *block0 = v->cfg->blocks[0];
EXPECT_EQ(0, block0->start_ip);
EXPECT_EQ(1, block0->end_ip);
EXPECT_TRUE(saturate_propagation(v));
EXPECT_EQ(0, block0->start_ip);
EXPECT_EQ(1, block0->end_ip);
EXPECT_EQ(BRW_OPCODE_ADD, instruction(block0, 0)->opcode);
EXPECT_TRUE(instruction(block0, 0)->saturate);
EXPECT_EQ(BRW_OPCODE_MOV, instruction(block0, 1)->opcode);
EXPECT_FALSE(instruction(block0, 1)->saturate);
}
```
becomes
```
TEST_F(saturate_propagation_test, basic)
{
brw_builder bld = make_shader(MESA_SHADER_FRAGMENT, 16);
brw_builder exp = make_shader(MESA_SHADER_FRAGMENT, 16);
brw_reg dst0 = vgrf(bld, exp, BRW_TYPE_F);
brw_reg dst1 = vgrf(bld, exp, BRW_TYPE_F);
brw_reg src0 = vgrf(bld, exp, BRW_TYPE_F);
brw_reg src1 = vgrf(bld, exp, BRW_TYPE_F);
bld.ADD(dst0, src0, src1);
bld.MOV(dst1, dst0)->saturate = true;
EXPECT_PROGRESS(brw_opt_saturate_propagation, bld);
exp.ADD(dst0, src0, src1)->saturate = true;
exp.MOV(dst1, dst0);
EXPECT_SHADERS_MATCH(bld, exp);
}
```
Reviewed-by: Ian Romanick <ian.d.romanick@intel.com>
Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/33936>
199 lines
4.8 KiB
C++
199 lines
4.8 KiB
C++
/*
|
|
* Copyright © 2025 Intel Corporation
|
|
* SPDX-License-Identifier: MIT
|
|
*/
|
|
|
|
#pragma once
|
|
|
|
#include <gtest/gtest.h>
|
|
#include "brw_shader.h"
|
|
#include "brw_builder.h"
|
|
|
|
#include <memory>
|
|
#include <vector>
|
|
|
|
std::string brw_shader_to_string(brw_shader *s);
|
|
|
|
void brw_reindex_vgrfs(brw_shader *s);
|
|
|
|
/* Note: the order of instructions must be the same for shaders to match
|
|
* so be careful when using patterns like bld.ADD(bld.MOV(), bld.MOV())
|
|
* in the tests since order of argument evaluation is unspecified.
|
|
*/
|
|
void EXPECT_SHADERS_MATCH(brw_shader *a, brw_shader *b);
|
|
|
|
inline void
|
|
EXPECT_SHADERS_MATCH(brw_builder &a, brw_builder &b)
|
|
{
|
|
EXPECT_SHADERS_MATCH(a.shader, b.shader);
|
|
}
|
|
|
|
template <typename PASS>
|
|
inline void
|
|
EXPECT_PROGRESS_RESULT(bool should_progress, PASS pass, brw_shader *s)
|
|
{
|
|
if (!s->cfg)
|
|
brw_calculate_cfg(*s);
|
|
|
|
brw_validate(*s);
|
|
|
|
auto before = brw_shader_to_string(s);
|
|
bool progress = pass(*s);
|
|
auto after = brw_shader_to_string(s);
|
|
|
|
brw_validate(*s);
|
|
|
|
if (progress != should_progress || getenv("TEST_DEBUG")) {
|
|
fprintf(stderr, "= Before =\n"
|
|
"%s\n"
|
|
"\n"
|
|
"= After =\n"
|
|
"%s\n",
|
|
before.c_str(),
|
|
after.c_str());
|
|
}
|
|
|
|
EXPECT_EQ(progress, should_progress);
|
|
if (should_progress)
|
|
EXPECT_NE(before, after);
|
|
else
|
|
EXPECT_EQ(before, after);
|
|
}
|
|
|
|
template <typename PASS>
|
|
inline void
|
|
EXPECT_PROGRESS(PASS pass, brw_shader *s)
|
|
{
|
|
EXPECT_PROGRESS_RESULT(true, pass, s);
|
|
}
|
|
|
|
template <typename PASS>
|
|
inline void
|
|
EXPECT_NO_PROGRESS(PASS pass, brw_shader *s)
|
|
{
|
|
EXPECT_PROGRESS_RESULT(false, pass, s);
|
|
}
|
|
|
|
template <typename PASS>
|
|
inline void
|
|
EXPECT_PROGRESS_RESULT(bool should_progress, PASS pass, brw_builder &bld)
|
|
{
|
|
EXPECT_PROGRESS_RESULT(should_progress, pass, bld.shader);
|
|
}
|
|
|
|
template <typename PASS>
|
|
inline void
|
|
EXPECT_PROGRESS(PASS pass, brw_builder &bld)
|
|
{
|
|
EXPECT_PROGRESS(pass, bld.shader);
|
|
}
|
|
|
|
template <typename PASS>
|
|
inline void
|
|
EXPECT_NO_PROGRESS(PASS pass, brw_builder &bld)
|
|
{
|
|
EXPECT_NO_PROGRESS(pass, bld.shader);
|
|
}
|
|
|
|
class brw_shader_pass_test : public ::testing::Test {
|
|
protected:
|
|
void *mem_ctx;
|
|
struct brw_compiler *compiler;
|
|
struct intel_device_info *devinfo;
|
|
|
|
/* TODO: Once brw_shader is ralloc-able, we can simply get rid of this. */
|
|
std::vector<std::unique_ptr<brw_shader>> shaders;
|
|
|
|
brw_shader_pass_test()
|
|
{
|
|
mem_ctx = ralloc_context(NULL);
|
|
compiler = rzalloc(mem_ctx, struct brw_compiler);
|
|
devinfo = rzalloc(mem_ctx, struct intel_device_info);
|
|
compiler->devinfo = devinfo;
|
|
|
|
set_gfx_verx10(90);
|
|
}
|
|
|
|
~brw_shader_pass_test() override
|
|
{
|
|
ralloc_free(mem_ctx);
|
|
}
|
|
|
|
void
|
|
set_gfx_verx10(unsigned verx10)
|
|
{
|
|
devinfo->verx10 = verx10;
|
|
devinfo->ver = verx10 / 10;
|
|
assert(devinfo->ver > 0);
|
|
brw_init_isa_info(&compiler->isa, devinfo);
|
|
}
|
|
|
|
brw_builder
|
|
make_shader(gl_shader_stage stage = MESA_SHADER_FRAGMENT,
|
|
unsigned dispatch_width = 0)
|
|
{
|
|
if (dispatch_width == 0)
|
|
dispatch_width = devinfo->ver >= 20 ? 16 : 8;
|
|
|
|
nir_shader *nir = nir_shader_create(mem_ctx, stage, NULL, NULL);
|
|
brw_stage_prog_data *pd =
|
|
(struct brw_stage_prog_data *)rzalloc(mem_ctx, brw_any_prog_data);
|
|
|
|
brw_compile_params params = {};
|
|
params.mem_ctx = mem_ctx;
|
|
|
|
shaders.push_back(std::make_unique<brw_shader>(compiler, ¶ms, nullptr, pd, nir,
|
|
dispatch_width, false, false));
|
|
|
|
brw_shader *s = shaders.back().get();
|
|
s->phase = BRW_SHADER_PHASE_AFTER_OPT_LOOP;
|
|
return brw_builder(s);
|
|
}
|
|
|
|
/* Helper to allocate same vgrf number in two shaders. */
|
|
static brw_reg
|
|
vgrf(brw_shader *a, brw_shader *b,
|
|
brw_reg_type type, unsigned n = 1,
|
|
unsigned dispatch_width = 0)
|
|
{
|
|
assert(a && b && a->dispatch_width == b->dispatch_width);
|
|
|
|
if (dispatch_width == 0)
|
|
dispatch_width = a->dispatch_width;
|
|
|
|
brw_reg reg_a = brw_allocate_vgrf(*a, type, n * dispatch_width);
|
|
brw_reg reg_b = brw_allocate_vgrf(*b, type, n * dispatch_width);
|
|
assert(brw_regs_equal(®_a, ®_b));
|
|
|
|
return reg_a;
|
|
}
|
|
|
|
static brw_reg
|
|
vgrf(brw_builder &a, brw_builder &b,
|
|
brw_reg_type type, unsigned n = 1,
|
|
unsigned dispatch_width = 0)
|
|
{
|
|
return vgrf(a.shader, b.shader, type, n, dispatch_width);
|
|
}
|
|
|
|
static brw_reg
|
|
vgrf(brw_shader *a,
|
|
brw_reg_type type, unsigned n = 1,
|
|
unsigned dispatch_width = 0)
|
|
{
|
|
assert(a);
|
|
|
|
if (dispatch_width == 0)
|
|
dispatch_width = a->dispatch_width;
|
|
|
|
return brw_allocate_vgrf(*a, type, n * dispatch_width);
|
|
}
|
|
|
|
static brw_reg
|
|
vgrf(brw_builder &a,
|
|
brw_reg_type type, unsigned n = 1,
|
|
unsigned dispatch_width = 0)
|
|
{
|
|
return vgrf(a.shader, type, n, dispatch_width);
|
|
}
|
|
};
|