#include #include #include #include #include using namespace mbgl; TEST(ParticleGpuField, EightBitSignedZeroAndCancellation) { constexpr float scale = 1.0f; const auto zero = particle::encodeSignedVelocity8(0.0f, scale, true); const auto east = particle::encodeSignedVelocity8(0.25f, scale, true); const auto west = particle::encodeSignedVelocity8(-0.25f, scale, true); EXPECT_EQ(zero, 128); EXPECT_FLOAT_EQ(particle::decodeSignedVelocity8(zero, scale), 0.0f); EXPECT_EQ(static_cast(east) + static_cast(west), 256); EXPECT_FLOAT_EQ((particle::decodeSignedVelocity8(east, scale) + particle::decodeSignedVelocity8(west, scale)) * 0.5f, 0.0f); EXPECT_LE(std::abs(particle::decodeSignedVelocity8(east, scale) - 0.25f), scale / 254.0f); EXPECT_EQ(particle::encodeSignedVelocity8(std::numeric_limits::quiet_NaN(), scale, false), 128); EXPECT_EQ(particle::encodeSignedVelocity8(1.0f, scale, false), 128); // RGBA8 cannot preserve values below half a quantum; this explicit bound // prevents a silent claim of arbitrary micro-wind accuracy. EXPECT_EQ(particle::encodeSignedVelocity8(0.001f, scale, true), 128); } TEST(ParticleGpuField, EndpointIdentityCoversBothPlanesValidityGridEncodingAndRevision) { particle::ParticleFieldSnapshot a; a.fieldID = "wind"; a.fieldType = "qbin-v4"; a.displayDomain = "GLOBAL"; a.generation = 7; a.dataVersion = 3; a.validTime = 1000; a.width = 1; a.height = 1; a.vector = {1.0f, 2.0f}; a.valid = {1}; a.vectorSemantics = particle::VectorSemantics::Velocity; a.gpuEncodingIdentity = "float32-le"; auto b = a; EXPECT_TRUE(particle::sameFieldGpuEndpoint(a, b)); b.vector[1] = 3.0f; EXPECT_FALSE(particle::sameFieldGpuEndpoint(a, b)); b = a; b.valid[0] = 0; EXPECT_FALSE(particle::sameFieldGpuEndpoint(a, b)); b = a; b.west = 1.0; EXPECT_FALSE(particle::sameFieldGpuEndpoint(a, b)); b = a; b.gpuEncodingIdentity = "int16-scaled"; EXPECT_FALSE(particle::sameFieldGpuEndpoint(a, b)); b = a; ++b.dataVersion; EXPECT_FALSE(particle::sameFieldGpuEndpoint(a, b)); b = a; ++b.validTime; EXPECT_FALSE(particle::sameFieldGpuEndpoint(a, b)); b = a; ++b.generation; EXPECT_FALSE(particle::sameFieldGpuEndpoint(a, b)); b = a; b.vectorSemantics = particle::VectorSemantics::Direction; EXPECT_FALSE(particle::sameFieldGpuEndpoint(a, b)); } TEST(ParticleGpuField, BoundaryReusesOldT1AndMixOnlyRequiresNoEndpointUpload) { particle::ParticleFieldSnapshot oldT0; oldT0.fieldID = "wind"; oldT0.generation = 1; oldT0.validTime = 1000; oldT0.width = 1; oldT0.height = 1; oldT0.vector = {1.0f, 2.0f}; oldT0.valid = {1}; auto oldT1 = oldT0; oldT1.validTime = 2000; auto newT1 = oldT0; newT1.validTime = 3000; EXPECT_EQ(particle::gpuEndpointSource(oldT0, oldT1, oldT0), particle::GpuEndpointSource::OldT0); EXPECT_EQ(particle::gpuEndpointSource(oldT0, oldT1, oldT1), particle::GpuEndpointSource::OldT1); EXPECT_EQ(particle::gpuEndpointSource(oldT0, oldT1, newT1), particle::GpuEndpointSource::Upload); // Changing MIX alone leaves the two immutable endpoint decisions intact. const float mixA = 0.25f, mixB = 0.75f; EXPECT_NE(mixA, mixB); EXPECT_EQ(particle::gpuEndpointSource(oldT0, oldT1, oldT0), particle::GpuEndpointSource::OldT0); EXPECT_EQ(particle::gpuEndpointSource(oldT0, oldT1, oldT1), particle::GpuEndpointSource::OldT1); auto onlyV = oldT1; onlyV.vector[1] = -2.0f; EXPECT_EQ(particle::gpuEndpointSource(oldT0, oldT1, onlyV), particle::GpuEndpointSource::Upload); auto onlyValidity = oldT1; onlyValidity.valid[0] = 0; EXPECT_EQ(particle::gpuEndpointSource(oldT0, oldT1, onlyValidity), particle::GpuEndpointSource::Upload); } TEST(ParticleGpuField, MercatorVelocityUsesSameLatitudeMetricForFourDirections) { constexpr double circumference = 40075016.68557849; for (const double latitude : {0.0, 45.0, 60.0}) { const double expected = 10.0 / (circumference * std::cos(latitude * 3.14159265358979323846 / 180.0)); const auto east = particle::mercatorVelocityDelta(10, 0, latitude, 1); const auto west = particle::mercatorVelocityDelta(-10, 0, latitude, 1); const auto north = particle::mercatorVelocityDelta(0, 10, latitude, 1); const auto south = particle::mercatorVelocityDelta(0, -10, latitude, 1); EXPECT_NEAR(east[0], expected, 1e-15); EXPECT_NEAR(west[0], -expected, 1e-15); EXPECT_NEAR(north[1], -expected, 1e-15); EXPECT_NEAR(south[1], expected, 1e-15); EXPECT_DOUBLE_EQ(east[1], 0); EXPECT_DOUBLE_EQ(north[0], 0); } const auto polar = particle::mercatorVelocityDelta(10, 10, 90, 2); EXPECT_TRUE(std::isfinite(polar[0])); EXPECT_TRUE(std::isfinite(polar[1])); EXPECT_DOUBLE_EQ(polar[0], -polar[1]); }