Roll external/skia 81b7e3df3..346f82c1c (17 commits)
https://skia.googlesource.com/skia.git/+log/81b7e3df3..346f82c1c
2019-06-04 mtklein@google.com print 1/K floats as fractions
2019-06-04 mtklein@google.com move peepholes where they're relevant
2019-06-04 fmalita@chromium.org [skottie] Initial text range selector support
2019-06-04 mtklein@google.com print SKVM test failures
2019-06-04 mtklein@google.com allow two immediates
2019-06-04 robertphillips@google.com Make color initialization version of createBackendTexture public
2019-06-04 mtklein@google.com add SkVMBuilders.* to DM deps in G3
2019-06-04 michaelludwig@google.com Extract GrQuadList into separate header
2019-06-04 borenet@google.com [infra] Fixes for chrome_release_branch
2019-06-04 mtklein@google.com dump register and instruction count
2019-06-04 mtklein@google.com add extract instruction
2019-06-04 mtklein@google.com add pack instruction
2019-06-04 recipe-mega-autoroller@chops-service-accounts.iam.gserviceaccount.com Roll recipe dependencies (trivial).
2019-06-04 mtklein@google.com add mul_unorm8 instruction
2019-06-04 mtklein@google.com centralize test/bench SkVM builders
2019-06-04 skia-autoroll@skia-public.iam.gserviceaccount.com Roll skia/third_party/skcms ca5b4470be98..386ae89d2b64 (1 commits)
2019-06-04 robertphillips@google.com Switch all internel uses of GrContext::createBackendTexture over to initialized versions
The AutoRoll server is located here: https://autoroll-internal.skia.org/r/android-master-autoroll
Documentation for the AutoRoller is here:
https://skia.googlesource.com/buildbot/+/master/autoroll/README.md
If the roll is causing failures, please contact the current sheriff, who should
be CC'd on the roll, and stop the roller if necessary.
Test: Presubmit checks will test this change.
Change-Id: I6938f1ea2314d732efc22076e5b4a889dd17650e
Exempt-From-Owner-Approval: The autoroll bot does not require owner approval.
diff --git a/Android.bp b/Android.bp
index 3b6dc6d..63316cc 100644
--- a/Android.bp
+++ b/Android.bp
@@ -1785,6 +1785,7 @@
"tools/LsanSuppressions.cpp",
"tools/ProcStats.cpp",
"tools/Resources.cpp",
+ "tools/SkVMBuilders.cpp",
"tools/ToolUtils.cpp",
"tools/UrlDataManager.cpp",
"tools/debugger/DebugCanvas.cpp",
@@ -2361,6 +2362,7 @@
"tools/LsanSuppressions.cpp",
"tools/ProcStats.cpp",
"tools/Resources.cpp",
+ "tools/SkVMBuilders.cpp",
"tools/ToolUtils.cpp",
"tools/UrlDataManager.cpp",
"tools/debugger/DebugCanvas.cpp",
diff --git a/BUILD.gn b/BUILD.gn
index b5efce4..3fba86e 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -1665,6 +1665,13 @@
}
}
+ test_lib("skvm_builders") {
+ sources = [
+ "tools/SkVMBuilders.cpp",
+ "tools/SkVMBuilders.h",
+ ]
+ }
+
import("gn/tests.gni")
test_lib("tests") {
sources = tests_sources + pathops_tests_sources
@@ -1681,6 +1688,7 @@
":experimental_svg_model",
":flags",
":skia",
+ ":skvm_builders",
":tool_utils",
"modules/skottie:tests",
"modules/sksg:tests",
@@ -1702,6 +1710,7 @@
":gm",
":gpu_tool_utils",
":skia",
+ ":skvm_builders",
":tool_utils",
]
}
diff --git a/bench/SkVMBench.cpp b/bench/SkVMBench.cpp
index eae0b70..32708ac 100644
--- a/bench/SkVMBench.cpp
+++ b/bench/SkVMBench.cpp
@@ -8,124 +8,13 @@
#include "bench/Benchmark.h"
#include "src/core/SkOpts.h"
#include "src/core/SkVM.h"
+#include "tools/SkVMBuilders.h"
namespace {
enum Mode {Opts, RP, F32, I32, I32_SWAR};
static const char* kMode_name[] = { "Opts", "RP","F32", "I32", "I32_SWAR" };
- struct SrcoverBuilder_F32 : public skvm::Builder {
- SrcoverBuilder_F32() {
-
- skvm::Arg src = arg(0),
- dst = arg(1);
-
- auto byte_to_f32 = [&](skvm::I32 byte) {
- return mul(splat(1/255.0f), to_f32(byte));
- };
- auto f32_to_byte = [&](skvm::F32 f32) {
- return to_i32(mad(f32, splat(255.0f), splat(0.5f)));
- };
-
- auto load = [&](skvm::Arg ptr,
- skvm::F32* r, skvm::F32* g, skvm::F32* b, skvm::F32* a) {
- skvm::I32 rgba = load32(ptr);
- *r = byte_to_f32(bit_and( rgba , splat(0xff)));
- *g = byte_to_f32(bit_and(shr(rgba, 8), splat(0xff)));
- *b = byte_to_f32(bit_and(shr(rgba, 16), splat(0xff)));
- *a = byte_to_f32( shr(rgba, 24) );
- };
-
- skvm::F32 r,g,b,a;
- load(src, &r,&g,&b,&a);
-
- skvm::F32 dr,dg,db,da;
- load(dst, &dr,&dg,&db,&da);
-
- skvm::F32 invA = sub(splat(1.0f), a);
- r = mad(dr, invA, r);
- g = mad(dg, invA, g);
- b = mad(db, invA, b);
- a = mad(da, invA, a);
-
- store32(dst, bit_or( f32_to_byte(r) ,
- bit_or(shl(f32_to_byte(g), 8),
- bit_or(shl(f32_to_byte(b), 16),
- shl(f32_to_byte(a), 24)))));
- }
- };
-
- struct SrcoverBuilder_I32 : public skvm::Builder {
- SrcoverBuilder_I32() {
- skvm::Arg src = arg(0),
- dst = arg(1);
-
- auto load = [&](skvm::Arg ptr,
- skvm::I32* r, skvm::I32* g, skvm::I32* b, skvm::I32* a) {
- skvm::I32 rgba = load32(ptr);
- *r = bit_and( rgba , splat(0xff));
- *g = bit_and(shr(rgba, 8), splat(0xff));
- *b = bit_and(shr(rgba, 16), splat(0xff));
- *a = shr(rgba, 24) ;
- };
-
- auto mul_unorm8 = [&](skvm::I32 x, skvm::I32 y) {
- // (x*y + 127)/255 ~= (x*y+255)/256
- return shr(add(mul(x, y), splat(0xff)), 8);
- };
-
- skvm::I32 r,g,b,a;
- load(src, &r,&g,&b,&a);
-
- skvm::I32 dr,dg,db,da;
- load(dst, &dr,&dg,&db,&da);
-
- skvm::I32 invA = sub(splat(0xff), a);
- r = add(r, mul_unorm8(dr, invA));
- g = add(g, mul_unorm8(dg, invA));
- b = add(b, mul_unorm8(db, invA));
- a = add(a, mul_unorm8(da, invA));
-
- store32(dst, bit_or( r ,
- bit_or(shl(g, 8),
- bit_or(shl(b, 16),
- shl(a, 24)))));
- }
- };
-
- struct SrcoverBuilder_I32_SWAR : public skvm::Builder {
- SrcoverBuilder_I32_SWAR() {
- skvm::Arg src = arg(0),
- dst = arg(1);
-
- auto load = [&](skvm::Arg ptr,
- skvm::I32* rb, skvm::I32* ga) {
- skvm::I32 rgba = load32(ptr);
- *rb = bit_and( rgba, splat(0x00ff00ff));
- *ga = bit_and(shr(rgba, 8), splat(0x00ff00ff));
- };
-
- auto mul_unorm8 = [&](skvm::I32 x, skvm::I32 y) {
- // As above, assuming x is two SWAR bytes in lanes 0 and 2, and y is a byte.
- return bit_and(shr(add(mul(x, y),
- splat(0x00ff00ff)),
- 8),
- splat(0x00ff00ff));
- };
-
- skvm::I32 rb, ga;
- load(src, &rb, &ga);
-
- skvm::I32 drb, dga;
- load(dst, &drb, &dga);
-
- skvm::I32 invA = sub(splat(0xff), shr(ga, 16));
- rb = add(rb, mul_unorm8(drb, invA));
- ga = add(ga, mul_unorm8(dga, invA));
-
- store32(dst, bit_or(rb, shl(ga, 8)));
- }
- };
}
class SkVMBench : public Benchmark {
diff --git a/dm/DMSrcSink.cpp b/dm/DMSrcSink.cpp
index 26373a1..473be2b 100644
--- a/dm/DMSrcSink.cpp
+++ b/dm/DMSrcSink.cpp
@@ -1389,7 +1389,7 @@
break;
case SkCommandLineConfigGpu::SurfType::kBackendTexture:
backendTexture = context->createBackendTexture(
- info.width(), info.height(), info.colorType(),
+ info.width(), info.height(), info.colorType(), SkColors::kTransparent,
GrMipMapped::kNo, GrRenderable::kYes);
surface = SkSurface::MakeFromBackendTexture(context, backendTexture,
kTopLeft_GrSurfaceOrigin, fSampleCount,
diff --git a/gm/imagefromyuvtextures.cpp b/gm/imagefromyuvtextures.cpp
index 53a828a..9db63e4 100644
--- a/gm/imagefromyuvtextures.cpp
+++ b/gm/imagefromyuvtextures.cpp
@@ -143,7 +143,8 @@
void createResultTexture(GrContext* context, int width, int height,
GrBackendTexture* resultTexture) {
*resultTexture = context->createBackendTexture(
- width, height, kRGBA_8888_SkColorType, GrMipMapped::kNo, GrRenderable::kYes);
+ width, height, kRGBA_8888_SkColorType, SkColors::kTransparent,
+ GrMipMapped::kNo, GrRenderable::kYes);
context->resetContext();
}
diff --git a/gn/gpu.gni b/gn/gpu.gni
index 9d012f0..78224cf 100644
--- a/gn/gpu.gni
+++ b/gn/gpu.gni
@@ -345,6 +345,7 @@
"$_src/gpu/geometry/GrPathUtils.h",
"$_src/gpu/geometry/GrQuad.cpp",
"$_src/gpu/geometry/GrQuad.h",
+ "$_src/gpu/geometry/GrQuadList.h",
"$_src/gpu/geometry/GrRect.h",
"$_src/gpu/geometry/GrShape.cpp",
"$_src/gpu/geometry/GrShape.h",
diff --git a/include/gpu/GrContext.h b/include/gpu/GrContext.h
index 850e157..f5914d9 100644
--- a/include/gpu/GrContext.h
+++ b/include/gpu/GrContext.h
@@ -346,8 +346,10 @@
* before deleting the GrContext used to create them. Additionally, clients should only
* delete these objects on the thread for which that GrContext is active.
*
- * Additionally, the client is responsible for ensuring synchronization between different uses
- * of the backend object.
+ * The client is responsible for ensuring synchronization between different uses
+ * of the backend object (i.e., wrapping it in a surface, rendering to it, deleting the
+ * surface, rewrapping it in a image and drawing the image will require explicit
+ * sychronization on the client's part).
*/
// If possible, create an uninitialized backend texture. The client should ensure that the
@@ -366,6 +368,20 @@
GrMipMapped,
GrRenderable);
+ // If possible, create a backend texture initialized to a particular color. The client should
+ // ensure that the returned backend texture is valid.
+ GrBackendTexture createBackendTexture(int width, int height,
+ GrBackendFormat, const SkColor4f& color,
+ GrMipMapped, GrRenderable);
+
+ // If possible, create a backend texture initialized to a particular color. The client should
+ // ensure that the returned backend texture is valid.
+ // If successful, the created backend texture will be compatible with the provided
+ // SkColorType.
+ GrBackendTexture createBackendTexture(int width, int height,
+ SkColorType, const SkColor4f& color,
+ GrMipMapped, GrRenderable);
+
void deleteBackendTexture(GrBackendTexture);
protected:
diff --git a/infra/config/recipes.cfg b/infra/config/recipes.cfg
index 19bae32..23c58a9 100644
--- a/infra/config/recipes.cfg
+++ b/infra/config/recipes.cfg
@@ -14,7 +14,7 @@
"deps": {
"depot_tools": {
"branch": "master",
- "revision": "0f476788122d4b40b0293226e193e534ec66cad6",
+ "revision": "2c48f24c7f88f2f09821db492e8cc9a4361ded58",
"url": "https://chromium.googlesource.com/chromium/tools/depot_tools.git"
},
"recipe_engine": {
diff --git a/modules/skottie/skottie.gni b/modules/skottie/skottie.gni
index 6a1488e..dc9c418 100644
--- a/modules/skottie/skottie.gni
+++ b/modules/skottie/skottie.gni
@@ -28,6 +28,8 @@
"$_src/SkottieValue.cpp",
"$_src/SkottieValue.h",
+ "$_src/text/RangeSelector.cpp",
+ "$_src/text/RangeSelector.h",
"$_src/text/SkottieShaper.cpp",
"$_src/text/SkottieShaper.h",
"$_src/text/TextAdapter.cpp",
diff --git a/modules/skottie/src/text/RangeSelector.cpp b/modules/skottie/src/text/RangeSelector.cpp
new file mode 100644
index 0000000..d2cd7f0
--- /dev/null
+++ b/modules/skottie/src/text/RangeSelector.cpp
@@ -0,0 +1,201 @@
+/*
+ * Copyright 2019 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "modules/skottie/src/text/RangeSelector.h"
+
+#include "modules/skottie/src/SkottieJson.h"
+#include "modules/skottie/src/SkottieValue.h"
+
+#include <algorithm>
+#include <cmath>
+
+namespace skottie {
+namespace internal {
+
+namespace {
+
+// Maps a 1-based JSON enum to one of the values in the array.
+template <typename T, typename TArray>
+T ParseEnum(const TArray& arr, const skjson::Value& jenum,
+ const AnimationBuilder* abuilder, const char* warn_name) {
+
+ const auto idx = ParseDefault<int>(jenum, 1);
+
+ if (idx > 0 && SkToSizeT(idx) <= SK_ARRAY_COUNT(arr)) {
+ return arr[idx - 1];
+ }
+
+ abuilder->log(Logger::Level::kWarning, nullptr,
+ "Ignoring unknown range selector %s '%d'", warn_name, idx);
+
+ static_assert(SK_ARRAY_COUNT(arr) > 0, "");
+ return arr[0];
+}
+
+template <RangeSelector::Units>
+struct UnitTraits;
+
+template <>
+struct UnitTraits<RangeSelector::Units::kPercentage> {
+ static constexpr auto Defaults() {
+ return std::make_tuple<float, float, float>(0, 100, 0);
+ }
+
+ static auto Resolve(float s, float e, float o, size_t domain_size) {
+ return std::make_tuple(domain_size * (s + o) / 100,
+ domain_size * (e + o) / 100);
+ }
+};
+
+template <>
+struct UnitTraits<RangeSelector::Units::kIndex> {
+ static constexpr auto Defaults() {
+ // It's OK to default fEnd to FLOAT_MAX, as it gets clamped when resolved.
+ return std::make_tuple<float, float, float>(0, std::numeric_limits<float>::max(), 0);
+ }
+
+ static auto Resolve(float s, float e, float o, size_t domain_size) {
+ return std::make_tuple(s + o, e + o);
+ }
+};
+
+template <RangeSelector::Mode>
+struct ModeTraits;
+
+template <>
+struct ModeTraits<RangeSelector::Mode::kAdd> {
+ static void modulate(TextAnimator::AnimatedPropsModulator* c_begin,
+ const TextAnimator::AnimatedPropsModulator* c_end, float amount) {
+ if (!amount) return; // 0 -> noop
+
+ for (auto c = c_begin; c < c_end; ++c) {
+ c->coverage = SkTPin<float>(c->coverage + amount, -1, 1);
+ }
+ }
+};
+
+} // namespace
+
+sk_sp<RangeSelector> RangeSelector::Make(const skjson::ObjectValue* jrange,
+ const AnimationBuilder* abuilder,
+ AnimatorScope *ascope) {
+ if (!jrange) {
+ return nullptr;
+ }
+
+ static constexpr Units gUnitMap[] = {
+ Units::kPercentage, // 'r': 1
+ Units::kIndex, // 'r': 2
+ };
+
+ static constexpr Domain gDomainMap[] = {
+ Domain::kChars, // 'b': 1
+ };
+
+ static constexpr Mode gModeMap[] = {
+ Mode::kAdd, // 'm': 1
+ };
+
+ auto selector = sk_sp<RangeSelector>(
+ new RangeSelector(ParseEnum<Units> (gUnitMap , (*jrange)["r"], abuilder, "units" ),
+ ParseEnum<Domain>(gDomainMap, (*jrange)["b"], abuilder, "domain"),
+ ParseEnum<Mode> (gModeMap , (*jrange)["m"], abuilder, "mode" )));
+
+ abuilder->bindProperty<ScalarValue>((*jrange)["s"], ascope,
+ [selector](const ScalarValue& s) {
+ selector->fStart = s;
+ });
+ abuilder->bindProperty<ScalarValue>((*jrange)["e"], ascope,
+ [selector](const ScalarValue& e) {
+ selector->fEnd = e;
+ });
+ abuilder->bindProperty<ScalarValue>((*jrange)["o"], ascope,
+ [selector](const ScalarValue& o) {
+ selector->fOffset = o;
+ });
+ abuilder->bindProperty<ScalarValue>((*jrange)["a"], ascope,
+ [selector](const ScalarValue& a) {
+ selector->fAmount = a;
+ });
+
+ return selector;
+}
+
+RangeSelector::RangeSelector(Units u, Domain d, Mode m)
+ : fUnits(u)
+ , fDomain(d)
+ , fMode(m) {
+
+ // Range defaults are unit-specific.
+ switch (fUnits) {
+ case Units::kPercentage:
+ std::tie(fStart, fEnd, fOffset) = UnitTraits<Units::kPercentage>::Defaults();
+ break;
+ case Units::kIndex:
+ std::tie(fStart, fEnd, fOffset) = UnitTraits<Units::kIndex >::Defaults();
+ break;
+ }
+}
+
+std::tuple<float, float> RangeSelector::resolve(size_t len) const {
+ float f_i0, f_i1;
+
+ SkASSERT(fUnits == Units::kPercentage || fUnits == Units::kIndex);
+ const auto resolver = (fUnits == Units::kPercentage)
+ ? UnitTraits<Units::kPercentage>::Resolve
+ : UnitTraits<Units::kIndex >::Resolve;
+
+ std::tie(f_i0, f_i1) = resolver(fStart, fEnd, fOffset, len);
+ if (f_i0 > f_i1) {
+ std::swap(f_i0, f_i1);
+ }
+
+ return std::make_tuple(SkTPin<float>(f_i0, 0, len),
+ SkTPin<float>(f_i1, 0, len));
+}
+
+void RangeSelector::modulateCoverage(TextAnimator::ModulatorBuffer& buf) const {
+ SkASSERT(!buf.empty());
+
+ // Amount is percentage based [-100% .. 100%].
+ const auto amount = SkTPin<float>(fAmount / 100, -1, 1);
+
+ // First, resolve to a float range in the given domain.
+ SkAssertResult(fDomain == Domain::kChars);
+ const auto f_range = this->resolve(buf.size());
+
+ // Figure out the integral/index coverage range.
+ const auto i0 = std::min<size_t>(std::get<0>(f_range), buf.size() - 1),
+ i1 = std::min<size_t>(std::get<1>(f_range), buf.size() - 1);
+
+ SkAssertResult(fMode == Mode::kAdd);
+ const auto modulate = ModeTraits<Mode::kAdd>::modulate;
+
+ // Apply coverage modulation across different domain segments.
+ modulate(buf.data() , buf.data() + i0 , 0); // [0 ..i0) -> zero coverage.
+ modulate(buf.data() + i0 + 1, buf.data() + i1 , amount); // (i0..i1) -> full coverage.
+ modulate(buf.data() + i1 + 1, buf.data() + buf.size(), 0); // (i1.. N] -> zero coverage.
+
+ // For i0 and i1 we have fractional coverage.
+ const auto fract_0 = 1 - (std::get<0>(f_range) - i0),
+ fract_1 = std::get<1>(f_range) - i1;
+ SkASSERT(fract_0 >= 0 && fract_0 <= 1);
+ SkASSERT(fract_1 >= 0 && fract_1 <= 1);
+
+ if (i0 == i1) {
+ // The range falls within a single index.
+ SkASSERT(fract_0 + fract_1 >= 1);
+ modulate(buf.data() + i0, buf.data() + i0 + 1, (fract_0 + fract_1 - 1) * amount);
+ } else {
+ // Separate indices for i0, i1.
+ modulate(buf.data() + i0, buf.data() + i0 + 1, fract_0 * amount);
+ modulate(buf.data() + i1, buf.data() + i1 + 1, fract_1 * amount);
+ }
+}
+
+} // namespace internal
+} // namespace skottie
diff --git a/modules/skottie/src/text/RangeSelector.h b/modules/skottie/src/text/RangeSelector.h
new file mode 100644
index 0000000..d78534b
--- /dev/null
+++ b/modules/skottie/src/text/RangeSelector.h
@@ -0,0 +1,69 @@
+/*
+ * Copyright 2019 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef SkottieRangeSelector_DEFINED
+#define SkottieRangeSelector_DEFINED
+
+#include "include/core/SkRefCnt.h"
+#include "modules/skottie/src/SkottiePriv.h"
+#include "modules/skottie/src/text/TextAnimator.h"
+
+#include <tuple>
+#include <vector>
+
+namespace skottie {
+namespace internal {
+
+class RangeSelector final : public SkNVRefCnt<RangeSelector> {
+public:
+ static sk_sp<RangeSelector> Make(const skjson::ObjectValue*,
+ const AnimationBuilder*,
+ AnimatorScope* ascope);
+
+ enum class Units : uint8_t {
+ kPercentage, // values are percentages of domain size
+ kIndex, // values are direct domain indices
+ };
+
+ enum class Domain : uint8_t {
+ kChars, // domain indices map to glyph indices
+ // kCharsExcludingSpaces, // domain indices map to glyph indices (ignoring spaces)
+ // kWords, // domain indices map to word indices
+ // kLines, // domain indices map to line indices
+ };
+
+ enum class Mode : uint8_t {
+ kAdd,
+ // kSubtract,
+ // kIntersect,
+ // kMin,
+ // kMax,
+ // kDifference,
+ };
+
+ void modulateCoverage(TextAnimator::ModulatorBuffer&) const;
+
+private:
+ RangeSelector(Units, Domain, Mode);
+
+ // Resolves this selector to a range in the coverage buffer index domain.
+ std::tuple<float, float> resolve(size_t domain_size) const;
+
+ const Units fUnits;
+ const Domain fDomain;
+ const Mode fMode;
+
+ float fStart,
+ fEnd,
+ fOffset,
+ fAmount = 100;
+};
+
+} // namespace internal
+} // namespace skottie
+
+#endif // SkottieRangeSelector_DEFINED
diff --git a/modules/skottie/src/text/TextAdapter.cpp b/modules/skottie/src/text/TextAdapter.cpp
index 899778b..993657b 100644
--- a/modules/skottie/src/text/TextAdapter.cpp
+++ b/modules/skottie/src/text/TextAdapter.cpp
@@ -6,6 +6,9 @@
*/
#include "modules/skottie/src/text/TextAdapter.h"
+
+#include "modules/skottie/src/text/RangeSelector.h"
+#include "modules/skottie/src/text/TextAnimator.h"
#include "modules/sksg/include/SkSGDraw.h"
#include "modules/sksg/include/SkSGGroup.h"
#include "modules/sksg/include/SkSGPaint.h"
@@ -14,6 +17,7 @@
#include "modules/sksg/include/SkSGTransform.h"
namespace skottie {
+namespace internal {
TextAdapter::TextAdapter(sk_sp<sksg::Group> root, bool hasAnimators)
: fRoot(std::move(root))
@@ -117,29 +121,52 @@
#endif
}
-void TextAdapter::applyAnimatedProps(const AnimatedProps& props) {
- // TODO: share this with TransformAdapter2D?
- auto t = SkMatrix::MakeTrans(props.position.x(), props.position.y());
- t.preRotate(props.rotation);
- t.preScale(props.scale, props.scale);
+void TextAdapter::applyAnimators(const std::vector<sk_sp<TextAnimator>>& animators) {
+ if (fFragments.empty()) {
+ return;
+ }
- const auto fc = SkColorSetA(props.fill_color,
- SkScalarRoundToInt(props.opacity*SkColorGetA(props.fill_color))),
- sc = SkColorSetA(props.stroke_color,
- SkScalarRoundToInt(props.opacity*SkColorGetA(props.stroke_color)));
+ const auto& txt_val = this->getText();
- for (const auto& rec : fFragments) {
- rec.fMatrixNode->setMatrix(SkMatrix::Concat(SkMatrix::MakeTrans(rec.fOrigin.x(),
- rec.fOrigin.y()),
- t));
+ // Seed props from the current text value.
+ TextAnimator::AnimatedProps seed_props;
+ seed_props.fill_color = txt_val.fFillColor;
+ seed_props.stroke_color = txt_val.fStrokeColor;
- if (rec.fFillColorNode) {
- rec.fFillColorNode->setColor(fc);
- }
- if (rec.fStrokeColorNode) {
- rec.fStrokeColorNode->setColor(sc);
- }
+ TextAnimator::ModulatorBuffer buf;
+ buf.resize(fFragments.size(), { seed_props, 0 });
+
+ // Apply all animators to the modulator buffer.
+ for (const auto& animator : animators) {
+ animator->modulateProps(buf);
+ }
+
+ // Finally, push all props to their corresponding fragment.
+ for (size_t i = 0; i < fFragments.size(); ++i) {
+ this->pushPropsToFragment(buf[i].props, fFragments[i]);
}
}
+void TextAdapter::pushPropsToFragment(const TextAnimator::AnimatedProps& props,
+ const FragmentRec& rec) const {
+ // TODO: share this with TransformAdapter2D?
+ auto t = SkMatrix::MakeTrans(rec.fOrigin.x() + props.position.x(),
+ rec.fOrigin.y() + props.position.y());
+ t.preRotate(props.rotation);
+ t.preScale(props.scale, props.scale);
+ rec.fMatrixNode->setMatrix(t);
+
+ const auto scale_alpha = [](SkColor c, float o) {
+ return SkColorSetA(c, SkScalarRoundToInt(o * SkColorGetA(c)));
+ };
+
+ if (rec.fFillColorNode) {
+ rec.fFillColorNode->setColor(scale_alpha(props.fill_color, props.opacity));
+ }
+ if (rec.fStrokeColorNode) {
+ rec.fStrokeColorNode->setColor(scale_alpha(props.stroke_color, props.opacity));
+ }
+}
+
+} // namespace internal
} // namespace skottie
diff --git a/modules/skottie/src/text/TextAdapter.h b/modules/skottie/src/text/TextAdapter.h
index 916392b..fb9da69 100644
--- a/modules/skottie/src/text/TextAdapter.h
+++ b/modules/skottie/src/text/TextAdapter.h
@@ -10,6 +10,7 @@
#include "modules/skottie/src/SkottieAdapter.h"
#include "modules/skottie/src/text/SkottieShaper.h"
+#include "modules/skottie/src/text/TextAnimator.h"
#include "modules/skottie/src/text/TextValue.h"
#include <vector>
@@ -19,6 +20,7 @@
} // namespace sksg
namespace skottie {
+namespace internal {
class TextAdapter final : public SkNVRefCnt<TextAdapter> {
public:
@@ -29,16 +31,7 @@
const sk_sp<sksg::Group>& root() const { return fRoot; }
- struct AnimatedProps {
- SkPoint position = { 0, 0 };
- SkColor fill_color = SK_ColorTRANSPARENT,
- stroke_color = SK_ColorTRANSPARENT;
- float opacity = 1,
- scale = 1,
- rotation = 0;
- };
-
- void applyAnimatedProps(const AnimatedProps&);
+ void applyAnimators(const std::vector<sk_sp<TextAnimator>>&);
private:
struct FragmentRec;
@@ -47,12 +40,15 @@
void apply();
+ void pushPropsToFragment(const TextAnimator::AnimatedProps&, const FragmentRec&) const;
+
sk_sp<sksg::Group> fRoot;
std::vector<FragmentRec> fFragments;
const bool fHasAnimators;
};
+} // namespace internal
} // namespace skottie
#endif // SkottieTextAdapter_DEFINED
diff --git a/modules/skottie/src/text/TextAnimator.cpp b/modules/skottie/src/text/TextAnimator.cpp
index c6c4da6..c9a868a 100644
--- a/modules/skottie/src/text/TextAnimator.cpp
+++ b/modules/skottie/src/text/TextAnimator.cpp
@@ -9,7 +9,8 @@
#include "include/core/SkColor.h"
#include "include/core/SkPoint.h"
-#include "modules/skottie/src/SkottiePriv.h"
+#include "include/private/SkNx.h"
+#include "modules/skottie/src/text/RangeSelector.h"
#include "modules/skottie/src/text/TextAdapter.h"
#include "src/utils/SkJSON.h"
@@ -56,95 +57,128 @@
* ...
* }
*/
-class TextAnimator final : public SkNVRefCnt<TextAnimator> {
-public:
- static sk_sp<TextAnimator> Make(const skjson::ObjectValue* janimator,
- const AnimationBuilder* abuilder,
- AnimatorScope* ascope) {
- if (!janimator) {
- return nullptr;
- }
-
- if (const skjson::ObjectValue* jselector = (*janimator)["s"]) {
- abuilder->log(Logger::Level::kWarning, jselector, "Unsupported text range selector.");
- }
-
- const skjson::ObjectValue* jprops = (*janimator)["a"];
-
- return jprops
- ? sk_sp<TextAnimator>(new TextAnimator(*jprops, abuilder, ascope))
- : nullptr;
+sk_sp<TextAnimator> TextAnimator::Make(const skjson::ObjectValue* janimator,
+ const AnimationBuilder* abuilder,
+ AnimatorScope* ascope) {
+ if (!janimator) {
+ return nullptr;
}
- void modulateProps(TextAdapter::AnimatedProps* dst) const {
- // Transform props compose.
- if (fHasPosition) {
- dst->position += fTextProps.position;
- }
- if (fHasScale) {
- dst->scale *= fTextProps.scale;
- }
- if (fHasRotation) {
- dst->rotation += fTextProps.rotation;
- }
-
- // Colors and opacity are overridden.
- if (fHasFillColor) {
- dst->fill_color = fTextProps.fill_color;
- }
- if (fHasStrokeColor) {
- dst->stroke_color = fTextProps.stroke_color;
- }
- if (fHasOpacity) {
- dst->opacity = fTextProps.opacity;
+ std::vector<sk_sp<RangeSelector>> selectors;
+ if (const skjson::ObjectValue* jselector = (*janimator)["s"]) {
+ // Single range selector for now.
+ if (auto sel = RangeSelector::Make(jselector, abuilder, ascope)) {
+ selectors.reserve(1);
+ selectors.push_back(std::move(sel));
}
}
-private:
- TextAnimator(const skjson::ObjectValue& jprops,
- const AnimationBuilder* abuilder,
- AnimatorScope* ascope) {
- // It's *probably* OK to capture a raw pointer to this animator, because the lambda
- // life time is limited to |ascope|, which is also the container for the TextAnimatorList
- // owning us. But for peace of mind (and future-proofing) let's grab a ref.
- auto animator = sk_ref_sp(this);
+ const skjson::ObjectValue* jprops = (*janimator)["a"];
- fHasPosition = abuilder->bindProperty<VectorValue>(jprops["p"], ascope,
- [animator](const VectorValue& p) {
- animator->fTextProps.position = ValueTraits<VectorValue>::As<SkPoint>(p);
- });
- fHasScale = abuilder->bindProperty<ScalarValue>(jprops["s"], ascope,
- [animator](const ScalarValue& s) {
- // Scale is 100-based.
- animator->fTextProps.scale = s * 0.01f;
- });
- fHasRotation = abuilder->bindProperty<ScalarValue>(jprops["r"], ascope,
- [animator](const ScalarValue& r) {
- animator->fTextProps.rotation = r;
- });
- fHasFillColor = abuilder->bindProperty<VectorValue>(jprops["fc"], ascope,
- [animator](const VectorValue& fc) {
- animator->fTextProps.fill_color = ValueTraits<VectorValue>::As<SkColor>(fc);
- });
- fHasStrokeColor = abuilder->bindProperty<VectorValue>(jprops["sc"], ascope,
- [animator](const VectorValue& sc) {
- animator->fTextProps.stroke_color = ValueTraits<VectorValue>::As<SkColor>(sc);
- });
- fHasOpacity = abuilder->bindProperty<ScalarValue>(jprops["o"], ascope,
- [animator](const ScalarValue& o) {
- // Opacity is 100-based.
- animator->fTextProps.opacity = SkTPin<float>(o * 0.01f, 0, 1);
- });
+ return jprops
+ ? sk_sp<TextAnimator>(new TextAnimator(std::move(selectors), *jprops, abuilder, ascope))
+ : nullptr;
+}
+
+void TextAnimator::modulateProps(ModulatorBuffer& buf) const {
+ // Coverage is scoped per animator.
+ for (auto& mod : buf) {
+ mod.coverage = 0;
}
- TextAdapter::AnimatedProps fTextProps;
- bool fHasPosition : 1,
- fHasScale : 1,
- fHasRotation : 1,
- fHasFillColor : 1,
- fHasStrokeColor : 1,
- fHasOpacity : 1;
-};
+ // Accumulate selector coverage.
+ for (const auto& selector : fSelectors) {
+ selector->modulateCoverage(buf);
+ }
+
+ // Modulate animated props.
+ for (auto& mod : buf) {
+ mod.props = this->modulateProps(mod.props, mod.coverage);
+ }
+}
+
+TextAnimator::AnimatedProps TextAnimator::modulateProps(const AnimatedProps& props,
+ float amount) const {
+ auto modulated_props = props;
+
+ // Transform props compose.
+ if (fHasPosition) {
+ modulated_props.position += fTextProps.position * amount;
+ }
+ if (fHasScale) {
+ modulated_props.scale *= 1 + (fTextProps.scale - 1) * amount;
+ }
+ if (fHasRotation) {
+ modulated_props.rotation += fTextProps.rotation * amount;
+ }
+
+ const auto lerp_color = [](SkColor c0, SkColor c1, float t) {
+ const auto c0_4f = SkNx_cast<float>(Sk4b::Load(&c0)),
+ c1_4f = SkNx_cast<float>(Sk4b::Load(&c1)),
+ c_4f = c0_4f + (c1_4f - c0_4f) * t;
+
+ SkColor c;
+ SkNx_cast<uint8_t>(Sk4f_round(c_4f)).store(&c);
+ return c;
+ };
+
+ // Colors and opacity are overridden, and use a clamped amount value.
+ const auto clamped_amount = std::max(amount, 0.0f);
+ if (fHasFillColor) {
+ modulated_props.fill_color = lerp_color(props.fill_color,
+ fTextProps.fill_color,
+ clamped_amount);
+ }
+ if (fHasStrokeColor) {
+ modulated_props.stroke_color = lerp_color(props.stroke_color,
+ fTextProps.stroke_color,
+ clamped_amount);
+ }
+ if (fHasOpacity) {
+ modulated_props.opacity = 1 + (fTextProps.opacity - 1) * clamped_amount;
+ }
+
+ return modulated_props;
+}
+
+TextAnimator::TextAnimator(std::vector<sk_sp<RangeSelector>>&& selectors,
+ const skjson::ObjectValue& jprops,
+ const AnimationBuilder* abuilder,
+ AnimatorScope* ascope)
+ : fSelectors(std::move(selectors)) {
+
+ // It's *probably* OK to capture a raw pointer to this animator, because the lambda
+ // life time is limited to |ascope|, which is also the container for the TextAnimatorList
+ // owning us. But for peace of mind (and future-proofing) let's grab a ref.
+ auto animator = sk_ref_sp(this);
+
+ fHasPosition = abuilder->bindProperty<VectorValue>(jprops["p"], ascope,
+ [animator](const VectorValue& p) {
+ animator->fTextProps.position = ValueTraits<VectorValue>::As<SkPoint>(p);
+ });
+ fHasScale = abuilder->bindProperty<ScalarValue>(jprops["s"], ascope,
+ [animator](const ScalarValue& s) {
+ // Scale is 100-based.
+ animator->fTextProps.scale = s * 0.01f;
+ });
+ fHasRotation = abuilder->bindProperty<ScalarValue>(jprops["r"], ascope,
+ [animator](const ScalarValue& r) {
+ animator->fTextProps.rotation = r;
+ });
+ fHasFillColor = abuilder->bindProperty<VectorValue>(jprops["fc"], ascope,
+ [animator](const VectorValue& fc) {
+ animator->fTextProps.fill_color = ValueTraits<VectorValue>::As<SkColor>(fc);
+ });
+ fHasStrokeColor = abuilder->bindProperty<VectorValue>(jprops["sc"], ascope,
+ [animator](const VectorValue& sc) {
+ animator->fTextProps.stroke_color = ValueTraits<VectorValue>::As<SkColor>(sc);
+ });
+ fHasOpacity = abuilder->bindProperty<ScalarValue>(jprops["o"], ascope,
+ [animator](const ScalarValue& o) {
+ // Opacity is 100-based.
+ animator->fTextProps.opacity = SkTPin<float>(o * 0.01f, 0, 1);
+ });
+}
std::unique_ptr<TextAnimatorList> TextAnimatorList::Make(const skjson::ArrayValue& janimators,
const AnimationBuilder* abuilder,
@@ -182,22 +216,7 @@
this->INHERITED::onTick(t);
// Then push the final property values to the text adapter.
- this->applyAnimators();
-}
-
-void TextAnimatorList::applyAnimators() const {
- const auto& txt_val = fAdapter->getText();
-
- // Seed props from the current text value.
- TextAdapter::AnimatedProps modulated_props;
- modulated_props.fill_color = txt_val.fFillColor;
- modulated_props.stroke_color = txt_val.fStrokeColor;
-
- for (const auto& animator : fAnimators) {
- animator->modulateProps(&modulated_props);
- }
-
- fAdapter->applyAnimatedProps(modulated_props);
+ fAdapter->applyAnimators(fAnimators);
}
} // namespace internal
diff --git a/modules/skottie/src/text/TextAnimator.h b/modules/skottie/src/text/TextAnimator.h
index 04a8f21..d48a7f7 100644
--- a/modules/skottie/src/text/TextAnimator.h
+++ b/modules/skottie/src/text/TextAnimator.h
@@ -8,24 +8,62 @@
#ifndef SkottieTextAnimator_DEFINED
#define SkottieTextAnimator_DEFINED
+#include "include/core/SkRefCnt.h"
#include "modules/skottie/src/SkottieAdapter.h"
+#include "modules/skottie/src/SkottiePriv.h"
#include "modules/sksg/include/SkSGScene.h"
#include <memory>
#include <vector>
-namespace skjson {
-class ArrayValue;
-}
-
namespace skottie {
-
-class TextAdapter;
-
namespace internal {
class AnimationBuilder;
-class TextAnimator;
+class RangeSelector;
+class TextAdapter;
+
+class TextAnimator final : public SkNVRefCnt<TextAnimator> {
+public:
+ static sk_sp<TextAnimator> Make(const skjson::ObjectValue*,
+ const AnimationBuilder*,
+ AnimatorScope*);
+
+ struct AnimatedProps {
+ SkPoint position = { 0, 0 };
+ float opacity = 1,
+ scale = 1,
+ rotation = 0;
+ SkColor fill_color = SK_ColorTRANSPARENT,
+ stroke_color = SK_ColorTRANSPARENT;
+ };
+
+ struct AnimatedPropsModulator {
+ AnimatedProps props; // accumulates properties across *all* animators
+ float coverage; // accumulates range selector coverage for a given animator
+ };
+ using ModulatorBuffer = std::vector<AnimatedPropsModulator>;
+
+ void modulateProps(ModulatorBuffer&) const;
+
+private:
+ TextAnimator(std::vector<sk_sp<RangeSelector>>&& selectors,
+ const skjson::ObjectValue& jprops,
+ const AnimationBuilder* abuilder,
+ AnimatorScope* ascope);
+
+ AnimatedProps modulateProps(const AnimatedProps&, float amount) const;
+
+ const std::vector<sk_sp<RangeSelector>> fSelectors;
+
+ AnimatedProps fTextProps;
+ bool fHasPosition : 1,
+ fHasScale : 1,
+ fHasRotation : 1,
+ fHasFillColor : 1,
+ fHasStrokeColor : 1,
+ fHasOpacity : 1;
+};
class TextAnimatorList final : public sksg::GroupAnimator {
public:
@@ -40,8 +78,6 @@
private:
TextAnimatorList(sk_sp<TextAdapter>, sksg::AnimatorList&&, std::vector<sk_sp<TextAnimator>>&&);
- void applyAnimators() const;
-
const std::vector<sk_sp<TextAnimator>> fAnimators;
const sk_sp<TextAdapter> fAdapter;
diff --git a/public.bzl b/public.bzl
index 2b52e15..7676751 100644
--- a/public.bzl
+++ b/public.bzl
@@ -474,6 +474,8 @@
"tools/ResourceFactory.h",
"tools/Resources.cpp",
"tools/Resources.h",
+ "tools/SkVMBuilders.cpp",
+ "tools/SkVMBuilders.h",
"tools/UrlDataManager.cpp",
"tools/UrlDataManager.h",
"tools/debugger/*.cpp",
diff --git a/resources/SkVMTest.expected b/resources/SkVMTest.expected
index b40149b..f4c80a8 100644
--- a/resources/SkVMTest.expected
+++ b/resources/SkVMTest.expected
@@ -1,6 +1,7 @@
A8 over A8
+3 registers, 15 instructions:
r0 = load8 arg(0)
-r1 = splat 3B808081 (0.0039215689)
+r1 = splat 3B808081 (1/255)
r0 = to_f32 r0
r0 = mul_f32 r1 r0
r2 = load8 arg(1)
@@ -10,14 +11,15 @@
r1 = sub_f32 r1 r0
r1 = mad_f32 r2 r1 r0
r2 = splat 437F0000 (255)
-r0 = splat 3F000000 (0.5)
+r0 = splat 3F000000 (1/2)
r0 = mad_f32 r1 r2 r0
r0 = to_i32 r0
store8 arg(1) r0
A8 over G8
+4 registers, 21 instructions:
r0 = load8 arg(0)
-r1 = splat 3B808081 (0.0039215689)
+r1 = splat 3B808081 (1/255)
r0 = to_f32 r0
r0 = mul_f32 r1 r0
r2 = load8 arg(1)
@@ -33,58 +35,54 @@
r3 = mad_f32 r1 r0 r3
r3 = mad_f32 r1 r2 r3
r2 = splat 437F0000 (255)
-r1 = splat 3F000000 (0.5)
+r1 = splat 3F000000 (1/2)
r1 = mad_f32 r3 r2 r1
r1 = to_i32 r1
store8 arg(1) r1
A8 over RGBA_8888
+6 registers, 37 instructions:
r0 = load8 arg(0)
-r1 = splat 3B808081 (0.0039215689)
+r1 = splat 3B808081 (1/255)
r0 = to_f32 r0
r0 = mul_f32 r1 r0
r2 = load32 arg(1)
-r3 = splat FF (3.5733111e-43)
-r4 = bit_and r2 r3
+r3 = extract r2 FF
+r3 = to_f32 r3
+r3 = mul_f32 r1 r3
+r4 = extract r2 FF00
r4 = to_f32 r4
r4 = mul_f32 r1 r4
-r5 = shr r2 8 (1.1210388e-44)
-r5 = bit_and r5 r3
+r5 = extract r2 FF0000
r5 = to_f32 r5
r5 = mul_f32 r1 r5
-r6 = shr r2 10 (2.2420775e-44)
-r6 = bit_and r6 r3
-r6 = to_f32 r6
-r6 = mul_f32 r1 r6
-r2 = shr r2 18 (3.3631163e-44)
+r2 = shr r2 24
r2 = to_f32 r2
r2 = mul_f32 r1 r2
r1 = splat 3F800000 (1)
r1 = sub_f32 r1 r0
+r3 = mul_f32 r3 r1
r4 = mul_f32 r4 r1
r5 = mul_f32 r5 r1
-r6 = mul_f32 r6 r1
r1 = mad_f32 r2 r1 r0
r2 = splat 437F0000 (255)
-r0 = splat 3F000000 (0.5)
+r0 = splat 3F000000 (1/2)
+r3 = mad_f32 r3 r2 r0
+r3 = to_i32 r3
r4 = mad_f32 r4 r2 r0
r4 = to_i32 r4
r5 = mad_f32 r5 r2 r0
r5 = to_i32 r5
-r5 = shl r5 8 (1.1210388e-44)
-r6 = mad_f32 r6 r2 r0
-r6 = to_i32 r6
-r6 = shl r6 10 (2.2420775e-44)
r0 = mad_f32 r1 r2 r0
r0 = to_i32 r0
-r0 = shl r0 18 (3.3631163e-44)
-r5 = bit_or r4 r5
-r5 = bit_or r5 r6
-r5 = bit_or r5 r0
-store32 arg(1) r5
+r4 = pack r3 r4 8
+r0 = pack r5 r0 8
+r0 = pack r4 r0 16
+store32 arg(1) r0
G8 over A8
-r0 = splat 3B808081 (0.0039215689)
+3 registers, 12 instructions:
+r0 = splat 3B808081 (1/255)
r1 = splat 3F800000 (1)
r2 = load8 arg(1)
r2 = to_f32 r2
@@ -92,14 +90,15 @@
r0 = sub_f32 r1 r1
r0 = mad_f32 r2 r0 r1
r2 = splat 437F0000 (255)
-r1 = splat 3F000000 (0.5)
+r1 = splat 3F000000 (1/2)
r1 = mad_f32 r0 r2 r1
r1 = to_i32 r1
store8 arg(1) r1
G8 over G8
+4 registers, 21 instructions:
r0 = load8 arg(0)
-r1 = splat 3B808081 (0.0039215689)
+r1 = splat 3B808081 (1/255)
r0 = to_f32 r0
r0 = mul_f32 r1 r0
r2 = splat 3F800000 (1)
@@ -115,60 +114,56 @@
r1 = mad_f32 r2 r0 r1
r1 = mad_f32 r2 r3 r1
r3 = splat 437F0000 (255)
-r2 = splat 3F000000 (0.5)
+r2 = splat 3F000000 (1/2)
r2 = mad_f32 r1 r3 r2
r2 = to_i32 r2
store8 arg(1) r2
G8 over RGBA_8888
+7 registers, 37 instructions:
r0 = load8 arg(0)
-r1 = splat 3B808081 (0.0039215689)
+r1 = splat 3B808081 (1/255)
r0 = to_f32 r0
r0 = mul_f32 r1 r0
r2 = splat 3F800000 (1)
r3 = load32 arg(1)
-r4 = splat FF (3.5733111e-43)
-r5 = bit_and r3 r4
+r4 = extract r3 FF
+r4 = to_f32 r4
+r4 = mul_f32 r1 r4
+r5 = extract r3 FF00
r5 = to_f32 r5
r5 = mul_f32 r1 r5
-r6 = shr r3 8 (1.1210388e-44)
-r6 = bit_and r6 r4
+r6 = extract r3 FF0000
r6 = to_f32 r6
r6 = mul_f32 r1 r6
-r7 = shr r3 10 (2.2420775e-44)
-r7 = bit_and r7 r4
-r7 = to_f32 r7
-r7 = mul_f32 r1 r7
-r3 = shr r3 18 (3.3631163e-44)
+r3 = shr r3 24
r3 = to_f32 r3
r3 = mul_f32 r1 r3
r1 = sub_f32 r2 r2
+r4 = mad_f32 r4 r1 r0
r5 = mad_f32 r5 r1 r0
r6 = mad_f32 r6 r1 r0
-r7 = mad_f32 r7 r1 r0
r1 = mad_f32 r3 r1 r2
r3 = splat 437F0000 (255)
-r2 = splat 3F000000 (0.5)
+r2 = splat 3F000000 (1/2)
+r4 = mad_f32 r4 r3 r2
+r4 = to_i32 r4
r5 = mad_f32 r5 r3 r2
r5 = to_i32 r5
r6 = mad_f32 r6 r3 r2
r6 = to_i32 r6
-r6 = shl r6 8 (1.1210388e-44)
-r7 = mad_f32 r7 r3 r2
-r7 = to_i32 r7
-r7 = shl r7 10 (2.2420775e-44)
r2 = mad_f32 r1 r3 r2
r2 = to_i32 r2
-r2 = shl r2 18 (3.3631163e-44)
-r6 = bit_or r5 r6
-r6 = bit_or r6 r7
-r6 = bit_or r6 r2
-store32 arg(1) r6
+r5 = pack r4 r5 8
+r2 = pack r6 r2 8
+r2 = pack r5 r2 16
+store32 arg(1) r2
RGBA_8888 over A8
+3 registers, 16 instructions:
r0 = load32 arg(0)
-r1 = splat 3B808081 (0.0039215689)
-r0 = shr r0 18 (3.3631163e-44)
+r1 = splat 3B808081 (1/255)
+r0 = shr r0 24
r0 = to_f32 r0
r0 = mul_f32 r1 r0
r2 = load8 arg(1)
@@ -178,103 +173,144 @@
r1 = sub_f32 r1 r0
r1 = mad_f32 r2 r1 r0
r2 = splat 437F0000 (255)
-r0 = splat 3F000000 (0.5)
+r0 = splat 3F000000 (1/2)
r0 = mad_f32 r1 r2 r0
r0 = to_i32 r0
store8 arg(1) r0
RGBA_8888 over G8
+6 registers, 33 instructions:
r0 = load32 arg(0)
-r1 = splat FF (3.5733111e-43)
-r2 = bit_and r0 r1
-r3 = splat 3B808081 (0.0039215689)
-r2 = to_f32 r2
-r2 = mul_f32 r3 r2
-r4 = shr r0 8 (1.1210388e-44)
-r4 = bit_and r4 r1
-r4 = to_f32 r4
-r4 = mul_f32 r3 r4
-r5 = shr r0 10 (2.2420775e-44)
-r5 = bit_and r5 r1
-r5 = to_f32 r5
-r5 = mul_f32 r3 r5
-r0 = shr r0 18 (3.3631163e-44)
-r0 = to_f32 r0
-r0 = mul_f32 r3 r0
-r1 = load8 arg(1)
+r1 = extract r0 FF
+r2 = splat 3B808081 (1/255)
r1 = to_f32 r1
-r1 = mul_f32 r3 r1
-r3 = splat 3F800000 (1)
-r3 = sub_f32 r3 r0
-r2 = mad_f32 r1 r3 r2
-r4 = mad_f32 r1 r3 r4
-r3 = mad_f32 r1 r3 r5
-r1 = splat 3E59B3D0 (0.21259999)
-r5 = splat 3F371759 (0.71520001)
+r1 = mul_f32 r2 r1
+r3 = extract r0 FF00
+r3 = to_f32 r3
+r3 = mul_f32 r2 r3
+r4 = extract r0 FF0000
+r4 = to_f32 r4
+r4 = mul_f32 r2 r4
+r0 = shr r0 24
+r0 = to_f32 r0
+r0 = mul_f32 r2 r0
+r5 = load8 arg(1)
+r5 = to_f32 r5
+r5 = mul_f32 r2 r5
+r2 = splat 3F800000 (1)
+r2 = sub_f32 r2 r0
+r1 = mad_f32 r5 r2 r1
+r3 = mad_f32 r5 r2 r3
+r2 = mad_f32 r5 r2 r4
+r5 = splat 3E59B3D0 (0.21259999)
+r4 = splat 3F371759 (0.71520001)
r0 = splat 3D93DD98 (0.0722)
-r0 = mul_f32 r3 r0
-r0 = mad_f32 r4 r5 r0
-r0 = mad_f32 r2 r1 r0
-r1 = splat 437F0000 (255)
-r2 = splat 3F000000 (0.5)
-r2 = mad_f32 r0 r1 r2
-r2 = to_i32 r2
-store8 arg(1) r2
+r0 = mul_f32 r2 r0
+r0 = mad_f32 r3 r4 r0
+r0 = mad_f32 r1 r5 r0
+r5 = splat 437F0000 (255)
+r1 = splat 3F000000 (1/2)
+r1 = mad_f32 r0 r5 r1
+r1 = to_i32 r1
+store8 arg(1) r1
RGBA_8888 over RGBA_8888
+9 registers, 47 instructions:
r0 = load32 arg(0)
-r1 = splat FF (3.5733111e-43)
-r2 = bit_and r0 r1
-r3 = splat 3B808081 (0.0039215689)
-r2 = to_f32 r2
-r2 = mul_f32 r3 r2
-r4 = shr r0 8 (1.1210388e-44)
-r4 = bit_and r4 r1
+r1 = extract r0 FF
+r2 = splat 3B808081 (1/255)
+r1 = to_f32 r1
+r1 = mul_f32 r2 r1
+r3 = extract r0 FF00
+r3 = to_f32 r3
+r3 = mul_f32 r2 r3
+r4 = extract r0 FF0000
r4 = to_f32 r4
-r4 = mul_f32 r3 r4
-r5 = shr r0 10 (2.2420775e-44)
-r5 = bit_and r5 r1
-r5 = to_f32 r5
-r5 = mul_f32 r3 r5
-r0 = shr r0 18 (3.3631163e-44)
+r4 = mul_f32 r2 r4
+r0 = shr r0 24
r0 = to_f32 r0
-r0 = mul_f32 r3 r0
-r6 = load32 arg(1)
-r7 = bit_and r6 r1
-r7 = to_f32 r7
-r7 = mul_f32 r3 r7
-r8 = shr r6 8 (1.1210388e-44)
-r8 = bit_and r8 r1
-r8 = to_f32 r8
-r8 = mul_f32 r3 r8
-r9 = shr r6 10 (2.2420775e-44)
-r9 = bit_and r9 r1
-r9 = to_f32 r9
-r9 = mul_f32 r3 r9
-r6 = shr r6 18 (3.3631163e-44)
+r0 = mul_f32 r2 r0
+r5 = load32 arg(1)
+r6 = extract r5 FF
r6 = to_f32 r6
-r6 = mul_f32 r3 r6
-r3 = splat 3F800000 (1)
-r3 = sub_f32 r3 r0
-r7 = mad_f32 r7 r3 r2
-r8 = mad_f32 r8 r3 r4
-r9 = mad_f32 r9 r3 r5
-r3 = mad_f32 r6 r3 r0
-r6 = splat 437F0000 (255)
-r0 = splat 3F000000 (0.5)
-r7 = mad_f32 r7 r6 r0
+r6 = mul_f32 r2 r6
+r7 = extract r5 FF00
+r7 = to_f32 r7
+r7 = mul_f32 r2 r7
+r8 = extract r5 FF0000
+r8 = to_f32 r8
+r8 = mul_f32 r2 r8
+r5 = shr r5 24
+r5 = to_f32 r5
+r5 = mul_f32 r2 r5
+r2 = splat 3F800000 (1)
+r2 = sub_f32 r2 r0
+r6 = mad_f32 r6 r2 r1
+r7 = mad_f32 r7 r2 r3
+r8 = mad_f32 r8 r2 r4
+r2 = mad_f32 r5 r2 r0
+r5 = splat 437F0000 (255)
+r0 = splat 3F000000 (1/2)
+r6 = mad_f32 r6 r5 r0
+r6 = to_i32 r6
+r7 = mad_f32 r7 r5 r0
r7 = to_i32 r7
-r8 = mad_f32 r8 r6 r0
+r8 = mad_f32 r8 r5 r0
r8 = to_i32 r8
-r8 = shl r8 8 (1.1210388e-44)
-r9 = mad_f32 r9 r6 r0
-r9 = to_i32 r9
-r9 = shl r9 10 (2.2420775e-44)
-r0 = mad_f32 r3 r6 r0
+r0 = mad_f32 r2 r5 r0
r0 = to_i32 r0
-r0 = shl r0 18 (3.3631163e-44)
-r8 = bit_or r7 r8
-r8 = bit_or r8 r9
-r8 = bit_or r8 r0
+r7 = pack r6 r7 8
+r0 = pack r8 r0 8
+r0 = pack r7 r0 16
+store32 arg(1) r0
+
+I32 8888 over 8888
+9 registers, 24 instructions:
+r0 = load32 arg(0)
+r1 = extract r0 FF
+r2 = extract r0 FF00
+r3 = extract r0 FF0000
+r0 = shr r0 24
+r4 = load32 arg(1)
+r5 = extract r4 FF
+r6 = extract r4 FF00
+r7 = extract r4 FF0000
+r4 = shr r4 24
+r8 = splat FF (3.5733111e-43)
+r8 = sub_i32 r8 r0
+r5 = mul_unorm8 r5 r8
+r5 = add_i32 r1 r5
+r6 = mul_unorm8 r6 r8
+r6 = add_i32 r2 r6
+r7 = mul_unorm8 r7 r8
+r7 = add_i32 r3 r7
+r8 = mul_unorm8 r4 r8
+r8 = add_i32 r0 r8
+r6 = pack r5 r6 8
+r8 = pack r7 r8 8
+r8 = pack r6 r8 16
store32 arg(1) r8
+I32 (SWAR) 8888 over 8888
+6 registers, 20 instructions:
+r0 = load32 arg(0)
+r1 = extract r0 FF00FF
+r0 = extract r0 FF00FF00
+r2 = load32 arg(1)
+r3 = extract r2 FF00FF
+r2 = extract r2 FF00FF00
+r4 = splat FF (3.5733111e-43)
+r5 = shr r0 16
+r5 = sub_i32 r4 r5
+r4 = splat FF00FF (2.3418409e-38)
+r3 = mul_i32 r3 r5
+r3 = add_i32 r3 r4
+r3 = extract r3 FF00FF00
+r3 = add_i32 r1 r3
+r5 = mul_i32 r2 r5
+r5 = add_i32 r5 r4
+r5 = extract r5 FF00FF00
+r5 = add_i32 r0 r5
+r5 = pack r3 r5 8
+store32 arg(1) r5
+
diff --git a/resources/skottie/skottie-text-animator-2.json b/resources/skottie/skottie-text-animator-2.json
new file mode 100644
index 0000000..62b6b88
--- /dev/null
+++ b/resources/skottie/skottie-text-animator-2.json
@@ -0,0 +1 @@
+{"v":"5.5.2","fr":60,"ip":0,"op":300,"w":500,"h":500,"nm":"range selectors","ddd":0,"assets":[],"fonts":{"list":[{"fName":"Roboto-Black","fFamily":"Roboto","fStyle":"Black","ascent":79.0488770231605}]},"layers":[{"ddd":0,"ind":1,"ty":5,"nm":"Foo Bar Baz","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[248,125.5,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"t":{"d":{"k":[{"s":{"sz":[496,88],"ps":[-248,-110.5],"s":72,"f":"Roboto-Black","t":"Foo Bar Baz","j":2,"tr":-100,"lh":86.4,"ls":0,"fc":[0.973,0,0]},"t":0}]},"p":{},"m":{"g":1,"a":{"a":0,"k":[0,0],"ix":2}},"a":[{"nm":"Animator 1","s":{"t":0,"xe":{"a":0,"k":0,"ix":7},"ne":{"a":0,"k":0,"ix":8},"a":{"a":0,"k":100,"ix":4},"b":1,"rn":0,"sh":1,"e":{"a":0,"k":20,"ix":2},"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[-20]},{"t":299,"s":[100]}],"ix":3},"r":1},"a":{"p":{"a":0,"k":[0,50,0],"ix":2}}}]},"ip":0,"op":300,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":5,"nm":"Foo Bar Baz 2","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[248,125.5,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"t":{"d":{"k":[{"s":{"sz":[496,88],"ps":[-248,-110.5],"s":72,"f":"Roboto-Black","t":"Foo Bar Baz","j":2,"tr":-100,"lh":86.4,"ls":0,"fc":[0.973,0,0]},"t":0}]},"p":{},"m":{"g":1,"a":{"a":0,"k":[0,0],"ix":2}},"a":[{"nm":"Animator 1","s":{"t":0,"xe":{"a":0,"k":0,"ix":7},"ne":{"a":0,"k":0,"ix":8},"a":{"a":0,"k":-100,"ix":4},"b":1,"rn":0,"sh":1,"e":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[12]},{"t":299,"s":[0]}],"ix":5},"r":2},"a":{"s":{"a":0,"k":[50,50,100],"ix":3}}},{"nm":"Animator 2","s":{"t":0,"xe":{"a":0,"k":0,"ix":7},"ne":{"a":0,"k":0,"ix":8},"a":{"a":0,"k":100,"ix":4},"b":1,"rn":0,"sh":1,"r":1},"a":{"p":{"a":0,"k":[0,147,0],"ix":2},"fc":{"a":0,"k":[0,1,0,1],"ix":12}}}]},"ip":0,"op":300,"st":0,"bm":0},{"ddd":0,"ind":3,"ty":5,"nm":"Foo Bar Baz 3","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[248,125.5,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"t":{"d":{"k":[{"s":{"sz":[496,88],"ps":[-248,-110.5],"s":72,"f":"Roboto-Black","t":"Foo Bar Baz","j":2,"tr":-100,"lh":86.4,"ls":0,"fc":[0.973,1,0]},"t":0}]},"p":{},"m":{"g":1,"a":{"a":0,"k":[0,0],"ix":2}},"a":[{"nm":"Animator 1","s":{"t":0,"xe":{"a":0,"k":0,"ix":7},"ne":{"a":0,"k":0,"ix":8},"a":{"a":0,"k":60,"ix":4},"b":1,"rn":0,"sh":1,"s":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[50]},{"t":299,"s":[0]}],"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[50]},{"t":299,"s":[100]}],"ix":2},"r":1},"a":{"fc":{"a":0,"k":[0,0,1,1],"ix":12}}},{"nm":"Animator 2","s":{"t":0,"xe":{"a":0,"k":0,"ix":7},"ne":{"a":0,"k":0,"ix":8},"a":{"a":0,"k":100,"ix":4},"b":1,"rn":0,"sh":1,"r":1},"a":{"p":{"a":0,"k":[0,325,0],"ix":2}}},{"nm":"Animator 3","s":{"t":0,"xe":{"a":0,"k":0,"ix":7},"ne":{"a":0,"k":0,"ix":8},"a":{"a":0,"k":100,"ix":4},"b":1,"rn":0,"sh":1,"r":1},"a":{"o":{"a":0,"k":50,"ix":9}}}]},"ip":0,"op":300,"st":0,"bm":0}],"markers":[],"chars":[{"ch":"F","size":72,"style":"Black","w":54.69,"data":{"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[50.146,-41.504],[22.51,-41.504],[22.51,-57.861],[52.93,-57.861],[52.93,-71.094],[5.371,-71.094],[5.371,0],[22.51,0],[22.51,-28.32],[50.146,-28.32]],"c":true},"ix":2},"nm":"F","mn":"ADBE Vector Shape - Group","hd":false}],"nm":"F","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}]},"fFamily":"Roboto"},{"ch":"o","size":72,"style":"Black","w":56.15,"data":{"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[-4.558,-4.508],[-7.52,0],[-4.574,4.834],[0,8.236],[0,0],[4.557,4.9],[7.91,0],[3.841,-2.213],[2.051,-4.118],[0,-5.273]],"o":[[0.358,7.715],[4.557,4.509],[7.812,0],[4.573,-4.834],[0,0],[0,-8.43],[-4.558,-4.899],[-5.176,0],[-3.841,2.214],[-2.051,4.118],[0,0]],"v":[[2.588,-24.121],[9.961,-5.786],[28.076,0.977],[46.655,-6.274],[53.516,-25.879],[53.516,-26.465],[46.68,-46.46],[27.979,-53.809],[14.453,-50.488],[5.615,-40.991],[2.539,-26.904]],"c":true},"ix":2},"nm":"o","mn":"ADBE Vector Shape - Group","hd":false},{"ind":1,"ty":"sh","ix":2,"ks":{"a":0,"k":{"i":[[0,0],[-5.502,0],[0,-9.473],[0,0],[5.859,0],[1.53,2.279],[0,4.883]],"o":[[0.455,-8.17],[6.055,0],[0,0],[-0.13,9.017],[-2.995,0],[-1.53,-2.278],[0,0]],"v":[[19.043,-28.857],[27.979,-41.113],[37.061,-26.904],[37.061,-25.244],[28.076,-11.719],[21.289,-15.137],[18.994,-25.879]],"c":true},"ix":2},"nm":"o","mn":"ADBE Vector Shape - Group","hd":false}],"nm":"o","np":5,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}]},"fFamily":"Roboto"},{"ch":" ","size":72,"style":"Black","w":24.9,"data":{},"fFamily":"Roboto"},{"ch":"B","size":72,"style":"Black","w":64.5,"data":{"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[-4.411,3.5],[0,6.966],[1.985,2.67],[3.711,0.977],[-1.693,2.605],[0,3.679],[4.768,3.353],[9.147,0],[0,0],[0,0]],"o":[[8.659,-0.065],[4.411,-3.499],[0,-3.841],[-1.986,-2.669],[3.288,-1.237],[1.692,-2.604],[0,-6.38],[-4.769,-3.352],[0,0],[0,0],[0,0]],"v":[[34.229,0],[53.833,-5.347],[60.449,-21.045],[57.471,-30.811],[48.926,-36.279],[56.396,-42.041],[58.936,-51.465],[51.782,-66.064],[30.908,-71.094],[5.371,-71.094],[5.371,0]],"c":true},"ix":2},"nm":"B","mn":"ADBE Vector Shape - Group","hd":false},{"ind":1,"ty":"sh","ix":2,"ks":{"a":0,"k":{"i":[[0,0],[0,-5.924],[1.66,-1.416],[3.092,0],[0,0],[0,0]],"o":[[5.891,0.033],[0,2.572],[-1.66,1.416],[0,0],[0,0],[0,0]],"v":[[34.521,-30.225],[43.359,-21.289],[40.869,-15.308],[33.74,-13.184],[22.51,-13.184],[22.51,-30.225]],"c":true},"ix":2},"nm":"B","mn":"ADBE Vector Shape - Group","hd":false},{"ind":2,"ty":"sh","ix":3,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[-1.726,-1.322],[0,-3.004],[1.595,-1.338],[3.743,-0.032],[0,0]],"o":[[0,0],[3.841,0],[1.725,1.322],[0,2.612],[-1.595,1.339],[0,0],[0,0]],"v":[[22.51,-57.91],[30.908,-57.91],[39.258,-55.927],[41.846,-49.438],[39.453,-43.512],[31.445,-41.455],[22.51,-41.455]],"c":true},"ix":2},"nm":"B","mn":"ADBE Vector Shape - Group","hd":false}],"nm":"B","np":6,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}]},"fFamily":"Roboto"},{"ch":"a","size":72,"style":"Black","w":53.08,"data":{"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0.065,4.851],[0,0],[3.906,3.288],[6.738,0],[4.346,-3.186],[0,-5.104],[0,0],[-3.906,0],[0,-4.874],[0,0],[0,0],[4.231,-2.93],[0,-5.729],[-3.484,-3.011],[-4.98,0],[-3.027,3.809],[-0.586,-1.074]],"o":[[0,0],[-1.433,-2.571],[0,0],[-0.098,-5.729],[-3.906,-3.288],[-6.738,0],[-4.346,3.186],[0,0],[0,-3.86],[4.199,0],[0,0],[0,0],[-8.073,0],[-4.232,2.93],[0,4.558],[3.483,3.011],[5.436,0],[0.455,2.084],[0,0]],"v":[[50.83,0],[50.83,-0.83],[48.584,-11.963],[48.584,-35.352],[42.578,-48.877],[26.611,-53.809],[9.985,-49.029],[3.467,-36.593],[19.922,-36.593],[25.781,-42.383],[32.08,-35.072],[32.08,-32.275],[27.002,-32.275],[8.545,-27.881],[2.197,-14.893],[7.422,-3.54],[20.117,0.977],[32.812,-4.736],[34.375,0]],"c":true},"ix":2},"nm":"a","mn":"ADBE Vector Shape - Group","hd":false},{"ind":1,"ty":"sh","ix":2,"ks":{"a":0,"k":{"i":[[1.009,0.928],[0,1.498],[-5.73,0],[0,0],[0,0],[1.35,-0.879],[2.051,0]],"o":[[-1.009,-0.928],[0,-5.143],[0,0],[0,0],[-0.586,1.107],[-1.351,0.879],[-1.562,0]],"v":[[20.215,-12.134],[18.701,-15.771],[27.295,-23.486],[32.08,-23.486],[32.08,-15.039],[29.175,-12.061],[24.072,-10.742]],"c":true},"ix":2},"nm":"a","mn":"ADBE Vector Shape - Group","hd":false}],"nm":"a","np":5,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}]},"fFamily":"Roboto"},{"ch":"r","size":72,"style":"Black","w":37.89,"data":{"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[1.758,0],[2.766,-5.176],[0,0],[0,0],[0,0],[0,0],[0,0],[-5.176,0],[0,0]],"o":[[-1.302,-0.391],[-4.948,0],[0,0],[0,0],[0,0],[0,0],[0,0],[1.465,-3.255],[0,0],[0,0]],"v":[[36.865,-53.223],[32.275,-53.809],[20.703,-46.045],[20.166,-52.832],[4.736,-52.832],[4.736,0],[21.191,0],[21.191,-33.447],[31.152,-38.33],[36.572,-37.939]],"c":true},"ix":2},"nm":"r","mn":"ADBE Vector Shape - Group","hd":false}],"nm":"r","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}]},"fFamily":"Roboto"},{"ch":"z","size":72,"style":"Black","w":51.46,"data":{"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[47.559,-43.945],[47.559,-52.832],[4.248,-52.832],[4.248,-40.137],[26.66,-40.137],[3.223,-9.18],[3.223,0],[48.096,0],[48.096,-12.695],[24.023,-12.695]],"c":true},"ix":2},"nm":"z","mn":"ADBE Vector Shape - Group","hd":false}],"nm":"z","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}]},"fFamily":"Roboto"}]}
\ No newline at end of file
diff --git a/src/core/SkVM.cpp b/src/core/SkVM.cpp
index fcf1246..efa7366 100644
--- a/src/core/SkVM.cpp
+++ b/src/core/SkVM.cpp
@@ -9,12 +9,12 @@
#include "src/core/SkOpts.h"
#include "src/core/SkVM.h"
#include <string.h>
+#if defined(SK_BUILD_FOR_WIN)
+ #include <intrin.h>
+#endif
namespace skvm {
- // We reserve the last ID as a sentinel meaning none, n/a, null, nil, etc.
- static const ID NA = ~0;
-
Program::Program(std::vector<Instruction> instructions, int regs)
: fInstructions(std::move(instructions))
, fRegs(regs)
@@ -94,14 +94,11 @@
inst.op,
lookup_register(id),
lookup_register(inst.x),
- lookup_register(inst.y),
+ {lookup_register(inst.y)},
{lookup_register(inst.z)},
};
- // If the z argument is the N/A sentinel, copy in the immediate instead.
- // (No Op uses both 3 arguments and an immediate.)
- if (inst.z == NA) {
- pinst.z.imm = inst.imm;
- }
+ if (inst.y == NA) { pinst.y.imm = inst.immy; }
+ if (inst.z == NA) { pinst.z.imm = inst.immz; }
program.push_back(pinst);
}
@@ -111,19 +108,8 @@
// Most instructions produce a value and return it by ID,
// the value-producing instruction's own index in the program vector.
- ID Builder::push(Op op, ID x=NA, ID y=NA, ID z=NA, int imm=0) {
- Instruction inst{op, /*life=*/NA, x, y, z, imm};
-
- // Simple peepholes that come up fairly often.
-
- auto is_zero = [&](ID id) {
- return fProgram[id].op == Op::splat
- && fProgram[id].imm == 0;
- };
-
- // x*y+0 --> x*y
- if (op == Op::mad_f32 && is_zero(z)) { inst = { Op::mul_f32, NA, x,y,NA, 0 }; }
-
+ ID Builder::push(Op op, ID x, ID y, ID z, int immy, int immz) {
+ Instruction inst{op, /*life=*/NA, x, y, z, immy, immz};
// Basic common subexpression elimination:
// if we've already seen this exact Instruction, use it instead of creating a new one.
@@ -138,6 +124,11 @@
return id;
}
+ bool Builder::isZero(ID id) const {
+ return fProgram[id].op == Op::splat
+ && fProgram[id].immy == 0;
+ }
+
Arg Builder::arg(int ix) { return {ix}; }
void Builder::store8 (Arg ptr, I32 val) { (void)this->push(Op::store8 , val.id,NA,NA, ptr.ix); }
@@ -154,11 +145,16 @@
return {this->push(Op::splat, NA,NA,NA, bits)};
}
- F32 Builder::add(F32 x, F32 y ) { return {this->push(Op::add_f32, x.id, y.id )}; }
- F32 Builder::sub(F32 x, F32 y ) { return {this->push(Op::sub_f32, x.id, y.id )}; }
- F32 Builder::mul(F32 x, F32 y ) { return {this->push(Op::mul_f32, x.id, y.id )}; }
- F32 Builder::div(F32 x, F32 y ) { return {this->push(Op::div_f32, x.id, y.id )}; }
- F32 Builder::mad(F32 x, F32 y, F32 z) { return {this->push(Op::mad_f32, x.id, y.id, z.id)}; }
+ F32 Builder::add(F32 x, F32 y ) { return {this->push(Op::add_f32, x.id, y.id)}; }
+ F32 Builder::sub(F32 x, F32 y ) { return {this->push(Op::sub_f32, x.id, y.id)}; }
+ F32 Builder::mul(F32 x, F32 y ) { return {this->push(Op::mul_f32, x.id, y.id)}; }
+ F32 Builder::div(F32 x, F32 y ) { return {this->push(Op::div_f32, x.id, y.id)}; }
+ F32 Builder::mad(F32 x, F32 y, F32 z) {
+ if (this->isZero(z.id)) {
+ return this->mul(x,y);
+ }
+ return {this->push(Op::mad_f32, x.id, y.id, z.id)};
+ }
I32 Builder::add(I32 x, I32 y) { return {this->push(Op::add_i32, x.id, y.id)}; }
I32 Builder::sub(I32 x, I32 y) { return {this->push(Op::sub_i32, x.id, y.id)}; }
@@ -172,12 +168,35 @@
I32 Builder::shr(I32 x, int bits) { return {this->push(Op::shr, x.id,NA,NA, bits)}; }
I32 Builder::sra(I32 x, int bits) { return {this->push(Op::sra, x.id,NA,NA, bits)}; }
+ I32 Builder::mul_unorm8(I32 x, I32 y) { return {this->push(Op::mul_unorm8, x.id, y.id)}; }
+
+ I32 Builder::extract(I32 x, int mask) {
+ SkASSERT(mask != 0);
+ #if defined(SK_BUILD_FOR_WIN)
+ unsigned long shift;
+ _BitScanForward(&shift, mask);
+ #else
+ const int shift = __builtin_ctz(mask);
+ #endif
+ if ((unsigned)mask == (~0u << shift)) {
+ return this->shr(x, shift);
+ }
+ return {this->push(Op::extract, x.id,NA,NA, mask, shift)};
+ }
+
+ I32 Builder::pack(I32 x, I32 y, int bits) {
+ return {this->push(Op::pack, x.id,y.id,NA, 0,bits)};
+ }
+
F32 Builder::to_f32(I32 x) { return {this->push(Op::to_f32, x.id)}; }
I32 Builder::to_i32(F32 x) { return {this->push(Op::to_i32, x.id)}; }
// ~~~~ Program::dump() and co. ~~~~ //
- struct Reg { ID id; };
+ struct R { ID id; };
+ struct Shift { int bits; };
+ struct Mask { int bits; };
+ struct Splat { int bits; };
static void write(SkWStream* o, const char* s) {
o->writeText(s);
@@ -188,17 +207,34 @@
o->writeDecAsText(a.ix);
write(o, ")");
}
- static void write(SkWStream* o, Reg r) {
+ static void write(SkWStream* o, R r) {
write(o, "r");
o->writeDecAsText(r.id);
}
-
- static void write(SkWStream* o, int bits) {
+ static void write(SkWStream* o, Shift s) {
+ o->writeDecAsText(s.bits);
+ }
+ static void write(SkWStream* o, Mask m) {
+ o->writeHexAsText(m.bits);
+ }
+ static void write(SkWStream* o, Splat s) {
+ o->writeHexAsText(s.bits);
float f;
- memcpy(&f, &bits, 4);
- o->writeHexAsText(bits);
+ memcpy(&f, &s.bits, 4);
write(o, " (");
- o->writeScalarAsText(f);
+
+ // It's friendlier to print floats that represent 1/K as fractions.
+ const int d = (int)(1.0f/f);
+
+ if (f < 1.0f && f == 1.0f/d) {
+ write(o, "1/");
+ o->writeDecAsText(d);
+ } else if (f < 1.0f && f == 1.0f/(d+1)) {
+ write(o, "1/");
+ o->writeDecAsText(d+1);
+ } else {
+ o->writeScalarAsText(f);
+ }
write(o, ")");
}
@@ -210,41 +246,50 @@
}
void Program::dump(SkWStream* o) const {
+ o->writeDecAsText(fRegs);
+ o->writeText(" registers, ");
+ o->writeDecAsText(fInstructions.size());
+ o->writeText(" instructions:\n");
for (const Instruction& inst : fInstructions) {
Op op = inst.op;
ID d = inst.d,
- x = inst.x,
- y = inst.y;
- auto z = inst.z;
+ x = inst.x;
+ auto y = inst.y,
+ z = inst.z;
switch (op) {
- case Op::store8: write(o, "store8" , Arg{z.imm}, Reg{x}); break;
- case Op::store32: write(o, "store32", Arg{z.imm}, Reg{x}); break;
+ case Op::store8: write(o, "store8" , Arg{y.imm}, R{x}); break;
+ case Op::store32: write(o, "store32", Arg{y.imm}, R{x}); break;
- case Op::load8: write(o, Reg{d}, "= load8" , Arg{z.imm}); break;
- case Op::load32: write(o, Reg{d}, "= load32", Arg{z.imm}); break;
+ case Op::load8: write(o, R{d}, "= load8" , Arg{y.imm}); break;
+ case Op::load32: write(o, R{d}, "= load32", Arg{y.imm}); break;
- case Op::splat: write(o, Reg{d}, "= splat", z.imm); break;
+ case Op::splat: write(o, R{d}, "= splat", Splat{y.imm}); break;
- case Op::add_f32: write(o, Reg{d}, "= add_f32", Reg{x}, Reg{y} ); break;
- case Op::sub_f32: write(o, Reg{d}, "= sub_f32", Reg{x}, Reg{y} ); break;
- case Op::mul_f32: write(o, Reg{d}, "= mul_f32", Reg{x}, Reg{y} ); break;
- case Op::div_f32: write(o, Reg{d}, "= div_f32", Reg{x}, Reg{y} ); break;
- case Op::mad_f32: write(o, Reg{d}, "= mad_f32", Reg{x}, Reg{y}, Reg{z.id}); break;
+ case Op::add_f32: write(o, R{d}, "= add_f32", R{x}, R{y.id} ); break;
+ case Op::sub_f32: write(o, R{d}, "= sub_f32", R{x}, R{y.id} ); break;
+ case Op::mul_f32: write(o, R{d}, "= mul_f32", R{x}, R{y.id} ); break;
+ case Op::div_f32: write(o, R{d}, "= div_f32", R{x}, R{y.id} ); break;
+ case Op::mad_f32: write(o, R{d}, "= mad_f32", R{x}, R{y.id}, R{z.id}); break;
- case Op::add_i32: write(o, Reg{d}, "= add_i32", Reg{x}, Reg{y}); break;
- case Op::sub_i32: write(o, Reg{d}, "= sub_i32", Reg{x}, Reg{y}); break;
- case Op::mul_i32: write(o, Reg{d}, "= mul_i32", Reg{x}, Reg{y}); break;
+ case Op::add_i32: write(o, R{d}, "= add_i32", R{x}, R{y.id}); break;
+ case Op::sub_i32: write(o, R{d}, "= sub_i32", R{x}, R{y.id}); break;
+ case Op::mul_i32: write(o, R{d}, "= mul_i32", R{x}, R{y.id}); break;
- case Op::bit_and: write(o, Reg{d}, "= bit_and", Reg{x}, Reg{y}); break;
- case Op::bit_or : write(o, Reg{d}, "= bit_or" , Reg{x}, Reg{y}); break;
- case Op::bit_xor: write(o, Reg{d}, "= bit_xor", Reg{x}, Reg{y}); break;
+ case Op::bit_and: write(o, R{d}, "= bit_and", R{x}, R{y.id}); break;
+ case Op::bit_or : write(o, R{d}, "= bit_or" , R{x}, R{y.id}); break;
+ case Op::bit_xor: write(o, R{d}, "= bit_xor", R{x}, R{y.id}); break;
- case Op::shl: write(o, Reg{d}, "= shl", Reg{x}, z.imm); break;
- case Op::shr: write(o, Reg{d}, "= shr", Reg{x}, z.imm); break;
- case Op::sra: write(o, Reg{d}, "= sra", Reg{x}, z.imm); break;
+ case Op::shl: write(o, R{d}, "= shl", R{x}, Shift{y.imm}); break;
+ case Op::shr: write(o, R{d}, "= shr", R{x}, Shift{y.imm}); break;
+ case Op::sra: write(o, R{d}, "= sra", R{x}, Shift{y.imm}); break;
- case Op::to_f32: write(o, Reg{d}, "= to_f32", Reg{x}); break;
- case Op::to_i32: write(o, Reg{d}, "= to_i32", Reg{x}); break;
+ case Op::mul_unorm8: write(o, R{d}, "= mul_unorm8", R{x}, R{y.id}); break;
+
+ case Op::extract: write(o, R{d}, "= extract", R{x}, Mask{y.imm}); break;
+ case Op::pack: write(o, R{d}, "= pack", R{x}, R{y.id}, Shift{z.imm}); break;
+
+ case Op::to_f32: write(o, R{d}, "= to_f32", R{x}); break;
+ case Op::to_i32: write(o, R{d}, "= to_i32", R{x}); break;
}
write(o, "\n");
}
diff --git a/src/core/SkVM.h b/src/core/SkVM.h
index 0069140..a392049 100644
--- a/src/core/SkVM.h
+++ b/src/core/SkVM.h
@@ -23,6 +23,9 @@
add_i32, sub_i32, mul_i32,
bit_and, bit_or, bit_xor,
shl, shr, sra,
+ mul_unorm8,
+ extract,
+ pack,
to_f32, to_i32,
};
@@ -30,10 +33,10 @@
class Program {
public:
- struct Instruction { // d = op(x,y, z.id/z.imm)
+ struct Instruction { // d = op(x, y.id/y.imm, z.id/z.imm)
Op op;
- ID d,x,y;
- union { ID id; int imm; } z;
+ ID d,x;
+ union { ID id; int imm; } y,z;
};
Program(std::vector<Instruction>, int regs);
@@ -92,15 +95,36 @@
I32 shr(I32 x, int bits);
I32 sra(I32 x, int bits);
+ I32 mul_unorm8(I32 x, I32 y); // (x*y+255)/256, approximating (x*y+127)/255.
+
+ // (x & mask) >> k, where k is the lowest set bit of mask. E.g.
+ // extract(x, 0xff) == (x & 0xff)
+ // extract(x, 0xff00) == (x & 0xff00) >> 8
+ //
+ // extract(x, 0x00ff00ff) == (x & 0x00ff00ff)
+ // extract(x, 0xff00ff00) == (x & 0xff00ff00) >> 8
+ //
+ // extract(x, 0x003ff) == (x & 0x003ff)
+ // extract(x, 0xffc00) == (x & 0xffc00) >> 10
+ I32 extract(I32 x, int mask);
+
+ // Interlace bits from x and y as if x | (y << bits),
+ // assuming no bits from x and (y << bits) collide with each other, (x & (y << bits)) == 0.
+ // (This allows implementation with SSE punpckl?? or NEON vzip.?? instructions.)
+ I32 pack(I32 x, I32 y, int bits);
+
F32 to_f32(I32 x);
I32 to_i32(F32 x);
private:
+ // We reserve the last ID as a sentinel meaning none, n/a, null, nil, etc.
+ static const ID NA = ~0;
+
struct Instruction {
- Op op; // v* = op(x,y,z,imm), where * == index of this Instruction.
- ID life; // ID of last instruction using this instruction's result.
- ID x,y,z; // Enough arguments for mad().
- int imm; // Immediate bit pattern, shift count, or argument index.
+ Op op; // v* = op(x,y,z,imm), where * == index of this Instruction.
+ ID life; // ID of last instruction using this instruction's result.
+ ID x,y,z; // Enough arguments for mad().
+ int immy,immz; // Immediate bit patterns, shift counts, argument indexes.
bool operator==(const Instruction& o) const {
return op == o.op
@@ -108,7 +132,8 @@
&& x == o.x
&& y == o.y
&& z == o.z
- && imm == o.imm;
+ && immy == o.immy
+ && immz == o.immz;
}
};
@@ -123,14 +148,16 @@
^ Hash(inst.x)
^ Hash(inst.y)
^ Hash(inst.z)
- ^ Hash(inst.imm);
+ ^ Hash(inst.immy)
+ ^ Hash(inst.immz);
}
};
+ ID push(Op, ID x, ID y=NA, ID z=NA, int immy=0, int immz=0);
+ bool isZero(ID) const;
+
std::unordered_map<Instruction, ID, InstructionHash> fIndex;
std::vector<Instruction> fProgram;
-
- ID push(Op, ID, ID, ID, int);
};
// TODO: comparison operations, if_then_else
diff --git a/src/gpu/GrContext.cpp b/src/gpu/GrContext.cpp
index d6bc93c..054456f 100644
--- a/src/gpu/GrContext.cpp
+++ b/src/gpu/GrContext.cpp
@@ -392,6 +392,49 @@
return this->createBackendTexture(width, height, format, mipMapped, renderable);
}
+GrBackendTexture GrContext::createBackendTexture(int width, int height,
+ GrBackendFormat backendFormat,
+ const SkColor4f& color,
+ GrMipMapped mipMapped,
+ GrRenderable renderable) {
+ if (!this->asDirectContext()) {
+ return GrBackendTexture();
+ }
+
+ if (this->abandoned()) {
+ return GrBackendTexture();
+ }
+
+ if (!backendFormat.isValid()) {
+ return GrBackendTexture();
+ }
+
+ return fGpu->createBackendTexture(width, height, backendFormat,
+ mipMapped, renderable,
+ nullptr, 0, color);
+}
+
+GrBackendTexture GrContext::createBackendTexture(int width, int height,
+ SkColorType colorType,
+ const SkColor4f& color,
+ GrMipMapped mipMapped,
+ GrRenderable renderable) {
+ if (!this->asDirectContext()) {
+ return GrBackendTexture();
+ }
+
+ if (this->abandoned()) {
+ return GrBackendTexture();
+ }
+
+ GrBackendFormat format = this->caps()->getBackendFormatFromColorType(colorType);
+ if (!format.isValid()) {
+ return GrBackendTexture();
+ }
+
+ return this->createBackendTexture(width, height, format, color, mipMapped, renderable);
+}
+
void GrContext::deleteBackendTexture(GrBackendTexture backendTex) {
if (this->abandoned() || !backendTex.isValid()) {
return;
diff --git a/src/gpu/GrContextPriv.cpp b/src/gpu/GrContextPriv.cpp
index 2ab6822..6a482d1 100644
--- a/src/gpu/GrContextPriv.cpp
+++ b/src/gpu/GrContextPriv.cpp
@@ -760,48 +760,3 @@
fContext->drawingManager()->testingOnly_removeOnFlushCallbackObject(cb);
}
#endif
-
-//////////////////////////////////////////////////////////////////////////////
-GrBackendTexture GrContextPriv::createBackendTexture(int width, int height,
- GrBackendFormat backendFormat,
- const SkColor4f& color,
- GrMipMapped mipMapped,
- GrRenderable renderable) {
- if (!fContext->asDirectContext()) {
- return GrBackendTexture();
- }
-
- if (this->abandoned()) {
- return GrBackendTexture();
- }
-
- if (!backendFormat.isValid()) {
- return GrBackendTexture();
- }
-
- GrGpu* gpu = fContext->fGpu.get();
-
- return gpu->createBackendTexture(width, height, backendFormat,
- mipMapped, renderable, nullptr, 0, color);
-}
-
-GrBackendTexture GrContextPriv::createBackendTexture(int width, int height,
- SkColorType colorType,
- const SkColor4f& color,
- GrMipMapped mipMapped,
- GrRenderable renderable) {
- if (!fContext->asDirectContext()) {
- return GrBackendTexture();
- }
-
- if (this->abandoned()) {
- return GrBackendTexture();
- }
-
- GrBackendFormat format = fContext->caps()->getBackendFormatFromColorType(colorType);
- if (!format.isValid()) {
- return GrBackendTexture();
- }
-
- return this->createBackendTexture(width, height, format, color, mipMapped, renderable);
-}
diff --git a/src/gpu/GrContextPriv.h b/src/gpu/GrContextPriv.h
index 5dbb038..86af1f3 100644
--- a/src/gpu/GrContextPriv.h
+++ b/src/gpu/GrContextPriv.h
@@ -292,20 +292,6 @@
void testingOnly_flushAndRemoveOnFlushCallbackObject(GrOnFlushCallbackObject*);
#endif
- // If possible, create a backend texture initialized to a particular color. The client should
- // ensure that the returned backend texture is valid.
- GrBackendTexture createBackendTexture(int width, int height,
- GrBackendFormat, const SkColor4f& color,
- GrMipMapped, GrRenderable);
-
- // If possible, create a backend texture initialized to a particular color. The client should
- // ensure that the returned backend texture is valid.
- // If successful, the created backend texture will be compatible with the provided
- // SkColorType.
- GrBackendTexture createBackendTexture(int width, int height,
- SkColorType, const SkColor4f& color,
- GrMipMapped, GrRenderable);
-
private:
explicit GrContextPriv(GrContext* context) : fContext(context) {}
GrContextPriv(const GrContextPriv&); // unimpl
diff --git a/src/gpu/geometry/GrQuad.h b/src/gpu/geometry/GrQuad.h
index cb2f15d..f7d31ce 100644
--- a/src/gpu/geometry/GrQuad.h
+++ b/src/gpu/geometry/GrQuad.h
@@ -11,7 +11,6 @@
#include "include/core/SkMatrix.h"
#include "include/core/SkPoint.h"
#include "include/core/SkPoint3.h"
-#include "include/private/SkTArray.h"
#include "include/private/SkVx.h"
enum class GrAAType : unsigned;
@@ -182,182 +181,4 @@
GrQuadType fType;
};
-// Underlying data used by GrQuadListBase. It is defined outside of GrQuadListBase due to compiler
-// issues related to specializing member types.
-template<typename T>
-struct QuadData {
- float fX[4];
- float fY[4];
- T fMetadata;
-};
-
-template<>
-struct QuadData<void> {
- float fX[4];
- float fY[4];
-};
-
-// A dynamic list of (possibly) perspective quads that tracks the most general quad type of all
-// added quads. It avoids storing the 3rd component if the quad type never becomes perspective.
-// Use GrQuadList subclass when only storing quads. Use GrTQuadList subclass when storing quads
-// and per-quad templated metadata (such as color or domain).
-template<typename T>
-class GrQuadListBase {
-public:
-
- int count() const { return fXYs.count(); }
-
- GrQuadType quadType() const { return fType; }
-
- void reserve(int count, bool needsPerspective) {
- fXYs.reserve(count);
- if (needsPerspective || fType == GrQuadType::kPerspective) {
- fWs.reserve(4 * count);
- }
- }
-
- GrPerspQuad operator[] (int i) const {
- SkASSERT(i < this->count());
- SkASSERT(i >= 0);
-
- const QuadData<T>& item = fXYs[i];
- if (fType == GrQuadType::kPerspective) {
- // Read the explicit ws
- return GrPerspQuad(item.fX, item.fY, fWs.begin() + 4 * i, fType);
- } else {
- // Ws are implicitly 1s.
- static constexpr float kNoPerspectiveWs[4] = {1.f, 1.f, 1.f, 1.f};
- return GrPerspQuad(item.fX, item.fY, kNoPerspectiveWs, fType);
- }
- }
-
- // Subclasses expose push_back(const GrQuad|GrPerspQuad&, GrQuadType, [const T&]), where
- // the metadata argument is only present in GrTQuadList's push_back definition.
-
-protected:
- GrQuadListBase() : fType(GrQuadType::kRect) {}
-
- void concatImpl(const GrQuadListBase<T>& that) {
- this->upgradeType(that.fType);
- fXYs.push_back_n(that.fXYs.count(), that.fXYs.begin());
- if (fType == GrQuadType::kPerspective) {
- if (that.fType == GrQuadType::kPerspective) {
- // Copy the other's ws into the end of this list's data
- fWs.push_back_n(that.fWs.count(), that.fWs.begin());
- } else {
- // This list stores ws but the appended list had implicit 1s, so add explicit 1s to
- // fill out the total list
- fWs.push_back_n(4 * that.count(), 1.f);
- }
- }
- }
-
- // Returns the added item data so that its metadata can be initialized if T is not void
- QuadData<T>& pushBackImpl(const GrQuad& quad) {
- this->upgradeType(quad.quadType());
- QuadData<T>& item = fXYs.push_back();
- memcpy(item.fX, quad.fX, 4 * sizeof(float));
- memcpy(item.fY, quad.fY, 4 * sizeof(float));
- if (fType == GrQuadType::kPerspective) {
- fWs.push_back_n(4, 1.f);
- }
- return item;
- }
-
- QuadData<T>& pushBackImpl(const GrPerspQuad& quad) {
- this->upgradeType(quad.quadType());
- QuadData<T>& item = fXYs.push_back();
- memcpy(item.fX, quad.fX, 4 * sizeof(float));
- memcpy(item.fY, quad.fY, 4 * sizeof(float));
- if (fType == GrQuadType::kPerspective) {
- fWs.push_back_n(4, quad.fW);
- }
- return item;
- }
-
- const QuadData<T>& item(int i) const {
- return fXYs[i];
- }
-
- QuadData<T>& item(int i) {
- return fXYs[i];
- }
-
-private:
- void upgradeType(GrQuadType type) {
- // Possibly upgrade the overall type tracked by the list
- if (type > fType) {
- fType = type;
- if (type == GrQuadType::kPerspective) {
- // All existing quads were 2D, so the ws array just needs to be filled with 1s
- fWs.push_back_n(4 * this->count(), 1.f);
- }
- }
- }
-
- // Interleaves xs, ys, and per-quad metadata so that all data for a single quad is together
- // (barring ws, which can be dropped entirely if the quad type allows it).
- SkSTArray<1, QuadData<T>, true> fXYs;
- // The w channel is kept separate so that it can remain empty when only dealing with 2D quads.
- SkTArray<float, true> fWs;
-
- GrQuadType fType;
-};
-
-// This list only stores the quad data itself.
-class GrQuadList : public GrQuadListBase<void> {
-public:
- GrQuadList() : INHERITED() {}
-
- void concat(const GrQuadList& that) {
- this->concatImpl(that);
- }
-
- void push_back(const GrQuad& quad) {
- this->pushBackImpl(quad);
- }
-
- void push_back(const GrPerspQuad& quad) {
- this->pushBackImpl(quad);
- }
-
-private:
- typedef GrQuadListBase<void> INHERITED;
-};
-
-// This variant of the list allows simple metadata to be stored per quad as well, such as color
-// or texture domain.
-template<typename T>
-class GrTQuadList : public GrQuadListBase<T> {
-public:
- GrTQuadList() : INHERITED() {}
-
- void concat(const GrTQuadList<T>& that) {
- this->concatImpl(that);
- }
-
- // Adding to the list requires metadata
- void push_back(const GrQuad& quad, T&& metadata) {
- QuadData<T>& item = this->pushBackImpl(quad);
- item.fMetadata = std::move(metadata);
- }
-
- void push_back(const GrPerspQuad& quad, T&& metadata) {
- QuadData<T>& item = this->pushBackImpl(quad);
- item.fMetadata = std::move(metadata);
- }
-
- // And provide access to the metadata per quad
- const T& metadata(int i) const {
- return this->item(i).fMetadata;
- }
-
- T& metadata(int i) {
- return this->item(i).fMetadata;
- }
-
-private:
- typedef GrQuadListBase<T> INHERITED;
-};
-
#endif
diff --git a/src/gpu/geometry/GrQuadList.h b/src/gpu/geometry/GrQuadList.h
new file mode 100644
index 0000000..a802f9e
--- /dev/null
+++ b/src/gpu/geometry/GrQuadList.h
@@ -0,0 +1,192 @@
+/*
+ * Copyright 2019 Google LLC
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef GrQuadList_DEFINED
+#define GrQuadList_DEFINED
+
+#include "include/private/SkTArray.h"
+#include "src/gpu/geometry/GrQuad.h"
+
+// Underlying data used by GrQuadListBase. It is defined outside of GrQuadListBase due to compiler
+// issues related to specializing member types.
+template<typename T>
+struct QuadData {
+ float fX[4];
+ float fY[4];
+ T fMetadata;
+};
+
+template<>
+struct QuadData<void> {
+ float fX[4];
+ float fY[4];
+};
+
+// A dynamic list of (possibly) perspective quads that tracks the most general quad type of all
+// added quads. It avoids storing the 3rd component if the quad type never becomes perspective.
+// Use GrQuadList subclass when only storing quads. Use GrTQuadList subclass when storing quads
+// and per-quad templated metadata (such as color or domain).
+template<typename T>
+class GrQuadListBase {
+public:
+
+ int count() const { return fXYs.count(); }
+
+ GrQuadType quadType() const { return fType; }
+
+ void reserve(int count, bool needsPerspective) {
+ fXYs.reserve(count);
+ if (needsPerspective || fType == GrQuadType::kPerspective) {
+ fWs.reserve(4 * count);
+ }
+ }
+
+ GrPerspQuad operator[] (int i) const {
+ SkASSERT(i < this->count());
+ SkASSERT(i >= 0);
+
+ const QuadData<T>& item = fXYs[i];
+ if (fType == GrQuadType::kPerspective) {
+ // Read the explicit ws
+ return GrPerspQuad(item.fX, item.fY, fWs.begin() + 4 * i, fType);
+ } else {
+ // Ws are implicitly 1s.
+ static constexpr float kNoPerspectiveWs[4] = {1.f, 1.f, 1.f, 1.f};
+ return GrPerspQuad(item.fX, item.fY, kNoPerspectiveWs, fType);
+ }
+ }
+
+ // Subclasses expose push_back(const GrQuad|GrPerspQuad&, GrQuadType, [const T&]), where
+ // the metadata argument is only present in GrTQuadList's push_back definition.
+
+protected:
+ GrQuadListBase() : fType(GrQuadType::kRect) {}
+
+ void concatImpl(const GrQuadListBase<T>& that) {
+ this->upgradeType(that.fType);
+ fXYs.push_back_n(that.fXYs.count(), that.fXYs.begin());
+ if (fType == GrQuadType::kPerspective) {
+ if (that.fType == GrQuadType::kPerspective) {
+ // Copy the other's ws into the end of this list's data
+ fWs.push_back_n(that.fWs.count(), that.fWs.begin());
+ } else {
+ // This list stores ws but the appended list had implicit 1s, so add explicit 1s to
+ // fill out the total list
+ fWs.push_back_n(4 * that.count(), 1.f);
+ }
+ }
+ }
+
+ // Returns the added item data so that its metadata can be initialized if T is not void
+ QuadData<T>& pushBackImpl(const GrQuad& quad) {
+ this->upgradeType(quad.quadType());
+ QuadData<T>& item = fXYs.push_back();
+ memcpy(item.fX, quad.fX, 4 * sizeof(float));
+ memcpy(item.fY, quad.fY, 4 * sizeof(float));
+ if (fType == GrQuadType::kPerspective) {
+ fWs.push_back_n(4, 1.f);
+ }
+ return item;
+ }
+
+ QuadData<T>& pushBackImpl(const GrPerspQuad& quad) {
+ this->upgradeType(quad.quadType());
+ QuadData<T>& item = fXYs.push_back();
+ memcpy(item.fX, quad.fX, 4 * sizeof(float));
+ memcpy(item.fY, quad.fY, 4 * sizeof(float));
+ if (fType == GrQuadType::kPerspective) {
+ fWs.push_back_n(4, quad.fW);
+ }
+ return item;
+ }
+
+ const QuadData<T>& item(int i) const {
+ return fXYs[i];
+ }
+
+ QuadData<T>& item(int i) {
+ return fXYs[i];
+ }
+
+private:
+ void upgradeType(GrQuadType type) {
+ // Possibly upgrade the overall type tracked by the list
+ if (type > fType) {
+ fType = type;
+ if (type == GrQuadType::kPerspective) {
+ // All existing quads were 2D, so the ws array just needs to be filled with 1s
+ fWs.push_back_n(4 * this->count(), 1.f);
+ }
+ }
+ }
+
+ // Interleaves xs, ys, and per-quad metadata so that all data for a single quad is together
+ // (barring ws, which can be dropped entirely if the quad type allows it).
+ SkSTArray<1, QuadData<T>, true> fXYs;
+ // The w channel is kept separate so that it can remain empty when only dealing with 2D quads.
+ SkTArray<float, true> fWs;
+
+ GrQuadType fType;
+};
+
+// This list only stores the quad data itself.
+class GrQuadList : public GrQuadListBase<void> {
+public:
+ GrQuadList() : INHERITED() {}
+
+ void concat(const GrQuadList& that) {
+ this->concatImpl(that);
+ }
+
+ void push_back(const GrQuad& quad) {
+ this->pushBackImpl(quad);
+ }
+
+ void push_back(const GrPerspQuad& quad) {
+ this->pushBackImpl(quad);
+ }
+
+private:
+ typedef GrQuadListBase<void> INHERITED;
+};
+
+// This variant of the list allows simple metadata to be stored per quad as well, such as color
+// or texture domain.
+template<typename T>
+class GrTQuadList : public GrQuadListBase<T> {
+public:
+ GrTQuadList() : INHERITED() {}
+
+ void concat(const GrTQuadList<T>& that) {
+ this->concatImpl(that);
+ }
+
+ // Adding to the list requires metadata
+ void push_back(const GrQuad& quad, T&& metadata) {
+ QuadData<T>& item = this->pushBackImpl(quad);
+ item.fMetadata = std::move(metadata);
+ }
+
+ void push_back(const GrPerspQuad& quad, T&& metadata) {
+ QuadData<T>& item = this->pushBackImpl(quad);
+ item.fMetadata = std::move(metadata);
+ }
+
+ // And provide access to the metadata per quad
+ const T& metadata(int i) const {
+ return this->item(i).fMetadata;
+ }
+
+ T& metadata(int i) {
+ return this->item(i).fMetadata;
+ }
+
+private:
+ typedef GrQuadListBase<T> INHERITED;
+};
+
+#endif
diff --git a/src/gpu/ops/GrFillRectOp.cpp b/src/gpu/ops/GrFillRectOp.cpp
index 8e8ae3f..18765d9 100644
--- a/src/gpu/ops/GrFillRectOp.cpp
+++ b/src/gpu/ops/GrFillRectOp.cpp
@@ -14,6 +14,7 @@
#include "src/gpu/GrPaint.h"
#include "src/gpu/SkGr.h"
#include "src/gpu/geometry/GrQuad.h"
+#include "src/gpu/geometry/GrQuadList.h"
#include "src/gpu/glsl/GrGLSLColorSpaceXformHelper.h"
#include "src/gpu/glsl/GrGLSLGeometryProcessor.h"
#include "src/gpu/glsl/GrGLSLVarying.h"
diff --git a/src/gpu/ops/GrTextureOp.cpp b/src/gpu/ops/GrTextureOp.cpp
index 70382860..342d7ba 100644
--- a/src/gpu/ops/GrTextureOp.cpp
+++ b/src/gpu/ops/GrTextureOp.cpp
@@ -30,6 +30,7 @@
#include "src/gpu/GrTexturePriv.h"
#include "src/gpu/SkGr.h"
#include "src/gpu/geometry/GrQuad.h"
+#include "src/gpu/geometry/GrQuadList.h"
#include "src/gpu/glsl/GrGLSLVarying.h"
#include "src/gpu/ops/GrMeshDrawOp.h"
#include "src/gpu/ops/GrQuadPerEdgeAA.h"
diff --git a/src/opts/SkVM_opts.h b/src/opts/SkVM_opts.h
index b692ba4..c824184 100644
--- a/src/opts/SkVM_opts.h
+++ b/src/opts/SkVM_opts.h
@@ -73,11 +73,11 @@
for (int i = 0; i < ninsts; i++) {
skvm::Program::Instruction inst = insts[i];
- // d = op(x, y, z.id/z.imm)
+ // d = op(x, y.id/z.imm, z.id/z.imm)
ID d = inst.d,
- x = inst.x,
- y = inst.y;
- auto z = inst.z;
+ x = inst.x;
+ auto y = inst.y,
+ z = inst.z;
// Ops that interact with memory need to know whether we're stride=1 or stride=K,
// but all non-memory ops can run the same code no matter the stride.
@@ -85,42 +85,48 @@
#define STRIDE_1(op) case 2*(int)op
#define STRIDE_K(op) case 2*(int)op + 1
- STRIDE_1(Op::store8 ): memcpy(arg(z.imm), &r(x).i32, 1); break;
- STRIDE_1(Op::store32): memcpy(arg(z.imm), &r(x).i32, 4); break;
+ STRIDE_1(Op::store8 ): memcpy(arg(y.imm), &r(x).i32, 1); break;
+ STRIDE_1(Op::store32): memcpy(arg(y.imm), &r(x).i32, 4); break;
- STRIDE_K(Op::store8 ): skvx::cast<uint8_t>(r(x).i32).store(arg(z.imm)); break;
- STRIDE_K(Op::store32): (r(x).i32).store(arg(z.imm)); break;
+ STRIDE_K(Op::store8 ): skvx::cast<uint8_t>(r(x).i32).store(arg(y.imm)); break;
+ STRIDE_K(Op::store32): (r(x).i32).store(arg(y.imm)); break;
- STRIDE_1(Op::load8 ): r(d).i32 = 0; memcpy(&r(d).i32, arg(z.imm), 1); break;
- STRIDE_1(Op::load32): r(d).i32 = 0; memcpy(&r(d).i32, arg(z.imm), 4); break;
+ STRIDE_1(Op::load8 ): r(d).i32 = 0; memcpy(&r(d).i32, arg(y.imm), 1); break;
+ STRIDE_1(Op::load32): r(d).i32 = 0; memcpy(&r(d).i32, arg(y.imm), 4); break;
- STRIDE_K(Op::load8 ): r(d).i32 = skvx::cast<int>(U8 ::Load(arg(z.imm))); break;
- STRIDE_K(Op::load32): r(d).i32 = I32::Load(arg(z.imm)) ; break;
+ STRIDE_K(Op::load8 ): r(d).i32 = skvx::cast<int>(U8 ::Load(arg(y.imm))); break;
+ STRIDE_K(Op::load32): r(d).i32 = I32::Load(arg(y.imm)) ; break;
#undef STRIDE_1
#undef STRIDE_K
// Ops that don't interact with memory should never care about the stride.
#define CASE(op) case 2*(int)op: /*fallthrough*/ case 2*(int)op+1
- CASE(Op::splat): r(d).i32 = z.imm; break;
+ CASE(Op::splat): r(d).i32 = y.imm; break;
- CASE(Op::add_f32): r(d).f32 = r(x).f32 + r(y).f32; break;
- CASE(Op::sub_f32): r(d).f32 = r(x).f32 - r(y).f32; break;
- CASE(Op::mul_f32): r(d).f32 = r(x).f32 * r(y).f32; break;
- CASE(Op::div_f32): r(d).f32 = r(x).f32 / r(y).f32; break;
+ CASE(Op::add_f32): r(d).f32 = r(x).f32 + r(y.id).f32; break;
+ CASE(Op::sub_f32): r(d).f32 = r(x).f32 - r(y.id).f32; break;
+ CASE(Op::mul_f32): r(d).f32 = r(x).f32 * r(y.id).f32; break;
+ CASE(Op::div_f32): r(d).f32 = r(x).f32 / r(y.id).f32; break;
- CASE(Op::mad_f32): r(d).f32 = r(x).f32 * r(y).f32 + r(z.id).f32; break;
+ CASE(Op::mad_f32): r(d).f32 = r(x).f32 * r(y.id).f32 + r(z.id).f32; break;
- CASE(Op::add_i32): r(d).i32 = r(x).i32 + r(y).i32; break;
- CASE(Op::sub_i32): r(d).i32 = r(x).i32 - r(y).i32; break;
- CASE(Op::mul_i32): r(d).i32 = r(x).i32 * r(y).i32; break;
+ CASE(Op::add_i32): r(d).i32 = r(x).i32 + r(y.id).i32; break;
+ CASE(Op::sub_i32): r(d).i32 = r(x).i32 - r(y.id).i32; break;
+ CASE(Op::mul_i32): r(d).i32 = r(x).i32 * r(y.id).i32; break;
- CASE(Op::bit_and): r(d).i32 = r(x).i32 & r(y).i32; break;
- CASE(Op::bit_or ): r(d).i32 = r(x).i32 | r(y).i32; break;
- CASE(Op::bit_xor): r(d).i32 = r(x).i32 ^ r(y).i32; break;
+ CASE(Op::bit_and): r(d).i32 = r(x).i32 & r(y.id).i32; break;
+ CASE(Op::bit_or ): r(d).i32 = r(x).i32 | r(y.id).i32; break;
+ CASE(Op::bit_xor): r(d).i32 = r(x).i32 ^ r(y.id).i32; break;
- CASE(Op::shl): r(d).i32 = r(x).i32 << z.imm ; break;
- CASE(Op::sra): r(d).i32 = r(x).i32 >> z.imm ; break;
- CASE(Op::shr): r(d).i32 = skvx::cast<int>(r(x).u32 >> z.imm); break;
+ CASE(Op::shl): r(d).i32 = r(x).i32 << y.imm; break;
+ CASE(Op::sra): r(d).i32 = r(x).i32 >> y.imm; break;
+ CASE(Op::shr): r(d).u32 = r(x).u32 >> y.imm; break;
+
+ CASE(Op::mul_unorm8): r(d).i32 = (r(x).i32 * r(y.id).i32 + 255) / 256; break;
+
+ CASE(Op::extract): r(d).u32 = (r(x).u32 & y.imm) >> z.imm; break;
+
+ CASE(Op::pack): r(d).i32 = r(x).i32 | (r(y.id).i32 << z.imm); break;
CASE(Op::to_f32): r(d).f32 = skvx::cast<float>(r(x).i32); break;
CASE(Op::to_i32): r(d).i32 = skvx::cast<int> (r(x).f32); break;
diff --git a/tests/BackendAllocationTest.cpp b/tests/BackendAllocationTest.cpp
index 701eae4..e76d210 100644
--- a/tests/BackendAllocationTest.cpp
+++ b/tests/BackendAllocationTest.cpp
@@ -365,8 +365,8 @@
const SkColor4f& color,
GrMipMapped mipMapped,
GrRenderable renderable) {
- return context->priv().createBackendTexture(32, 32, colorType, color,
- mipMapped, renderable);
+ return context->createBackendTexture(32, 32, colorType, color,
+ mipMapped, renderable);
};
test_color_init(context, reporter, createWithColorMtd,
@@ -527,8 +527,8 @@
const SkColor4f& color,
GrMipMapped mipMapped,
GrRenderable renderable) {
- return context->priv().createBackendTexture(32, 32, format, color,
- mipMapped, renderable);
+ return context->createBackendTexture(32, 32, format, color,
+ mipMapped, renderable);
};
test_color_init(context, reporter, createWithColorMtd,
@@ -637,8 +637,8 @@
const SkColor4f& color,
GrMipMapped mipMapped,
GrRenderable renderable) {
- return context->priv().createBackendTexture(32, 32, format, color,
- mipMapped, renderable);
+ return context->createBackendTexture(32, 32, format, color,
+ mipMapped, renderable);
};
test_color_init(context, reporter, createWithColorMtd,
diff --git a/tests/DeferredDisplayListTest.cpp b/tests/DeferredDisplayListTest.cpp
index 687f9e2..f128f6c 100644
--- a/tests/DeferredDisplayListTest.cpp
+++ b/tests/DeferredDisplayListTest.cpp
@@ -186,6 +186,7 @@
}
*backend = context->createBackendTexture(fWidth, fHeight, fColorType,
+ SkColors::kTransparent,
mipmapped, GrRenderable::kYes);
if (!backend->isValid() || !gpu->isTestingOnlyBackendTexture(*backend)) {
return nullptr;
@@ -578,7 +579,8 @@
GrContext* context = ctxInfo.grContext();
GrBackendTexture backendTex = context->createBackendTexture(
- kSize, kSize, kRGBA_8888_SkColorType, GrMipMapped::kNo, GrRenderable::kNo);
+ kSize, kSize, kRGBA_8888_SkColorType,
+ SkColors::kTransparent, GrMipMapped::kNo, GrRenderable::kNo);
if (!backendTex.isValid()) {
return;
}
diff --git a/tests/EGLImageTest.cpp b/tests/EGLImageTest.cpp
index 808dd10..96483a5 100644
--- a/tests/EGLImageTest.cpp
+++ b/tests/EGLImageTest.cpp
@@ -88,6 +88,7 @@
static const int kSize = 100;
backendTexture1 =
context1->createBackendTexture(kSize, kSize, kRGBA_8888_SkColorType,
+ SkColors::kTransparent,
GrMipMapped::kNo, GrRenderable::kNo);
if (!backendTexture1.isValid() || !gpu1->isTestingOnlyBackendTexture(backendTexture1)) {
diff --git a/tests/GrMipMappedTest.cpp b/tests/GrMipMappedTest.cpp
index 15718c7..ef0ef66 100644
--- a/tests/GrMipMappedTest.cpp
+++ b/tests/GrMipMappedTest.cpp
@@ -41,7 +41,8 @@
// so we don't send any. However, we pretend there is data for the checks below which is
// fine since we are never actually using these textures for any work on the gpu.
GrBackendTexture backendTex = context->createBackendTexture(
- kSize, kSize, kRGBA_8888_SkColorType, mipMapped, renderable);
+ kSize, kSize, kRGBA_8888_SkColorType,
+ SkColors::kTransparent, mipMapped, renderable);
sk_sp<GrTextureProxy> proxy;
sk_sp<SkImage> image;
@@ -106,7 +107,8 @@
for (auto mipMapped : {GrMipMapped::kNo, GrMipMapped::kYes}) {
for (auto willUseMips : {false, true}) {
GrBackendTexture backendTex = context->createBackendTexture(
- kSize, kSize, kRGBA_8888_SkColorType, mipMapped, GrRenderable::kNo);
+ kSize, kSize, kRGBA_8888_SkColorType,
+ SkColors::kTransparent, mipMapped, GrRenderable::kNo);
sk_sp<SkImage> image = SkImage::MakeFromTexture(context, backendTex,
kTopLeft_GrSurfaceOrigin,
@@ -248,7 +250,8 @@
GrMipMapped mipMapped = willUseMips ? GrMipMapped::kYes : GrMipMapped::kNo;
sk_sp<SkSurface> surface;
GrBackendTexture backendTex = context->createBackendTexture(
- kSize, kSize, kRGBA_8888_SkColorType, mipMapped, GrRenderable::kYes);
+ kSize, kSize, kRGBA_8888_SkColorType,
+ SkColors::kTransparent, mipMapped, GrRenderable::kYes);
if (isWrapped) {
surface = SkSurface::MakeFromBackendTexture(context,
backendTex,
diff --git a/tests/GrPorterDuffTest.cpp b/tests/GrPorterDuffTest.cpp
index 2d124a8..79b36e6 100644
--- a/tests/GrPorterDuffTest.cpp
+++ b/tests/GrPorterDuffTest.cpp
@@ -997,7 +997,7 @@
}
GrBackendTexture backendTex =
- ctx->createBackendTexture(100, 100, kRGBA_8888_SkColorType,
+ ctx->createBackendTexture(100, 100, kRGBA_8888_SkColorType, SkColors::kTransparent,
GrMipMapped::kNo, GrRenderable::kNo);
GrXferProcessor::DstProxy fakeDstProxy;
diff --git a/tests/GrQuadListTest.cpp b/tests/GrQuadListTest.cpp
index b8d0acb..89e7607 100644
--- a/tests/GrQuadListTest.cpp
+++ b/tests/GrQuadListTest.cpp
@@ -7,7 +7,7 @@
#include "tests/Test.h"
-#include "src/gpu/geometry/GrQuad.h"
+#include "src/gpu/geometry/GrQuadList.h"
#define ASSERT(cond) REPORTER_ASSERT(r, cond)
#define ASSERTF(cond, ...) REPORTER_ASSERT(r, cond, __VA_ARGS__)
diff --git a/tests/GrSurfaceTest.cpp b/tests/GrSurfaceTest.cpp
index 45867fe..178d6d6 100644
--- a/tests/GrSurfaceTest.cpp
+++ b/tests/GrSurfaceTest.cpp
@@ -53,7 +53,8 @@
REPORTER_ASSERT(reporter, static_cast<GrSurface*>(tex1.get()) == tex1->asTexture());
GrBackendTexture backendTex = context->createBackendTexture(
- 256, 256, kRGBA_8888_SkColorType, GrMipMapped::kNo, GrRenderable::kNo);
+ 256, 256, kRGBA_8888_SkColorType,
+ SkColors::kTransparent, GrMipMapped::kNo, GrRenderable::kNo);
sk_sp<GrSurface> texRT2 = resourceProvider->wrapRenderableBackendTexture(
backendTex, 1, kBorrow_GrWrapOwnership, GrWrapCacheable::kNo);
@@ -331,7 +332,8 @@
if (context->priv().caps()->mipMapSupport()) {
delete_backend_texture(context, backendTex);
backendTex = context->createBackendTexture(
- kSize, kSize, kRGBA_8888_SkColorType, GrMipMapped::kYes, GrRenderable::kYes);
+ kSize, kSize, kRGBA_8888_SkColorType,
+ SkColors::kTransparent, GrMipMapped::kYes, GrRenderable::kYes);
proxy = proxyProvider->wrapBackendTexture(backendTex, kTopLeft_GrSurfaceOrigin,
kBorrow_GrWrapOwnership, GrWrapCacheable::kNo,
ioType);
@@ -347,7 +349,7 @@
static sk_sp<GrTexture> make_wrapped_texture(GrContext* context, GrRenderable renderable) {
auto backendTexture = context->createBackendTexture(
- 10, 10, kRGBA_8888_SkColorType, GrMipMapped::kNo, renderable);
+ 10, 10, kRGBA_8888_SkColorType, SkColors::kTransparent, GrMipMapped::kNo, renderable);
sk_sp<GrTexture> texture;
if (GrRenderable::kYes == renderable) {
texture = context->priv().resourceProvider()->wrapRenderableBackendTexture(
diff --git a/tests/ImageTest.cpp b/tests/ImageTest.cpp
index 6a4df50..c8fbacf 100644
--- a/tests/ImageTest.cpp
+++ b/tests/ImageTest.cpp
@@ -491,7 +491,8 @@
bool can = context->colorTypeSupportedAsImage(colorType);
GrBackendTexture backendTex = context->createBackendTexture(
- kSize, kSize, colorType, GrMipMapped::kNo, GrRenderable::kNo);
+ kSize, kSize, colorType, SkColors::kTransparent,
+ GrMipMapped::kNo, GrRenderable::kNo);
auto img = SkImage::MakeFromTexture(context, backendTex, kTopLeft_GrSurfaceOrigin,
colorType, kOpaque_SkAlphaType, nullptr);
diff --git a/tests/LazyProxyTest.cpp b/tests/LazyProxyTest.cpp
index b5a60b3..da92b6b 100644
--- a/tests/LazyProxyTest.cpp
+++ b/tests/LazyProxyTest.cpp
@@ -472,7 +472,8 @@
desc.fConfig = kRGBA_8888_GrPixelConfig;
GrBackendTexture backendTex = ctx->createBackendTexture(
- kSize, kSize, kRGBA_8888_SkColorType, GrMipMapped::kNo, GrRenderable::kNo);
+ kSize, kSize, kRGBA_8888_SkColorType, SkColors::kTransparent,
+ GrMipMapped::kNo, GrRenderable::kNo);
sk_sp<GrTextureProxy> lazyProxy = proxyProvider->createLazyProxy(
[instantiatePtr, releasePtr,
diff --git a/tests/PromiseImageTest.cpp b/tests/PromiseImageTest.cpp
index 6a60970..38c513e 100644
--- a/tests/PromiseImageTest.cpp
+++ b/tests/PromiseImageTest.cpp
@@ -168,7 +168,8 @@
GrGpu* gpu = ctx->priv().getGpu();
GrBackendTexture backendTex = ctx->createBackendTexture(
- kWidth, kHeight, kRGBA_8888_SkColorType, GrMipMapped::kNo, GrRenderable::kYes);
+ kWidth, kHeight, kRGBA_8888_SkColorType,
+ SkColors::kTransparent, GrMipMapped::kNo, GrRenderable::kYes);
REPORTER_ASSERT(reporter, backendTex.isValid());
GrBackendFormat backendFormat = backendTex.getBackendFormat();
@@ -243,11 +244,13 @@
GrGpu* gpu = ctx->priv().getGpu();
GrBackendTexture backendTex1 = ctx->createBackendTexture(
- kWidth, kHeight, kGray_8_SkColorType, GrMipMapped::kNo, GrRenderable::kNo);
+ kWidth, kHeight, kGray_8_SkColorType,
+ SkColors::kTransparent, GrMipMapped::kNo, GrRenderable::kNo);
REPORTER_ASSERT(reporter, backendTex1.isValid());
GrBackendTexture backendTex2 = ctx->createBackendTexture(
- kWidth, kHeight, kAlpha_8_SkColorType, GrMipMapped::kNo, GrRenderable::kNo);
+ kWidth, kHeight, kAlpha_8_SkColorType,
+ SkColors::kTransparent, GrMipMapped::kNo, GrRenderable::kNo);
REPORTER_ASSERT(reporter, backendTex2.isValid());
if (backendTex1.getBackendFormat() != backendTex2.getBackendFormat()) {
ctx->deleteBackendTexture(backendTex1);
@@ -353,7 +356,8 @@
}
GrBackendTexture backendTex = ctx->createBackendTexture(
- kWidth, kHeight, kAlpha_8_SkColorType, GrMipMapped::kNo, GrRenderable::kNo);
+ kWidth, kHeight, kAlpha_8_SkColorType,
+ SkColors::kTransparent, GrMipMapped::kNo, GrRenderable::kNo);
REPORTER_ASSERT(reporter, backendTex.isValid());
SkImageInfo info = SkImageInfo::Make(kWidth, kHeight, kRGBA_8888_SkColorType,
@@ -391,7 +395,8 @@
GrContext* ctx = ctxInfo.grContext();
GrBackendTexture backendTex = ctx->createBackendTexture(
- kWidth, kHeight, kAlpha_8_SkColorType, GrMipMapped::kNo, GrRenderable::kNo);
+ kWidth, kHeight, kAlpha_8_SkColorType,
+ SkColors::kTransparent, GrMipMapped::kNo, GrRenderable::kNo);
REPORTER_ASSERT(reporter, backendTex.isValid());
SkImageInfo info =
@@ -454,7 +459,8 @@
// Do all this just to get a valid backend format for the image.
GrBackendTexture backendTex = ctx->createBackendTexture(
- kWidth, kHeight, kRGBA_8888_SkColorType, GrMipMapped::kNo, GrRenderable::kYes);
+ kWidth, kHeight, kRGBA_8888_SkColorType,
+ SkColors::kTransparent, GrMipMapped::kNo, GrRenderable::kYes);
REPORTER_ASSERT(reporter, backendTex.isValid());
GrBackendFormat backendFormat = backendTex.getBackendFormat();
REPORTER_ASSERT(reporter, backendFormat.isValid());
diff --git a/tests/ProxyTest.cpp b/tests/ProxyTest.cpp
index ffd2106..8b5e130 100644
--- a/tests/ProxyTest.cpp
+++ b/tests/ProxyTest.cpp
@@ -277,6 +277,7 @@
GrBackendTexture backendTex =
context->createBackendTexture(kWidthHeight, kWidthHeight,
colorType,
+ SkColors::kTransparent,
GrMipMapped::kNo,
GrRenderable::kYes);
sk_sp<GrSurfaceProxy> sProxy = proxyProvider->wrapBackendTextureAsRenderTarget(
@@ -302,6 +303,7 @@
GrBackendTexture backendTex =
context->createBackendTexture(kWidthHeight, kWidthHeight,
colorType,
+ SkColors::kTransparent,
GrMipMapped::kNo,
GrRenderable::kYes);
@@ -330,6 +332,7 @@
GrBackendTexture backendTex =
context->createBackendTexture(kWidthHeight, kWidthHeight,
colorType,
+ SkColors::kTransparent,
GrMipMapped::kNo,
GrRenderable::kNo);
diff --git a/tests/ResourceAllocatorTest.cpp b/tests/ResourceAllocatorTest.cpp
index f0cba29..c23c85e 100644
--- a/tests/ResourceAllocatorTest.cpp
+++ b/tests/ResourceAllocatorTest.cpp
@@ -63,6 +63,7 @@
GrProxyProvider* proxyProvider = context->priv().proxyProvider();
*backendTex = context->createBackendTexture(p.fSize, p.fSize, p.fColorType,
+ SkColors::kTransparent,
GrMipMapped::kNo, GrRenderable::kNo);
if (!backendTex->isValid()) {
return nullptr;
diff --git a/tests/ResourceCacheTest.cpp b/tests/ResourceCacheTest.cpp
index 17b21bd..caed56b 100644
--- a/tests/ResourceCacheTest.cpp
+++ b/tests/ResourceCacheTest.cpp
@@ -210,8 +210,10 @@
static const int kH = 100;
backendTextures[0] = context->createBackendTexture(kW, kH, kRGBA_8888_SkColorType,
+ SkColors::kTransparent,
GrMipMapped::kNo, GrRenderable::kNo);
backendTextures[1] = context->createBackendTexture(kW, kH, kRGBA_8888_SkColorType,
+ SkColors::kTransparent,
GrMipMapped::kNo, GrRenderable::kNo);
REPORTER_ASSERT(reporter, backendTextures[0].isValid());
REPORTER_ASSERT(reporter, backendTextures[1].isValid());
diff --git a/tests/SkVMTest.cpp b/tests/SkVMTest.cpp
index 7d8c0b0..fdb62ca 100644
--- a/tests/SkVMTest.cpp
+++ b/tests/SkVMTest.cpp
@@ -10,117 +10,27 @@
#include "src/core/SkVM.h"
#include "tests/Test.h"
#include "tools/Resources.h"
+#include "tools/SkVMBuilders.h"
-enum Fmt { A8, G8, RGBA_8888 };
+using Fmt = SrcoverBuilder_F32::Fmt;
const char* fmt_name(Fmt fmt) {
switch (fmt) {
- case A8: return "A8";
- case G8: return "G8";
- case RGBA_8888: return "RGBA_8888";
+ case Fmt::A8: return "A8";
+ case Fmt::G8: return "G8";
+ case Fmt::RGBA_8888: return "RGBA_8888";
}
return "";
}
-// Here's a cute little trick that avoids the need to explicitly thread
-// and skvm::Builder* through and make a lot of builder->foo() calls.
-// Instead the builder becomes this, with this-> omitted for clarity.
-//
-// Think of this as
-// static void srcover(skvm::Builder*, Fmt srcFmt, Fmt dstFmt) { ... }
-//
-// Some parts of this builder code are written less fluently than possible,
-// to avoid any ambiguity of function argument evaluation order. This lets
-// our golden tests work portably. In general there's no reason to fear
-// nesting calls to Builder routines.
-
-struct SrcoverBuilder : public skvm::Builder {
- SrcoverBuilder(Fmt srcFmt, Fmt dstFmt) {
- skvm::Arg src = arg(0),
- dst = arg(1);
-
- auto byte_to_f32 = [&](skvm::I32 byte) {
- skvm::F32 _1_255 = splat(1/255.0f);
- return mul(_1_255, to_f32(byte));
- };
-
- auto load = [&](skvm::Arg ptr, Fmt fmt,
- skvm::F32* r, skvm::F32* g, skvm::F32* b, skvm::F32* a) {
- switch (fmt) {
- case A8: {
- *r = *g = *b = splat(0.0f);
- *a = byte_to_f32(load8(ptr));
- } break;
-
- case G8: {
- *r = *g = *b = byte_to_f32(load8(ptr));
- *a = splat(1.0f);
- } break;
-
- case RGBA_8888: {
- skvm::I32 rgba = load32(ptr),
- _255 = splat(255);
- *r = byte_to_f32(bit_and( rgba , _255));
- *g = byte_to_f32(bit_and(shr(rgba, 8), _255));
- *b = byte_to_f32(bit_and(shr(rgba, 16), _255));
- *a = byte_to_f32( shr(rgba, 24) );
- } break;
- }
- };
-
- skvm::F32 r,g,b,a;
- load(src, srcFmt, &r,&g,&b,&a);
-
- skvm::F32 dr,dg,db,da;
- load(dst, dstFmt, &dr,&dg,&db,&da);
-
- skvm::F32 invA = sub(splat(1.0f), a);
- r = mad(dr, invA, r);
- g = mad(dg, invA, g);
- b = mad(db, invA, b);
- a = mad(da, invA, a);
-
- auto f32_to_byte = [&](skvm::F32 f32) {
- skvm::F32 _255 = splat(255.0f),
- _0_5 = splat(0.5f);
- return to_i32(mad(f32, _255, _0_5));
- };
- switch (dstFmt) {
- case A8: {
- store8(dst, f32_to_byte(a));
- } break;
-
- case G8: {
- skvm::F32 _2126 = splat(0.2126f),
- _7152 = splat(0.7152f),
- _0722 = splat(0.0722f);
- store8(dst, f32_to_byte(mad(r, _2126,
- mad(g, _7152,
- mul(b, _0722)))));
- } break;
-
- case RGBA_8888: {
- skvm::I32 R = f32_to_byte(r) ,
- G = shl(f32_to_byte(g), 8),
- B = shl(f32_to_byte(b), 16),
- A = shl(f32_to_byte(a), 24);
-
- R = bit_or(R,G);
- R = bit_or(R,B);
- R = bit_or(R,A);
-
- store32(dst, R);
- } break;
- }
- }
-};
-
DEF_TEST(SkVM, r) {
SkDynamicMemoryWStream buf;
+
+ // Write all combinations of SrcoverBuilder_F32
for (int s = 0; s < 3; s++)
for (int d = 0; d < 3; d++) {
auto srcFmt = (Fmt)s,
dstFmt = (Fmt)d;
- skvm::Program program = SrcoverBuilder{srcFmt, dstFmt}.done();
+ skvm::Program program = SrcoverBuilder_F32{srcFmt, dstFmt}.done();
buf.writeText(fmt_name(srcFmt));
buf.writeText(" over ");
@@ -130,22 +40,43 @@
buf.writeText("\n");
}
+ // Write the I32 Srcovers also.
+ {
+ skvm::Program program = SrcoverBuilder_I32{}.done();
+ buf.writeText("I32 8888 over 8888\n");
+ program.dump(&buf);
+ buf.writeText("\n");
+ }
+ {
+ skvm::Program program = SrcoverBuilder_I32_SWAR{}.done();
+ buf.writeText("I32 (SWAR) 8888 over 8888\n");
+ program.dump(&buf);
+ buf.writeText("\n");
+ }
+
sk_sp<SkData> blob = buf.detachAsData();
{
sk_sp<SkData> expected = GetResourceAsData("SkVMTest.expected");
- REPORTER_ASSERT(r, expected
- && blob->size() == expected->size()
- && 0 == memcmp(blob->data(), expected->data(), blob->size()));
+ REPORTER_ASSERT(r, expected, "Couldn't load SkVMTest.expected.");
+ if (expected) {
+ if (blob->size() != expected->size()
+ || 0 != memcmp(blob->data(), expected->data(), blob->size())) {
- SkFILEWStream out(GetResourcePath("SkVMTest.expected").c_str());
- if (out.isValid()) {
- out.write(blob->data(), blob->size());
+ ERRORF(r, "SkVMTest expected\n%.*s\nbut got\n%.*s\n",
+ expected->size(), expected->data(),
+ blob->size(), blob->data());
+ }
+
+ SkFILEWStream out(GetResourcePath("SkVMTest.expected").c_str());
+ if (out.isValid()) {
+ out.write(blob->data(), blob->size());
+ }
}
}
{
- skvm::Program program = SrcoverBuilder{RGBA_8888, RGBA_8888}.done();
+ skvm::Program program = SrcoverBuilder_F32{Fmt::RGBA_8888, Fmt::RGBA_8888}.done();
uint32_t src = 0xbb007733,
dst = 0xffaaccee;
@@ -164,7 +95,45 @@
}
{
- skvm::Program program = SrcoverBuilder{RGBA_8888, G8}.done();
+ skvm::Program program = SrcoverBuilder_I32{}.done();
+
+ uint32_t src = 0xbb007733,
+ dst = 0xffaaccee;
+ SkPMColor want = SkPMSrcOver(src, dst); // 0xff2dad73
+
+ program.eval(1, &src, &dst);
+
+ // dst is probably 0xff2dad72.
+ for (int i = 0; i < 4; i++) {
+ uint8_t d = dst & 0xff,
+ w = want & 0xff;
+ REPORTER_ASSERT(r, abs(d-w) < 2);
+ dst >>= 8;
+ want >>= 8;
+ }
+ }
+
+ {
+ skvm::Program program = SrcoverBuilder_I32_SWAR{}.done();
+
+ uint32_t src = 0xbb007733,
+ dst = 0xffaaccee;
+ SkPMColor want = SkPMSrcOver(src, dst); // 0xff2dad73
+
+ program.eval(1, &src, &dst);
+
+ // dst is probably 0xff2dad72.
+ for (int i = 0; i < 4; i++) {
+ uint8_t d = dst & 0xff,
+ w = want & 0xff;
+ REPORTER_ASSERT(r, abs(d-w) < 2);
+ dst >>= 8;
+ want >>= 8;
+ }
+ }
+
+ {
+ skvm::Program program = SrcoverBuilder_F32{Fmt::RGBA_8888, Fmt::G8}.done();
uint32_t src = 0xbb007733;
uint8_t dst = 0x42;
@@ -179,7 +148,7 @@
}
{
- skvm::Program program = SrcoverBuilder{A8, A8}.done();
+ skvm::Program program = SrcoverBuilder_F32{Fmt::A8, Fmt::A8}.done();
uint8_t src[256],
dst[256];
diff --git a/tests/SurfaceTest.cpp b/tests/SurfaceTest.cpp
index 6fb6df4..fa9fe79 100644
--- a/tests/SurfaceTest.cpp
+++ b/tests/SurfaceTest.cpp
@@ -103,7 +103,8 @@
colorType, can, SkToBool(surf));
GrBackendTexture backendTex = context->createBackendTexture(
- kSize, kSize, colorType, GrMipMapped::kNo, GrRenderable::kYes);
+ kSize, kSize, colorType,
+ SkColors::kTransparent, GrMipMapped::kNo, GrRenderable::kYes);
surf = SkSurface::MakeFromBackendTexture(context, backendTex,
kTopLeft_GrSurfaceOrigin, 0, colorType, nullptr,
nullptr);
@@ -128,6 +129,7 @@
colorType, can, SkToBool(surf));
backendTex = context->createBackendTexture(kSize, kSize, colorType,
+ SkColors::kTransparent,
GrMipMapped::kNo, GrRenderable::kYes);
surf = SkSurface::MakeFromBackendTexture(context, backendTex,
kTopLeft_GrSurfaceOrigin, kSampleCnt, colorType,
@@ -197,7 +199,8 @@
continue;
}
GrBackendTexture backendTex = context->createBackendTexture(
- kSize, kSize, colorType, GrMipMapped::kNo, GrRenderable::kYes);
+ kSize, kSize, colorType, SkColors::kTransparent,
+ GrMipMapped::kNo, GrRenderable::kYes);
if (!backendTex.isValid()) {
continue;
}
diff --git a/tests/TextureBindingsResetTest.cpp b/tests/TextureBindingsResetTest.cpp
index 3daa712..67c0994 100644
--- a/tests/TextureBindingsResetTest.cpp
+++ b/tests/TextureBindingsResetTest.cpp
@@ -102,7 +102,8 @@
if (supportExternal) {
GrBackendTexture texture2D = context->createBackendTexture(
- 10, 10, kRGBA_8888_SkColorType, GrMipMapped::kNo, GrRenderable::kNo);
+ 10, 10, kRGBA_8888_SkColorType,
+ SkColors::kTransparent, GrMipMapped::kNo, GrRenderable::kNo);
GrGLTextureInfo info2D;
REPORTER_ASSERT(reporter, texture2D.getGLTextureInfo(&info2D));
GrEGLImage eglImage = ctxInfo.glContext()->texture2DToEGLImage(info2D.fID);
diff --git a/tests/VkBackendSurfaceTest.cpp b/tests/VkBackendSurfaceTest.cpp
index adc40d9..ced9f88 100644
--- a/tests/VkBackendSurfaceTest.cpp
+++ b/tests/VkBackendSurfaceTest.cpp
@@ -34,6 +34,7 @@
GrBackendTexture backendTex = context->createBackendTexture(1, 1,
kRGBA_8888_SkColorType,
+ SkColors::kTransparent,
GrMipMapped::kNo,
GrRenderable::kNo);
REPORTER_ASSERT(reporter, backendTex.isValid());
@@ -140,6 +141,7 @@
for (bool useExternal : {false, true}) {
GrBackendTexture backendTex = context->createBackendTexture(1, 1,
kRGBA_8888_SkColorType,
+ SkColors::kTransparent,
GrMipMapped::kNo,
GrRenderable::kNo);
sk_sp<SkImage> image;
@@ -223,7 +225,8 @@
continue;
}
GrBackendTexture backendTex = context->createBackendTexture(
- 4, 4, kRGBA_8888_SkColorType, GrMipMapped::kNo,
+ 4, 4, kRGBA_8888_SkColorType,
+ SkColors::kTransparent, GrMipMapped::kNo,
useSurface ? GrRenderable::kYes : GrRenderable::kNo);
// Make a backend texture with an external queue family and general layout.
@@ -339,7 +342,8 @@
}
GrBackendTexture backendTex = context->createBackendTexture(
- 1, 1, kRGBA_8888_SkColorType, GrMipMapped::kNo, GrRenderable::kNo);
+ 1, 1, kRGBA_8888_SkColorType,
+ SkColors::kTransparent, GrMipMapped::kNo, GrRenderable::kNo);
sk_sp<SkImage> image;
// Make a backend texture with an external queue family and general layout.
GrVkImageInfo vkInfo;
diff --git a/tests/VkWrapTests.cpp b/tests/VkWrapTests.cpp
index 2dcf5de..081cfe4 100644
--- a/tests/VkWrapTests.cpp
+++ b/tests/VkWrapTests.cpp
@@ -38,6 +38,7 @@
GrBackendTexture origBackendTex = context->createBackendTexture(kW, kH,
kColorType,
+ SkColors::kTransparent,
GrMipMapped::kNo,
GrRenderable::kNo);
GrVkImageInfo imageInfo;
@@ -92,6 +93,7 @@
GrBackendTexture origBackendTex = context->createBackendTexture(kW, kH,
kColorType,
+ SkColors::kTransparent,
GrMipMapped::kNo,
GrRenderable::kYes);
@@ -135,6 +137,7 @@
GrBackendTexture origBackendTex = context->createBackendTexture(kW, kH,
kColorType,
+ SkColors::kTransparent,
GrMipMapped::kNo,
GrRenderable::kYes);
GrVkImageInfo imageInfo;
diff --git a/tests/WritePixelsTest.cpp b/tests/WritePixelsTest.cpp
index 12d5f1b..0badd6b 100644
--- a/tests/WritePixelsTest.cpp
+++ b/tests/WritePixelsTest.cpp
@@ -456,7 +456,8 @@
for (auto& origin : { kTopLeft_GrSurfaceOrigin, kBottomLeft_GrSurfaceOrigin }) {
GrBackendTexture backendTex = context->createBackendTexture(
- DEV_W, DEV_H, kRGBA_8888_SkColorType, GrMipMapped::kNo, GrRenderable::kYes);
+ DEV_W, DEV_H, kRGBA_8888_SkColorType,
+ SkColors::kTransparent, GrMipMapped::kNo, GrRenderable::kYes);
if (!backendTex.isValid()) {
continue;
}
diff --git a/third_party/skcms/version.sha1 b/third_party/skcms/version.sha1
index ebc0b05..36a939f 100755
--- a/third_party/skcms/version.sha1
+++ b/third_party/skcms/version.sha1
@@ -1 +1 @@
-ca5b4470be98480d38290c27ccd45330e020d6cd
\ No newline at end of file
+386ae89d2b6441ca431a1fb98843ae044fc12ba5
\ No newline at end of file
diff --git a/tools/SkVMBuilders.cpp b/tools/SkVMBuilders.cpp
new file mode 100644
index 0000000..5db79e3
--- /dev/null
+++ b/tools/SkVMBuilders.cpp
@@ -0,0 +1,154 @@
+/*
+ * Copyright 2019 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "tools/SkVMBuilders.h"
+
+// Some parts of this builder code are written less fluently than possible,
+// to avoid any ambiguity of function argument evaluation order. This lets
+// our golden tests work portably. In general there's no reason to fear
+// nesting calls to Builder routines.
+
+SrcoverBuilder_F32::SrcoverBuilder_F32(Fmt srcFmt, Fmt dstFmt) {
+ skvm::Arg src = arg(0),
+ dst = arg(1);
+
+ auto byte_to_f32 = [&](skvm::I32 byte) {
+ skvm::F32 _1_255 = splat(1/255.0f);
+ return mul(_1_255, to_f32(byte));
+ };
+
+ auto load = [&](skvm::Arg ptr, Fmt fmt,
+ skvm::F32* r, skvm::F32* g, skvm::F32* b, skvm::F32* a) {
+ switch (fmt) {
+ case Fmt::A8: {
+ *r = *g = *b = splat(0.0f);
+ *a = byte_to_f32(load8(ptr));
+ } break;
+
+ case Fmt::G8: {
+ *r = *g = *b = byte_to_f32(load8(ptr));
+ *a = splat(1.0f);
+ } break;
+
+ case Fmt::RGBA_8888: {
+ skvm::I32 rgba = load32(ptr);
+ *r = byte_to_f32(extract(rgba, 0xff));
+ *g = byte_to_f32(extract(rgba, 0xff00));
+ *b = byte_to_f32(extract(rgba, 0xff0000));
+ *a = byte_to_f32(extract(rgba, 0xff000000));
+ } break;
+ }
+ };
+
+ skvm::F32 r,g,b,a;
+ load(src, srcFmt, &r,&g,&b,&a);
+
+ skvm::F32 dr,dg,db,da;
+ load(dst, dstFmt, &dr,&dg,&db,&da);
+
+ skvm::F32 invA = sub(splat(1.0f), a);
+ r = mad(dr, invA, r);
+ g = mad(dg, invA, g);
+ b = mad(db, invA, b);
+ a = mad(da, invA, a);
+
+ auto f32_to_byte = [&](skvm::F32 f32) {
+ skvm::F32 _255 = splat(255.0f),
+ _0_5 = splat(0.5f);
+ return to_i32(mad(f32, _255, _0_5));
+ };
+ switch (dstFmt) {
+ case Fmt::A8: {
+ store8(dst, f32_to_byte(a));
+ } break;
+
+ case Fmt::G8: {
+ skvm::F32 _2126 = splat(0.2126f),
+ _7152 = splat(0.7152f),
+ _0722 = splat(0.0722f);
+ store8(dst, f32_to_byte(mad(r, _2126,
+ mad(g, _7152,
+ mul(b, _0722)))));
+ } break;
+
+ case Fmt::RGBA_8888: {
+ skvm::I32 R = f32_to_byte(r),
+ G = f32_to_byte(g),
+ B = f32_to_byte(b),
+ A = f32_to_byte(a);
+
+ R = pack(R, G, 8);
+ B = pack(B, A, 8);
+ R = pack(R, B, 16);
+
+ store32(dst, R);
+ } break;
+ }
+}
+
+SrcoverBuilder_I32::SrcoverBuilder_I32() {
+ skvm::Arg src = arg(0),
+ dst = arg(1);
+
+ auto load = [&](skvm::Arg ptr,
+ skvm::I32* r, skvm::I32* g, skvm::I32* b, skvm::I32* a) {
+ skvm::I32 rgba = load32(ptr);
+ *r = extract(rgba, 0xff);
+ *g = extract(rgba, 0xff00);
+ *b = extract(rgba, 0xff0000);
+ *a = extract(rgba, 0xff000000);
+ };
+
+ skvm::I32 r,g,b,a;
+ load(src, &r,&g,&b,&a);
+
+ skvm::I32 dr,dg,db,da;
+ load(dst, &dr,&dg,&db,&da);
+
+ skvm::I32 invA = sub(splat(0xff), a);
+ r = add(r, mul_unorm8(dr, invA));
+ g = add(g, mul_unorm8(dg, invA));
+ b = add(b, mul_unorm8(db, invA));
+ a = add(a, mul_unorm8(da, invA));
+
+ r = pack(r, g, 8);
+ b = pack(b, a, 8);
+ r = pack(r, b, 16);
+ store32(dst, r);
+}
+
+SrcoverBuilder_I32_SWAR::SrcoverBuilder_I32_SWAR() {
+ skvm::Arg src = arg(0),
+ dst = arg(1);
+
+ auto load = [&](skvm::Arg ptr,
+ skvm::I32* rb, skvm::I32* ga) {
+ skvm::I32 rgba = load32(ptr);
+ *rb = extract(rgba, 0x00ff00ff);
+ *ga = extract(rgba, 0xff00ff00);
+ };
+
+ auto mul_unorm8_SWAR = [&](skvm::I32 x, skvm::I32 y) {
+ // As above, assuming x is two SWAR bytes in lanes 0 and 2, and y is a byte.
+ skvm::I32 _255 = splat(0x00ff00ff);
+ return extract(add(mul(x, y), _255),
+ 0xff00ff00);
+ };
+
+ skvm::I32 rb, ga;
+ load(src, &rb, &ga);
+
+ skvm::I32 drb, dga;
+ load(dst, &drb, &dga);
+
+ skvm::I32 _255 = splat(0xff),
+ invA = sub(_255, shr(ga, 16));
+ rb = add(rb, mul_unorm8_SWAR(drb, invA));
+ ga = add(ga, mul_unorm8_SWAR(dga, invA));
+
+ store32(dst, pack(rb, ga, 8));
+}
diff --git a/tools/SkVMBuilders.h b/tools/SkVMBuilders.h
new file mode 100644
index 0000000..9d57837
--- /dev/null
+++ b/tools/SkVMBuilders.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2019 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef SkVMBuilders_DEFINED
+#define SkVMBuilders_DEFINED
+
+#include "src/core/SkVM.h"
+
+// SkVM builders used by both SkVMBench.cpp and SkVMTest.cpp.
+
+struct SrcoverBuilder_F32 : public skvm::Builder {
+ enum class Fmt { A8, G8, RGBA_8888 };
+ SrcoverBuilder_F32(Fmt srcFmt = Fmt::RGBA_8888,
+ Fmt dstFmt = Fmt::RGBA_8888);
+};
+
+struct SrcoverBuilder_I32 : public skvm::Builder {
+ SrcoverBuilder_I32(); // 8888 over 8888
+};
+
+struct SrcoverBuilder_I32_SWAR : public skvm::Builder {
+ SrcoverBuilder_I32_SWAR(); // 8888 over 8888
+};
+
+#endif//SkVMBuilders_DEFINED
diff --git a/tools/chrome_release_branch.py b/tools/chrome_release_branch.py
index d5e52ed..d9085aa 100644
--- a/tools/chrome_release_branch.py
+++ b/tools/chrome_release_branch.py
@@ -77,15 +77,15 @@
sys.exit(1)
go.get(go.INFRA_GO+'/go/supported_branches/cmd/new-branch')
subprocess.check_call(['new-branch',
- '--branch', new_branch,
- '--delete', old_branch,
+ '--branch', new_branch[len(REFS_HEADS_PREFIX):],
+ '--delete', old_branch[len(REFS_HEADS_PREFIX):],
'--owner', owner,
'--exclude-trybots=chromium.*',
'--exclude-trybots=.*Android_Framework.*'])
def main():
- if len(sys.argv) != 2:
+ if len(sys.argv) != 2 or '--help' in sys.argv or '-h' in sys.argv:
print >> sys.stderr, 'Usage: %s <commit hash for branch>' % sys.argv[0]
sys.exit(1)
go.check()
diff --git a/tools/gpu/ProxyUtils.cpp b/tools/gpu/ProxyUtils.cpp
index d3cc1f1..9483088 100644
--- a/tools/gpu/ProxyUtils.cpp
+++ b/tools/gpu/ProxyUtils.cpp
@@ -35,7 +35,7 @@
if (kBottomLeft_GrSurfaceOrigin == origin) {
// We (soon will) only support using kBottomLeft with wrapped textures.
auto backendTex = context->createBackendTexture(
- width, height, format, GrMipMapped::kNo, renderable);
+ width, height, format, SkColors::kTransparent, GrMipMapped::kNo, renderable);
if (!backendTex.isValid()) {
return nullptr;
}