Add shader-jit unit-tests (#458)

* Rename `dynapica` TU to `shader`

These unit-tests in particular only actually test
the shader-interpreter and not any of the JITs.

* Conditionally test the shader-jit

In the case that the host supports the shader-jit, the interpreter and the shader-jit will both be tested with the same unit-tests. Allowing for even more coverage.

* Remove weird git submodule
This commit is contained in:
Wunk 2024-03-11 17:29:58 -07:00 committed by GitHub
parent 18df066463
commit fe9939689d
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 58 additions and 28 deletions

View file

@ -534,7 +534,7 @@ if(ENABLE_TESTS)
target_include_directories(nihstro-headers SYSTEM INTERFACE ./third_party/nihstro/include) target_include_directories(nihstro-headers SYSTEM INTERFACE ./third_party/nihstro/include)
add_executable(AlberTests add_executable(AlberTests
tests/dynapica.cpp tests/shader.cpp
) )
target_link_libraries( target_link_libraries(
AlberTests AlberTests

View file

@ -3,6 +3,7 @@
#include <PICA/dynapica/shader_rec.hpp> #include <PICA/dynapica/shader_rec.hpp>
#include <PICA/shader.hpp> #include <PICA/shader.hpp>
#include <catch2/catch_approx.hpp> #include <catch2/catch_approx.hpp>
#include <catch2/catch_template_test_macros.hpp>
#include <catch2/catch_test_macros.hpp> #include <catch2/catch_test_macros.hpp>
#include <initializer_list> #include <initializer_list>
#include <memory> #include <memory>
@ -27,12 +28,12 @@ static std::unique_ptr<PICAShader> assembleVertexShader(std::initializer_list<ni
return newShader; return newShader;
} }
class VertexShaderTest { class ShaderInterpreterTest final {
private: private:
std::unique_ptr<PICAShader> shader; std::unique_ptr<PICAShader> shader = {};
public: public:
explicit VertexShaderTest(std::initializer_list<nihstro::InlineAsm> code) : shader(assembleVertexShader(code)) {} explicit ShaderInterpreterTest(std::initializer_list<nihstro::InlineAsm> code) : shader(assembleVertexShader(code)) {}
// Multiple inputs, singular scalar output // Multiple inputs, singular scalar output
float runScalar(std::initializer_list<float> inputs) { float runScalar(std::initializer_list<float> inputs) {
@ -45,13 +46,42 @@ class VertexShaderTest {
return shader->outputs[0][0]; return shader->outputs[0][0];
} }
static std::unique_ptr<VertexShaderTest> assembleTest(std::initializer_list<nihstro::InlineAsm> code) { static std::unique_ptr<ShaderInterpreterTest> assembleTest(std::initializer_list<nihstro::InlineAsm> code) {
return std::make_unique<VertexShaderTest>(code); return std::make_unique<ShaderInterpreterTest>(code);
} }
}; };
TEST_CASE("ADD", "[shader][vertex]") { #if defined(PANDA3DS_SHADER_JIT_SUPPORTED)
const auto shader = VertexShaderTest::assembleTest({ class ShaderJITTest final {
private:
std::unique_ptr<PICAShader> shader = {};
ShaderJIT shaderJit = {};
public:
explicit ShaderJITTest(std::initializer_list<nihstro::InlineAsm> code) : shader(assembleVertexShader(code)) { shaderJit.prepare(*shader.get()); }
// Multiple inputs, singular scalar output
float runScalar(std::initializer_list<float> inputs) {
usize inputIndex = 0;
for (const float& input : inputs) {
const std::array<Floats::f24, 4> input_vec = std::array<Floats::f24, 4>{f24::fromFloat32(input), f24::zero(), f24::zero(), f24::zero()};
shader->inputs[inputIndex++] = input_vec;
}
shaderJit.run(*shader.get());
return shader->outputs[0][0];
}
static std::unique_ptr<ShaderJITTest> assembleTest(std::initializer_list<nihstro::InlineAsm> code) {
return std::make_unique<ShaderJITTest>(code);
}
};
#define SHADER_TEST_CASE(NAME, TAG) TEMPLATE_TEST_CASE(NAME, TAG, ShaderInterpreterTest, ShaderJITTest)
#else
#define SHADER_TEST_CASE(NAME, TAG) TEMPLATE_TEST_CASE(NAME, TAG, ShaderInterpreterTest)
#endif
SHADER_TEST_CASE("ADD", "[shader][vertex]") {
const auto shader = TestType::assembleTest({
{nihstro::OpCode::Id::ADD, output0, input0, input1}, {nihstro::OpCode::Id::ADD, output0, input0, input1},
{nihstro::OpCode::Id::END}, {nihstro::OpCode::Id::END},
}); });
@ -63,8 +93,8 @@ TEST_CASE("ADD", "[shader][vertex]") {
REQUIRE(std::isinf(shader->runScalar({INFINITY, -1.0f}))); REQUIRE(std::isinf(shader->runScalar({INFINITY, -1.0f})));
} }
TEST_CASE("MUL", "[shader][vertex]") { SHADER_TEST_CASE("MUL", "[shader][vertex]") {
const auto shader = VertexShaderTest::assembleTest({ const auto shader = TestType::assembleTest({
{nihstro::OpCode::Id::MUL, output0, input0, input1}, {nihstro::OpCode::Id::MUL, output0, input0, input1},
{nihstro::OpCode::Id::END}, {nihstro::OpCode::Id::END},
}); });
@ -77,8 +107,8 @@ TEST_CASE("MUL", "[shader][vertex]") {
REQUIRE(std::isnan(shader->runScalar({NAN, 0.0f}))); REQUIRE(std::isnan(shader->runScalar({NAN, 0.0f})));
} }
TEST_CASE("RCP", "[shader][vertex]") { SHADER_TEST_CASE("RCP", "[shader][vertex]") {
const auto shader = VertexShaderTest::assembleTest({ const auto shader = TestType::assembleTest({
{nihstro::OpCode::Id::RCP, output0, input0}, {nihstro::OpCode::Id::RCP, output0, input0},
{nihstro::OpCode::Id::END}, {nihstro::OpCode::Id::END},
}); });
@ -99,8 +129,8 @@ TEST_CASE("RCP", "[shader][vertex]") {
REQUIRE(shader->runScalar({0.0625f}) == Catch::Approx(16.0f).margin(0.004f)); REQUIRE(shader->runScalar({0.0625f}) == Catch::Approx(16.0f).margin(0.004f));
} }
TEST_CASE("RSQ", "[shader][vertex]") { SHADER_TEST_CASE("RSQ", "[shader][vertex]") {
const auto shader = VertexShaderTest::assembleTest({ const auto shader = TestType::assembleTest({
{nihstro::OpCode::Id::RSQ, output0, input0}, {nihstro::OpCode::Id::RSQ, output0, input0},
{nihstro::OpCode::Id::END}, {nihstro::OpCode::Id::END},
}); });
@ -121,8 +151,8 @@ TEST_CASE("RSQ", "[shader][vertex]") {
REQUIRE(shader->runScalar({0.0625f}) == Catch::Approx(4.0f).margin(0.004f)); REQUIRE(shader->runScalar({0.0625f}) == Catch::Approx(4.0f).margin(0.004f));
} }
TEST_CASE("LG2", "[shader][vertex]") { SHADER_TEST_CASE("LG2", "[shader][vertex]") {
const auto shader = VertexShaderTest::assembleTest({ const auto shader = TestType::assembleTest({
{nihstro::OpCode::Id::LG2, output0, input0}, {nihstro::OpCode::Id::LG2, output0, input0},
{nihstro::OpCode::Id::END}, {nihstro::OpCode::Id::END},
}); });
@ -135,8 +165,8 @@ TEST_CASE("LG2", "[shader][vertex]") {
REQUIRE(shader->runScalar({1.e24f}) == Catch::Approx(79.7262742773f)); REQUIRE(shader->runScalar({1.e24f}) == Catch::Approx(79.7262742773f));
} }
TEST_CASE("EX2", "[shader][vertex]") { SHADER_TEST_CASE("EX2", "[shader][vertex]") {
const auto shader = VertexShaderTest::assembleTest({ const auto shader = TestType::assembleTest({
{nihstro::OpCode::Id::EX2, output0, input0}, {nihstro::OpCode::Id::EX2, output0, input0},
{nihstro::OpCode::Id::END}, {nihstro::OpCode::Id::END},
}); });
@ -150,8 +180,8 @@ TEST_CASE("EX2", "[shader][vertex]") {
REQUIRE(std::isinf(shader->runScalar({800.f}))); REQUIRE(std::isinf(shader->runScalar({800.f})));
} }
TEST_CASE("MAX", "[shader][vertex]") { SHADER_TEST_CASE("MAX", "[shader][vertex]") {
const auto shader = VertexShaderTest::assembleTest({ const auto shader = TestType::assembleTest({
{nihstro::OpCode::Id::MAX, output0, input0, input1}, {nihstro::OpCode::Id::MAX, output0, input0, input1},
{nihstro::OpCode::Id::END}, {nihstro::OpCode::Id::END},
}); });
@ -165,8 +195,8 @@ TEST_CASE("MAX", "[shader][vertex]") {
REQUIRE(std::isnan(shader->runScalar({0.0f, NAN}))); REQUIRE(std::isnan(shader->runScalar({0.0f, NAN})));
} }
TEST_CASE("MIN", "[shader][vertex]") { SHADER_TEST_CASE("MIN", "[shader][vertex]") {
const auto shader = VertexShaderTest::assembleTest({ const auto shader = TestType::assembleTest({
{nihstro::OpCode::Id::MIN, output0, input0, input1}, {nihstro::OpCode::Id::MIN, output0, input0, input1},
{nihstro::OpCode::Id::END}, {nihstro::OpCode::Id::END},
}); });
@ -180,8 +210,8 @@ TEST_CASE("MIN", "[shader][vertex]") {
REQUIRE(std::isnan(shader->runScalar({0.0f, NAN}))); REQUIRE(std::isnan(shader->runScalar({0.0f, NAN})));
} }
TEST_CASE("SGE", "[shader][vertex]") { SHADER_TEST_CASE("SGE", "[shader][vertex]") {
const auto shader = VertexShaderTest::assembleTest({ const auto shader = TestType::assembleTest({
{nihstro::OpCode::Id::SGE, output0, input0, input1}, {nihstro::OpCode::Id::SGE, output0, input0, input1},
{nihstro::OpCode::Id::END}, {nihstro::OpCode::Id::END},
}); });
@ -197,8 +227,8 @@ TEST_CASE("SGE", "[shader][vertex]") {
REQUIRE(shader->runScalar({-1.0f, +1.0f}) == 0.0f); REQUIRE(shader->runScalar({-1.0f, +1.0f}) == 0.0f);
} }
TEST_CASE("SLT", "[shader][vertex]") { SHADER_TEST_CASE("SLT", "[shader][vertex]") {
const auto shader = VertexShaderTest::assembleTest({ const auto shader = TestType::assembleTest({
{nihstro::OpCode::Id::SLT, output0, input0, input1}, {nihstro::OpCode::Id::SLT, output0, input0, input1},
{nihstro::OpCode::Id::END}, {nihstro::OpCode::Id::END},
}); });
@ -214,8 +244,8 @@ TEST_CASE("SLT", "[shader][vertex]") {
REQUIRE(shader->runScalar({-1.0f, +1.0f}) == 1.0f); REQUIRE(shader->runScalar({-1.0f, +1.0f}) == 1.0f);
} }
TEST_CASE("FLR", "[shader][vertex]") { SHADER_TEST_CASE("FLR", "[shader][vertex]") {
const auto shader = VertexShaderTest::assembleTest({ const auto shader = TestType::assembleTest({
{nihstro::OpCode::Id::FLR, output0, input0}, {nihstro::OpCode::Id::FLR, output0, input0},
{nihstro::OpCode::Id::END}, {nihstro::OpCode::Id::END},
}); });