diff --git a/tests/testChunkPipeline.cpp b/tests/testChunkPipeline.cpp index 8a27a78..0cbdb6e 100644 --- a/tests/testChunkPipeline.cpp +++ b/tests/testChunkPipeline.cpp @@ -152,3 +152,125 @@ VF_TEST(readyChunksLandInTheCachePinnedInsideTheRadius) { ChunkPipeline pipeline(baseConfig(4)); primePipeline(pipeline); + CHECK_EQ(pipeline.cache().size(), pipeline.results().size()); + for (const ChunkTaskResult& produced : pipeline.results()) { + CHECK(pipeline.cache().contains(produced.coord)); + CHECK(pipeline.cache().isPinned(produced.coord)); + Chunk cached; + CHECK(pipeline.cache().peekChunk(produced.coord, cached)); + CHECK_EQ(std::memcmp(cached.raw().data(), produced.chunk.raw().data(), chunkVolume), 0); + } + CHECK(pipeline.cache().bytesUsed() > 0); + CHECK_EQ(pipeline.cache().bytesUsed(), pipeline.stats().cachedBytes); +} + +VF_TEST(pipelineStatsCountEveryStageAndCancellation) { + ChunkPipeline pipeline(baseConfig(4)); + primePipeline(pipeline); + PipelineStats counted = pipeline.stats(); + std::uint64_t ready = static_cast(pipeline.results().size()); + + CHECK_EQ(counted.chunksReady, ready); + CHECK_EQ(counted.cancelled, static_cast(0)); + for (int stage = 0; stage < chunkStageCount; ++stage) { + CHECK_EQ(counted.stageCompleted[static_cast(stage)], ready); + CHECK(counted.averageStageMs(static_cast(stage)) >= 0.0); + } + CHECK(counted.stageNanos[static_cast(ChunkStage::Mesh)] > 0); + + pipeline.resetStats(); + CHECK_EQ(pipeline.stats().chunksReady, static_cast(0)); +} + +VF_TEST(nearChunksOutrankFarOnesInThePendingQueue) { + PipelineConfig config = baseConfig(0); + config.viewRadius = 6; + config.verticalChunks = 1; + ChunkPipeline pipeline(config); + pipeline.setObserver({0, 0, 0}); + pipeline.enqueueViewVolume(); + + std::vector tasks = pipeline.pendingTasks(); + CHECK(tasks.size() > 20); + CHECK(tasks.front().coord == (Vec3i{0, 0, 0})); + CHECK(tasks.front().priority == JobPriority::High); + CHECK(tasks.back().priority == JobPriority::Low); + for (std::size_t i = 1; i < tasks.size(); ++i) { + CHECK(tasks[i - 1].distanceSq <= tasks[i].distanceSq); + CHECK(static_cast(tasks[i - 1].priority) <= static_cast(tasks[i].priority)); + CHECK(tasks[i].inRange); + } +} + +VF_TEST(pipelineBlocksMatchDirectWorldGenOutput) { + ChunkPipeline pipeline(baseConfig(8)); + primePipeline(pipeline); + + WorldGen reference(pipeline.settings().gen); + int compared = 0; + for (const ChunkTaskResult& produced : pipeline.results()) { + Chunk expected; + reference.generateChunk(expected, produced.coord); + CHECK_EQ(std::memcmp(expected.raw().data(), produced.chunk.raw().data(), chunkVolume), 0); + const Chunk* resident = pipeline.worldView().chunkAt(produced.coord); + CHECK(resident != nullptr); + CHECK_EQ(std::memcmp(expected.raw().data(), resident->raw().data(), chunkVolume), 0); + ++compared; + } + CHECK(compared > 0); + CHECK(!pipeline.worldView().chunkAt({0, 0, 0})->empty()); +} + +VF_TEST(pipelineMeshMatchesDirectLitMeshing) { + ChunkPipeline pipeline(baseConfig(8)); + primePipeline(pipeline); + + const Vec3i probe{0, 1, 0}; + const ChunkTaskResult* produced = pipeline.resultFor(probe); + CHECK(produced != nullptr); + + Vec3i origin{probe.x * chunkSize - 1, probe.y * chunkSize - 1, probe.z * chunkSize - 1}; + Vec3i dims{chunkSize + 2, chunkSize + 2, chunkSize + 2}; + LightField expectedLight = computeLight(pipeline.worldView(), origin, dims); + CHECK_EQ(expectedLight.sun.size(), produced->light.sun.size()); + CHECK_EQ(std::memcmp(expectedLight.sun.data(), produced->light.sun.data(), + expectedLight.sun.size()), + 0); + CHECK_EQ(std::memcmp(expectedLight.block.data(), produced->light.block.data(), + expectedLight.block.size()), + 0); + + ChunkMesh expectedMesh = meshChunkLit(pipeline.worldView(), probe, expectedLight); + CHECK(sameMesh(expectedMesh.opaque, produced->mesh.opaque)); + CHECK(sameMesh(expectedMesh.transparent, produced->mesh.transparent)); + CHECK(!produced->mesh.opaque.empty()); +} + +VF_TEST(pipelineDrivesItsWorkThroughTheJobSystem) { + ChunkPipeline pipeline(baseConfig(4)); + primePipeline(pipeline); + JobStats counted = pipeline.jobSystem().stats(); + std::uint64_t nodes = static_cast(pipeline.results().size()) * chunkStageCount; + + CHECK_EQ(counted.executed, nodes); + CHECK_EQ(counted.submitted, nodes); + CHECK_EQ(counted.failed, static_cast(0)); + CHECK(counted.peakBusyWorkers >= 1u); + CHECK(counted.peakBusyWorkers <= 4u); +} + +VF_TEST(secondRunAfterMovingTheObserverUnpinsWhatLeftTheRadius) { + ChunkPipeline pipeline(baseConfig(4)); + primePipeline(pipeline); + const Vec3i edge{-2, 0, 0}; + CHECK(pipeline.cache().isPinned(edge)); + + pipeline.setObserver({4, 0, 0}); + pipeline.enqueueViewVolume(); + pipeline.run(); + + CHECK(!pipeline.inViewVolume(edge)); + CHECK(pipeline.cache().contains(edge)); + CHECK(!pipeline.cache().isPinned(edge)); + CHECK(pipeline.cache().isPinned({4, 0, 0})); +}