Split engine to packages NazaraUtils and NZSL (#375)

* Move code to NazaraUtils and NZSL packages

* Reorder includes

* Tests: Remove glslang and spirv-tools deps

* Tests: Remove glslang init

* Remove NazaraUtils tests and fix Vector4Test

* Fix Linux compilation

* Update msys2-build.yml

* Fix assimp package

* Update xmake.lua

* Update xmake.lua

* Fix shader compilation on MinGW

* Final fixes

* The final fix 2: the fix strikes back!

* Disable cache on CI

* The return of the fix™️
This commit is contained in:
Jérôme Leclercq
2022-05-25 19:36:10 +02:00
committed by GitHub
parent 3f8f1c4653
commit 03e2801dbe
483 changed files with 1139 additions and 59112 deletions

View File

@@ -9,31 +9,6 @@
std::filesystem::path GetResourceDir();
TEST_CASE("Apply", "[CORE][ALGORITHM]")
{
SECTION("Apply lambda to two vector2")
{
Nz::Vector2<int> vector = Nz::Vector2<int>::Unit();
auto lambda = [](const Nz::Vector2<int>& vec1, const Nz::Vector2<int>& vec2)
{
return vec1 + vec2;
};
Nz::Vector2<int> result = Nz::Apply(lambda, std::make_tuple(vector, vector));
REQUIRE(result == (Nz::Vector2<int>::Unit() * 2));
}
/*SECTION("Apply member function to vector2")
{
Nz::Vector2<int> vector = Nz::Vector2<int>::Unit();
int result = Nz::Apply(vector, (int(Nz::Vector2<int>::*)(const Nz::Vector2<int>&)) &Nz::Vector2<int>::Distance<int>, std::make_tuple(vector));
REQUIRE(result == 0);
}*/
}
TEST_CASE("ComputeHash", "[CORE][ALGORITHM]")
{
std::filesystem::path testFilePath = GetResourceDir() / "Logo.png";

View File

@@ -1,372 +0,0 @@
#include <Nazara/Core/Algorithm.hpp>
#include <Nazara/Core/Bitset.hpp>
#include <catch2/catch.hpp>
#include <array>
#include <string>
#include <iostream>
template<typename Block> void Check(const char* title);
template<typename Block> void CheckAppend(const char* title);
template<typename Block> void CheckBitOps(const char* title);
template<typename Block> void CheckBitOpsMultipleBlocks(const char* title);
template<typename Block> void CheckConstructor(const char* title);
template<typename Block> void CheckCopyMoveSwap(const char* title);
template<typename Block> void CheckRead(const char* title);
template<typename Block> void CheckReverse(const char* title);
SCENARIO("Bitset", "[CORE][BITSET]")
{
Check<Nz::UInt8>("Bitset made of 8bits blocks");
Check<Nz::UInt16>("Bitset made of 16bits blocks");
Check<Nz::UInt32>("Bitset made of 32bits blocks");
Check<Nz::UInt64>("Bitset made of 64bits blocks");
}
template<typename Block>
void Check(const char* title)
{
CheckConstructor<Block>(title);
CheckCopyMoveSwap<Block>(title);
CheckBitOps<Block>(title);
CheckBitOpsMultipleBlocks<Block>(title);
CheckAppend<Block>(title);
CheckRead<Block>(title);
CheckReverse<Block>(title);
}
template<typename Block>
void CheckAppend(const char* title)
{
SECTION(title)
{
GIVEN("An empty bitset filled by bytes")
{
#define BitVal1 00110111
#define BitVal2 11011110
#define BitVal3 01000010
std::array<Nz::UInt8, 3> data = {{NazaraPrefixMacro(BitVal1, 0b), NazaraPrefixMacro(BitVal2, 0b), NazaraPrefixMacro(BitVal3, 0b)}};
const char result[] = NazaraStringifyMacro(BitVal3) NazaraStringifyMacro(BitVal2) NazaraStringifyMacro(BitVal1);
std::size_t bitCount = data.size() * 8;
#undef BitVal1
#undef BitVal2
#undef BitVal3
std::array<std::pair<const char*, std::size_t>, 7> tests = {
{
{"We append bits one by one", 1},
{"We append bits two by two", 2},
{"We append bits three by three", 3},
{"We append bits four by four", 4},
{"We append bits six by six", 6},
{"We append bits byte by byte", 8},
{"We append bits twelve by twelve", 12}
}
};
for (auto& pair : tests)
{
WHEN(pair.first)
{
Nz::Bitset<Block> bitset;
for (std::size_t i = 0; i < bitCount; i += pair.second)
{
Nz::UInt16 value = data[i / 8] >> (i % 8);
if ((i % 8) + pair.second > 8 && i/8 != data.size()-1)
value |= static_cast<Nz::UInt16>(data[i / 8 + 1]) << (8 - (i % 8));
bitset.AppendBits(value, pair.second);
}
REQUIRE(bitset.GetSize() == bitCount);
Nz::Bitset<Block> expectedBitset(result);
CHECK(bitset == expectedBitset);
CHECK(bitset.GetBlockCount() == (bitCount / bitset.bitsPerBlock + std::min<std::size_t>(1, bitCount % bitset.bitsPerBlock)));
}
}
}
}
}
template<typename Block>
void CheckBitOps(const char* title)
{
SECTION(title)
{
GIVEN("Two bitsets")
{
Nz::Bitset<Block> first("01001");
Nz::Bitset<Block> second("10111");
WHEN("We perform operators")
{
Nz::Bitset<Block> andBitset = first & second;
Nz::Bitset<Block> orBitset = first | second;
Nz::Bitset<Block> xorBitset = first ^ second;
THEN("They should operate as logical operators")
{
CHECK(andBitset == Nz::Bitset<Block>("00001"));
CHECK(orBitset == Nz::Bitset<Block>("11111"));
CHECK(orBitset.TestAll());
CHECK(xorBitset == Nz::Bitset<Block>("11110"));
CHECK(!xorBitset.TestAll());
CHECK((~orBitset).TestNone());
}
}
WHEN("We perform bit shifts")
{
first.ShiftLeft(1);
second.ShiftRight(2);
THEN("We should obtain these")
{
CHECK(first == Nz::Bitset<Block>("10010"));
CHECK(second == Nz::Bitset<Block>("101"));
}
}
}
}
}
template<typename Block>
void CheckBitOpsMultipleBlocks(const char* title)
{
SECTION(title)
{
GIVEN("Two bitsets")
{
Nz::Bitset<Block> first("01001011010010101001010011010101001");
Nz::Bitset<Block> second("10111111101101110110111101101110110");
WHEN("We perform operators")
{
Nz::Bitset<Block> andBitset = first & second;
Nz::Bitset<Block> orBitset = first | second;
Nz::Bitset<Block> xorBitset = first ^ second;
THEN("They should operate as logical operators")
{
CHECK(andBitset == Nz::Bitset<Block>("00001011000000100000010001000100000"));
CHECK(orBitset == Nz::Bitset<Block>("11111111111111111111111111111111111"));
CHECK(orBitset.TestAll());
CHECK(xorBitset == Nz::Bitset<Block>("11110100111111011111101110111011111"));
CHECK(!xorBitset.TestAll());
CHECK((~orBitset).TestNone());
}
}
WHEN("We perform bit shifts")
{
first.ShiftLeft(16);
second.ShiftRight(16);
THEN("We should obtain these")
{
CHECK(first == Nz::Bitset<Block>("10010100110101010010000000000000000"));
first.ShiftLeft(1);
CHECK(first == Nz::Bitset<Block>("00101001101010100100000000000000000"));
CHECK(second == Nz::Bitset<Block>("1011111110110111011"));
second.ShiftRight(1);
CHECK(second == Nz::Bitset<Block>("101111111011011101"));
}
}
}
}
}
template<typename Block>
void CheckConstructor(const char* title)
{
SECTION(title)
{
GIVEN("Allocate and constructor")
{
Nz::Bitset<Block> bitset(3, false);
THEN("Capacity is 3 and size is 3")
{
CHECK(bitset.GetSize() == 3);
CHECK(bitset.GetCapacity() >= 3);
}
}
GIVEN("Iterator and default constructor")
{
std::string anotherDataString("0101");
Nz::Bitset<Block> defaultByte;
Nz::Bitset<Block> anotherData(anotherDataString.c_str());
WHEN("We assign 'anotherData'")
{
defaultByte = anotherDataString;
CHECK(anotherData == defaultByte);
CHECK(defaultByte.GetSize() == 4);
CHECK(defaultByte.GetCapacity() >= 4);
CHECK(anotherData.GetSize() == 4);
CHECK(anotherData.GetCapacity() >= 4);
}
}
}
}
template<typename Block>
void CheckCopyMoveSwap(const char* title)
{
SECTION(title)
{
GIVEN("Copy and Move constructor")
{
Nz::Bitset<Block> originalArray(3, true);
WHEN("We copy")
{
Nz::Bitset<Block> copyBitset(originalArray);
THEN("We get a copy")
{
CHECK(copyBitset == originalArray);
AND_WHEN("We modify one")
{
for (std::size_t i = 0; i < copyBitset.GetSize(); ++i)
copyBitset[i] = false;
THEN("They are no more equal")
{
CHECK(copyBitset != originalArray);
CHECK(copyBitset == Nz::Bitset<Block>(3, false));
}
}
}
}
WHEN("We move")
{
Nz::Bitset<Block> moveBitset(std::move(originalArray));
THEN("These results are expected")
{
CHECK(moveBitset == Nz::Bitset<Block>(3, true));
CHECK(originalArray.GetCapacity() == 0);
}
}
}
GIVEN("Three bitsets")
{
Nz::Bitset<Block> first("01001");
Nz::Bitset<Block> second("10110");
Nz::Bitset<Block> third;
WHEN("We swap first and third, then second and third and finally third and first")
{
Nz::Bitset<Block> oldFirst(first);
Nz::Bitset<Block> oldSecond(second);
first.Swap(third);
std::swap(second, third);
third.Swap(first);
THEN("First and second have been swapped and third is still empty.")
{
CHECK(oldFirst == second);
CHECK(oldSecond == first);
CHECK(third.GetSize() == 0);
}
}
}
}
}
template<typename Block>
void CheckRead(const char* title)
{
SECTION(title)
{
GIVEN("An empty bitset filled by reading")
{
#define BitVal1 10010101
#define BitVal2 11010010
#define BitVal3 01101010
std::array<Nz::UInt8, 3> data = {{NazaraPrefixMacro(BitVal1, 0b), NazaraPrefixMacro(BitVal2, 0b), NazaraPrefixMacro(BitVal3, 0b)}};
const char result[] = NazaraStringifyMacro(BitVal3) NazaraStringifyMacro(BitVal2) NazaraStringifyMacro(BitVal1);
std::size_t bitCount = data.size() * 8;
#undef BitVal1
#undef BitVal2
#undef BitVal3
std::array<std::pair<const char*, std::size_t>, 8> tests = {
{
{"We read bits one by one", 1},
{"We read bits two by two", 2},
{"We read bits three by three", 3},
{"We read bits four by four", 4},
{"We read bits six by six", 6},
{"We read bits byte by byte", 8},
{"We read bits twelve by twelve", 12},
{"We read bits all at once", 24}
}
};
for (auto& pair : tests)
{
WHEN(pair.first)
{
Nz::Bitset<Block> bitset;
auto seq = bitset.Write(data.data(), pair.second);
for (std::size_t i = pair.second; i < bitCount; i += pair.second)
seq = bitset.Write(seq, pair.second);
REQUIRE(bitset.GetSize() == bitCount);
Nz::Bitset<Block> expectedBitset(result);
CHECK(bitset == expectedBitset);
CHECK(bitset.GetBlockCount() == (bitCount / bitset.bitsPerBlock + std::min<std::size_t>(1, bitCount % bitset.bitsPerBlock)));
}
}
}
}
}
template<typename Block>
void CheckReverse(const char* title)
{
SECTION(title)
{
GIVEN("A bitset")
{
std::string bits = "010011100010001101001111";
Nz::Bitset<Block> expected(bits);
WHEN("We reverse the order of bits")
{
Nz::Bitset<Block> bitset(bits);
bitset.Reverse();
THEN("The order of bits should be reversed")
{
std::string reversedBits = bits;
std::reverse(reversedBits.begin(), reversedBits.end());
CHECK(bitset == Nz::Bitset<Block>(reversedBits));
}
AND_WHEN("We reverse the bit order again")
{
bitset.Reverse();
THEN("It should be back to normal")
{
CHECK(bitset == expected);
}
}
}
}
}
}

View File

@@ -1,141 +0,0 @@
#include <Nazara/Core/MemoryPool.hpp>
#include <catch2/catch.hpp>
#include <Nazara/Math/Vector2.hpp>
namespace
{
std::size_t allocationCount = 0;
template<typename T>
struct AllocatorTest : T
{
template<typename... Args>
AllocatorTest(Args&&... args) :
T(std::forward<Args>(args)...)
{
allocationCount++;
}
AllocatorTest(const AllocatorTest&) = delete;
AllocatorTest(AllocatorTest&&) = delete;
~AllocatorTest()
{
assert(allocationCount > 0);
allocationCount--;
}
};
}
SCENARIO("MemoryPool", "[CORE][MEMORYPOOL]")
{
GIVEN("A MemoryPool to contain one Nz::Vector2<int>")
{
using T = AllocatorTest<Nz::Vector2<int>>;
allocationCount = 0;
Nz::MemoryPool<T> memoryPool(2);
CHECK(memoryPool.GetAllocatedEntryCount() == 0);
CHECK(memoryPool.GetBlockCount() == 1);
CHECK(memoryPool.GetBlockSize() == 2);
CHECK(memoryPool.GetFreeEntryCount() == 2);
CHECK(allocationCount == 0);
WHEN("We construct a Nz::Vector2<int>")
{
std::size_t index;
T* vector2 = memoryPool.Allocate(index, 1, 2);
CHECK(allocationCount == 1);
CHECK(memoryPool.GetAllocatedEntryCount() == 1);
CHECK(memoryPool.GetFreeEntryCount() == 1);
CHECK(memoryPool.RetrieveEntryIndex(vector2) == index);
THEN("Memory is available")
{
vector2->x = 3;
REQUIRE(*vector2 == Nz::Vector2<int>(3, 2));
}
memoryPool.Free(index);
CHECK(allocationCount == 0);
CHECK(memoryPool.GetAllocatedEntryCount() == 0);
CHECK(memoryPool.GetFreeEntryCount() == 2);
}
WHEN("We construct three vectors")
{
CHECK(memoryPool.GetAllocatedEntryCount() == 0);
CHECK(memoryPool.GetFreeEntryCount() == 2);
std::size_t index1, index2, index3;
T* vector1 = memoryPool.Allocate(index1, 1, 2);
CHECK(allocationCount == 1);
CHECK(memoryPool.GetAllocatedEntryCount() == 1);
CHECK(memoryPool.GetBlockCount() == 1);
CHECK(memoryPool.GetFreeEntryCount() == 1);
T* vector2 = memoryPool.Allocate(index2, 3, 4);
CHECK(allocationCount == 2);
CHECK(memoryPool.GetAllocatedEntryCount() == 2);
CHECK(memoryPool.GetBlockCount() == 1);
CHECK(memoryPool.GetFreeEntryCount() == 0);
T* vector3 = memoryPool.Allocate(index3, 5, 6);
CHECK(allocationCount == 3);
CHECK(memoryPool.GetAllocatedEntryCount() == 3);
CHECK(memoryPool.GetBlockCount() == 2);
CHECK(memoryPool.GetFreeEntryCount() == 1); //< a new block has been allocated
CHECK(memoryPool.RetrieveEntryIndex(vector1) == index1);
CHECK(memoryPool.RetrieveEntryIndex(vector2) == index2);
CHECK(memoryPool.RetrieveEntryIndex(vector3) == index3);
THEN("Memory is available")
{
vector1->x = 3;
vector2->y = 5;
CHECK(*vector1 == Nz::Vector2<int>(3, 2));
CHECK(*vector2 == Nz::Vector2<int>(3, 5));
CHECK(vector3->GetSquaredLength() == Approx(61.f));
AND_THEN("We iterate on the memory pool")
{
std::size_t count = 0;
int sumX = 0;
int sumY = 0;
for (T& vec : memoryPool)
{
count++;
sumX += vec.x;
sumY += vec.y;
}
CHECK(count == 3);
CHECK(sumX == 11);
CHECK(sumY == 13);
}
}
memoryPool.Reset();
CHECK(allocationCount == 0);
CHECK(memoryPool.GetAllocatedEntryCount() == 0);
CHECK(memoryPool.GetBlockCount() == 2);
CHECK(memoryPool.GetFreeEntryCount() == 4);
bool failure = false;
for (T& vec : memoryPool)
{
NazaraUnused(vec);
failure = true;
}
CHECK_FALSE(failure);
memoryPool.Clear();
CHECK(allocationCount == 0);
CHECK(memoryPool.GetAllocatedEntryCount() == 0);
CHECK(memoryPool.GetBlockCount() == 0);
CHECK(memoryPool.GetFreeEntryCount() == 0);
}
}
}

View File

@@ -1,48 +0,0 @@
#include <Nazara/Core/Signal.hpp>
#include <catch2/catch.hpp>
struct Incrementer
{
void increment(int* inc)
{
*inc += 1;
}
};
void increment(int* inc)
{
*inc += 1;
}
SCENARIO("Signal", "[CORE][SIGNAL]")
{
GIVEN("A signal")
{
Nz::Signal<int*> signal;
WHEN("We connection different callbacks")
{
auto connection = signal.Connect(increment);
signal.Connect([](int* inc){ *inc += 1; });
Incrementer incrementer;
signal.Connect(incrementer, &Incrementer::increment);
THEN("The call of signal with inc = 0 must return 3")
{
int inc = 0;
signal(&inc);
REQUIRE(inc == 3);
}
AND_THEN("When we disconnect one function, there should be only two listeners")
{
connection.Disconnect();
REQUIRE(!connection.IsConnected());
int inc = 0;
signal(&inc);
REQUIRE(inc == 2);
}
}
}
}

View File

@@ -1,47 +0,0 @@
#include <Nazara/Core/SparsePtr.hpp>
#include <catch2/catch.hpp>
#include <array>
SCENARIO("SparsePtr", "[CORE][SPARSEPTR]")
{
GIVEN("A sparse pointer pointing to an array with a stride of 2")
{
std::array<int, 5> arrays = { {0, 1, 2, 3, 4} };
Nz::SparsePtr<int> sparsePtr(arrays.data(), 2 * sizeof(int));
WHEN("We use operators")
{
THEN("Operator[] with 2 should be 4")
{
CHECK(4 == sparsePtr[2]);
}
THEN("Operator++ and Operator-- should be opposite")
{
++sparsePtr;
CHECK(2 == *sparsePtr);
auto old = sparsePtr++;
CHECK(2 == *old);
CHECK(4 == *sparsePtr);
--sparsePtr;
CHECK(2 == *sparsePtr);
auto oldMinus = sparsePtr--;
CHECK(2 == *oldMinus);
CHECK(0 == *sparsePtr);
}
THEN("Operator+ and operator-")
{
auto offsetTwo = sparsePtr + 2;
CHECK(4 == *offsetTwo);
auto offsetZero = offsetTwo - 2;
CHECK(0 == *offsetZero);
CHECK((offsetTwo - offsetZero) == 2);
}
}
}
}

View File

@@ -1,350 +0,0 @@
#include <Nazara/Core/MovablePtr.hpp>
#include <Nazara/Core/StackVector.hpp>
#include <catch2/catch.hpp>
#include <array>
#include <numeric>
// This is a quick way to check that checks are valid
#define USE_STD_VECTOR 0
class DestructionCounter
{
public:
DestructionCounter() :
m_counter(nullptr),
m_value(0)
{
}
DestructionCounter(std::size_t* counter, int value) :
m_counter(counter),
m_value(value)
{
if (m_counter)
(*m_counter)++;
}
DestructionCounter(const DestructionCounter& counter) :
m_counter(counter.m_counter),
m_value(counter.m_value)
{
if (m_counter)
(*m_counter)++;
}
DestructionCounter(DestructionCounter&& counter) :
m_counter(counter.m_counter),
m_value(counter.m_value)
{
if (m_counter)
(*m_counter)++;
}
~DestructionCounter()
{
if (m_counter)
{
assert(*m_counter > 0);
(*m_counter)--;
}
}
operator int() const
{
return m_value;
}
DestructionCounter& operator=(const DestructionCounter& counter)
{
if (m_counter)
{
assert(*m_counter > 0);
(*m_counter)--;
}
m_counter = counter.m_counter;
m_value = counter.m_value;
if (m_counter)
(*m_counter)++;
return *this;
}
DestructionCounter& operator=(DestructionCounter&& counter)
{
if (this == &counter)
return *this;
if (m_counter)
{
assert(*m_counter > 0);
(*m_counter)--;
}
m_counter = counter.m_counter;
m_value = counter.m_value;
if (m_counter)
(*m_counter)++;
return *this;
}
private:
std::size_t* m_counter;
int m_value;
};
SCENARIO("StackVector", "[CORE][STACKVECTOR]")
{
GIVEN("A StackVector to contain multiple int")
{
std::size_t counter = 0;
{
volatile std::size_t capacity = 50;
#if USE_STD_VECTOR
std::vector<DestructionCounter> vector;
vector.reserve(capacity);
#else
Nz::StackVector<DestructionCounter> vector = NazaraStackVector(DestructionCounter, capacity);
#endif
WHEN("At construction, the vector is empty but has capacity")
{
CHECK(vector.capacity() == capacity);
CHECK(vector.empty());
CHECK(vector.size() == 0);
#if !USE_STD_VECTOR
CHECK(vector.max_size() == capacity);
#endif
}
WHEN("Resizing it changes its size and create/destroy elements")
{
vector.resize(vector.capacity());
CHECK(vector.size() == vector.capacity());
CHECK(counter == 0);
vector.resize(0);
CHECK(vector.empty());
CHECK(vector.size() == 0);
CHECK(counter == 0);
}
WHEN("Resizing it allocates elements")
{
vector.resize(vector.capacity(), DestructionCounter(&counter, 0));
CHECK(vector.size() == vector.capacity());
CHECK(counter == capacity);
vector.resize(0);
CHECK(vector.empty());
CHECK(vector.size() == 0);
CHECK(counter == 0);
}
WHEN("Emplacing five elements, vector size increase accordingly")
{
for (std::size_t i = 0; i < 5; ++i)
{
#if USE_STD_VECTOR
vector.emplace_back(&counter, int(i));
#else
CHECK(vector.emplace_back(&counter, int(i)) == int(i));
#endif
}
CHECK(!vector.empty());
CHECK(vector.size() == 5);
std::array<int, 5> expectedValues = { 0, 1, 2, 3, 4 };
CHECK(std::equal(vector.begin(), vector.end(), expectedValues.begin(), expectedValues.end()));
}
WHEN("Pushing three elements, vector size increase accordingly")
{
for (std::size_t i = 0; i < 3; ++i)
{
DestructionCounter val(&counter, int(i));
#if USE_STD_VECTOR
vector.push_back(val);
#else
CHECK(vector.push_back(val) == val);
#endif
}
CHECK(!vector.empty());
CHECK(vector.size() == 3);
{
std::array<int, 3> expectedValues = { 0, 1, 2 };
CHECK(std::equal(vector.begin(), vector.end(), expectedValues.begin(), expectedValues.end()));
}
THEN("We resize to five")
{
vector.resize(5);
CHECK(!vector.empty());
CHECK(vector.size() == 5);
std::array<int, 5> expectedValues = { 0, 1, 2, 0, 0 };
CHECK(std::equal(vector.begin(), vector.end(), expectedValues.begin(), expectedValues.end()));
AND_THEN("We resize it back to zero")
{
vector.resize(0);
CHECK(vector.empty());
CHECK(vector.size() == 0);
}
AND_THEN("We clear it")
{
vector.clear();
CHECK(vector.empty());
CHECK(vector.size() == 0);
CHECK(counter == 0);
}
}
}
WHEN("We generate its content will iota")
{
vector.resize(10);
for (std::size_t i = 0; i < vector.size(); ++i)
vector[i] = DestructionCounter(&counter, -5 + int(i));
{
std::array<int, 10> expectedValues = { -5, -4, -3, -2, -1, 0, 1, 2, 3, 4 };
CHECK(vector.size() == expectedValues.size());
CHECK(std::equal(vector.begin(), vector.end(), expectedValues.begin(), expectedValues.end()));
}
AND_WHEN("We pop back some elements")
{
for (std::size_t i = 0; i < 5; ++i)
vector.pop_back();
std::array<int, 5> expectedValues = { -5, -4, -3, -2, -1 };
CHECK(vector.size() == expectedValues.size());
CHECK(std::equal(vector.begin(), vector.end(), expectedValues.begin(), expectedValues.end()));
}
AND_WHEN("We erase elements at the beginning")
{
vector.erase(vector.begin());
vector.erase(vector.begin());
std::array<int, 8> expectedValues = { -3, -2, -1, 0, 1, 2, 3, 4 };
CHECK(vector.size() == expectedValues.size());
CHECK(std::equal(vector.begin(), vector.end(), expectedValues.begin(), expectedValues.end()));
}
AND_WHEN("We erase elements in the middle")
{
vector.erase(vector.begin() + 2);
vector.erase(vector.begin() + 2);
vector.erase(vector.begin() + 6);
std::array<int, 7> expectedValues = { -5, -4, -1, 0, 1, 2, 4 };
CHECK(vector.size() == expectedValues.size());
CHECK(std::equal(vector.begin(), vector.end(), expectedValues.begin(), expectedValues.end()));
}
AND_WHEN("We erase elements at the end")
{
vector.erase(vector.end() - 1);
vector.erase(vector.end() - 1);
std::array<int, 8> expectedValues = { -5, -4, -3, -2, -1, 0, 1, 2 };
CHECK(vector.size() == expectedValues.size());
CHECK(std::equal(vector.begin(), vector.end(), expectedValues.begin(), expectedValues.end()));
}
AND_WHEN("We erase a range")
{
vector.erase(vector.begin() + 2, vector.end() - 3);
std::array<int, 5> expectedValues = { -5, -4, 2, 3, 4 };
CHECK(vector.size() == expectedValues.size());
CHECK(std::equal(vector.begin(), vector.end(), expectedValues.begin(), expectedValues.end()));
}
AND_WHEN("We erase everything")
{
vector.erase(vector.begin(), vector.end());
CHECK(vector.empty());
}
}
WHEN("We generate its content using emplace")
{
for (std::size_t i = 0; i < 5; ++i)
CHECK(*vector.emplace(vector.end(), &counter, int(i)) == int(i));
CHECK(!vector.empty());
CHECK(vector.size() == 5);
std::array<int, 5> expectedValues = { 0, 1, 2, 3, 4 };
CHECK(std::equal(vector.begin(), vector.end(), expectedValues.begin(), expectedValues.end()));
}
WHEN("We generate its content using emplace, in reverse order")
{
for (std::size_t i = 0; i < 5; ++i)
CHECK(*vector.emplace(vector.begin(), &counter, int(i)) == int(i));
CHECK(!vector.empty());
CHECK(vector.size() == 5);
std::array<int, 5> expectedValues = { 4, 3, 2, 1, 0 };
CHECK(std::equal(vector.begin(), vector.end(), expectedValues.begin(), expectedValues.end()));
}
WHEN("We generate its content using emplace, at the middle")
{
for (std::size_t i = 0; i < 10; ++i)
CHECK(*vector.emplace(vector.begin() + i / 2, &counter, int(i)) == int(i));
CHECK(!vector.empty());
CHECK(vector.size() == 10);
std::array<int, 10> expectedValues = { 1, 3, 5, 7, 9, 8, 6, 4, 2, 0 };
CHECK(std::equal(vector.begin(), vector.end(), expectedValues.begin(), expectedValues.end()));
}
WHEN("We generate its content using insert")
{
for (std::size_t i = 0; i < 5; ++i)
CHECK(*vector.insert(vector.end(), DestructionCounter(&counter, int(i))) == int(i));
CHECK(!vector.empty());
CHECK(vector.size() == 5);
std::array<int, 5> expectedValues = { 0, 1, 2, 3, 4 };
CHECK(std::equal(vector.begin(), vector.end(), expectedValues.begin(), expectedValues.end()));
}
WHEN("We generate its content using insert, in reverse order")
{
for (std::size_t i = 0; i < 5; ++i)
CHECK(*vector.insert(vector.begin(), DestructionCounter(&counter, int(i))) == int(i));
CHECK(!vector.empty());
CHECK(vector.size() == 5);
std::array<int, 5> expectedValues = { 4, 3, 2, 1, 0 };
CHECK(std::equal(vector.begin(), vector.end(), expectedValues.begin(), expectedValues.end()));
}
WHEN("We generate its content using insert, at the middle")
{
for (std::size_t i = 0; i < 10; ++i)
CHECK(*vector.insert(vector.begin() + i / 2, DestructionCounter(&counter, int(i))) == int(i));
CHECK(!vector.empty());
CHECK(vector.size() == 10);
std::array<int, 10> expectedValues = { 1, 3, 5, 7, 9, 8, 6, 4, 2, 0 };
CHECK(std::equal(vector.begin(), vector.end(), expectedValues.begin(), expectedValues.end()));
}
}
CHECK(counter == 0);
}
}

View File

@@ -4,5 +4,4 @@
#include <Nazara/Network.hpp>
#include <Nazara/Physics2D.hpp>
#include <Nazara/Physics3D.hpp>
#include <Nazara/Shader.hpp>
#include <Nazara/Utility.hpp>

View File

@@ -1,111 +0,0 @@
#include <Engine/Shader/ShaderUtils.hpp>
#include <Nazara/Core/File.hpp>
#include <Nazara/Core/StringExt.hpp>
#include <Nazara/Shader/ShaderBuilder.hpp>
#include <Nazara/Shader/ShaderLangParser.hpp>
#include <catch2/catch.hpp>
#include <cctype>
TEST_CASE("structure member access", "[Shader]")
{
SECTION("Nested member loading")
{
std::string_view nzslSource = R"(
[nzsl_version("1.0")]
module;
struct innerStruct
{
field: vec3[f32]
}
struct outerStruct
{
s: innerStruct
}
external
{
[set(0), binding(0)] ubo: uniform[outerStruct]
}
)";
Nz::ShaderAst::ModulePtr shaderModule = Nz::ShaderLang::Parse(nzslSource);
shaderModule = SanitizeModule(*shaderModule);
SECTION("Nested AccessMember")
{
auto ubo = Nz::ShaderBuilder::Identifier("ubo");
auto firstAccess = Nz::ShaderBuilder::AccessMember(std::move(ubo), { "s" });
auto secondAccess = Nz::ShaderBuilder::AccessMember(std::move(firstAccess), { "field" });
auto swizzle = Nz::ShaderBuilder::Swizzle(std::move(secondAccess), { 2u });
auto varDecl = Nz::ShaderBuilder::DeclareVariable("result", Nz::ShaderAst::ExpressionType{ Nz::ShaderAst::PrimitiveType::Float32 }, std::move(swizzle));
shaderModule->rootNode->statements.push_back(Nz::ShaderBuilder::DeclareFunction(Nz::ShaderStageType::Vertex, "main", std::move(varDecl)));
ExpectGLSL(*shaderModule, R"(
void main()
{
float result = ubo.s.field.z;
}
)");
ExpectNZSL(*shaderModule, R"(
[entry(vert)]
fn main()
{
let result: f32 = ubo.s.field.z;
}
)");
ExpectSPIRV(*shaderModule, R"(
OpFunction
OpLabel
OpVariable
OpAccessChain
OpLoad
OpCompositeExtract
OpStore
OpReturn
OpFunctionEnd)");
}
SECTION("AccessMember with multiples fields")
{
auto ubo = Nz::ShaderBuilder::Identifier("ubo");
auto access = Nz::ShaderBuilder::AccessMember(std::move(ubo), { "s", "field" });
auto swizzle = Nz::ShaderBuilder::Swizzle(std::move(access), { 2u });
auto varDecl = Nz::ShaderBuilder::DeclareVariable("result", Nz::ShaderAst::ExpressionType{ Nz::ShaderAst::PrimitiveType::Float32 }, std::move(swizzle));
shaderModule->rootNode->statements.push_back(Nz::ShaderBuilder::DeclareFunction(Nz::ShaderStageType::Vertex, "main", std::move(varDecl)));
ExpectGLSL(*shaderModule, R"(
void main()
{
float result = ubo.s.field.z;
}
)");
ExpectNZSL(*shaderModule, R"(
[entry(vert)]
fn main()
{
let result: f32 = ubo.s.field.z;
}
)");
ExpectSPIRV(*shaderModule, R"(
OpFunction
OpLabel
OpVariable
OpAccessChain
OpLoad
OpCompositeExtract
OpStore
OpReturn
OpFunctionEnd)");
}
}
}

View File

@@ -1,98 +0,0 @@
#include <Engine/Shader/ShaderUtils.hpp>
#include <Nazara/Core/File.hpp>
#include <Nazara/Core/StringExt.hpp>
#include <Nazara/Shader/ShaderBuilder.hpp>
#include <Nazara/Shader/ShaderLangParser.hpp>
#include <catch2/catch.hpp>
#include <cctype>
TEST_CASE("aliases", "[Shader]")
{
SECTION("Alias of structs")
{
std::string_view nzslSource = R"(
[nzsl_version("1.0")]
module;
struct Data
{
value: f32
}
alias ExtData = Data;
external
{
[binding(0)] extData: uniform[ExtData]
}
struct Input
{
value: f32
}
alias In = Input;
struct Output
{
[location(0)] value: f32
}
alias Out = Output;
alias FragOut = Out;
[entry(frag)]
fn main(input: In) -> FragOut
{
let output: Out;
output.value = extData.value * input.value;
return output;
}
)";
Nz::ShaderAst::ModulePtr shaderModule = Nz::ShaderLang::Parse(nzslSource);
shaderModule = SanitizeModule(*shaderModule);
ExpectGLSL(*shaderModule, R"(
void main()
{
Input input_;
input_.value = _NzIn_value;
Output output_;
output_.value = extData.value * input_.value;
_NzOut_value = output_.value;
return;
}
)");
ExpectNZSL(*shaderModule, R"(
[entry(frag)]
fn main(input: In) -> FragOut
{
let output: Out;
output.value = extData.value * input.value;
return output;
}
)");
ExpectSPIRV(*shaderModule, R"(
OpFunction
OpLabel
OpVariable
OpVariable
OpAccessChain
OpLoad
OpAccessChain
OpLoad
OpFMul
OpAccessChain
OpStore
OpLoad
OpCompositeExtract
OpStore
OpReturn
OpFunctionEnd)");
}
}

View File

@@ -1,377 +0,0 @@
#include <Engine/Shader/ShaderUtils.hpp>
#include <Nazara/Core/File.hpp>
#include <Nazara/Core/StringExt.hpp>
#include <Nazara/Shader/ShaderBuilder.hpp>
#include <Nazara/Shader/ShaderLangParser.hpp>
#include <catch2/catch.hpp>
#include <cctype>
TEST_CASE("branching", "[Shader]")
{
WHEN("using a simple branch")
{
std::string_view nzslSource = R"(
[nzsl_version("1.0")]
module;
struct inputStruct
{
value: f32
}
external
{
[set(0), binding(0)] data: uniform[inputStruct]
}
[entry(frag)]
fn main()
{
let value: f32;
if (data.value > 0.0)
value = 1.0;
else
value = 0.0;
}
)";
Nz::ShaderAst::ModulePtr shaderModule = Nz::ShaderLang::Parse(nzslSource);
shaderModule = SanitizeModule(*shaderModule);
ExpectGLSL(*shaderModule, R"(
void main()
{
float value;
if (data.value > (0.000000))
{
value = 1.000000;
}
else
{
value = 0.000000;
}
}
)");
ExpectNZSL(*shaderModule, R"(
[entry(frag)]
fn main()
{
let value: f32;
if (data.value > (0.000000))
{
value = 1.000000;
}
else
{
value = 0.000000;
}
}
)");
ExpectSPIRV(*shaderModule, R"(
OpFunction
OpLabel
OpVariable
OpAccessChain
OpLoad
OpFOrdGreaterThanEqual
OpSelectionMerge
OpBranchConditional
OpLabel
OpStore
OpBranch
OpLabel
OpStore
OpBranch
OpLabel
OpReturn
OpFunctionEnd)");
}
WHEN("using a more complex branch")
{
std::string_view nzslSource = R"(
[nzsl_version("1.0")]
module;
struct inputStruct
{
value: f32
}
external
{
[set(0), binding(0)] data: uniform[inputStruct]
}
[entry(frag)]
fn main()
{
let value: f32;
if (data.value > 42.0 || data.value <= 50.0 && data.value < 0.0)
value = 1.0;
else
value = 0.0;
}
)";
Nz::ShaderAst::ModulePtr shaderModule = Nz::ShaderLang::Parse(nzslSource);
shaderModule = SanitizeModule(*shaderModule);
ExpectGLSL(*shaderModule, R"(
void main()
{
float value;
if ((data.value > (42.000000)) || ((data.value <= (50.000000)) && (data.value < (0.000000))))
{
value = 1.000000;
}
else
{
value = 0.000000;
}
}
)");
ExpectNZSL(*shaderModule, R"(
[entry(frag)]
fn main()
{
let value: f32;
if ((data.value > (42.000000)) || ((data.value <= (50.000000)) && (data.value < (0.000000))))
{
value = 1.000000;
}
else
{
value = 0.000000;
}
}
)");
ExpectSPIRV(*shaderModule, R"(
OpFunction
OpLabel
OpVariable
OpAccessChain
OpLoad
OpFOrdGreaterThanEqual
OpAccessChain
OpLoad
OpFOrdLessThanEqual
OpAccessChain
OpLoad
OpFOrdLessThan
OpLogicalAnd
OpLogicalOr
OpSelectionMerge
OpBranchConditional
OpLabel
OpStore
OpBranch
OpLabel
OpStore
OpBranch
OpLabel
OpReturn
OpFunctionEnd)");
}
WHEN("discarding in a branch")
{
std::string_view nzslSource = R"(
[nzsl_version("1.0")]
module;
struct inputStruct
{
value: f32
}
external
{
[set(0), binding(0)] data: uniform[inputStruct]
}
[entry(frag)]
fn main()
{
if (data.value > 0.0)
discard;
}
)";
Nz::ShaderAst::ModulePtr shaderModule = Nz::ShaderLang::Parse(nzslSource);
shaderModule = SanitizeModule(*shaderModule);
ExpectGLSL(*shaderModule, R"(
void main()
{
if (data.value > (0.000000))
{
discard;
}
}
)");
ExpectNZSL(*shaderModule, R"(
[entry(frag)]
fn main()
{
if (data.value > (0.000000))
{
discard;
}
}
)");
ExpectSPIRV(*shaderModule, R"(
OpFunction
OpLabel
OpAccessChain
OpLoad
OpFOrdGreaterThanEqual
OpSelectionMerge
OpBranchConditional
OpLabel
OpKill
OpLabel
OpBranch
OpLabel
OpReturn
OpFunctionEnd)");
}
WHEN("using a complex branch")
{
std::string_view nzslSource = R"(
[nzsl_version("1.0")]
module;
struct inputStruct
{
value: f32
}
external
{
[set(0), binding(0)] data: uniform[inputStruct]
}
[entry(frag)]
fn main()
{
let value: f32;
if (data.value > 3.0)
value = 3.0;
else if (data.value > 2.0)
value = 2.0;
else if (data.value > 1.0)
value = 1.0;
else
value = 0.0;
}
)";
Nz::ShaderAst::ModulePtr shaderModule = Nz::ShaderLang::Parse(nzslSource);
shaderModule = SanitizeModule(*shaderModule);
ExpectGLSL(*shaderModule, R"(
void main()
{
float value;
if (data.value > (3.000000))
{
value = 3.000000;
}
else if (data.value > (2.000000))
{
value = 2.000000;
}
else if (data.value > (1.000000))
{
value = 1.000000;
}
else
{
value = 0.000000;
}
}
)");
ExpectNZSL(*shaderModule, R"(
[entry(frag)]
fn main()
{
let value: f32;
if (data.value > (3.000000))
{
value = 3.000000;
}
else if (data.value > (2.000000))
{
value = 2.000000;
}
else if (data.value > (1.000000))
{
value = 1.000000;
}
else
{
value = 0.000000;
}
}
)");
ExpectSPIRV(*shaderModule, R"(
OpFunction
OpLabel
OpVariable
OpAccessChain
OpLoad
OpFOrdGreaterThanEqual
OpSelectionMerge
OpBranchConditional
OpLabel
OpStore
OpBranch
OpLabel
OpAccessChain
OpLoad
OpFOrdGreaterThanEqual
OpSelectionMerge
OpBranchConditional
OpLabel
OpStore
OpBranch
OpLabel
OpAccessChain
OpLoad
OpFOrdGreaterThanEqual
OpSelectionMerge
OpBranchConditional
OpLabel
OpStore
OpBranch
OpLabel
OpStore
OpBranch
OpLabel
OpBranch
OpLabel
OpBranch
OpLabel
OpReturn
OpFunctionEnd)");
}
}

View File

@@ -1,255 +0,0 @@
#include <Engine/Shader/ShaderUtils.hpp>
#include <Nazara/Core/File.hpp>
#include <Nazara/Core/StringExt.hpp>
#include <Nazara/Shader/ShaderBuilder.hpp>
#include <Nazara/Shader/ShaderLangParser.hpp>
#include <Nazara/Shader/Ast/AstConstantPropagationVisitor.hpp>
#include <Nazara/Shader/Ast/SanitizeVisitor.hpp>
#include <catch2/catch.hpp>
#include <cctype>
void ExpectOutput(Nz::ShaderAst::Module& shaderModule, const Nz::ShaderAst::SanitizeVisitor::Options& options, std::string_view expectedOptimizedResult)
{
Nz::ShaderAst::ModulePtr sanitizedShader;
sanitizedShader = SanitizeModule(shaderModule, options);
ExpectNZSL(*sanitizedShader, expectedOptimizedResult);
}
TEST_CASE("const", "[Shader]")
{
WHEN("using const if")
{
std::string_view sourceCode = R"(
[nzsl_version("1.0")]
module;
option UseInt: bool = false;
[cond(UseInt)]
struct inputStruct
{
value: i32
}
[cond(!UseInt)]
struct inputStruct
{
value: f32
}
external
{
[set(0), binding(0)] data: uniform[inputStruct]
}
[entry(frag)]
fn main()
{
let value: f32;
const if (UseInt)
{
value = f32(data.value);
}
else
{
value = data.value;
}
}
)";
Nz::ShaderAst::ModulePtr shaderModule;
REQUIRE_NOTHROW(shaderModule = Nz::ShaderLang::Parse(sourceCode));
Nz::ShaderAst::SanitizeVisitor::Options options;
WHEN("Enabling option")
{
options.optionValues[Nz::CRC32("UseInt")] = true;
ExpectOutput(*shaderModule, options, R"(
struct inputStruct
{
value: i32
}
external
{
[set(0), binding(0)] data: uniform[inputStruct]
}
[entry(frag)]
fn main()
{
let value: f32;
value = f32(data.value);
}
)");
}
WHEN("Disabling option")
{
options.optionValues[Nz::CRC32("UseInt")] = false;
ExpectOutput(*shaderModule, options, R"(
struct inputStruct
{
value: f32
}
external
{
[set(0), binding(0)] data: uniform[inputStruct]
}
[entry(frag)]
fn main()
{
let value: f32;
value = data.value;
}
)");
}
}
WHEN("using [unroll] attribute on numerical for")
{
std::string_view sourceCode = R"(
[nzsl_version("1.0")]
module;
const LightCount = 3;
[layout(std140)]
struct Light
{
color: vec4[f32]
}
[layout(std140)]
struct LightData
{
lights: array[Light, LightCount]
}
external
{
[set(0), binding(0)] data: uniform[LightData]
}
[entry(frag)]
fn main()
{
let color = (0.0).xxxx;
[unroll]
for i in 0 -> 10 : 2
{
color += data.lights[i].color;
}
}
)";
Nz::ShaderAst::ModulePtr shaderModule;
REQUIRE_NOTHROW(shaderModule = Nz::ShaderLang::Parse(sourceCode));
ExpectOutput(*shaderModule, {}, R"(
[entry(frag)]
fn main()
{
let color: vec4[f32] = (0.000000).xxxx;
{
let i: i32 = 0;
color += data.lights[i].color;
}
{
let i: i32 = 2;
color += data.lights[i].color;
}
{
let i: i32 = 4;
color += data.lights[i].color;
}
{
let i: i32 = 6;
color += data.lights[i].color;
}
{
let i: i32 = 8;
color += data.lights[i].color;
}
}
)");
}
WHEN("using [unroll] attribute on for-each")
{
std::string_view sourceCode = R"(
[nzsl_version("1.0")]
module;
const LightCount = 3;
[layout(std140)]
struct Light
{
color: vec4[f32]
}
[layout(std140)]
struct LightData
{
lights: array[Light, LightCount]
}
external
{
[set(0), binding(0)] data: uniform[LightData]
}
[entry(frag)]
fn main()
{
let color = (0.0).xxxx;
[unroll]
for light in data.lights
{
color += light.color;
}
}
)";
Nz::ShaderAst::ModulePtr shaderModule;
REQUIRE_NOTHROW(shaderModule = Nz::ShaderLang::Parse(sourceCode));
ExpectOutput(*shaderModule, {}, R"(
[entry(frag)]
fn main()
{
let color: vec4[f32] = (0.000000).xxxx;
{
let light: Light = data.lights[0];
color += light.color;
}
{
let light: Light = data.lights[1];
color += light.color;
}
{
let light: Light = data.lights[2];
color += light.color;
}
}
)");
}
}

View File

@@ -1,24 +0,0 @@
#include <Engine/Shader/ShaderUtils.hpp>
#include <Nazara/Shader/ShaderLangParser.hpp>
#include <catch2/catch.hpp>
TEST_CASE("errors", "[Shader]")
{
SECTION("Checking lexer errors")
{
CHECK_THROWS_WITH(Nz::ShaderLang::Tokenize("1x42"), "(1,1 -> 4): LBadNumber error: bad number");
CHECK_THROWS_WITH(Nz::ShaderLang::Tokenize("123456789876543210123456789"), "(1,1 -> 27): LNumberOutOfRange error: number is out of range");
CHECK_THROWS_WITH(Nz::ShaderLang::Tokenize("\"Hello world"), "(1,1 -> 13): LUnfinishedString error: unfinished string");
CHECK_THROWS_WITH(Nz::ShaderLang::Tokenize(R"("hello \p")"), "(1,1 -> 9): LUnrecognizedChar error: unrecognized character");
CHECK_THROWS_WITH(Nz::ShaderLang::Tokenize("$"), "(1, 1): LUnrecognizedToken error: unrecognized token");
}
SECTION("Checking parser errors")
{
CHECK_THROWS_WITH(Nz::ShaderLang::Parse("nazara"), "(1,1 -> 6): PUnexpectedToken error: unexpected token Identifier");
CHECK_THROWS_WITH(Nz::ShaderLang::Parse("module;"), "(1,1 -> 6): PMissingAttribute error: missing attribute nzsl_version");
CHECK_THROWS_WITH(Nz::ShaderLang::Parse("[nzsl_version] module;"), "(1,2 -> 13): PAttributeMissingParameter error: attribute nzsl_version requires a parameter");
CHECK_THROWS_WITH(Nz::ShaderLang::Parse("[nzsl_version(\"1.0\"), nzsl_version(\"1.0\")] module;"), "(1,23 -> 41): PAttributeMultipleUnique error: attribute nzsl_version can only be present once");
CHECK_THROWS_WITH(Nz::ShaderLang::Parse("[nzsl_version(\"1.0\"), uuid(\"Nazara\")] module;"), "(1,23 -> 36): PInvalidUuid error: \"Nazara\" is not a valid UUID");
}
}

View File

@@ -1,92 +0,0 @@
#include <Engine/Shader/ShaderUtils.hpp>
#include <Nazara/Core/File.hpp>
#include <Nazara/Core/StringExt.hpp>
#include <Nazara/Shader/ShaderBuilder.hpp>
#include <Nazara/Shader/ShaderLangParser.hpp>
#include <catch2/catch.hpp>
#include <cctype>
TEST_CASE("functions", "[Shader]")
{
SECTION("Simple function call")
{
std::string_view nzslSource = R"(
[nzsl_version("1.0")]
module;
struct FragOut
{
[location(0)] value: f32
}
fn GetValue() -> f32
{
return 42.0;
}
[entry(frag)]
fn main() -> FragOut
{
let output: FragOut;
output.value = -GetValue();
return output;
}
)";
Nz::ShaderAst::ModulePtr shaderModule = Nz::ShaderLang::Parse(nzslSource);
shaderModule = SanitizeModule(*shaderModule);
ExpectGLSL(*shaderModule, R"(
float GetValue()
{
return 42.000000;
}
/*************** Outputs ***************/
layout(location = 0) out float _NzOut_value;
void main()
{
FragOut output_;
output_.value = -GetValue();
_NzOut_value = output_.value;
return;
}
)");
ExpectNZSL(*shaderModule, R"(
fn GetValue() -> f32
{
return 42.000000;
}
[entry(frag)]
fn main() -> FragOut
{
let output: FragOut;
output.value = -GetValue();
return output;
}
)");
ExpectSPIRV(*shaderModule, R"(
OpFunction
OpLabel
OpReturnValue
OpFunctionEnd
OpFunction
OpLabel
OpVariable
OpFunctionCall
OpFNegate
OpAccessChain
OpStore
OpLoad
OpCompositeExtract
OpStore
OpReturn
OpFunctionEnd)");
}
}

View File

@@ -1,213 +0,0 @@
#include <Engine/Shader/ShaderUtils.hpp>
#include <Nazara/Core/File.hpp>
#include <Nazara/Core/StringExt.hpp>
#include <Nazara/Shader/ShaderBuilder.hpp>
#include <Nazara/Shader/ShaderLangParser.hpp>
#include <catch2/catch.hpp>
#include <cctype>
TEST_CASE("intrinsics", "[Shader]")
{
WHEN("using intrinsics")
{
std::string_view nzslSource = R"(
[nzsl_version("1.0")]
module;
external
{
[set(0), binding(0)] tex: sampler2D[f32]
}
[entry(frag)]
fn main()
{
let f1 = 42.0;
let f2 = 1337.0;
let i1 = 42;
let i2 = 1337;
let uv = vec2[f32](0.0, 1.0);
let v1 = vec3[f32](0.0, 1.0, 2.0);
let v2 = vec3[f32](2.0, 1.0, 0.0);
let crossResult = cross(v1, v2);
let dotResult = dot(v1, v2);
let expResult1 = exp(v1);
let expResult2 = exp(f1);
let lengthResult = length(v1);
let maxResult1 = max(f1, f2);
let maxResult2 = max(i1, i2);
let maxResult3 = max(v1, v2);
let minResult1 = min(f1, f2);
let minResult2 = min(i1, i2);
let minResult3 = min(v1, v2);
let normalizeResult = normalize(v1);
let powResult1 = pow(f1, f2);
let powResult2 = pow(v1, v2);
let reflectResult = reflect(v1, v2);
let sampleResult = tex.Sample(uv);
}
)";
Nz::ShaderAst::ModulePtr shaderModule = Nz::ShaderLang::Parse(nzslSource);
shaderModule = SanitizeModule(*shaderModule);
ExpectGLSL(*shaderModule, R"(
void main()
{
float f1 = 42.000000;
float f2 = 1337.000000;
int i1 = 42;
int i2 = 1337;
vec2 uv = vec2(0.000000, 1.000000);
vec3 v1 = vec3(0.000000, 1.000000, 2.000000);
vec3 v2 = vec3(2.000000, 1.000000, 0.000000);
vec3 crossResult = cross(v1, v2);
float dotResult = dot(v1, v2);
vec3 expResult1 = exp(v1);
float expResult2 = exp(f1);
float lengthResult = length(v1);
float maxResult1 = max(f1, f2);
int maxResult2 = max(i1, i2);
vec3 maxResult3 = max(v1, v2);
float minResult1 = min(f1, f2);
int minResult2 = min(i1, i2);
vec3 minResult3 = min(v1, v2);
vec3 normalizeResult = normalize(v1);
float powResult1 = pow(f1, f2);
vec3 powResult2 = pow(v1, v2);
vec3 reflectResult = reflect(v1, v2);
vec4 sampleResult = texture(tex, uv);
}
)");
ExpectNZSL(*shaderModule, R"(
fn main()
{
let f1: f32 = 42.000000;
let f2: f32 = 1337.000000;
let i1: i32 = 42;
let i2: i32 = 1337;
let uv: vec2[f32] = vec2[f32](0.000000, 1.000000);
let v1: vec3[f32] = vec3[f32](0.000000, 1.000000, 2.000000);
let v2: vec3[f32] = vec3[f32](2.000000, 1.000000, 0.000000);
let crossResult: vec3[f32] = cross(v1, v2);
let dotResult: f32 = dot(v1, v2);
let expResult1: vec3[f32] = exp(v1);
let expResult2: f32 = exp(f1);
let lengthResult: f32 = length(v1);
let maxResult1: f32 = max(f1, f2);
let maxResult2: i32 = max(i1, i2);
let maxResult3: vec3[f32] = max(v1, v2);
let minResult1: f32 = min(f1, f2);
let minResult2: i32 = min(i1, i2);
let minResult3: vec3[f32] = min(v1, v2);
let normalizeResult: vec3[f32] = normalize(v1);
let powResult1: f32 = pow(f1, f2);
let powResult2: vec3[f32] = pow(v1, v2);
let reflectResult: vec3[f32] = reflect(v1, v2);
let sampleResult: vec4[f32] = tex.Sample(uv);
}
)");
ExpectSPIRV(*shaderModule, R"(
OpFunction
OpLabel
OpVariable
OpVariable
OpVariable
OpVariable
OpVariable
OpVariable
OpVariable
OpVariable
OpVariable
OpVariable
OpVariable
OpVariable
OpVariable
OpVariable
OpVariable
OpVariable
OpVariable
OpVariable
OpVariable
OpVariable
OpVariable
OpVariable
OpVariable
OpStore
OpStore
OpStore
OpStore
OpCompositeConstruct
OpStore
OpCompositeConstruct
OpStore
OpCompositeConstruct
OpStore
OpLoad
OpLoad
OpExtInst
OpStore
OpLoad
OpLoad
OpDot
OpStore
OpLoad
OpExtInst
OpStore
OpLoad
OpExtInst
OpStore
OpLoad
OpExtInst
OpStore
OpLoad
OpLoad
OpExtInst
OpStore
OpLoad
OpLoad
OpExtInst
OpStore
OpLoad
OpLoad
OpExtInst
OpStore
OpLoad
OpLoad
OpExtInst
OpStore
OpLoad
OpLoad
OpExtInst
OpStore
OpLoad
OpLoad
OpExtInst
OpStore
OpLoad
OpExtInst
OpStore
OpLoad
OpLoad
OpExtInst
OpStore
OpLoad
OpLoad
OpExtInst
OpStore
OpLoad
OpLoad
OpExtInst
OpStore
OpLoad
OpLoad
OpImageSampleImplicitLod
OpStore
OpReturn
OpFunctionEnd)");
}
}

View File

@@ -1,38 +0,0 @@
#include <Engine/Shader/ShaderUtils.hpp>
#include <Nazara/Core/File.hpp>
#include <Nazara/Core/StringExt.hpp>
#include <Nazara/Shader/ShaderBuilder.hpp>
#include <Nazara/Shader/ShaderLangLexer.hpp>
#include <catch2/catch.hpp>
#include <cctype>
TEST_CASE("lexer", "[Shader]")
{
SECTION("Simple code")
{
std::string_view nzslSource = R"(
[nzsl_version("1.0")]
module;
[entry(frag)]
fn main()
{
let vec = vec4[f32](0.0, 1.0, 2.0, 3.0);
let i = 42;
let value = vec.xyz;
}
)";
std::vector<Nz::ShaderLang::Token> tokens = Nz::ShaderLang::Tokenize(nzslSource);
CHECK(Nz::ShaderLang::ToString(tokens) == R"(OpenSquareBracket Identifier(nzsl_version) OpenParenthesis StringValue("1.0") ClosingParenthesis ClosingSquareBracket
Module Semicolon
OpenSquareBracket Identifier(entry) OpenParenthesis Identifier(frag) ClosingParenthesis ClosingSquareBracket
FunctionDeclaration Identifier(main) OpenParenthesis ClosingParenthesis
OpenCurlyBracket
Let Identifier(vec) Assign Identifier(vec4) OpenSquareBracket Identifier(f32) ClosingSquareBracket OpenParenthesis FloatingPointValue(0) Comma FloatingPointValue(1) Comma FloatingPointValue(2) Comma FloatingPointValue(3) ClosingParenthesis Semicolon
Let Identifier(i) Assign IntegerValue(42) Semicolon
Let Identifier(value) Assign Identifier(vec) Dot Identifier(xyz) Semicolon
ClosingCurlyBracket
EndOfStream)");
}
}

View File

@@ -1,347 +0,0 @@
#include <Engine/Shader/ShaderUtils.hpp>
#include <Nazara/Core/File.hpp>
#include <Nazara/Core/StringExt.hpp>
#include <Nazara/Shader/ShaderBuilder.hpp>
#include <Nazara/Shader/ShaderLangParser.hpp>
#include <catch2/catch.hpp>
#include <cctype>
TEST_CASE("loops", "[Shader]")
{
WHEN("using a while")
{
std::string_view nzslSource = R"(
[nzsl_version("1.0")]
module;
struct inputStruct
{
value: f32
}
external
{
[set(0), binding(0)] data: uniform[inputStruct]
}
[entry(frag)]
fn main()
{
let value = 0.0;
let i = 0;
while (i < 10)
{
value += 0.1;
i += 1;
}
}
)";
Nz::ShaderAst::ModulePtr shaderModule = Nz::ShaderLang::Parse(nzslSource);
shaderModule = SanitizeModule(*shaderModule);
ExpectGLSL(*shaderModule, R"(
void main()
{
float value = 0.000000;
int i = 0;
while (i < (10))
{
value += 0.100000;
i += 1;
}
}
)");
ExpectNZSL(*shaderModule, R"(
[entry(frag)]
fn main()
{
let value: f32 = 0.000000;
let i: i32 = 0;
while (i < (10))
{
value += 0.100000;
i += 1;
}
}
)");
ExpectSPIRV(*shaderModule, R"(
OpFunction
OpLabel
OpVariable
OpVariable
OpStore
OpStore
OpBranch
OpLabel
OpLoad
OpSLessThan
OpLoopMerge
OpBranchConditional
OpLabel
OpLoad
OpFAdd
OpStore
OpLoad
OpIAdd
OpStore
OpBranch
OpLabel
OpReturn
OpFunctionEnd)");
}
WHEN("using a for range")
{
std::string_view nzslSource = R"(
[nzsl_version("1.0")]
module;
[entry(frag)]
fn main()
{
let x = 0;
for v in 0 -> 10
{
x += v;
}
}
)";
Nz::ShaderAst::ModulePtr shaderModule = Nz::ShaderLang::Parse(nzslSource);
shaderModule = SanitizeModule(*shaderModule);
ExpectGLSL(*shaderModule, R"(
void main()
{
int x = 0;
int v = 0;
int to = 10;
while (v < to)
{
x += v;
v += 1;
}
}
)");
ExpectNZSL(*shaderModule, R"(
[entry(frag)]
fn main()
{
let x: i32 = 0;
for v in 0 -> 10
{
x += v;
}
}
)");
ExpectSPIRV(*shaderModule, R"(
OpFunction
OpLabel
OpVariable
OpVariable
OpVariable
OpStore
OpStore
OpStore
OpBranch
OpLabel
OpLoad
OpLoad
OpSLessThan
OpLoopMerge
OpBranchConditional
OpLabel
OpLoad
OpLoad
OpIAdd
OpStore
OpLoad
OpIAdd
OpStore
OpBranch
OpLabel
OpReturn
OpFunctionEnd)");
}
WHEN("using a for range with step")
{
std::string_view nzslSource = R"(
[nzsl_version("1.0")]
module;
[entry(frag)]
fn main()
{
let x = 0;
for v in 0 -> 10 : 2
{
x += v;
}
}
)";
Nz::ShaderAst::ModulePtr shaderModule = Nz::ShaderLang::Parse(nzslSource);
shaderModule = SanitizeModule(*shaderModule);
ExpectGLSL(*shaderModule, R"(
void main()
{
int x = 0;
int v = 0;
int to = 10;
int step = 2;
while (v < to)
{
x += v;
v += step;
}
}
)");
ExpectNZSL(*shaderModule, R"(
[entry(frag)]
fn main()
{
let x: i32 = 0;
for v in 0 -> 10 : 2
{
x += v;
}
}
)");
ExpectSPIRV(*shaderModule, R"(
OpFunction
OpLabel
OpVariable
OpVariable
OpVariable
OpVariable
OpStore
OpStore
OpStore
OpStore
OpBranch
OpLabel
OpLoad
OpLoad
OpSLessThan
OpLoopMerge
OpBranchConditional
OpLabel
OpLoad
OpLoad
OpIAdd
OpStore
OpLoad
OpLoad
OpIAdd
OpStore
OpBranch
OpLabel
OpReturn
OpFunctionEnd)");
}
WHEN("using a for-each")
{
std::string_view nzslSource = R"(
[nzsl_version("1.0")]
module;
struct inputStruct
{
value: array[f32, 10]
}
external
{
[set(0), binding(0)] data: uniform[inputStruct]
}
[entry(frag)]
fn main()
{
let x = 0.0;
for v in data.value
{
x += v;
}
}
)";
Nz::ShaderAst::ModulePtr shaderModule = Nz::ShaderLang::Parse(nzslSource);
shaderModule = SanitizeModule(*shaderModule);
ExpectGLSL(*shaderModule, R"(
void main()
{
float x = 0.000000;
uint i = 0u;
while (i < (10u))
{
float v = data.value[i];
x += v;
i += 1u;
}
}
)");
ExpectNZSL(*shaderModule, R"(
[entry(frag)]
fn main()
{
let x: f32 = 0.000000;
for v in data.value
{
x += v;
}
}
)");
ExpectSPIRV(*shaderModule, R"(
OpFunction
OpLabel
OpVariable
OpVariable
OpVariable
OpStore
OpStore
OpBranch
OpLabel
OpLoad
OpULessThan
OpLoopMerge
OpBranchConditional
OpLabel
OpLoad
OpAccessChain
OpLoad
OpStore
OpLoad
OpLoad
OpFAdd
OpStore
OpLoad
OpIAdd
OpStore
OpBranch
OpLabel
OpReturn
OpFunctionEnd)");
}
}

View File

@@ -1,449 +0,0 @@
#include <Engine/Shader/ShaderUtils.hpp>
#include <Nazara/Core/File.hpp>
#include <Nazara/Core/StringExt.hpp>
#include <Nazara/Shader/FilesystemModuleResolver.hpp>
#include <Nazara/Shader/LangWriter.hpp>
#include <Nazara/Shader/ShaderBuilder.hpp>
#include <Nazara/Shader/ShaderLangParser.hpp>
#include <Nazara/Shader/Ast/SanitizeVisitor.hpp>
#include <catch2/catch.hpp>
#include <cctype>
TEST_CASE("Modules", "[Shader]")
{
WHEN("using a simple module")
{
// UUID are required here to have a stable output
std::string_view importedSource = R"(
[nzsl_version("1.0")]
[uuid("ad3aed6e-0619-4a26-b5ce-abc2ec0836c4")]
module SimpleModule;
[layout(std140)]
struct Data
{
value: f32
}
[export]
[layout(std140)]
struct Block
{
data: Data
}
[export]
fn GetDataValue(data: Data) -> f32
{
return data.value;
}
struct Unused {}
[export]
struct InputData
{
value: f32
}
[export]
struct OutputData
{
value: f32
}
)";
std::string_view shaderSource = R"(
[nzsl_version("1.0")]
module;
import SimpleModule;
external
{
[binding(0)] block: uniform[Block]
}
[entry(frag)]
fn main(input: InputData) -> OutputData
{
let output: OutputData;
output.value = GetDataValue(block.data) * input.value;
return output;
}
)";
Nz::ShaderAst::ModulePtr shaderModule = Nz::ShaderLang::Parse(shaderSource);
auto directoryModuleResolver = std::make_shared<Nz::FilesystemModuleResolver>();
directoryModuleResolver->RegisterModule(importedSource);
Nz::ShaderAst::SanitizeVisitor::Options sanitizeOpt;
sanitizeOpt.moduleResolver = directoryModuleResolver;
shaderModule = SanitizeModule(*shaderModule, sanitizeOpt);
ExpectGLSL(*shaderModule, R"(
// Module SimpleModule
struct Data_181c45e9
{
float value;
};
struct Block_181c45e9
{
Data_181c45e9 data;
};
float GetDataValue_181c45e9(Data_181c45e9 data)
{
return data.value;
}
struct InputData_181c45e9
{
float value;
};
struct OutputData_181c45e9
{
float value;
};
// Main file
layout(std140) uniform _NzBinding_block
{
Data_181c45e9 data;
} block;
/**************** Inputs ****************/
in float _NzIn_value;
/*************** Outputs ***************/
out float _NzOut_value;
void main()
{
InputData_181c45e9 input_;
input_.value = _NzIn_value;
OutputData_181c45e9 output_;
output_.value = (GetDataValue_181c45e9(block.data)) * input_.value;
_NzOut_value = output_.value;
return;
}
)");
ExpectNZSL(*shaderModule, R"(
[nzsl_version("1.0")]
module;
[nzsl_version("1.0")]
[uuid("ad3aed6e-0619-4a26-b5ce-abc2ec0836c4")]
module _181c45e9
{
[layout(std140)]
struct Data
{
value: f32
}
[layout(std140)]
struct Block
{
data: Data
}
fn GetDataValue(data: Data) -> f32
{
return data.value;
}
struct InputData
{
value: f32
}
struct OutputData
{
value: f32
}
}
alias Block = _181c45e9.Block;
alias GetDataValue = _181c45e9.GetDataValue;
alias InputData = _181c45e9.InputData;
alias OutputData = _181c45e9.OutputData;
external
{
[set(0), binding(0)] block: uniform[_181c45e9.Block]
}
[entry(frag)]
fn main(input: InputData) -> OutputData
{
let output: OutputData;
output.value = (GetDataValue(block.data)) * input.value;
return output;
}
)");
ExpectSPIRV(*shaderModule, R"(
OpFunction
OpFunctionParameter
OpLabel
OpAccessChain
OpLoad
OpReturnValue
OpFunctionEnd
OpFunction
OpLabel
OpVariable
OpVariable
OpVariable
OpAccessChain
OpLoad
OpStore
OpFunctionCall
OpAccessChain
OpLoad
OpFMul
OpAccessChain
OpStore
OpLoad
OpReturn
OpFunctionEnd)");
}
WHEN("Using nested modules")
{
// UUID are required here to have a stable output
std::string_view dataModule = R"(
[nzsl_version("1.0")]
[uuid("ad3aed6e-0619-4a26-b5ce-abc2ec0836c4")]
module Modules.Data;
fn dummy() {}
[export]
[layout(std140)]
struct Data
{
value: f32
}
)";
std::string_view blockModule = R"(
[nzsl_version("1.0")]
[uuid("7a548506-89e6-4944-897f-4f695a8bca01")]
module Modules.Block;
import Modules.Data;
[export]
[layout(std140)]
struct Block
{
data: Data
}
struct Unused {}
)";
std::string_view inputOutputModule = R"(
[nzsl_version("1.0")]
[uuid("e66c6e98-fc37-4390-a7e1-c81508ff8e49")]
module Modules.InputOutput;
[export]
struct InputData
{
value: f32
}
[export]
struct OutputData
{
value: f32
}
)";
std::string_view shaderSource = R"(
[nzsl_version("1.0")]
module;
import Modules.Block;
import Modules.InputOutput;
external
{
[binding(0)] block: uniform[Block]
}
[entry(frag)]
fn main(input: InputData) -> OutputData
{
let output: OutputData;
output.value = block.data.value * input.value;
return output;
}
)";
Nz::ShaderAst::ModulePtr shaderModule = Nz::ShaderLang::Parse(shaderSource);
auto directoryModuleResolver = std::make_shared<Nz::FilesystemModuleResolver>();
directoryModuleResolver->RegisterModule(dataModule);
directoryModuleResolver->RegisterModule(blockModule);
directoryModuleResolver->RegisterModule(inputOutputModule);
Nz::ShaderAst::SanitizeVisitor::Options sanitizeOpt;
sanitizeOpt.moduleResolver = directoryModuleResolver;
shaderModule = SanitizeModule(*shaderModule, sanitizeOpt);
ExpectGLSL(*shaderModule, R"(
// Module Modules.Data
struct Data_181c45e9
{
float value;
};
// Module Modules.Block
struct Block_e528265d
{
Data_181c45e9 data;
};
// Module Modules.InputOutput
struct InputData_26cce136
{
float value;
};
struct OutputData_26cce136
{
float value;
};
// Main file
layout(std140) uniform _NzBinding_block
{
Data_181c45e9 data;
} block;
/**************** Inputs ****************/
in float _NzIn_value;
/*************** Outputs ***************/
out float _NzOut_value;
void main()
{
InputData_26cce136 input_;
input_.value = _NzIn_value;
OutputData_26cce136 output_;
output_.value = block.data.value * input_.value;
_NzOut_value = output_.value;
return;
}
)");
ExpectNZSL(*shaderModule, R"(
[nzsl_version("1.0")]
module;
[nzsl_version("1.0")]
[uuid("ad3aed6e-0619-4a26-b5ce-abc2ec0836c4")]
module _181c45e9
{
[layout(std140)]
struct Data
{
value: f32
}
}
[nzsl_version("1.0")]
[uuid("7a548506-89e6-4944-897f-4f695a8bca01")]
module _e528265d
{
alias Data = _181c45e9.Data;
[layout(std140)]
struct Block
{
data: Data
}
}
[nzsl_version("1.0")]
[uuid("e66c6e98-fc37-4390-a7e1-c81508ff8e49")]
module _26cce136
{
struct InputData
{
value: f32
}
struct OutputData
{
value: f32
}
}
alias Block = _e528265d.Block;
alias InputData = _26cce136.InputData;
alias OutputData = _26cce136.OutputData;
external
{
[set(0), binding(0)] block: uniform[_e528265d.Block]
}
[entry(frag)]
fn main(input: InputData) -> OutputData
{
let output: OutputData;
output.value = block.data.value * input.value;
return output;
}
)");
ExpectSPIRV(*shaderModule, R"(
OpFunction
OpLabel
OpVariable
OpVariable
OpAccessChain
OpLoad
OpAccessChain
OpLoad
OpFMul
OpAccessChain
OpStore
OpLoad
OpReturn
OpFunctionEnd)");
}
}

View File

@@ -1,373 +0,0 @@
#include <Engine/Shader/ShaderUtils.hpp>
#include <Nazara/Core/File.hpp>
#include <Nazara/Core/StringExt.hpp>
#include <Nazara/Shader/ShaderBuilder.hpp>
#include <Nazara/Shader/ShaderLangParser.hpp>
#include <Nazara/Shader/Ast/AstConstantPropagationVisitor.hpp>
#include <Nazara/Shader/Ast/EliminateUnusedPassVisitor.hpp>
#include <Nazara/Shader/Ast/SanitizeVisitor.hpp>
#include <catch2/catch.hpp>
#include <cctype>
void PropagateConstantAndExpect(std::string_view sourceCode, std::string_view expectedOptimizedResult)
{
Nz::ShaderAst::ModulePtr shaderModule;
REQUIRE_NOTHROW(shaderModule = Nz::ShaderLang::Parse(sourceCode));
shaderModule = SanitizeModule(*shaderModule);
REQUIRE_NOTHROW(shaderModule = Nz::ShaderAst::PropagateConstants(*shaderModule));
ExpectNZSL(*shaderModule, expectedOptimizedResult);
}
void EliminateUnusedAndExpect(std::string_view sourceCode, std::string_view expectedOptimizedResult)
{
Nz::ShaderAst::DependencyCheckerVisitor::Config depConfig;
depConfig.usedShaderStages = Nz::ShaderStageType_All;
Nz::ShaderAst::ModulePtr shaderModule;
REQUIRE_NOTHROW(shaderModule = Nz::ShaderLang::Parse(sourceCode));
shaderModule = SanitizeModule(*shaderModule);
REQUIRE_NOTHROW(shaderModule = Nz::ShaderAst::EliminateUnusedPass(*shaderModule, depConfig));
ExpectNZSL(*shaderModule, expectedOptimizedResult);
}
TEST_CASE("optimizations", "[Shader]")
{
WHEN("propagating constants")
{
PropagateConstantAndExpect(R"(
[nzsl_version("1.0")]
module;
[entry(frag)]
fn main()
{
let output = 8.0 * (7.0 + 5.0) * 2.0 / 4.0 - 6.0;
}
)", R"(
[entry(frag)]
fn main()
{
let output: f32 = 42.000000;
}
)");
}
WHEN("propagating vector constants")
{
PropagateConstantAndExpect(R"(
[nzsl_version("1.0")]
module;
[entry(frag)]
fn main()
{
let output = vec4[f32](8.0, 2.0, -7.0, 0.0) * (7.0 + 5.0) * 2.0 / 4.0;
}
)", R"(
[entry(frag)]
fn main()
{
let output: vec4[f32] = vec4[f32](48.000000, 12.000000, -42.000000, 0.000000);
)");
}
WHEN("eliminating simple branch")
{
PropagateConstantAndExpect(R"(
[nzsl_version("1.0")]
module;
[entry(frag)]
fn main()
{
if (5 + 3 < 2)
discard;
}
)", R"(
[entry(frag)]
fn main()
{
}
)");
}
WHEN("eliminating multiple branches")
{
PropagateConstantAndExpect(R"(
[nzsl_version("1.0")]
module;
[entry(frag)]
fn main()
{
let output = 0.0;
if (5 <= 3)
output = 5.0;
else if (4 <= 3)
output = 4.0;
else if (3 <= 3)
output = 3.0;
else if (2 <= 3)
output = 2.0;
else if (1 <= 3)
output = 1.0;
else
output = 0.0;
}
)", R"(
[entry(frag)]
fn main()
{
let output: f32 = 0.000000;
output = 3.000000;
}
)");
}
WHEN("eliminating multiple split branches")
{
PropagateConstantAndExpect(R"(
[nzsl_version("1.0")]
module;
[entry(frag)]
fn main()
{
let output = 0.0;
if (5 <= 3)
output = 5.0;
else
{
if (4 <= 3)
output = 4.0;
else
{
if (3 <= 3)
output = 3.0;
else
{
if (2 <= 3)
output = 2.0;
else
{
if (1 <= 3)
output = 1.0;
else
output = 0.0;
}
}
}
}
}
)", R"(
[entry(frag)]
fn main()
{
let output: f32 = 0.000000;
output = 3.000000;
}
)");
}
WHEN("optimizing out scalar swizzle")
{
PropagateConstantAndExpect(R"(
[nzsl_version("1.0")]
module;
[entry(frag)]
fn main()
{
let value = vec3[f32](3.0, 0.0, 1.0).z;
}
)", R"(
[entry(frag)]
fn main()
{
let value: f32 = 1.000000;
}
)");
}
WHEN("optimizing out scalar swizzle to vector")
{
PropagateConstantAndExpect(R"(
[nzsl_version("1.0")]
module;
[entry(frag)]
fn main()
{
let value = (42.0).xxxx;
}
)", R"(
[entry(frag)]
fn main()
{
let value: vec4[f32] = vec4[f32](42.000000, 42.000000, 42.000000, 42.000000);
}
)");
}
WHEN("optimizing out vector swizzle")
{
PropagateConstantAndExpect(R"(
[nzsl_version("1.0")]
module;
[entry(frag)]
fn main()
{
let value = vec4[f32](3.0, 0.0, 1.0, 2.0).yzwx;
}
)", R"(
[entry(frag)]
fn main()
{
let value: vec4[f32] = vec4[f32](0.000000, 1.000000, 2.000000, 3.000000);
}
)");
}
WHEN("optimizing out vector swizzle with repetition")
{
PropagateConstantAndExpect(R"(
[nzsl_version("1.0")]
module;
[entry(frag)]
fn main()
{
let value = vec4[f32](3.0, 0.0, 1.0, 2.0).zzxx;
}
)", R"(
[entry(frag)]
fn main()
{
let value: vec4[f32] = vec4[f32](1.000000, 1.000000, 3.000000, 3.000000);
}
)");
}
WHEN("optimizing out complex swizzle")
{
PropagateConstantAndExpect(R"(
[nzsl_version("1.0")]
module;
[entry(frag)]
fn main()
{
let value = vec4[f32](0.0, 1.0, 2.0, 3.0).xyz.yz.y.x.xxxx;
}
)", R"(
[entry(frag)]
fn main()
{
let value: vec4[f32] = vec4[f32](2.000000, 2.000000, 2.000000, 2.000000);
}
)");
}
WHEN("optimizing out complex swizzle on unknown value")
{
PropagateConstantAndExpect(R"(
[nzsl_version("1.0")]
module;
struct inputStruct
{
value: vec4[f32]
}
external
{
[set(0), binding(0)] data: uniform[inputStruct]
}
[entry(frag)]
fn main()
{
let value = data.value.xyz.yz.y.x.xxxx;
}
)", R"(
[entry(frag)]
fn main()
{
let value: vec4[f32] = data.value.zzzz;
}
)");
}
WHEN("eliminating unused code")
{
EliminateUnusedAndExpect(R"(
[nzsl_version("1.0")]
module;
struct inputStruct
{
value: vec4[f32]
}
struct notUsed
{
value: vec4[f32]
}
external
{
[set(0), binding(0)] unusedData: uniform[notUsed],
[set(0), binding(1)] data: uniform[inputStruct]
}
fn unusedFunction() -> vec4[f32]
{
return unusedData.value;
}
struct Output
{
value: vec4[f32]
}
[entry(frag)]
fn main() -> Output
{
let unusedvalue = unusedFunction();
let output: Output;
output.value = data.value;
return output;
})", R"(
[nzsl_version("1.0")]
module;
struct inputStruct
{
value: vec4[f32]
}
external
{
[set(0), binding(1)] data: uniform[inputStruct]
}
struct Output
{
value: vec4[f32]
}
[entry(frag)]
fn main() -> Output
{
let output: Output;
output.value = data.value;
return output;
})");
}
}

View File

@@ -1,309 +0,0 @@
#include <Engine/Shader/ShaderUtils.hpp>
#include <Nazara/Core/File.hpp>
#include <Nazara/Core/StringExt.hpp>
#include <Nazara/Shader/ShaderBuilder.hpp>
#include <Nazara/Shader/ShaderLangParser.hpp>
#include <Nazara/Shader/Ast/SanitizeVisitor.hpp>
#include <catch2/catch.hpp>
#include <cctype>
TEST_CASE("sanitizing", "[Shader]")
{
WHEN("splitting branches")
{
std::string_view nzslSource = R"(
[nzsl_version("1.0")]
module;
struct inputStruct
{
value: f32
}
external
{
[set(0), binding(0)] data: uniform[inputStruct]
}
[entry(frag)]
fn main()
{
let value: f32;
if (data.value > 3.0)
value = 3.0;
else if (data.value > 2.0)
value = 2.0;
else if (data.value > 1.0)
value = 1.0;
else
value = 0.0;
}
)";
Nz::ShaderAst::ModulePtr shaderModule = Nz::ShaderLang::Parse(nzslSource);
Nz::ShaderAst::SanitizeVisitor::Options options;
options.splitMultipleBranches = true;
REQUIRE_NOTHROW(shaderModule = Nz::ShaderAst::Sanitize(*shaderModule, options));
ExpectNZSL(*shaderModule, R"(
[entry(frag)]
fn main()
{
let value: f32;
if (data.value > (3.000000))
{
value = 3.000000;
}
else
{
if (data.value > (2.000000))
{
value = 2.000000;
}
else
{
if (data.value > (1.000000))
{
value = 1.000000;
}
else
{
value = 0.000000;
}
}
}
}
)");
}
WHEN("reducing for-each to while")
{
std::string_view nzslSource = R"(
[nzsl_version("1.0")]
module;
struct inputStruct
{
value: array[f32, 10]
}
external
{
[set(0), binding(0)] data: uniform[inputStruct]
}
[entry(frag)]
fn main()
{
let x = 0.0;
for v in data.value
{
x += v;
}
}
)";
Nz::ShaderAst::ModulePtr shaderModule = Nz::ShaderLang::Parse(nzslSource);
Nz::ShaderAst::SanitizeVisitor::Options options;
options.reduceLoopsToWhile = true;
REQUIRE_NOTHROW(shaderModule = Nz::ShaderAst::Sanitize(*shaderModule, options));
ExpectNZSL(*shaderModule, R"(
[entry(frag)]
fn main()
{
let x: f32 = 0.000000;
let i: u32 = 0;
while (i < (10))
{
let v: f32 = data.value[i];
x += v;
i += 1;
}
}
)");
}
WHEN("removing matrix casts")
{
std::string_view nzslSource = R"(
[nzsl_version("1.0")]
module;
fn testMat2ToMat2(input: mat2[f32]) -> mat2[f32]
{
return mat2[f32](input);
}
fn testMat2ToMat3(input: mat2[f32]) -> mat3[f32]
{
return mat3[f32](input);
}
fn testMat2ToMat4(input: mat2[f32]) -> mat4[f32]
{
return mat4[f32](input);
}
fn testMat3ToMat2(input: mat3[f32]) -> mat2[f32]
{
return mat2[f32](input);
}
fn testMat3ToMat3(input: mat3[f32]) -> mat3[f32]
{
return mat3[f32](input);
}
fn testMat3ToMat4(input: mat3[f32]) -> mat4[f32]
{
return mat4[f32](input);
}
fn testMat4ToMat2(input: mat4[f32]) -> mat2[f32]
{
return mat2[f32](input);
}
fn testMat4ToMat3(input: mat4[f32]) -> mat3[f32]
{
return mat3[f32](input);
}
fn testMat4ToMat4(input: mat4[f32]) -> mat4[f32]
{
return mat4[f32](input);
}
)";
Nz::ShaderAst::ModulePtr shaderModule = Nz::ShaderLang::Parse(nzslSource);
Nz::ShaderAst::SanitizeVisitor::Options options;
options.removeMatrixCast = true;
REQUIRE_NOTHROW(shaderModule = Nz::ShaderAst::Sanitize(*shaderModule, options));
ExpectNZSL(*shaderModule, R"(
fn testMat2ToMat2(input: mat2[f32]) -> mat2[f32]
{
return input;
}
fn testMat2ToMat3(input: mat2[f32]) -> mat3[f32]
{
let temp: mat3[f32];
temp[0] = vec3[f32](input[0], 0.000000);
temp[1] = vec3[f32](input[1], 0.000000);
temp[2] = vec3[f32](input[2], 1.000000);
return temp;
}
fn testMat2ToMat4(input: mat2[f32]) -> mat4[f32]
{
let temp: mat4[f32];
temp[0] = vec4[f32](input[0], 0.000000, 0.000000);
temp[1] = vec4[f32](input[1], 0.000000, 0.000000);
temp[2] = vec4[f32](input[2], 1.000000, 0.000000);
temp[3] = vec4[f32](input[3], 0.000000, 1.000000);
return temp;
}
fn testMat3ToMat2(input: mat3[f32]) -> mat2[f32]
{
let temp: mat2[f32];
temp[0] = input[0].xy;
temp[1] = input[1].xy;
return temp;
}
fn testMat3ToMat3(input: mat3[f32]) -> mat3[f32]
{
return input;
}
fn testMat3ToMat4(input: mat3[f32]) -> mat4[f32]
{
let temp: mat4[f32];
temp[0] = vec4[f32](input[0], 0.000000);
temp[1] = vec4[f32](input[1], 0.000000);
temp[2] = vec4[f32](input[2], 0.000000);
temp[3] = vec4[f32](input[3], 1.000000);
return temp;
}
fn testMat4ToMat2(input: mat4[f32]) -> mat2[f32]
{
let temp: mat2[f32];
temp[0] = input[0].xy;
temp[1] = input[1].xy;
return temp;
}
fn testMat4ToMat3(input: mat4[f32]) -> mat3[f32]
{
let temp: mat3[f32];
temp[0] = input[0].xyz;
temp[1] = input[1].xyz;
temp[2] = input[2].xyz;
return temp;
}
fn testMat4ToMat4(input: mat4[f32]) -> mat4[f32]
{
return input;
}
)");
}
WHEN("removing aliases")
{
std::string_view nzslSource = R"(
[nzsl_version("1.0")]
module;
struct inputStruct
{
value: f32
}
alias Input = inputStruct;
alias In = Input;
external
{
[set(0), binding(0)] data: uniform[In]
}
)";
Nz::ShaderAst::ModulePtr shaderModule = Nz::ShaderLang::Parse(nzslSource);
Nz::ShaderAst::SanitizeVisitor::Options options;
options.removeAliases = true;
REQUIRE_NOTHROW(shaderModule = Nz::ShaderAst::Sanitize(*shaderModule, options));
ExpectNZSL(*shaderModule, R"(
struct inputStruct
{
value: f32
}
external
{
[set(0), binding(0)] data: uniform[inputStruct]
}
)");
}
}

View File

@@ -1,201 +0,0 @@
#include <Engine/Shader/ShaderUtils.hpp>
#include <Nazara/Core/File.hpp>
#include <Nazara/Core/StringExt.hpp>
#include <Nazara/Shader/ShaderBuilder.hpp>
#include <Nazara/Shader/ShaderLangParser.hpp>
#include <Nazara/Shader/Ast/AstSerializer.hpp>
#include <Nazara/Shader/Ast/AstCompare.hpp>
#include <Nazara/Shader/Ast/SanitizeVisitor.hpp>
#include <catch2/catch.hpp>
#include <cctype>
void ParseSerializeUnserialize(std::string_view sourceCode, bool sanitize)
{
Nz::ShaderAst::ModulePtr shaderModule;
REQUIRE_NOTHROW(shaderModule = Nz::ShaderLang::Parse(sourceCode));
if (sanitize)
REQUIRE_NOTHROW(shaderModule = Nz::ShaderAst::Sanitize(*shaderModule));
Nz::ByteArray serializedModule;
REQUIRE_NOTHROW(serializedModule = Nz::ShaderAst::SerializeShader(shaderModule));
Nz::ByteStream byteStream(&serializedModule);
Nz::ShaderAst::ModulePtr unserializedShader;
REQUIRE_NOTHROW(unserializedShader = Nz::ShaderAst::UnserializeShader(byteStream));
CHECK(Nz::ShaderAst::Compare(*shaderModule, *unserializedShader));
}
void ParseSerializeUnserialize(std::string_view sourceCode)
{
ParseSerializeUnserialize(sourceCode, false);
ParseSerializeUnserialize(sourceCode, true);
}
TEST_CASE("serialization", "[Shader]")
{
WHEN("serializing and unserializing a simple shader")
{
ParseSerializeUnserialize(R"(
[nzsl_version("1.0")]
module;
struct Data
{
value: f32
}
struct Output
{
[location(0)] value: f32
}
external
{
[set(0), binding(0)] data: uniform[Data]
}
[entry(frag)]
fn main() -> Output
{
let output: Output;
output.value = data.value;
return output;
}
)");
}
WHEN("serializing and unserializing branches")
{
ParseSerializeUnserialize(R"(
[nzsl_version("1.0")]
module;
struct inputStruct
{
value: f32
}
external
{
[set(0), binding(0)] data: uniform[inputStruct]
}
[entry(frag)]
fn main()
{
let value: f32;
if (data.value > 3.0)
value = 3.0;
else if (data.value > 2.0)
value = 2.0;
else if (data.value > 1.0)
value = 1.0;
else
value = 0.0;
}
)");
}
WHEN("serializing and unserializing consts")
{
ParseSerializeUnserialize(R"(
[nzsl_version("1.0")]
module;
option UseInt: bool = false;
[cond(UseInt)]
struct inputStruct
{
value: i32
}
[cond(!UseInt)]
struct inputStruct
{
value: f32
}
external
{
[set(0), binding(0)] data: uniform[inputStruct]
}
[entry(frag)]
fn main()
{
let value: f32;
const if (UseInt)
{
value = f32(data.value);
}
else
{
value = data.value;
}
}
)");
}
WHEN("serializing and unserializing loops")
{
ParseSerializeUnserialize(R"(
[nzsl_version("1.0")]
module;
struct inputStruct
{
value: array[f32, 10]
}
external
{
[set(0), binding(0)] data: uniform[inputStruct]
}
[entry(frag)]
fn main()
{
let value = 0.0;
let i = 0;
while (i < 10)
{
value += 0.1;
i += 1;
}
let x = 0;
for v in 0 -> 10
{
x += v;
}
let x = 0.0;
for v in data.value
{
x += v;
}
}
)");
}
WHEN("serializing and unserializing swizzles")
{
ParseSerializeUnserialize(R"(
[nzsl_version("1.0")]
module;
[entry(frag)]
fn main()
{
let vec = vec4[f32](0.0, 1.0, 2.0, 3.0);
vec.wyxz.bra.ts.x = 0.0;
vec.zyxw.ar.xy.yx = vec2[f32](1.0, 0.0);
}
)");
}
}

View File

@@ -1,302 +0,0 @@
#include <Engine/Shader/ShaderUtils.hpp>
#include <Nazara/Core/StringExt.hpp>
#include <Nazara/Shader/GlslWriter.hpp>
#include <Nazara/Shader/LangWriter.hpp>
#include <Nazara/Shader/ShaderLangParser.hpp>
#include <Nazara/Shader/SpirvPrinter.hpp>
#include <Nazara/Shader/SpirvWriter.hpp>
#include <Nazara/Shader/Ast/AstReflect.hpp>
#include <Nazara/Shader/Ast/SanitizeVisitor.hpp>
#include <catch2/catch.hpp>
#include <glslang/Public/ShaderLang.h>
#include <spirv-tools/libspirv.hpp>
namespace
{
// Use OpenGL default minimal values (from https://www.khronos.org/registry/OpenGL-Refpages/es3.0/html/glGet.xhtml, https://www.khronos.org/registry/OpenGL-Refpages/gl4/html/glGet.xhtml and https://www.khronos.org/registry/OpenGL-Refpages/gl2.1/)
const TBuiltInResource s_minResources = {
8, //< maxLights
6, //< maxClipPlanes
32, //< maxTextureUnits
32, //< maxTextureCoords
16, //< maxVertexAttribs
1024, //< maxVertexUniformComponents
60, //< maxVaryingFloats
16, //< maxVertexTextureImageUnits
32, //< maxCombinedTextureImageUnits
16, //< maxTextureImageUnits
896, //< maxFragmentUniformComponents
4, //< maxDrawBuffers
256, //< maxVertexUniformVectors
15, //< maxVaryingVectors
224, //< maxFragmentUniformVectors
256, //< maxVertexOutputVectors
224, //< maxFragmentInputVectors
-8, //< minProgramTexelOffset
7, //< maxProgramTexelOffset
8, //< maxClipDistances
0xFFFF, //< maxComputeWorkGroupCountX
0xFFFF, //< maxComputeWorkGroupCountY
0xFFFF, //< maxComputeWorkGroupCountZ
1024, //< maxComputeWorkGroupSizeX
1024, //< maxComputeWorkGroupSizeY
64, //< maxComputeWorkGroupSizeZ
1024, //< maxComputeUniformComponents
16, //< maxComputeTextureImageUnits
8, //< maxComputeImageUniforms
8, //< maxComputeAtomicCounters
1, //< maxComputeAtomicCounterBuffers
60, //< maxVaryingComponents
64, //< maxVertexOutputComponents
64, //< maxGeometryInputComponents
128, //< maxGeometryOutputComponents
128, //< maxFragmentInputComponents
8, //< maxImageUnits
8, //< maxCombinedImageUnitsAndFragmentOutputs
8, //< maxCombinedShaderOutputResources
0, //< maxImageSamples
0, //< maxVertexImageUniforms
0, //< maxTessControlImageUniforms
0, //< maxTessEvaluationImageUniforms
0, //< maxGeometryImageUniforms
8, //< maxFragmentImageUniforms
8, //< maxCombinedImageUniforms
16, //< maxGeometryTextureImageUnits
256, //< maxGeometryOutputVertices
1024, //< maxGeometryTotalOutputComponents
1024, //< maxGeometryUniformComponents
64, //< maxGeometryVaryingComponents
128, //< maxTessControlInputComponents
128, //< maxTessControlOutputComponents
16, //< maxTessControlTextureImageUnits
1024, //< maxTessControlUniformComponents
4096, //< maxTessControlTotalOutputComponents
128, //< maxTessEvaluationInputComponents
128, //< maxTessEvaluationOutputComponents
16, //< maxTessEvaluationTextureImageUnits
1024, //< maxTessEvaluationUniformComponents
120, //< maxTessPatchComponents
32, //< maxPatchVertices
64, //< maxTessGenLevel
16, //< maxViewports
0, //< maxVertexAtomicCounters
0, //< maxTessControlAtomicCounters
0, //< maxTessEvaluationAtomicCounters
0, //< maxGeometryAtomicCounters
8, //< maxFragmentAtomicCounters
8, //< maxCombinedAtomicCounters
1, //< maxAtomicCounterBindings
0, //< maxVertexAtomicCounterBuffers
0, //< maxTessControlAtomicCounterBuffers
0, //< maxTessEvaluationAtomicCounterBuffers
0, //< maxGeometryAtomicCounterBuffers
1, //< maxFragmentAtomicCounterBuffers
1, //< maxCombinedAtomicCounterBuffers
16384, //< maxAtomicCounterBufferSize
4, //< maxTransformFeedbackBuffers
64, //< maxTransformFeedbackInterleavedComponents
8, //< maxCullDistances
8, //< maxCombinedClipAndCullDistances
4, //< maxSamples
256, //< maxMeshOutputVerticesNV
512, //< maxMeshOutputPrimitivesNV
32, //< maxMeshWorkGroupSizeX_NV
1, //< maxMeshWorkGroupSizeY_NV
1, //< maxMeshWorkGroupSizeZ_NV
32, //< maxTaskWorkGroupSizeX_NV
1, //< maxTaskWorkGroupSizeY_NV
1, //< maxTaskWorkGroupSizeZ_NV
4, //< maxMeshViewCountNV
1, //< maxDualSourceDrawBuffersEXT
{ //< limits
true, //< nonInductiveForLoops
true, //< whileLoops
true, //< doWhileLoops
true, //< generalUniformIndexing
true, //< generalAttributeMatrixVectorIndexing
true, //< generalVaryingIndexing
true, //< generalSamplerIndexing
true, //< generalVariableIndexing
true, //< generalConstantMatrixVectorIndexing
}
};
}
void ExpectGLSL(const Nz::ShaderAst::Module& shaderModule, std::string_view expectedOutput)
{
expectedOutput = Nz::Trim(expectedOutput);
SECTION("Generating GLSL")
{
Nz::ShaderAst::ModulePtr sanitizedModule;
WHEN("Sanitizing a second time")
{
CHECK_NOTHROW(sanitizedModule = Nz::ShaderAst::Sanitize(shaderModule));
}
const Nz::ShaderAst::Module& targetModule = (sanitizedModule) ? *sanitizedModule : shaderModule;
// Retrieve entry-point to get shader type
std::optional<Nz::ShaderStageType> entryShaderStage;
Nz::ShaderAst::AstReflect::Callbacks callbacks;
callbacks.onEntryPointDeclaration = [&](Nz::ShaderStageType stageType, const std::string& functionName)
{
INFO("multiple entry points found! (" << functionName << ")");
REQUIRE((!entryShaderStage.has_value() || stageType == entryShaderStage));
entryShaderStage = stageType;
};
Nz::ShaderAst::AstReflect reflectVisitor;
reflectVisitor.Reflect(*targetModule.rootNode, callbacks);
{
INFO("no entry point found");
REQUIRE(entryShaderStage.has_value());
}
Nz::GlslWriter writer;
std::string output = writer.Generate(entryShaderStage, targetModule);
WHEN("Validating expected code")
{
INFO("full GLSL output:\n" << output << "\nexcepted output:\n" << expectedOutput);
REQUIRE(output.find(expectedOutput) != std::string::npos);
}
WHEN("Validating full GLSL code (using glslang)")
{
EShLanguage stage = EShLangVertex;
switch (*entryShaderStage)
{
case Nz::ShaderStageType::Fragment:
stage = EShLangFragment;
break;
case Nz::ShaderStageType::Vertex:
stage = EShLangVertex;
break;
}
glslang::TShader glslangShader(stage);
glslangShader.setEnvInput(glslang::EShSourceGlsl, stage, glslang::EShClientOpenGL, 300);
glslangShader.setEnvClient(glslang::EShClientOpenGL, glslang::EShTargetOpenGL_450);
glslangShader.setEnvTarget(glslang::EShTargetNone, static_cast<glslang::EShTargetLanguageVersion>(0));
glslangShader.setEntryPoint("main");
const char* source = output.c_str();
glslangShader.setStrings(&source, 1);
if (!glslangShader.parse(&s_minResources, 100, false, static_cast<EShMessages>(EShMsgDefault | EShMsgKeepUncalled)))
{
INFO("full GLSL output:\n" << output << "\nerror:\n" << glslangShader.getInfoLog());
REQUIRE(false);
}
}
}
}
void ExpectNZSL(const Nz::ShaderAst::Module& shaderModule, std::string_view expectedOutput)
{
expectedOutput = Nz::Trim(expectedOutput);
SECTION("Generating NZSL")
{
Nz::ShaderAst::ModulePtr sanitizedModule;
WHEN("Sanitizing a second time")
{
CHECK_NOTHROW(sanitizedModule = Nz::ShaderAst::Sanitize(shaderModule));
}
const Nz::ShaderAst::Module& targetModule = (sanitizedModule) ? *sanitizedModule : shaderModule;
Nz::LangWriter writer;
std::string output = writer.Generate(targetModule);
WHEN("Validating expected code")
{
INFO("full NZSL output:\n" << output << "\nexcepted output:\n" << expectedOutput);
REQUIRE(output.find(expectedOutput) != std::string::npos);
}
WHEN("Validating full NZSL code (by recompiling it)")
{
// validate NZSL by recompiling it
REQUIRE_NOTHROW(Nz::ShaderLang::Parse(output));
}
}
}
void ExpectSPIRV(const Nz::ShaderAst::Module& shaderModule, std::string_view expectedOutput, bool outputParameter)
{
expectedOutput = Nz::Trim(expectedOutput);
SECTION("Generating SPIRV")
{
Nz::ShaderAst::ModulePtr sanitizedModule;
WHEN("Sanitizing a second time")
{
CHECK_NOTHROW(sanitizedModule = Nz::ShaderAst::Sanitize(shaderModule));
}
const Nz::ShaderAst::Module& targetModule = (sanitizedModule) ? *sanitizedModule : shaderModule;
Nz::SpirvWriter writer;
Nz::SpirvPrinter printer;
Nz::SpirvPrinter::Settings settings;
settings.printHeader = false;
settings.printParameters = outputParameter;
auto spirv = writer.Generate(targetModule);
std::string output = printer.Print(spirv.data(), spirv.size(), settings);
WHEN("Validating expected code")
{
INFO("full SPIRV output:\n" << output << "\nexcepted output:\n" << expectedOutput);
REQUIRE(output.find(expectedOutput) != std::string::npos);
}
WHEN("Validating full SPIRV code (using libspirv)")
{
// validate SPIRV with libspirv
spvtools::SpirvTools spirvTools(spv_target_env::SPV_ENV_VULKAN_1_0);
spirvTools.SetMessageConsumer([&](spv_message_level_t /*level*/, const char* /*source*/, const spv_position_t& /*position*/, const char* message)
{
std::string fullSpirv;
if (!spirvTools.Disassemble(spirv, &fullSpirv))
fullSpirv = "<failed to disassemble SPIRV>";
UNSCOPED_INFO(fullSpirv + "\n" + message);
});
REQUIRE(spirvTools.Validate(spirv));
}
}
}
Nz::ShaderAst::ModulePtr SanitizeModule(const Nz::ShaderAst::Module& module)
{
Nz::ShaderAst::SanitizeVisitor::Options defaultOptions;
return SanitizeModule(module, defaultOptions);
}
Nz::ShaderAst::ModulePtr SanitizeModule(const Nz::ShaderAst::Module& module, const Nz::ShaderAst::SanitizeVisitor::Options& options)
{
Nz::ShaderAst::ModulePtr shaderModule;
WHEN("We sanitize the shader")
{
REQUIRE_NOTHROW(shaderModule = Nz::ShaderAst::Sanitize(module, options));
}
WHEN("We output NZSL and try to parse it again")
{
Nz::LangWriter langWriter;
std::string outputCode = langWriter.Generate((shaderModule) ? *shaderModule : module);
REQUIRE_NOTHROW(shaderModule = Nz::ShaderAst::Sanitize(*Nz::ShaderLang::Parse(outputCode), options));
}
// Ensure sanitization
if (!shaderModule)
REQUIRE_NOTHROW(shaderModule = Nz::ShaderAst::Sanitize(module, options));
return shaderModule;
}

View File

@@ -1,17 +0,0 @@
#pragma once
#ifndef NAZARA_UNITTESTS_SHADER_SHADERUTILS_HPP
#define NAZARA_UNITTESTS_SHADER_SHADERUTILS_HPP
#include <Nazara/Shader/Ast/Module.hpp>
#include <Nazara/Shader/Ast/SanitizeVisitor.hpp>
#include <string>
void ExpectGLSL(const Nz::ShaderAst::Module& shader, std::string_view expectedOutput);
void ExpectNZSL(const Nz::ShaderAst::Module& shader, std::string_view expectedOutput);
void ExpectSPIRV(const Nz::ShaderAst::Module& shader, std::string_view expectedOutput, bool outputParameter = false);
Nz::ShaderAst::ModulePtr SanitizeModule(const Nz::ShaderAst::Module& module);
Nz::ShaderAst::ModulePtr SanitizeModule(const Nz::ShaderAst::Module& module, const Nz::ShaderAst::SanitizeVisitor::Options& options);
#endif

View File

@@ -1,317 +0,0 @@
#include <Engine/Shader/ShaderUtils.hpp>
#include <Nazara/Core/File.hpp>
#include <Nazara/Core/StringExt.hpp>
#include <Nazara/Shader/ShaderBuilder.hpp>
#include <Nazara/Shader/ShaderLangParser.hpp>
#include <catch2/catch.hpp>
#include <cctype>
TEST_CASE("swizzle", "[Shader]")
{
SECTION("Simple swizzle")
{
WHEN("reading")
{
std::string_view nzslSource = R"(
[nzsl_version("1.0")]
module;
[entry(frag)]
fn main()
{
let vec = vec4[f32](0.0, 1.0, 2.0, 3.0);
let value = vec.xyz;
}
)";
Nz::ShaderAst::ModulePtr shaderModule = Nz::ShaderLang::Parse(nzslSource);
shaderModule = SanitizeModule(*shaderModule);
ExpectGLSL(*shaderModule, R"(
void main()
{
vec4 vec = vec4(0.000000, 1.000000, 2.000000, 3.000000);
vec3 value = vec.xyz;
}
)");
ExpectNZSL(*shaderModule, R"(
[entry(frag)]
fn main()
{
let vec: vec4[f32] = vec4[f32](0.000000, 1.000000, 2.000000, 3.000000);
let value: vec3[f32] = vec.xyz;
}
)");
ExpectSPIRV(*shaderModule, R"(
OpFunction
OpLabel
OpVariable
OpVariable
OpCompositeConstruct
OpStore
OpLoad
OpVectorShuffle
OpStore
OpReturn
OpFunctionEnd)");
}
WHEN("writing")
{
std::string_view nzslSource = R"(
[nzsl_version("1.0")]
module;
[entry(frag)]
fn main()
{
let vec = vec4[f32](0.0, 0.0, 0.0, 0.0);
vec.yzw = vec3[f32](1.0, 2.0, 3.0);
}
)";
Nz::ShaderAst::ModulePtr shaderModule = Nz::ShaderLang::Parse(nzslSource);
shaderModule = SanitizeModule(*shaderModule);
ExpectGLSL(*shaderModule, R"(
void main()
{
vec4 vec = vec4(0.000000, 0.000000, 0.000000, 0.000000);
vec.yzw = vec3(1.000000, 2.000000, 3.000000);
}
)");
ExpectNZSL(*shaderModule, R"(
[entry(frag)]
fn main()
{
let vec: vec4[f32] = vec4[f32](0.000000, 0.000000, 0.000000, 0.000000);
vec.yzw = vec3[f32](1.000000, 2.000000, 3.000000);
}
)");
ExpectSPIRV(*shaderModule, R"(
OpFunction
OpLabel
OpVariable
OpCompositeConstruct
OpStore
OpCompositeConstruct
OpLoad
OpVectorShuffle
OpStore
OpReturn
OpFunctionEnd)");
}
}
SECTION("Scalar swizzle")
{
GIVEN("a variable")
{
std::string_view nzslSource = R"(
[nzsl_version("1.0")]
module;
[entry(frag)]
fn main()
{
let value = 42.0;
let vec = value.xxx;
}
)";
Nz::ShaderAst::ModulePtr shaderModule = Nz::ShaderLang::Parse(nzslSource);
shaderModule = SanitizeModule(*shaderModule);
ExpectGLSL(*shaderModule, R"(
void main()
{
float value = 42.000000;
vec3 vec = vec3(value, value, value);
}
)");
ExpectNZSL(*shaderModule, R"(
[entry(frag)]
fn main()
{
let value: f32 = 42.000000;
let vec: vec3[f32] = value.xxx;
}
)");
ExpectSPIRV(*shaderModule, R"(
OpFunction
OpLabel
OpVariable
OpVariable
OpStore
OpLoad
OpCompositeConstruct
OpStore
OpReturn
OpFunctionEnd)");
}
GIVEN("a function value")
{
std::string_view nzslSource = R"(
[nzsl_version("1.0")]
module;
[entry(frag)]
fn main()
{
let v = max(2.0, 1.0).xxx;
let v2 = min(2.0, 1.0).xxx;
}
)";
Nz::ShaderAst::ModulePtr shaderModule = Nz::ShaderLang::Parse(nzslSource);
shaderModule = SanitizeModule(*shaderModule);
ExpectGLSL(*shaderModule, R"(
void main()
{
float cachedResult = max(2.000000, 1.000000);
vec3 v = vec3(cachedResult, cachedResult, cachedResult);
float cachedResult_2 = min(2.000000, 1.000000);
vec3 v2 = vec3(cachedResult_2, cachedResult_2, cachedResult_2);
}
)");
ExpectNZSL(*shaderModule, R"(
[entry(frag)]
fn main()
{
let v: vec3[f32] = (max(2.000000, 1.000000)).xxx;
let v2: vec3[f32] = (min(2.000000, 1.000000)).xxx;
}
)");
ExpectSPIRV(*shaderModule, R"(
OpFunction
OpLabel
OpVariable
OpVariable
OpExtInst
OpCompositeConstruct
OpStore
OpExtInst
OpCompositeConstruct
OpStore
OpReturn
OpFunctionEnd)");
}
}
SECTION("Complex swizzle")
{
WHEN("reading")
{
std::string_view nzslSource = R"(
[nzsl_version("1.0")]
module;
[entry(frag)]
fn main()
{
let vec = vec4[f32](0.0, 1.0, 2.0, 3.0);
let value = vec.xyz.yz.y.x.xxxx;
}
)";
Nz::ShaderAst::ModulePtr shaderModule = Nz::ShaderLang::Parse(nzslSource);
shaderModule = SanitizeModule(*shaderModule);
ExpectGLSL(*shaderModule, R"(
void main()
{
vec4 vec = vec4(0.000000, 1.000000, 2.000000, 3.000000);
vec4 value = vec4(vec.xyz.yz.y, vec.xyz.yz.y, vec.xyz.yz.y, vec.xyz.yz.y);
}
)");
ExpectNZSL(*shaderModule, R"(
[entry(frag)]
fn main()
{
let vec: vec4[f32] = vec4[f32](0.000000, 1.000000, 2.000000, 3.000000);
let value: vec4[f32] = vec.xyz.yz.y.x.xxxx;
}
)");
ExpectSPIRV(*shaderModule, R"(
OpFunction
OpLabel
OpVariable
OpVariable
OpCompositeConstruct
OpStore
OpLoad
OpVectorShuffle
OpVectorShuffle
OpCompositeExtract
OpCompositeConstruct
OpStore
OpReturn
OpFunctionEnd)");
}
WHEN("writing")
{
std::string_view nzslSource = R"(
[nzsl_version("1.0")]
module;
[entry(frag)]
fn main()
{
let vec = vec4[f32](0.0, 1.0, 2.0, 3.0);
vec.wyxz.bra.ts.x = 0.0;
vec.zyxw.ar.xy.yx = vec2[f32](1.0, 0.0);
}
)";
Nz::ShaderAst::ModulePtr shaderModule = Nz::ShaderLang::Parse(nzslSource);
shaderModule = SanitizeModule(*shaderModule);
ExpectGLSL(*shaderModule, R"(
void main()
{
vec4 vec = vec4(0.000000, 1.000000, 2.000000, 3.000000);
vec.wyxz.zxw.yx.x = 0.000000;
vec.zyxw.wx.xy.yx = vec2(1.000000, 0.000000);
}
)");
ExpectNZSL(*shaderModule, R"(
[entry(frag)]
fn main()
{
let vec: vec4[f32] = vec4[f32](0.000000, 1.000000, 2.000000, 3.000000);
vec.wyxz.zxw.yx.x = 0.000000;
vec.zyxw.wx.xy.yx = vec2[f32](1.000000, 0.000000);
}
)");
ExpectSPIRV(*shaderModule, R"(
OpFunction
OpLabel
OpVariable
OpCompositeConstruct
OpStore
OpAccessChain
OpStore
OpCompositeConstruct
OpLoad
OpVectorShuffle
OpStore
OpReturn
OpFunctionEnd)");
}
}
}

View File

@@ -7,20 +7,11 @@
#include <Nazara/Core/Modules.hpp>
#include <Nazara/Network/Network.hpp>
#include <Nazara/Physics2D/Physics2D.hpp>
#include <Nazara/Shader/Shader.hpp>
#include <Nazara/Utility/Utility.hpp>
#include <glslang/Public/ShaderLang.h>
int main(int argc, char* argv[])
{
Nz::Modules<Nz::Audio, Nz::Network, Nz::Physics2D, Nz::Shader, Nz::Utility> nazaza;
Nz::Modules<Nz::Audio, Nz::Network, Nz::Physics2D, Nz::Utility> nazaza;
if (!glslang::InitializeProcess())
return EXIT_FAILURE;
int result = Catch::Session().run(argc, argv);
glslang::FinalizeProcess();
return result;
return Catch::Session().run(argc, argv);
}

View File

@@ -11,14 +11,14 @@ if has_config("tests") then
add_defines("CATCH_CONFIG_NO_POSIX_SIGNALS")
end
add_requires("catch2", "glslang", "spirv-tools")
add_requires("catch2")
-- Common config
set_group("Tests")
set_kind("binary")
add_deps("NazaraAudio", "NazaraCore", "NazaraNetwork", "NazaraPhysics2D", "NazaraShader")
add_packages("catch2", "entt", "glslang", "spirv-tools")
add_deps("NazaraAudio", "NazaraCore", "NazaraNetwork", "NazaraPhysics2D")
add_packages("catch2", "entt")
add_headerfiles("Engine/**.hpp")
add_files("resources.cpp")
add_files("Engine/**.cpp")