fix: preserve canonical color semantics after QBIN decode
This commit is contained in:
@@ -5,16 +5,23 @@
|
|||||||
|
|
||||||
#include <optional>
|
#include <optional>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
#include <variant>
|
||||||
|
|
||||||
namespace mbgl::particle {
|
namespace mbgl::particle {
|
||||||
|
|
||||||
// Renderer-neutral canonical snapshots. QBIN stays confined to ingress and
|
// Renderer-neutral canonical snapshots. QBIN stays confined to ingress and
|
||||||
// field Core; renderers receive only the existing canonical field shape.
|
// field Core; renderers receive only the existing canonical field shape.
|
||||||
|
enum class MaterializedColorValueSource : uint8_t {
|
||||||
|
Scalar,
|
||||||
|
VectorMagnitude,
|
||||||
|
};
|
||||||
|
|
||||||
struct MaterializedColorProjection {
|
struct MaterializedColorProjection {
|
||||||
std::shared_ptr<const ParticleFieldSnapshot> t0;
|
std::shared_ptr<const ParticleFieldSnapshot> t0;
|
||||||
std::shared_ptr<const ParticleFieldSnapshot> t1;
|
std::shared_ptr<const ParticleFieldSnapshot> t1;
|
||||||
ColorProjectionContentIdentity contentIdentity;
|
ColorProjectionContentIdentity contentIdentity;
|
||||||
FieldSampleState sampleState;
|
FieldSampleState sampleState;
|
||||||
|
MaterializedColorValueSource valueSource = MaterializedColorValueSource::Scalar;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct MaterializedParticleProjection {
|
struct MaterializedParticleProjection {
|
||||||
|
|||||||
@@ -138,23 +138,76 @@ bool vectorSnapshot(const DecodedFieldFrame& frame, const ParticleBinding& bindi
|
|||||||
|
|
||||||
std::optional<MaterializedColorProjection> materializeColorProjection(const ColorProjection& projection) {
|
std::optional<MaterializedColorProjection> materializeColorProjection(const ColorProjection& projection) {
|
||||||
if (!projection.t0Frame || !projection.t1Frame || !compatible(*projection.t0Frame, *projection.t1Frame)) return {};
|
if (!projection.t0Frame || !projection.t1Frame || !compatible(*projection.t0Frame, *projection.t1Frame)) return {};
|
||||||
const auto* p0 = findPlane(*projection.t0Frame, projection.contentIdentity.binding.plane);
|
|
||||||
const auto* p1 = findPlane(*projection.t1Frame, projection.contentIdentity.binding.plane);
|
const auto& binding = projection.contentIdentity.binding;
|
||||||
if (!p0 || !p1) return {};
|
if (binding.expectedValueClass == FieldValueClass::VelocityComponent) {
|
||||||
auto t0 = std::make_shared<ParticleFieldSnapshot>(baseSnapshot(*projection.t0Frame, projection.contentIdentity.t0Plane));
|
const auto findComponent = [&](const DecodedFieldFrame& frame, const FieldComponent component) {
|
||||||
auto t1 = std::make_shared<ParticleFieldSnapshot>(baseSnapshot(*projection.t1Frame, projection.contentIdentity.t1Plane));
|
const DecodedFieldPlane* result = nullptr;
|
||||||
if (!scalarSnapshot(*projection.t0Frame, *p0, *t0) || !scalarSnapshot(*projection.t1Frame, *p1, *t1))
|
for (const auto& plane : frame.planes) {
|
||||||
|
if (plane.valueClass != FieldValueClass::VelocityComponent || plane.unitID != binding.expectedUnitID ||
|
||||||
|
plane.key.groupID != binding.plane.groupID || plane.key.component != component)
|
||||||
|
continue;
|
||||||
|
if (result) return static_cast<const DecodedFieldPlane*>(nullptr);
|
||||||
|
result = &plane;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
|
||||||
|
const auto* u0 = findComponent(*projection.t0Frame, FieldComponent::UEast);
|
||||||
|
const auto* v0 = findComponent(*projection.t0Frame, FieldComponent::VNorth);
|
||||||
|
const auto* u1 = findComponent(*projection.t1Frame, FieldComponent::UEast);
|
||||||
|
const auto* v1 = findComponent(*projection.t1Frame, FieldComponent::VNorth);
|
||||||
|
if (!u0 || !v0 || !u1 || !v1 || !u0->isInterpolationCompatibleWith(*u1) ||
|
||||||
|
!v0->isInterpolationCompatibleWith(*v1))
|
||||||
return {};
|
return {};
|
||||||
return MaterializedColorProjection{std::move(t0), std::move(t1), projection.contentIdentity, projection.sampleState};
|
|
||||||
|
ParticleBinding vectorBinding{
|
||||||
|
ParticleBindingMode::VectorComponents,
|
||||||
|
binding.fieldKey,
|
||||||
|
u0->key,
|
||||||
|
v0->key,
|
||||||
|
{},
|
||||||
|
binding.expectedUnitID,
|
||||||
|
};
|
||||||
|
auto t0 = std::make_shared<ParticleFieldSnapshot>(
|
||||||
|
baseSnapshot(*projection.t0Frame, u0->contentIdentity + "|" + v0->contentIdentity));
|
||||||
|
auto t1 = std::make_shared<ParticleFieldSnapshot>(
|
||||||
|
baseSnapshot(*projection.t1Frame, u1->contentIdentity + "|" + v1->contentIdentity));
|
||||||
|
if (!vectorSnapshot(*projection.t0Frame, vectorBinding, *t0) ||
|
||||||
|
!vectorSnapshot(*projection.t1Frame, vectorBinding, *t1))
|
||||||
|
return {};
|
||||||
|
return MaterializedColorProjection{std::move(t0),
|
||||||
|
std::move(t1),
|
||||||
|
projection.contentIdentity,
|
||||||
|
projection.sampleState,
|
||||||
|
MaterializedColorValueSource::VectorMagnitude};
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto* p0 = findPlane(*projection.t0Frame, binding.plane);
|
||||||
|
const auto* p1 = findPlane(*projection.t1Frame, binding.plane);
|
||||||
|
if (!p0 || !p1) return {};
|
||||||
|
auto t0 = std::make_shared<ParticleFieldSnapshot>(
|
||||||
|
baseSnapshot(*projection.t0Frame, projection.contentIdentity.t0Plane));
|
||||||
|
auto t1 = std::make_shared<ParticleFieldSnapshot>(
|
||||||
|
baseSnapshot(*projection.t1Frame, projection.contentIdentity.t1Plane));
|
||||||
|
if (!scalarSnapshot(*projection.t0Frame, *p0, *t0) || !scalarSnapshot(*projection.t1Frame, *p1, *t1)) return {};
|
||||||
|
return MaterializedColorProjection{std::move(t0),
|
||||||
|
std::move(t1),
|
||||||
|
projection.contentIdentity,
|
||||||
|
projection.sampleState,
|
||||||
|
MaterializedColorValueSource::Scalar};
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<MaterializedParticleProjection> materializeParticleProjection(const ParticleProjection& projection) {
|
std::optional<MaterializedParticleProjection> materializeParticleProjection(const ParticleProjection& projection) {
|
||||||
if (!projection.t0Frame || !projection.t1Frame || !compatible(*projection.t0Frame, *projection.t1Frame)) return {};
|
if (!projection.t0Frame || !projection.t1Frame || !compatible(*projection.t0Frame, *projection.t1Frame)) return {};
|
||||||
auto t0 = std::make_shared<ParticleFieldSnapshot>(baseSnapshot(*projection.t0Frame, projection.contentIdentity.t0FirstPlane));
|
auto t0 = std::make_shared<ParticleFieldSnapshot>(
|
||||||
auto t1 = std::make_shared<ParticleFieldSnapshot>(baseSnapshot(*projection.t1Frame, projection.contentIdentity.t1FirstPlane));
|
baseSnapshot(*projection.t0Frame, projection.contentIdentity.t0FirstPlane));
|
||||||
|
auto t1 = std::make_shared<ParticleFieldSnapshot>(
|
||||||
|
baseSnapshot(*projection.t1Frame, projection.contentIdentity.t1FirstPlane));
|
||||||
if (!vectorSnapshot(*projection.t0Frame, projection.contentIdentity.binding, *t0) ||
|
if (!vectorSnapshot(*projection.t0Frame, projection.contentIdentity.binding, *t0) ||
|
||||||
!vectorSnapshot(*projection.t1Frame, projection.contentIdentity.binding, *t1))
|
!vectorSnapshot(*projection.t1Frame, projection.contentIdentity.binding, *t1))
|
||||||
return {};
|
return {};
|
||||||
return MaterializedParticleProjection{std::move(t0), std::move(t1), projection.contentIdentity, projection.sampleState};
|
return MaterializedParticleProjection{
|
||||||
|
std::move(t0), std::move(t1), projection.contentIdentity, projection.sampleState};
|
||||||
}
|
}
|
||||||
} // namespace mbgl::particle
|
} // namespace mbgl::particle
|
||||||
|
|||||||
@@ -100,7 +100,8 @@ struct Resources {
|
|||||||
class Tweaker final : public gfx::DrawableTweaker {
|
class Tweaker final : public gfx::DrawableTweaker {
|
||||||
public:
|
public:
|
||||||
Tweaker(std::shared_ptr<Resources> resources_, int16_t wrap_)
|
Tweaker(std::shared_ptr<Resources> resources_, int16_t wrap_)
|
||||||
: resources(std::move(resources_)), wrap(wrap_) {}
|
: resources(std::move(resources_)),
|
||||||
|
wrap(wrap_) {}
|
||||||
void init(gfx::Drawable&) override {}
|
void init(gfx::Drawable&) override {}
|
||||||
void execute(gfx::Drawable& drawable, PaintParameters& parameters) override {
|
void execute(gfx::Drawable& drawable, PaintParameters& parameters) override {
|
||||||
drawable.setTexture(resources->texture0, shaders::idParticleGpuValidationPreviousTexture);
|
drawable.setTexture(resources->texture0, shaders::idParticleGpuValidationPreviousTexture);
|
||||||
@@ -199,6 +200,7 @@ void RenderWeatherColorLayer::prepare(const LayerPrepareParameters& parameters)
|
|||||||
if (!parameters.source || parameters.source->baseImpl->type != style::SourceType::RasterArray) return;
|
if (!parameters.source || parameters.source->baseImpl->type != style::SourceType::RasterArray) return;
|
||||||
const auto& source = static_cast<const RenderRasterArraySource&>(*parameters.source);
|
const auto& source = static_cast<const RenderRasterArraySource&>(*parameters.source);
|
||||||
sourceUsesV4Projection = false;
|
sourceUsesV4Projection = false;
|
||||||
|
sourceProjectionUsesVectorMagnitude = false;
|
||||||
sourceProjectionMix = 0.0f;
|
sourceProjectionMix = 0.0f;
|
||||||
if (source.impl().v4RendererState.color) {
|
if (source.impl().v4RendererState.color) {
|
||||||
const auto& projection = *source.impl().v4RendererState.color;
|
const auto& projection = *source.impl().v4RendererState.color;
|
||||||
@@ -207,6 +209,8 @@ void RenderWeatherColorLayer::prepare(const LayerPrepareParameters& parameters)
|
|||||||
sourceProjectionMix = projection.sampleState.mix;
|
sourceProjectionMix = projection.sampleState.mix;
|
||||||
sourceTimeline.reset();
|
sourceTimeline.reset();
|
||||||
sourceUsesV4Projection = true;
|
sourceUsesV4Projection = true;
|
||||||
|
sourceProjectionUsesVectorMagnitude = projection.valueSource ==
|
||||||
|
particle::MaterializedColorValueSource::VectorMagnitude;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
sourceTimeline = source.impl().timeline;
|
sourceTimeline = source.impl().timeline;
|
||||||
@@ -242,10 +246,11 @@ void RenderWeatherColorLayer::update(gfx::ShaderRegistry& shaders,
|
|||||||
resources.reset();
|
resources.reset();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// V4 ColorBinding has already selected and materialized its scalar plane;
|
// Encoded ingress is decoder-only. After materialization, WeatherColor
|
||||||
// it must not be reinterpreted through the legacy style value source.
|
// consumes the same canonical scalar/vector semantics as the legacy path.
|
||||||
const auto valueSource = sourceUsesV4Projection
|
const auto valueSource = sourceUsesV4Projection
|
||||||
? style::FieldValueSource::Scalar
|
? (sourceProjectionUsesVectorMagnitude ? style::FieldValueSource::VectorMagnitude
|
||||||
|
: style::FieldValueSource::Scalar)
|
||||||
: static_cast<const style::WeatherColorLayer::Impl&>(*baseImpl).valueSource;
|
: static_cast<const style::WeatherColorLayer::Impl&>(*baseImpl).valueSource;
|
||||||
if (!supportsValueSource(*sourceField, valueSource) || !supportsValueSource(*sourceNextField, valueSource)) {
|
if (!supportsValueSource(*sourceField, valueSource) || !supportsValueSource(*sourceNextField, valueSource)) {
|
||||||
if (layerGroup) static_cast<LayerGroup&>(*layerGroup).clearDrawables();
|
if (layerGroup) static_cast<LayerGroup&>(*layerGroup).clearDrawables();
|
||||||
|
|||||||
@@ -34,6 +34,7 @@ private:
|
|||||||
std::shared_ptr<style::RasterArrayTimelineRuntime> sourceTimeline;
|
std::shared_ptr<style::RasterArrayTimelineRuntime> sourceTimeline;
|
||||||
float sourceProjectionMix = 0.0f;
|
float sourceProjectionMix = 0.0f;
|
||||||
bool sourceUsesV4Projection = false;
|
bool sourceUsesV4Projection = false;
|
||||||
|
bool sourceProjectionUsesVectorMagnitude = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace mbgl
|
} // namespace mbgl
|
||||||
|
|||||||
@@ -66,8 +66,8 @@ template <class Projection>
|
|||||||
bool sameProjection(const std::shared_ptr<const Projection>& a, const std::shared_ptr<const Projection>& b) {
|
bool sameProjection(const std::shared_ptr<const Projection>& a, const std::shared_ptr<const Projection>& b) {
|
||||||
if (static_cast<bool>(a) != static_cast<bool>(b)) return false;
|
if (static_cast<bool>(a) != static_cast<bool>(b)) return false;
|
||||||
return !a || (a->contentIdentity == b->contentIdentity && a->sampleState.mix == b->sampleState.mix &&
|
return !a || (a->contentIdentity == b->contentIdentity && a->sampleState.mix == b->sampleState.mix &&
|
||||||
a->sampleState.requestedForecastTimeMs == b->sampleState.requestedForecastTimeMs &&
|
a->sampleState.requestedForecastTimeMs == b->sampleState.requestedForecastTimeMs && a->t0 && b->t0 &&
|
||||||
a->t0 && b->t0 && a->t1 && b->t1 && sameSnapshot(*a->t0, *b->t0) && sameSnapshot(*a->t1, *b->t1));
|
a->t1 && b->t1 && sameSnapshot(*a->t0, *b->t0) && sameSnapshot(*a->t1, *b->t1));
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
@@ -428,8 +428,7 @@ RasterArrayEncodedFieldSubmitResult RasterArraySource::submitEncodedField(
|
|||||||
// checks metadata, but validate it here to make the Host ordering explicit.
|
// checks metadata, but validate it here to make the Host ordering explicit.
|
||||||
if (!validEncodedFieldKey(fieldKey)) return RasterArrayEncodedFieldSubmitResult::RejectedInvalidFieldKey;
|
if (!validEncodedFieldKey(fieldKey)) return RasterArrayEncodedFieldSubmitResult::RejectedInvalidFieldKey;
|
||||||
if (!validRuntimeMetadata(runtimeMetadata)) return RasterArrayEncodedFieldSubmitResult::RejectedDecode;
|
if (!validRuntimeMetadata(runtimeMetadata)) return RasterArrayEncodedFieldSubmitResult::RejectedDecode;
|
||||||
auto ingress = particle::ingress::EncodedFieldIngress::decodeAndValidate(
|
auto ingress = particle::ingress::EncodedFieldIngress::decodeAndValidate(runtimeMetadata, &contract, rawQbinBytes);
|
||||||
runtimeMetadata, &contract, rawQbinBytes);
|
|
||||||
if (ingress.code == particle::ingress::EncodedFieldIngressCode::DecodeRejected)
|
if (ingress.code == particle::ingress::EncodedFieldIngressCode::DecodeRejected)
|
||||||
return RasterArrayEncodedFieldSubmitResult::RejectedDecode;
|
return RasterArrayEncodedFieldSubmitResult::RejectedDecode;
|
||||||
if (ingress.code != particle::ingress::EncodedFieldIngressCode::Accepted)
|
if (ingress.code != particle::ingress::EncodedFieldIngressCode::Accepted)
|
||||||
@@ -501,14 +500,17 @@ void RasterArraySource::refreshV4RendererState() {
|
|||||||
if (const auto materialized = resolved.accepted() ? particle::materializeColorProjection(*resolved.projection)
|
if (const auto materialized = resolved.accepted() ? particle::materializeColorProjection(*resolved.projection)
|
||||||
: std::nullopt)
|
: std::nullopt)
|
||||||
next.color = std::make_shared<const particle::MaterializedColorProjection>(std::move(*materialized));
|
next.color = std::make_shared<const particle::MaterializedColorProjection>(std::move(*materialized));
|
||||||
if (next.color) particle::incrementRendererDebugCounter(particle::RendererDebugCounter::V4ProjectionMaterialize);
|
if (next.color)
|
||||||
|
particle::incrementRendererDebugCounter(particle::RendererDebugCounter::V4ProjectionMaterialize);
|
||||||
}
|
}
|
||||||
if (particleBinding) {
|
if (particleBinding) {
|
||||||
const auto resolved = resolveParticleProjection();
|
const auto resolved = resolveParticleProjection();
|
||||||
if (const auto materialized = resolved.accepted() ? particle::materializeParticleProjection(*resolved.projection)
|
if (const auto materialized = resolved.accepted()
|
||||||
|
? particle::materializeParticleProjection(*resolved.projection)
|
||||||
: std::nullopt)
|
: std::nullopt)
|
||||||
next.particle = std::make_shared<const particle::MaterializedParticleProjection>(std::move(*materialized));
|
next.particle = std::make_shared<const particle::MaterializedParticleProjection>(std::move(*materialized));
|
||||||
if (next.particle) particle::incrementRendererDebugCounter(particle::RendererDebugCounter::V4ProjectionMaterialize);
|
if (next.particle)
|
||||||
|
particle::incrementRendererDebugCounter(particle::RendererDebugCounter::V4ProjectionMaterialize);
|
||||||
}
|
}
|
||||||
const auto& current = impl().v4RendererState;
|
const auto& current = impl().v4RendererState;
|
||||||
if (sameProjection(current.color, next.color) && sameProjection(current.particle, next.particle)) return;
|
if (sameProjection(current.color, next.color) && sameProjection(current.particle, next.particle)) return;
|
||||||
|
|||||||
@@ -414,6 +414,7 @@ TEST(Source, RasterArrayV4RendererStateMaterializesBindingsAndMixWithoutTouching
|
|||||||
EXPECT_EQ(particles.t0->vector, (std::vector<float>{1, 5, 2, 6, 3, 7, 4, 8}));
|
EXPECT_EQ(particles.t0->vector, (std::vector<float>{1, 5, 2, 6, 3, 7, 4, 8}));
|
||||||
EXPECT_EQ(particles.t0->valid, (std::vector<uint8_t>{1, 1, 1, 1}));
|
EXPECT_EQ(particles.t0->valid, (std::vector<uint8_t>{1, 1, 1, 1}));
|
||||||
EXPECT_EQ(color.t0->scalar, (std::vector<float>{1, 2, 3, 4}));
|
EXPECT_EQ(color.t0->scalar, (std::vector<float>{1, 2, 3, 4}));
|
||||||
|
EXPECT_EQ(color.valueSource, particle::MaterializedColorValueSource::Scalar);
|
||||||
const auto particleIdentity = particles.contentIdentity;
|
const auto particleIdentity = particles.contentIdentity;
|
||||||
const auto retainedParticleT0 = particles.t0;
|
const auto retainedParticleT0 = particles.t0;
|
||||||
ASSERT_TRUE(source.setRequestedForecastTime(Base + Hour / 4));
|
ASSERT_TRUE(source.setRequestedForecastTime(Base + Hour / 4));
|
||||||
@@ -429,6 +430,46 @@ TEST(Source, RasterArrayV4RendererStateMaterializesBindingsAndMixWithoutTouching
|
|||||||
EXPECT_FALSE(source.impl().v4RendererState.particle);
|
EXPECT_FALSE(source.impl().v4RendererState.particle);
|
||||||
EXPECT_FALSE(source.impl().v4RendererState.color);
|
EXPECT_FALSE(source.impl().v4RendererState.color);
|
||||||
}
|
}
|
||||||
|
TEST(Source, RasterArrayV4VelocityColorReconstructsDecodedUVIndependentlyOfParticles) {
|
||||||
|
style::RasterArraySource source{"v4-vector-color"};
|
||||||
|
submit(source, "wind", "wind", Base, 1);
|
||||||
|
submit(source, "wind", "wind", Base + Hour, 2);
|
||||||
|
|
||||||
|
source.setColorBinding(
|
||||||
|
{"wind", {1, 1, particle::FieldComponent::UEast}, particle::FieldValueClass::VelocityComponent, 1});
|
||||||
|
ASSERT_TRUE(source.setRequestedForecastTime(Base + Hour / 2));
|
||||||
|
|
||||||
|
ASSERT_TRUE(source.impl().v4RendererState.color);
|
||||||
|
EXPECT_FALSE(source.impl().v4RendererState.particle);
|
||||||
|
const auto& colorWithoutParticles = *source.impl().v4RendererState.color;
|
||||||
|
EXPECT_EQ(colorWithoutParticles.valueSource, particle::MaterializedColorValueSource::VectorMagnitude);
|
||||||
|
ASSERT_TRUE(colorWithoutParticles.t0);
|
||||||
|
ASSERT_TRUE(colorWithoutParticles.t1);
|
||||||
|
EXPECT_TRUE(colorWithoutParticles.t0->hasVector());
|
||||||
|
EXPECT_FALSE(colorWithoutParticles.t0->hasScalar());
|
||||||
|
EXPECT_EQ(colorWithoutParticles.t0->vector, (std::vector<float>{1, 5, 2, 6, 3, 7, 4, 8}));
|
||||||
|
EXPECT_EQ(colorWithoutParticles.t0->valid, (std::vector<uint8_t>{1, 1, 1, 1}));
|
||||||
|
EXPECT_FLOAT_EQ(colorWithoutParticles.sampleState.mix, 0.5f);
|
||||||
|
|
||||||
|
source.setParticleBinding({particle::ParticleBindingMode::VectorComponents,
|
||||||
|
"wind",
|
||||||
|
{1, 1, particle::FieldComponent::UEast},
|
||||||
|
{2, 1, particle::FieldComponent::VNorth},
|
||||||
|
{},
|
||||||
|
1});
|
||||||
|
ASSERT_TRUE(source.impl().v4RendererState.color);
|
||||||
|
ASSERT_TRUE(source.impl().v4RendererState.particle);
|
||||||
|
const auto& color = *source.impl().v4RendererState.color;
|
||||||
|
const auto& particles = *source.impl().v4RendererState.particle;
|
||||||
|
EXPECT_EQ(color.valueSource, particle::MaterializedColorValueSource::VectorMagnitude);
|
||||||
|
EXPECT_EQ(color.t0->vector, particles.t0->vector);
|
||||||
|
EXPECT_EQ(color.t1->vector, particles.t1->vector);
|
||||||
|
EXPECT_EQ(color.t0->valid, particles.t0->valid);
|
||||||
|
EXPECT_FALSE(source.impl().field);
|
||||||
|
EXPECT_FALSE(source.impl().nextField);
|
||||||
|
EXPECT_FALSE(source.impl().timeline);
|
||||||
|
}
|
||||||
|
|
||||||
TEST(Source, RasterArrayV4MaterializerUsesExplicitLittleEndianScaleOffsetAndStableRevision) {
|
TEST(Source, RasterArrayV4MaterializerUsesExplicitLittleEndianScaleOffsetAndStableRevision) {
|
||||||
const auto t0 = materializerFrame("scalar",
|
const auto t0 = materializerFrame("scalar",
|
||||||
Base,
|
Base,
|
||||||
@@ -457,8 +498,8 @@ TEST(Source, RasterArrayV4MaterializerUsesExplicitLittleEndianScaleOffsetAndStab
|
|||||||
particle::ColorBinding binding{
|
particle::ColorBinding binding{
|
||||||
"scalar", {99, 0, particle::FieldComponent::None}, particle::FieldValueClass::Scalar, 77};
|
"scalar", {99, 0, particle::FieldComponent::None}, particle::FieldValueClass::Scalar, 77};
|
||||||
particle::ColorProjection projection;
|
particle::ColorProjection projection;
|
||||||
projection.contentIdentity =
|
projection.contentIdentity = {
|
||||||
{binding, {"scalar", Base}, {"scalar", Base + Hour}, "plane-t0", "plane-t1", "validity-t0", "validity-t1"};
|
binding, {"scalar", Base}, {"scalar", Base + Hour}, "plane-t0", "plane-t1", "validity-t0", "validity-t1"};
|
||||||
projection.sampleState = {Base + Hour / 2, 0.5f};
|
projection.sampleState = {Base + Hour / 2, 0.5f};
|
||||||
projection.t0Frame = t0;
|
projection.t0Frame = t0;
|
||||||
projection.t1Frame = t1;
|
projection.t1Frame = t1;
|
||||||
|
|||||||
Reference in New Issue
Block a user