Skip to content

Commit ecebd9f

Browse files
maxmmitchellcopybara-github
authored andcommitted
Populate min_version BrushFamily proto field.
Introduces code to calculate the minimum required version of Ink for a BrushFamily (and each part of a BrushFamily). The code is used during serialization to compute the value to store in the `min_version` field. During deserialization, the `min_version` field is used to determine if a BrushFamily is compatible with the current version of Ink; if it isn't, decoding it is aborted. All future features added to custom brushes must be associated with a version number in the proto, and that same number must associated with that feature in the appropriate part of `CalculateMinimumRequiredVersion`. There are two options when selecting a version for a new feature: * `version::kMax + 1`. This will create a new version level and cement the new feature as part of the custom brush API that the Ink team will support. If this option is chosen, `kMax` itself must be incremented as well. * `version::kDevelopment`. This value is a sentinel (represented under the hood with `int32 max()`) to hold features which aren't ready to be released yet. Brushes containing features associated with `kDevelopment` will fail to be deserialized under normal circumstances (an optional argument to `DecodeBrush/BrushFamily()` controls this). This is WAI, since `kDevelopment > kMax`. Features associated with `kDevelopment` are unstable, so Ink makes no guarantees they will be supported by future versions. Serializing them is risky, and deserializing them is especially risky. Brushes with the version `kDevelopment` may contain features no longer supported, or whose interfaces have changed. These features are available for the purposes of prototyping and research, and deserializing them is only provided as an option for the purpose of convenience. Deserializing a brush with a version of `kDevelopment` may lead to unexpected behavior. To ensure the code in the web of `CalculateMinimumRequiredVersion` functions lines up with expectations, protobuf options are used to build consistency check tests. `CalculateMinimumRequiredVersion` serves as the enforcement of the expectations set in the protobuf itself. The tests are: * A fuzz test to generate valid brush families and serialize them. Then, it checks the value in `min_version` against all the options set for the fields, messages, and enum values, to ensure it is equal to the highest value set for the relevant options. This ensures `CalculateMinimumRequiredVersion` is consistent with the protobuf options. * A test to iterate over all the messages, fields, and enum values in `brush_family.proto`, starting at the top-level `BrushFamily` message, and verify that they all have a relevant `field_min_version`, `message_min_version`, or `enum_value_min_version` set, and that this value set is less than or equal to the maximum compatible version (`version::kMax`). * A test to verify that decoding a `proto::Version` out of the acceptable range (higher than `version::kMax`, lower than `version::kMin`) will fail. * A test to verify that decoding a `proto::BrushFamily` with no value set for its `min_version` field will ***not*** fail, to ensure older brushes are still able to be loaded. * Tests to verify that `DecodeBrush()` and `DecodeBrushFamily()` will accept brushes with the version `kDevelopment` if passed the appropriate argument. A more direct implementation, using reflection to examine the descriptors of the protobuf directly during serialization, was considered, but ultimately rejected. It would have required both static and runtime reflection, and increased `libink.so` size by ~2%. PiperOrigin-RevId: 872916698
1 parent 61991bc commit ecebd9f

23 files changed

Lines changed: 1255 additions & 235 deletions

ink/brush/BUILD.bazel

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,9 +82,9 @@ cc_library(
8282
srcs = ["brush_coat.cc"],
8383
hdrs = ["brush_coat.h"],
8484
deps = [
85-
":brush_behavior",
8685
":brush_paint",
8786
":brush_tip",
87+
":version",
8888
"//ink/geometry:mesh_format",
8989
"@com_google_absl//absl/container:flat_hash_set",
9090
"@com_google_absl//absl/container:inlined_vector",
@@ -120,6 +120,7 @@ cc_library(
120120
":brush_coat",
121121
":brush_paint",
122122
":brush_tip",
123+
":version",
123124
"//ink/types:duration",
124125
"@com_google_absl//absl/status",
125126
"@com_google_absl//absl/status:statusor",
@@ -160,6 +161,7 @@ cc_library(
160161
hdrs = ["brush_tip.h"],
161162
deps = [
162163
":brush_behavior",
164+
":version",
163165
"//ink/geometry:angle",
164166
"//ink/geometry:mesh_format",
165167
"//ink/geometry:vec",
@@ -190,6 +192,7 @@ cc_library(
190192
srcs = ["color_function.cc"],
191193
hdrs = ["color_function.h"],
192194
deps = [
195+
":version",
193196
"//ink/color",
194197
"@com_google_absl//absl/status",
195198
"@com_google_absl//absl/strings",
@@ -219,6 +222,7 @@ cc_library(
219222
srcs = ["easing_function.cc"],
220223
hdrs = ["easing_function.h"],
221224
deps = [
225+
":version",
222226
"//ink/geometry:point",
223227
"@com_google_absl//absl/status",
224228
"@com_google_absl//absl/strings",
@@ -245,6 +249,7 @@ cc_library(
245249
hdrs = ["brush_behavior.h"],
246250
deps = [
247251
":easing_function",
252+
":version",
248253
"@com_google_absl//absl/status",
249254
"@com_google_absl//absl/strings",
250255
"@com_google_absl//absl/strings:str_format",
@@ -273,6 +278,7 @@ cc_library(
273278
hdrs = ["brush_paint.h"],
274279
deps = [
275280
":color_function",
281+
":version",
276282
"//ink/geometry:angle",
277283
"//ink/geometry:mesh_format",
278284
"//ink/geometry:vec",
@@ -365,6 +371,16 @@ cc_library(
365371
],
366372
)
367373

374+
cc_library(
375+
name = "version",
376+
hdrs = ["version.h"],
377+
deps = [
378+
"@com_google_absl//absl/base:no_destructor",
379+
"@com_google_absl//absl/status",
380+
"@com_google_absl//absl/strings",
381+
],
382+
)
383+
368384
cc_test(
369385
name = "stock_brushes_test",
370386
srcs = ["stock_brushes_test.cc"],

ink/brush/brush_behavior.cc

Lines changed: 202 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,11 @@
1414

1515
#include "ink/brush/brush_behavior.h"
1616

17+
#include <algorithm>
1718
#include <array>
1819
#include <cmath>
1920
#include <cstddef>
21+
#include <cstdint>
2022
#include <string>
2123
#include <variant>
2224

@@ -454,6 +456,206 @@ absl::Status ValidateBrushBehavior(const BrushBehavior& behavior) {
454456
return absl::OkStatus();
455457
}
456458

459+
namespace {
460+
461+
int32_t CalculateMinimumRequiredVersion(BrushBehavior::Source source) {
462+
switch (source) {
463+
case BrushBehavior::Source::kNormalizedPressure:
464+
case BrushBehavior::Source::kTiltInRadians:
465+
case BrushBehavior::Source::kTiltXInRadians:
466+
case BrushBehavior::Source::kTiltYInRadians:
467+
case BrushBehavior::Source::kOrientationInRadians:
468+
case BrushBehavior::Source::kOrientationAboutZeroInRadians:
469+
case BrushBehavior::Source::kSpeedInMultiplesOfBrushSizePerSecond:
470+
case BrushBehavior::Source::kVelocityXInMultiplesOfBrushSizePerSecond:
471+
case BrushBehavior::Source::kVelocityYInMultiplesOfBrushSizePerSecond:
472+
case BrushBehavior::Source::kDirectionInRadians:
473+
case BrushBehavior::Source::kDirectionAboutZeroInRadians:
474+
case BrushBehavior::Source::kNormalizedDirectionX:
475+
case BrushBehavior::Source::kNormalizedDirectionY:
476+
case BrushBehavior::Source::kDistanceTraveledInMultiplesOfBrushSize:
477+
case BrushBehavior::Source::kTimeOfInputInSeconds:
478+
case BrushBehavior::Source::
479+
kPredictedDistanceTraveledInMultiplesOfBrushSize:
480+
case BrushBehavior::Source::kPredictedTimeElapsedInSeconds:
481+
case BrushBehavior::Source::kDistanceRemainingInMultiplesOfBrushSize:
482+
case BrushBehavior::Source::kTimeSinceInputInSeconds:
483+
case BrushBehavior::Source::
484+
kAccelerationInMultiplesOfBrushSizePerSecondSquared:
485+
case BrushBehavior::Source::
486+
kAccelerationXInMultiplesOfBrushSizePerSecondSquared:
487+
case BrushBehavior::Source::
488+
kAccelerationYInMultiplesOfBrushSizePerSecondSquared:
489+
case BrushBehavior::Source::
490+
kAccelerationForwardInMultiplesOfBrushSizePerSecondSquared:
491+
case BrushBehavior::Source::
492+
kAccelerationLateralInMultiplesOfBrushSizePerSecondSquared:
493+
case BrushBehavior::Source::kSpeedInCentimetersPerSecond:
494+
case BrushBehavior::Source::kVelocityXInCentimetersPerSecond:
495+
case BrushBehavior::Source::kVelocityYInCentimetersPerSecond:
496+
case BrushBehavior::Source::kDistanceTraveledInCentimeters:
497+
case BrushBehavior::Source::kPredictedDistanceTraveledInCentimeters:
498+
case BrushBehavior::Source::kAccelerationInCentimetersPerSecondSquared:
499+
case BrushBehavior::Source::kAccelerationXInCentimetersPerSecondSquared:
500+
case BrushBehavior::Source::kAccelerationYInCentimetersPerSecondSquared:
501+
case BrushBehavior::Source::
502+
kAccelerationForwardInCentimetersPerSecondSquared:
503+
case BrushBehavior::Source::
504+
kAccelerationLateralInCentimetersPerSecondSquared:
505+
case BrushBehavior::Source::kDistanceRemainingAsFractionOfStrokeLength:
506+
return 0;
507+
case BrushBehavior::Source::kTimeSinceStrokeEndInSeconds:
508+
return 1;
509+
case BrushBehavior::Source::kTimeFromInputToStrokeEndInSeconds:
510+
return 2;
511+
}
512+
}
513+
514+
int32_t CalculateMinimumRequiredVersion(BrushBehavior::Target target) {
515+
switch (target) {
516+
case BrushBehavior::Target::kWidthMultiplier:
517+
case BrushBehavior::Target::kHeightMultiplier:
518+
case BrushBehavior::Target::kSizeMultiplier:
519+
case BrushBehavior::Target::kSlantOffsetInRadians:
520+
case BrushBehavior::Target::kPinchOffset:
521+
case BrushBehavior::Target::kRotationOffsetInRadians:
522+
case BrushBehavior::Target::kCornerRoundingOffset:
523+
case BrushBehavior::Target::kPositionOffsetXInMultiplesOfBrushSize:
524+
case BrushBehavior::Target::kPositionOffsetYInMultiplesOfBrushSize:
525+
case BrushBehavior::Target::kPositionOffsetForwardInMultiplesOfBrushSize:
526+
case BrushBehavior::Target::kPositionOffsetLateralInMultiplesOfBrushSize:
527+
case BrushBehavior::Target::kTextureAnimationProgressOffset:
528+
case BrushBehavior::Target::kHueOffsetInRadians:
529+
case BrushBehavior::Target::kSaturationMultiplier:
530+
case BrushBehavior::Target::kLuminosityOffset:
531+
case BrushBehavior::Target::kOpacityMultiplier:
532+
return 0;
533+
}
534+
}
535+
536+
int32_t CalculateMinimumRequiredVersion(BrushBehavior::PolarTarget target) {
537+
switch (target) {
538+
case BrushBehavior::PolarTarget::
539+
kPositionOffsetAbsoluteInRadiansAndMultiplesOfBrushSize:
540+
case BrushBehavior::PolarTarget::
541+
kPositionOffsetRelativeInRadiansAndMultiplesOfBrushSize:
542+
return 0;
543+
}
544+
}
545+
546+
int32_t CalculateMinimumRequiredVersion(
547+
BrushBehavior::OutOfRange out_of_range) {
548+
switch (out_of_range) {
549+
case BrushBehavior::OutOfRange::kClamp:
550+
case BrushBehavior::OutOfRange::kMirror:
551+
case BrushBehavior::OutOfRange::kRepeat:
552+
return 0;
553+
}
554+
}
555+
556+
int32_t CalculateMinimumRequiredVersion(
557+
BrushBehavior::EnabledToolTypes enabled) {
558+
return 0;
559+
}
560+
561+
int32_t CalculateMinimumRequiredVersion(BrushBehavior::BinaryOp operation) {
562+
switch (operation) {
563+
case BrushBehavior::BinaryOp::kProduct:
564+
case BrushBehavior::BinaryOp::kSum:
565+
return 0;
566+
case BrushBehavior::BinaryOp::kMin:
567+
case BrushBehavior::BinaryOp::kMax:
568+
case BrushBehavior::BinaryOp::kAndThen:
569+
case BrushBehavior::BinaryOp::kOrElse:
570+
case BrushBehavior::BinaryOp::kXorElse:
571+
return 1;
572+
}
573+
}
574+
575+
int32_t CalculateMinimumRequiredVersion(
576+
BrushBehavior::ProgressDomain progress_domain) {
577+
switch (progress_domain) {
578+
case BrushBehavior::ProgressDomain::kDistanceInCentimeters:
579+
case BrushBehavior::ProgressDomain::kDistanceInMultiplesOfBrushSize:
580+
case BrushBehavior::ProgressDomain::kTimeInSeconds:
581+
return 0;
582+
}
583+
}
584+
585+
int32_t CalculateMinimumRequiredVersion(
586+
BrushBehavior::Interpolation interpolation) {
587+
switch (interpolation) {
588+
case BrushBehavior::Interpolation::kLerp:
589+
case BrushBehavior::Interpolation::kInverseLerp:
590+
return 0;
591+
}
592+
}
593+
594+
int32_t CalculateMinimumRequiredVersion(BrushBehavior::SourceNode node) {
595+
return std::max(
596+
CalculateMinimumRequiredVersion(node.source_out_of_range_behavior),
597+
CalculateMinimumRequiredVersion(node.source));
598+
}
599+
600+
int32_t CalculateMinimumRequiredVersion(BrushBehavior::ConstantNode node) {
601+
return 0;
602+
}
603+
604+
int32_t CalculateMinimumRequiredVersion(BrushBehavior::NoiseNode node) {
605+
return CalculateMinimumRequiredVersion(node.vary_over);
606+
}
607+
608+
int32_t CalculateMinimumRequiredVersion(
609+
BrushBehavior::ToolTypeFilterNode node) {
610+
return CalculateMinimumRequiredVersion(node.enabled_tool_types);
611+
}
612+
613+
int32_t CalculateMinimumRequiredVersion(BrushBehavior::DampingNode node) {
614+
return CalculateMinimumRequiredVersion(node.damping_source);
615+
}
616+
617+
int32_t CalculateMinimumRequiredVersion(BrushBehavior::ResponseNode node) {
618+
return brush_internal::CalculateMinimumRequiredVersion(node.response_curve);
619+
}
620+
621+
int32_t CalculateMinimumRequiredVersion(BrushBehavior::IntegralNode node) {
622+
return std::max(
623+
{1, CalculateMinimumRequiredVersion(node.integrate_over),
624+
CalculateMinimumRequiredVersion(node.integral_out_of_range_behavior)});
625+
}
626+
627+
int32_t CalculateMinimumRequiredVersion(BrushBehavior::BinaryOpNode node) {
628+
return CalculateMinimumRequiredVersion(node.operation);
629+
}
630+
631+
int32_t CalculateMinimumRequiredVersion(BrushBehavior::InterpolationNode node) {
632+
return CalculateMinimumRequiredVersion(node.interpolation);
633+
}
634+
635+
int32_t CalculateMinimumRequiredVersion(BrushBehavior::TargetNode node) {
636+
return CalculateMinimumRequiredVersion(node.target);
637+
}
638+
639+
int32_t CalculateMinimumRequiredVersion(BrushBehavior::PolarTargetNode node) {
640+
return CalculateMinimumRequiredVersion(node.target);
641+
}
642+
643+
int32_t CalculateMinimumRequiredVersion(const BrushBehavior::Node& node) {
644+
return std::visit(
645+
[](const auto& node) { return CalculateMinimumRequiredVersion(node); },
646+
node);
647+
}
648+
649+
} // namespace
650+
651+
int32_t CalculateMinimumRequiredVersion(const BrushBehavior& behavior) {
652+
int32_t max_version = 0;
653+
for (const BrushBehavior::Node& node : behavior.nodes) {
654+
max_version = std::max(max_version, CalculateMinimumRequiredVersion(node));
655+
}
656+
return max_version;
657+
}
658+
457659
std::string ToFormattedString(BrushBehavior::Source source) {
458660
switch (source) {
459661
case BrushBehavior::Source::kNormalizedPressure:

ink/brush/brush_behavior.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323

2424
#include "absl/status/status.h"
2525
#include "ink/brush/easing_function.h"
26+
#include "ink/brush/version.h"
2627

2728
namespace ink {
2829

@@ -671,6 +672,10 @@ absl::Status ValidateBrushBehaviorTopLevel(const BrushBehavior& behavior);
671672

672673
absl::Status ValidateBrushBehaviorNode(const BrushBehavior::Node& node);
673674

675+
// Calculates the minimum version of the Ink library that is required to use
676+
// this brush behavior.
677+
int32_t CalculateMinimumRequiredVersion(const BrushBehavior& behavior);
678+
674679
std::string ToFormattedString(BrushBehavior::Source source);
675680
std::string ToFormattedString(BrushBehavior::Target target);
676681
std::string ToFormattedString(BrushBehavior::PolarTarget target);

ink/brush/brush_coat.cc

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,14 +14,15 @@
1414

1515
#include "ink/brush/brush_coat.h"
1616

17+
#include <algorithm>
18+
#include <cstdint>
1719
#include <string>
1820

1921
#include "absl/container/flat_hash_set.h"
2022
#include "absl/status/status.h"
2123
#include "absl/strings/str_cat.h"
2224
#include "absl/strings/str_format.h"
2325
#include "absl/strings/str_join.h"
24-
#include "ink/brush/brush_behavior.h"
2526
#include "ink/brush/brush_paint.h"
2627
#include "ink/brush/brush_tip.h"
2728
#include "ink/geometry/mesh_format.h"
@@ -45,6 +46,14 @@ absl::Status ValidateBrushCoat(const BrushCoat& coat) {
4546
return absl::OkStatus();
4647
}
4748

49+
int32_t CalculateMinimumRequiredVersion(const BrushCoat& coat) {
50+
int32_t max_version = CalculateMinimumRequiredVersion(coat.tip);
51+
for (const BrushPaint& paint : coat.paint_preferences) {
52+
max_version = std::max(max_version, CalculateMinimumRequiredVersion(paint));
53+
}
54+
return max_version;
55+
}
56+
4857
void AddAttributeIdsRequiredByCoat(
4958
const BrushCoat& coat,
5059
absl::flat_hash_set<MeshFormat::AttributeId>& attribute_ids) {

ink/brush/brush_coat.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
#ifndef INK_STROKES_BRUSH_BRUSH_COAT_H_
1616
#define INK_STROKES_BRUSH_BRUSH_COAT_H_
1717

18+
#include <cstdint>
1819
#include <string>
1920

2021
#include "absl/container/flat_hash_set.h"
@@ -46,6 +47,10 @@ namespace brush_internal {
4647
// BrushFamily, and returns an error if not.
4748
absl::Status ValidateBrushCoat(const BrushCoat& coat);
4849

50+
// Calculates the minimum version of the Ink library that is required to use
51+
// this brush coat.
52+
int32_t CalculateMinimumRequiredVersion(const BrushCoat& coat);
53+
4954
// Adds the mesh attribute IDs that are required to properly render a mesh
5055
// made with this brush coat to the given `attribute_ids` set. This will always
5156
// include `kPosition` and certain other attribute IDs (`kSideDerivative`,

ink/brush/brush_family.cc

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414

1515
#include "ink/brush/brush_family.h"
1616

17+
#include <algorithm>
1718
#include <cstdint>
1819
#include <string>
1920
#include <variant>
@@ -94,6 +95,15 @@ std::string BrushFamily::ToFormattedString() const {
9495
return formatted;
9596
}
9697

98+
int32_t BrushFamily::CalculateMinimumRequiredVersion() const {
99+
int32_t max_version = 0;
100+
for (const BrushCoat& coat : coats_) {
101+
max_version = std::max(
102+
max_version, brush_internal::CalculateMinimumRequiredVersion(coat));
103+
}
104+
return max_version;
105+
}
106+
97107
namespace brush_internal {
98108
namespace {
99109

ink/brush/brush_family.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,10 @@ class BrushFamily {
152152
// otherwise used internally by Ink.
153153
const Metadata& GetMetadata() const;
154154

155+
// Calculates the minimum version of the Ink library that is required to use
156+
// this brush family.
157+
int32_t CalculateMinimumRequiredVersion() const;
158+
155159
template <typename Sink>
156160
friend void AbslStringify(Sink& sink, const BrushFamily& family) {
157161
sink.Append(family.ToFormattedString());

0 commit comments

Comments
 (0)