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:
@@ -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";
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -4,5 +4,4 @@
|
||||
#include <Nazara/Network.hpp>
|
||||
#include <Nazara/Physics2D.hpp>
|
||||
#include <Nazara/Physics3D.hpp>
|
||||
#include <Nazara/Shader.hpp>
|
||||
#include <Nazara/Utility.hpp>
|
||||
|
||||
@@ -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)");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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)");
|
||||
}
|
||||
}
|
||||
@@ -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)");
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
)");
|
||||
}
|
||||
}
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
@@ -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)");
|
||||
}
|
||||
}
|
||||
@@ -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)");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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)");
|
||||
}
|
||||
}
|
||||
@@ -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)");
|
||||
}
|
||||
}
|
||||
@@ -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)");
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
})");
|
||||
}
|
||||
}
|
||||
@@ -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]
|
||||
}
|
||||
)");
|
||||
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
)");
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
@@ -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)");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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")
|
||||
|
||||
Reference in New Issue
Block a user