aboutsummaryrefslogtreecommitdiff
path: root/NvBlast/test/src/unit/AssetTests.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'NvBlast/test/src/unit/AssetTests.cpp')
-rw-r--r--NvBlast/test/src/unit/AssetTests.cpp529
1 files changed, 529 insertions, 0 deletions
diff --git a/NvBlast/test/src/unit/AssetTests.cpp b/NvBlast/test/src/unit/AssetTests.cpp
new file mode 100644
index 0000000..24e5f77
--- /dev/null
+++ b/NvBlast/test/src/unit/AssetTests.cpp
@@ -0,0 +1,529 @@
+#include "NvBlastAsset.h"
+
+#include "BlastBaseTest.h"
+
+#include "NvBlastTkFramework.h"
+
+#include <algorithm>
+
+
+#if defined(_MSC_VER) && _MSC_VER < 1900 || defined(_XBOX_ONE) || defined(PS4) || PX_LINUX
+#define ENABLE_SERIALIZATION_TESTS 0
+#else
+#define ENABLE_SERIALIZATION_TESTS 1
+#endif
+
+#pragma warning( push )
+#pragma warning( disable : 4267 )
+// NOTE: Instead of excluding serialization and the tests when on VC12, should break the tests out into a separate C++ file.
+
+#if ENABLE_SERIALIZATION_TESTS
+#include "NvBlastExtSerializationInterface.h"
+
+#include "generated/NvBlastExtSerialization.capn.h"
+#endif
+
+#pragma warning( pop )
+
+#include <fstream>
+#include <iosfwd>
+
+#ifdef WIN32
+#include <windows.h>
+#endif
+
+template<int FailLevel, int Verbosity>
+class AssetTest : public BlastBaseTest<FailLevel, Verbosity>
+{
+public:
+
+ AssetTest()
+ {
+ Nv::Blast::TkFrameworkDesc desc;
+ desc.allocatorCallback = this;
+ desc.errorCallback = this;
+ NvBlastTkFrameworkCreate(desc);
+ }
+
+ ~AssetTest()
+ {
+ NvBlastTkFrameworkGet()->release();
+ }
+
+ static void messageLog(int type, const char* msg, const char* file, int line)
+ {
+ BlastBaseTest<FailLevel, Verbosity>::messageLog(type, msg, file, line);
+ }
+
+ static void* alloc(size_t size)
+ {
+ return BlastBaseTest<FailLevel, Verbosity>::alloc(size);
+ }
+
+ static void free(void* mem)
+ {
+ BlastBaseTest<FailLevel, Verbosity>::free(mem);
+ }
+
+ void testSubtreeLeafChunkCounts(const Nv::Blast::Asset& a)
+ {
+ const NvBlastChunk* chunks = a.getChunks();
+ const uint32_t* subtreeLeafChunkCounts = a.getSubtreeLeafChunkCounts();
+ uint32_t totalLeafChunkCount = 0;
+ for (uint32_t chunkIndex = 0; chunkIndex < a.m_chunkCount; ++chunkIndex)
+ {
+ const NvBlastChunk& chunk = chunks[chunkIndex];
+ if (Nv::Blast::isInvalidIndex(chunk.parentChunkIndex))
+ {
+ totalLeafChunkCount += subtreeLeafChunkCounts[chunkIndex];
+ }
+ const bool isLeafChunk = chunk.firstChildIndex >= chunk.childIndexStop;
+ uint32_t subtreeLeafChunkCount = isLeafChunk ? 1 : 0;
+ for (uint32_t childIndex = chunk.firstChildIndex; childIndex < chunk.childIndexStop; ++childIndex)
+ {
+ subtreeLeafChunkCount += subtreeLeafChunkCounts[childIndex];
+ }
+ EXPECT_EQ(subtreeLeafChunkCount, subtreeLeafChunkCounts[chunkIndex]);
+ }
+ EXPECT_EQ(totalLeafChunkCount, a.m_leafChunkCount);
+ }
+
+ void testChunkToNodeMap(const Nv::Blast::Asset& a)
+ {
+ for (uint32_t chunkIndex = 0; chunkIndex < a.m_chunkCount; ++chunkIndex)
+ {
+ const uint32_t nodeIndex = a.getChunkToGraphNodeMap()[chunkIndex];
+ if (!Nv::Blast::isInvalidIndex(nodeIndex))
+ {
+ EXPECT_LT(nodeIndex, a.m_graph.m_nodeCount);
+ EXPECT_EQ(chunkIndex, a.m_graph.getChunkIndices()[nodeIndex]);
+ }
+ else
+ {
+ const uint32_t* chunkIndexStop = a.m_graph.getChunkIndices() + a.m_graph.m_nodeCount;
+ const uint32_t* it = std::find<const uint32_t*, uint32_t>(a.m_graph.getChunkIndices(), chunkIndexStop, chunkIndex);
+ EXPECT_EQ(chunkIndexStop, it);
+ }
+ }
+ }
+
+ NvBlastAsset* buildAsset(const ExpectedAssetValues& expected, const NvBlastAssetDesc* desc)
+ {
+ std::vector<char> scratch;
+ scratch.resize((size_t)NvBlastGetRequiredScratchForCreateAsset(desc, messageLog));
+ void* mem = alloc(NvBlastGetAssetMemorySize(desc, messageLog));
+ NvBlastAsset* asset = NvBlastCreateAsset(mem, desc, &scratch[0], messageLog);
+ EXPECT_TRUE(asset != nullptr);
+ if (asset == nullptr)
+ {
+ free(mem);
+ return nullptr;
+ }
+ Nv::Blast::Asset& a = *(Nv::Blast::Asset*)asset;
+ EXPECT_EQ(expected.totalChunkCount, a.m_chunkCount);
+ EXPECT_EQ(expected.graphNodeCount, a.m_graph.m_nodeCount);
+ EXPECT_EQ(expected.bondCount, a.m_graph.getAdjacencyPartition()[a.m_graph.m_nodeCount] / 2);
+ EXPECT_EQ(expected.leafChunkCount, a.m_leafChunkCount);
+ EXPECT_EQ(expected.subsupportChunkCount, a.m_chunkCount - a.m_firstSubsupportChunkIndex);
+ testSubtreeLeafChunkCounts(a);
+ testChunkToNodeMap(a);
+ return asset;
+ }
+
+ void checkAssetsExpected(Nv::Blast::Asset& asset, const ExpectedAssetValues& expected)
+ {
+ EXPECT_EQ(expected.totalChunkCount, asset.m_chunkCount);
+ EXPECT_EQ(expected.graphNodeCount, asset.m_graph.m_nodeCount);
+ EXPECT_EQ(expected.bondCount, asset.m_graph.getAdjacencyPartition()[asset.m_graph.m_nodeCount] / 2);
+ EXPECT_EQ(expected.leafChunkCount, asset.m_leafChunkCount);
+ EXPECT_EQ(expected.subsupportChunkCount, asset.m_chunkCount - asset.m_firstSubsupportChunkIndex);
+ testSubtreeLeafChunkCounts(asset);
+ testChunkToNodeMap(asset);
+ }
+
+ void buildAssetShufflingDescriptors(const NvBlastAssetDesc* desc, const ExpectedAssetValues& expected, uint32_t shuffleCount, bool useTk)
+ {
+ NvBlastAssetDesc shuffledDesc = *desc;
+ std::vector<NvBlastChunkDesc> chunkDescs(desc->chunkDescs, desc->chunkDescs + desc->chunkCount);
+ shuffledDesc.chunkDescs = &chunkDescs[0];
+ std::vector<NvBlastBondDesc> bondDescs(desc->bondDescs, desc->bondDescs + desc->bondCount);
+ shuffledDesc.bondDescs = &bondDescs[0];
+ if (!useTk)
+ {
+ std::vector<char> scratch(desc->chunkCount);
+ NvBlastEnsureAssetExactSupportCoverage(chunkDescs.data(), desc->chunkCount, scratch.data(), messageLog);
+ }
+ else
+ {
+ NvBlastTkFrameworkGet()->ensureAssetExactSupportCoverage(chunkDescs.data(), desc->chunkCount);
+ }
+ for (uint32_t i = 0; i < shuffleCount; ++i)
+ {
+ shuffleAndFixChunkDescs(&chunkDescs[0], desc->chunkCount, &bondDescs[0], desc->bondCount, useTk);
+ NvBlastAsset* asset = buildAsset(expected, &shuffledDesc);
+ EXPECT_TRUE(asset != nullptr);
+ if (asset)
+ {
+ free(asset);
+ }
+ }
+ }
+
+ void shuffleAndFixChunkDescs(NvBlastChunkDesc* chunkDescs, uint32_t chunkDescCount, NvBlastBondDesc* bondDescs, uint32_t bondDescCount, bool useTk)
+ {
+ // Create reorder array and fill with identity map
+ std::vector<uint32_t> shuffledOrder(chunkDescCount);
+ for (uint32_t i = 0; i < chunkDescCount; ++i)
+ {
+ shuffledOrder[i] = i;
+ }
+
+ // An array into which to copy the reordered descs
+ std::vector<NvBlastChunkDesc> shuffledChunkDescs(chunkDescCount);
+
+ std::vector<char> scratch;
+ const uint32_t trials = 30;
+ uint32_t attempt = 0;
+ while(1)
+ {
+ // Shuffle the reorder array
+ std::random_shuffle(shuffledOrder.begin(), shuffledOrder.end());
+
+ // Save initial bonds
+ std::vector<NvBlastBondDesc> savedBondDescs(bondDescs, bondDescs + bondDescCount);
+
+ // Shuffle chunks and bonds
+ NvBlastApplyAssetDescChunkReorderMap(shuffledChunkDescs.data(), chunkDescs, chunkDescCount, bondDescs, bondDescCount, shuffledOrder.data(), nullptr);
+
+ // Check the results
+ for (uint32_t i = 0; i < chunkDescCount; ++i)
+ {
+ EXPECT_EQ(chunkDescs[i].userData, shuffledChunkDescs[shuffledOrder[i]].userData);
+ EXPECT_TRUE(chunkDescs[i].parentChunkIndex > chunkDescCount || shuffledChunkDescs[shuffledOrder[i]].parentChunkIndex == shuffledOrder[chunkDescs[i].parentChunkIndex]);
+ }
+ for (uint32_t i = 0; i < bondDescCount; ++i)
+ {
+ for (uint32_t k = 0; k < 2; ++k)
+ {
+ EXPECT_EQ(shuffledOrder[savedBondDescs[i].chunkIndices[k]], bondDescs[i].chunkIndices[k]);
+ }
+ }
+
+ // Try creating asset, usually it should fail (otherwise make another attempt)
+ NvBlastAssetDesc desc = { chunkDescCount, shuffledChunkDescs.data(), bondDescCount, bondDescs };
+ scratch.resize((size_t)NvBlastGetRequiredScratchForCreateAsset(&desc, nullptr));
+ void* mem = alloc(NvBlastGetAssetMemorySize(&desc, nullptr));
+ NvBlastAsset* asset = NvBlastCreateAsset(mem, &desc, scratch.data(), nullptr);
+ if (asset == nullptr)
+ {
+ free(mem);
+ break;
+ }
+ else
+ {
+ free(asset);
+ memcpy(bondDescs, savedBondDescs.data(), sizeof(NvBlastBondDesc) * bondDescCount);
+ attempt++;
+ if (attempt >= trials)
+ {
+ GTEST_NONFATAL_FAILURE_("Shuffled chunk descs should fail asset creation (most of the time).");
+ break;
+ }
+ }
+ }
+
+ // Now we want to fix that order
+ if (!useTk)
+ {
+ std::vector<uint32_t> chunkReorderMap(chunkDescCount);
+ std::vector<char> scratch2(2 * chunkDescCount * sizeof(uint32_t));
+ const bool isIdentity = NvBlastBuildAssetDescChunkReorderMap(chunkReorderMap.data(), shuffledChunkDescs.data(), chunkDescCount, scratch2.data(), messageLog);
+ EXPECT_FALSE(isIdentity);
+ NvBlastApplyAssetDescChunkReorderMap(chunkDescs, shuffledChunkDescs.data(), chunkDescCount, bondDescs, bondDescCount, chunkReorderMap.data(), messageLog);
+ }
+ else
+ {
+ memcpy(chunkDescs, shuffledChunkDescs.data(), chunkDescCount * sizeof(NvBlastChunkDesc));
+ const bool isIdentity = NvBlastTkFrameworkGet()->reorderAssetDescChunks(chunkDescs, chunkDescCount, bondDescs, bondDescCount);
+ EXPECT_FALSE(isIdentity);
+ }
+ }
+};
+
+typedef AssetTest<NvBlastMessage::Error, 0> AssetTestAllowWarningsSilently;
+typedef AssetTest<NvBlastMessage::Error, 1> AssetTestAllowWarnings;
+typedef AssetTest<NvBlastMessage::Warning, 1> AssetTestStrict;
+
+
+TEST_F(AssetTestStrict, BuildAssets)
+{
+ const uint32_t assetDescCount = sizeof(g_assetDescs) / sizeof(g_assetDescs[0]);
+
+ std::vector<NvBlastAsset*> assets(assetDescCount);
+
+ // Build
+ for (uint32_t i = 0; i < assetDescCount; ++i)
+ {
+ assets[i] = buildAsset(g_assetExpectedValues[i], &g_assetDescs[i]);
+ }
+
+ // Destroy
+ for (uint32_t i = 0; i < assetDescCount; ++i)
+ {
+ if (assets[i])
+ {
+ free(assets[i]);
+ }
+ }
+}
+
+#if ENABLE_SERIALIZATION_TESTS
+// Restricting this test to windows since we don't have a handy cross platform temp file.
+#if defined(WIN32) || defined(WIN64)
+TEST_F(AssetTestStrict, SerializeAssetIntoFile)
+{
+ const uint32_t assetDescCount = sizeof(g_assetDescs) / sizeof(g_assetDescs[0]);
+
+ std::vector<Nv::Blast::Asset *> assets(assetDescCount);
+
+ // Build
+ for (uint32_t i = 0; i < assetDescCount; ++i)
+ {
+ assets[i] = reinterpret_cast<Nv::Blast::Asset*>(buildAsset(g_assetExpectedValues[i], &g_assetDescs[i]));
+ }
+
+ char tempPath[1024];
+ GetTempPathA(1024, tempPath);
+
+ char tempFilename[1024];
+
+ GetTempFileNameA(tempPath, nullptr, 0, tempFilename);
+
+ std::ofstream myFile(tempFilename, std::ios::out | std::ios::binary);
+
+ EXPECT_TRUE(serializeAssetIntoStream(assets[0], myFile));
+
+ myFile.flush();
+
+ // Load it back
+
+ std::ifstream myFileReader(tempFilename, std::ios::binary);
+
+ Nv::Blast::Asset* rtAsset = reinterpret_cast<Nv::Blast::Asset *>(deserializeAssetFromStream(myFileReader));
+ EXPECT_TRUE(rtAsset != nullptr);
+
+ checkAssetsExpected(*rtAsset, g_assetExpectedValues[0]);
+
+ for (uint32_t i = 0; i < assetDescCount; ++i)
+ {
+ free(assets[i]);
+ }
+ free(rtAsset);
+}
+#endif
+
+TEST_F(AssetTestStrict, SerializeAssetsNewBuffer)
+{
+ const uint32_t assetDescCount = sizeof(g_assetDescs) / sizeof(g_assetDescs[0]);
+
+ std::vector<Nv::Blast::Asset *> assets(assetDescCount);
+
+ // Build
+ for (uint32_t i = 0; i < assetDescCount; ++i)
+ {
+ assets[i] = reinterpret_cast<Nv::Blast::Asset*>(buildAsset(g_assetExpectedValues[i], &g_assetDescs[i]));
+ }
+
+ // Serialize them
+ for (Nv::Blast::Asset* asset : assets)
+ {
+ uint32_t size = 0;
+ unsigned char* buffer = nullptr;
+
+
+// auto result = Nv::Blast::BlastSerialization<Nv::Blast::Asset, Nv::Blast::Serialization::Asset::Reader, Nv::Blast::Serialization::Asset::Builder>::serializeIntoNewBuffer(asset, &buffer, size);
+ EXPECT_TRUE(serializeAssetIntoNewBuffer(asset, &buffer, size));
+
+ free(static_cast<void*>(buffer));
+ }
+
+ // Destroy
+ for (uint32_t i = 0; i < assetDescCount; ++i)
+ {
+ if (assets[i])
+ {
+ free(assets[i]);
+ }
+ }
+
+}
+
+TEST_F(AssetTestStrict, SerializeAssetsExistingBuffer)
+{
+ const uint32_t assetDescCount = sizeof(g_assetDescs) / sizeof(g_assetDescs[0]);
+
+ std::vector<Nv::Blast::Asset *> assets(assetDescCount);
+
+ // Build
+ for (uint32_t i = 0; i < assetDescCount; ++i)
+ {
+ assets[i] = reinterpret_cast<Nv::Blast::Asset*>(buildAsset(g_assetExpectedValues[i], &g_assetDescs[i]));
+ }
+
+ // How big does our buffer need to be? Guess.
+
+ uint32_t maxSize = 1024 * 1024;
+ void* buffer = alloc(maxSize);
+
+ // Serialize them
+ for (Nv::Blast::Asset* asset : assets)
+ {
+ uint32_t usedSize = 0;
+
+ EXPECT_TRUE(serializeAssetIntoExistingBuffer(asset, (unsigned char *)buffer, maxSize, usedSize));
+ }
+
+ free(static_cast<void*>(buffer));
+
+ // Destroy
+ for (uint32_t i = 0; i < assetDescCount; ++i)
+ {
+ if (assets[i])
+ {
+ free(assets[i]);
+ }
+ }
+
+}
+
+TEST_F(AssetTestStrict, SerializeAssetsRoundTrip)
+{
+ const uint32_t assetDescCount = sizeof(g_assetDescs) / sizeof(g_assetDescs[0]);
+
+ std::vector<Nv::Blast::Asset *> assets(assetDescCount);
+
+ // Build
+ for (uint32_t i = 0; i < assetDescCount; ++i)
+ {
+ assets[i] = reinterpret_cast<Nv::Blast::Asset*>(buildAsset(g_assetExpectedValues[i], &g_assetDescs[i]));
+ }
+
+ // Serialize them
+ for (uint32_t i = 0; i < assetDescCount; ++i)
+ {
+ Nv::Blast::Asset* asset = assets[i];
+ uint32_t size = 0;
+ unsigned char* buffer = nullptr;
+
+ EXPECT_TRUE(serializeAssetIntoNewBuffer(asset, &buffer, size));
+
+ // No release needed for this asset since it's never put into that system
+ Nv::Blast::Asset* rtAsset = reinterpret_cast<Nv::Blast::Asset*>(deserializeAsset(buffer, size));
+
+ //TODO: Compare assets
+ checkAssetsExpected(*rtAsset, g_assetExpectedValues[i]);
+
+ free(static_cast<void*>(buffer));
+ free(static_cast<void*>(rtAsset));
+ }
+
+ // Destroy
+ for (uint32_t i = 0; i < assetDescCount; ++i)
+ {
+ if (assets[i])
+ {
+ free(assets[i]);
+ }
+ }
+}
+#endif
+
+
+#if 0
+TEST_F(AssetTestStrict, AssociateAsset)
+{
+ const uint32_t assetDescCount = sizeof(g_assetDescs) / sizeof(g_assetDescs[0]);
+
+ for (uint32_t i = 0; i < assetDescCount; ++i)
+ {
+ // Build
+ NvBlastAsset asset;
+ if (!buildAsset(&asset, g_assetExpectedValues[i], &g_assetDescs[i]))
+ {
+ continue;
+ }
+
+ // Copy
+ const char* data = (const char*)NvBlastAssetGetData(&asset, messageLog);
+ const size_t dataSize = NvBlastAssetDataGetSize(data, messageLog);
+ NvBlastAsset duplicate;
+ char* duplicateData = (char*)alloc(dataSize);
+ memcpy(duplicateData, data, dataSize);
+ const bool assetAssociateResult = NvBlastAssetAssociateData(&duplicate, duplicateData, messageLog);
+ EXPECT_TRUE(assetAssociateResult);
+
+ // Destroy
+ NvBlastAssetFreeData(&asset, free, messageLog);
+ NvBlastAssetFreeData(&duplicate, free, messageLog);
+ }
+}
+#endif
+
+TEST_F(AssetTestAllowWarnings, BuildAssetsMissingCoverage)
+{
+ const uint32_t assetDescCount = sizeof(g_assetDescsMissingCoverage) / sizeof(g_assetDescsMissingCoverage[0]);
+
+ std::vector<NvBlastAsset*> assets(assetDescCount);
+
+ // Build
+ for (uint32_t i = 0; i < assetDescCount; ++i)
+ {
+ const NvBlastAssetDesc* desc = &g_assetDescsMissingCoverage[i];
+ NvBlastAssetDesc fixedDesc = *desc;
+ std::vector<NvBlastChunkDesc> chunkDescs(desc->chunkDescs, desc->chunkDescs + desc->chunkCount);
+ std::vector<NvBlastBondDesc> bondDescs(desc->bondDescs, desc->bondDescs + desc->bondCount);
+ std::vector<uint32_t> chunkReorderMap(desc->chunkCount);
+ std::vector<char> scratch(desc->chunkCount * sizeof(NvBlastChunkDesc));
+ const bool changedCoverage = !NvBlastEnsureAssetExactSupportCoverage(chunkDescs.data(), fixedDesc.chunkCount, scratch.data(), messageLog);
+ EXPECT_TRUE(changedCoverage);
+ NvBlastReorderAssetDescChunks(chunkDescs.data(), fixedDesc.chunkCount, bondDescs.data(), fixedDesc.bondCount, chunkReorderMap.data(), scratch.data(), messageLog);
+ fixedDesc.chunkDescs = chunkDescs.data();
+ fixedDesc.bondDescs = bondDescs.data();
+ assets[i] = buildAsset(g_assetsFromMissingCoverageExpectedValues[i], &fixedDesc);
+ }
+
+ // Destroy
+ for (uint32_t i = 0; i < assetDescCount; ++i)
+ {
+ if (assets[i])
+ {
+ free(assets[i]);
+ }
+ }
+}
+
+TEST_F(AssetTestAllowWarningsSilently, BuildAssetsShufflingChunkDescriptors)
+{
+ for (uint32_t i = 0; i < sizeof(g_assetDescs) / sizeof(g_assetDescs[0]); ++i)
+ {
+ buildAssetShufflingDescriptors(&g_assetDescs[i], g_assetExpectedValues[i], 10, false);
+ }
+
+ for (uint32_t i = 0; i < sizeof(g_assetDescsMissingCoverage) / sizeof(g_assetDescsMissingCoverage[0]); ++i)
+ {
+ buildAssetShufflingDescriptors(&g_assetDescsMissingCoverage[i], g_assetsFromMissingCoverageExpectedValues[i], 10, false);
+ }
+}
+
+TEST_F(AssetTestAllowWarningsSilently, BuildAssetsShufflingChunkDescriptorsUsingTk)
+{
+ for (uint32_t i = 0; i < sizeof(g_assetDescs) / sizeof(g_assetDescs[0]); ++i)
+ {
+ buildAssetShufflingDescriptors(&g_assetDescs[i], g_assetExpectedValues[i], 10, true);
+ }
+
+ for (uint32_t i = 0; i < sizeof(g_assetDescsMissingCoverage) / sizeof(g_assetDescsMissingCoverage[0]); ++i)
+ {
+ buildAssetShufflingDescriptors(&g_assetDescsMissingCoverage[i], g_assetsFromMissingCoverageExpectedValues[i], 10, true);
+ }
+}