sketch Half in skvm
Key design points... A) Take care to not commit too much to Half's range or precision. In particular, don't offer operations whose result might not be representable in Half. E.g., shy away from Half divide, not just because we might not be able to do a native divide, but even more so because division produces results that might not fit in Half, ±inf or NaN. B) No native Half loads, stores, uniforms or splats, instead converting from I32 or F32. This keeps the entire front-end (user code, Builder, etc.) specified in terms of precise format and oblivious to the various backends' representations of Half. Native Half splats would be less trouble than uniforms I think, and uniforms less trouble than loads and stores, but still enough a pain that we're better of deferring any of that for now. (Explicit fp16 uniforms do make sense to me though.) C) Keep the current F32-based Color and all the effect virtuals that use it around, introducing parallel Half-based HalfColors and entry points for those. The key cool idea here is to have the default entry points for F32/Color and Half/HalfColor call each other, so that any given effect can implement one, the other, or both and always be compatble with however it's called. This is mostly about incremental rollout, but I suspect we'll have areas that stick to F32 forever. (Think the IEEE 754 32-bit float specific bit hacks we used for approx_log/approx_exp.) D) (not done yet) allow implicit Half->F32 conversion, but not the other way around of course. This makes it easier to lean on the body of F32 routines we already have, and again mostly helps enable incremental rollout. Change-Id: I8bb38efbe476ff89dd2591411e115c2ab3757854 Reviewed-on: https://skia-review.googlesource.com/c/skia/+/341800 Reviewed-by: Herb Derby <herb@google.com> Commit-Queue: Mike Klein <mtklein@google.com>
This commit is contained in:
parent
b339bbe9dc
commit
9b0fc9c9c1
@ -81,6 +81,39 @@ skvm::Color SkColorFilterBase::program(skvm::Builder* p, skvm::Color c,
|
||||
return {};
|
||||
}
|
||||
|
||||
// This should look familiar... see just above.
|
||||
skvm::HalfColor SkColorFilterBase::program(skvm::Builder* p, skvm::HalfColor c,
|
||||
SkColorSpace* dstCS,
|
||||
skvm::Uniforms* uniforms, SkArenaAlloc* alloc) const {
|
||||
skvm::Half original = c.a;
|
||||
if ((c = this->onProgram(p,c, dstCS, uniforms,alloc))) {
|
||||
if (this->isAlphaUnchanged()) {
|
||||
c.a = original;
|
||||
}
|
||||
return c;
|
||||
}
|
||||
//SkDebugf("cannot onProgram %s\n", this->getTypeName());
|
||||
return {};
|
||||
}
|
||||
|
||||
skvm::Color SkColorFilterBase::onProgram(skvm::Builder* p, skvm::Color c,
|
||||
SkColorSpace* dstCS,
|
||||
skvm::Uniforms* uniforms, SkArenaAlloc* alloc) const {
|
||||
if (skvm::HalfColor hc = this->onProgram(p, to_Half(c), dstCS, uniforms, alloc)) {
|
||||
return to_F32(hc);
|
||||
}
|
||||
return {};
|
||||
}
|
||||
skvm::HalfColor SkColorFilterBase::onProgram(skvm::Builder* p, skvm::HalfColor hc,
|
||||
SkColorSpace* dstCS,
|
||||
skvm::Uniforms* uniforms, SkArenaAlloc* alloc) const {
|
||||
if (skvm::Color c = this->onProgram(p, to_F32(hc), dstCS, uniforms, alloc)) {
|
||||
return to_Half(c);
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
|
||||
SkColor SkColorFilter::filterColor(SkColor c) const {
|
||||
// This is mostly meaningless. We should phase-out this call entirely.
|
||||
SkColorSpace* cs = nullptr;
|
||||
|
@ -29,6 +29,11 @@ public:
|
||||
skvm::Color program(skvm::Builder*, skvm::Color,
|
||||
SkColorSpace* dstCS, skvm::Uniforms*, SkArenaAlloc*) const;
|
||||
|
||||
SK_WARN_UNUSED_RESULT
|
||||
skvm::HalfColor program(skvm::Builder*, skvm::HalfColor,
|
||||
SkColorSpace* dstCS, skvm::Uniforms*, SkArenaAlloc*) const;
|
||||
|
||||
|
||||
/** Returns the flags for this filter. Override in subclasses to return custom flags.
|
||||
*/
|
||||
virtual uint32_t onGetFlags() const { return 0; }
|
||||
@ -78,8 +83,11 @@ protected:
|
||||
private:
|
||||
virtual bool onAppendStages(const SkStageRec& rec, bool shaderIsOpaque) const = 0;
|
||||
|
||||
virtual skvm::Color onProgram(skvm::Builder*, skvm::Color,
|
||||
SkColorSpace* dstCS, skvm::Uniforms*, SkArenaAlloc*) const = 0;
|
||||
// These defaults just call each other, so you must override at least one of them.
|
||||
virtual skvm::Color onProgram(skvm::Builder*, skvm::Color,
|
||||
SkColorSpace* dstCS, skvm::Uniforms*, SkArenaAlloc*) const;
|
||||
virtual skvm::HalfColor onProgram(skvm::Builder*, skvm::HalfColor,
|
||||
SkColorSpace* dstCS, skvm::Uniforms*, SkArenaAlloc*) const;
|
||||
|
||||
friend class SkColorFilter;
|
||||
|
||||
|
@ -1079,6 +1079,11 @@ namespace skvm {
|
||||
return round(mul(x, limit));
|
||||
}
|
||||
|
||||
// Shhh... it's a secret, but Half is secretly F32 underneath for now!
|
||||
// (This will definitely change. :P)
|
||||
F32 Builder::to_F32 (Half x) { return {x.builder, x.id}; }
|
||||
Half Builder::to_Half(F32 x) { return {x.builder, x.id}; }
|
||||
|
||||
bool SkColorType_to_PixelFormat(SkColorType ct, PixelFormat* f) {
|
||||
auto UNORM = PixelFormat::UNORM,
|
||||
FLOAT = PixelFormat::FLOAT;
|
||||
|
146
src/core/SkVM.h
146
src/core/SkVM.h
@ -472,6 +472,8 @@ namespace skvm {
|
||||
|
||||
struct Arg { int ix; };
|
||||
|
||||
// 32-bit signed integer (with both signed sra() and unsigned/logical shr() available).
|
||||
// Think "int" or "int32_t".
|
||||
struct I32 {
|
||||
Builder* builder = nullptr;
|
||||
Val id = NA;
|
||||
@ -479,6 +481,7 @@ namespace skvm {
|
||||
Builder* operator->() const { return builder; }
|
||||
};
|
||||
|
||||
// 32-bit IEEE 754 float, think "float".
|
||||
struct F32 {
|
||||
Builder* builder = nullptr;
|
||||
Val id = NA;
|
||||
@ -486,8 +489,27 @@ namespace skvm {
|
||||
Builder* operator->() const { return builder; }
|
||||
};
|
||||
|
||||
// Comparisons of F32 or I32 return I32 masks, with false=0 and true=~0.
|
||||
|
||||
// An opaque float-y type with ambiguous precision and at least [-2,+2) range.
|
||||
// This could be FP16, FP32, signed 1.14 fixed point, bfloat16, etc.
|
||||
struct Half {
|
||||
Builder* builder = nullptr;
|
||||
Val id = NA;
|
||||
explicit operator bool() const { return id != NA; }
|
||||
Builder* operator->() const { return builder; }
|
||||
};
|
||||
|
||||
// Integer mask returned by comparisons of Half, with false=0 and true=~0 as usual.
|
||||
struct HalfMask {
|
||||
Builder* builder = nullptr;
|
||||
Val id = NA;
|
||||
explicit operator bool() const { return id != NA; }
|
||||
Builder* operator->() const { return builder; }
|
||||
};
|
||||
|
||||
// Some operations make sense with immediate arguments,
|
||||
// so we use I32a and F32a to receive them transparently.
|
||||
// so we use I32a/F32a/Halfa to receive them transparently.
|
||||
//
|
||||
// We omit overloads that may indicate a bug or performance issue.
|
||||
// In general it does not make sense to pass immediates to unary operations,
|
||||
@ -517,6 +539,15 @@ namespace skvm {
|
||||
float imm = 0;
|
||||
};
|
||||
|
||||
struct Halfa {
|
||||
Halfa(Half v) : SkDEBUGCODE(builder(v.builder),) id(v.id) {}
|
||||
Halfa(float v) : imm(v) {}
|
||||
|
||||
SkDEBUGCODE(Builder* builder = nullptr;)
|
||||
Val id = NA;
|
||||
float imm = 0;
|
||||
};
|
||||
|
||||
struct Color {
|
||||
F32 r,g,b,a;
|
||||
explicit operator bool() const { return r && g && b && a; }
|
||||
@ -535,6 +566,12 @@ namespace skvm {
|
||||
Builder* operator->() const { return x.operator->(); }
|
||||
};
|
||||
|
||||
struct HalfColor {
|
||||
Half r,g,b,a;
|
||||
explicit operator bool() const { return r && g && b && a; }
|
||||
Builder* operator->() const { return a.operator->(); }
|
||||
};
|
||||
|
||||
struct Uniform {
|
||||
Arg ptr;
|
||||
int offset;
|
||||
@ -623,6 +660,8 @@ namespace skvm {
|
||||
void assert_true(I32 cond, F32 debug) { assert_true(cond, pun_to_I32(debug)); }
|
||||
void assert_true(I32 cond) { assert_true(cond, cond); }
|
||||
|
||||
// TODO: Half asserts?
|
||||
|
||||
// Store {8,16,32,64,128}-bit varying.
|
||||
void store8 (Arg ptr, I32 val);
|
||||
void store16 (Arg ptr, I32 val);
|
||||
@ -673,6 +712,7 @@ namespace skvm {
|
||||
memcpy(&bits, &f, 4);
|
||||
return pun_to_F32(splat(bits));
|
||||
}
|
||||
Half half(float f) { return to_Half(splat(f)); }
|
||||
|
||||
// float math, comparisons, etc.
|
||||
F32 add(F32, F32); F32 add(F32a x, F32a y) { return add(_(x), _(y)); }
|
||||
@ -731,6 +771,45 @@ namespace skvm {
|
||||
I32 gt (F32, F32); I32 gt (F32a x, F32a y) { return gt (_(x), _(y)); }
|
||||
I32 gte(F32, F32); I32 gte(F32a x, F32a y) { return gte(_(x), _(y)); }
|
||||
|
||||
|
||||
// Half math, comparisons, etc.
|
||||
Half add(Half, Half); Half add(Halfa x, Halfa y) { return add(_(x), _(y)); }
|
||||
Half sub(Half, Half); Half sub(Halfa x, Halfa y) { return sub(_(x), _(y)); }
|
||||
Half mul(Half, Half); Half mul(Halfa x, Halfa y) { return mul(_(x), _(y)); }
|
||||
Half min(Half, Half); Half min(Halfa x, Halfa y) { return min(_(x), _(y)); }
|
||||
Half max(Half, Half); Half max(Halfa x, Halfa y) { return max(_(x), _(y)); }
|
||||
|
||||
Half sqrt(Half);
|
||||
Half abs(Half);
|
||||
Half ceil(Half);
|
||||
Half floor(Half);
|
||||
Half fract(Half x) { return sub(x, floor(x)); }
|
||||
|
||||
Half lerp(Half lo, Half hi, Half t);
|
||||
Half lerp(Halfa lo, Halfa hi, Halfa t) { return lerp(_(lo), _(hi), _(t)); }
|
||||
|
||||
Half clamp (Half x, Half lo, Half hi) { return max(lo, min(x, hi)); }
|
||||
Half clamp (Halfa x, Halfa lo, Halfa hi) { return clamp(_(x), _(lo), _(hi)); }
|
||||
Half clamp01(Half x) { return clamp(x, 0.0f, 1.0f); }
|
||||
|
||||
Half to_Half(F32 );
|
||||
F32 to_F32 (Half);
|
||||
|
||||
HalfMask eq(Half, Half); HalfMask eq(Halfa x, Halfa y) { return eq(_(x), _(y)); }
|
||||
HalfMask neq(Half, Half); HalfMask neq(Halfa x, Halfa y) { return neq(_(x), _(y)); }
|
||||
HalfMask lt (Half, Half); HalfMask lt (Halfa x, Halfa y) { return lt (_(x), _(y)); }
|
||||
HalfMask lte(Half, Half); HalfMask lte(Halfa x, Halfa y) { return lte(_(x), _(y)); }
|
||||
HalfMask gt (Half, Half); HalfMask gt (Halfa x, Halfa y) { return gt (_(x), _(y)); }
|
||||
HalfMask gte(Half, Half); HalfMask gte(Halfa x, Halfa y) { return gte(_(x), _(y)); }
|
||||
|
||||
HalfMask bit_and (HalfMask, HalfMask);
|
||||
HalfMask bit_or (HalfMask, HalfMask);
|
||||
HalfMask bit_xor (HalfMask, HalfMask);
|
||||
HalfMask bit_clear(HalfMask, HalfMask);
|
||||
|
||||
Half select(HalfMask, Half, Half);
|
||||
Half select(HalfMask cond, Halfa t, Halfa f) { return select(cond, _(t), _(f)); }
|
||||
|
||||
// int math, comparisons, etc.
|
||||
I32 add(I32, I32); I32 add(I32a x, I32a y) { return add(_(x), _(y)); }
|
||||
I32 sub(I32, I32); I32 sub(I32a x, I32a y) { return sub(_(x), _(y)); }
|
||||
@ -829,6 +908,14 @@ namespace skvm {
|
||||
return splat(x.imm);
|
||||
}
|
||||
|
||||
Half _(Halfa x) {
|
||||
if (x.id != NA) {
|
||||
SkASSERT(x.builder == this);
|
||||
return {this, x.id};
|
||||
}
|
||||
return half(x.imm);
|
||||
}
|
||||
|
||||
bool allImm() const;
|
||||
|
||||
template <typename T, typename... Rest>
|
||||
@ -998,6 +1085,44 @@ namespace skvm {
|
||||
static inline F32& operator-=(F32& x, F32a y) { return (x = x - y); }
|
||||
static inline F32& operator*=(F32& x, F32a y) { return (x = x * y); }
|
||||
|
||||
static inline Half operator+(Half x, Halfa y) { return x->add(x,y); }
|
||||
static inline Half operator+(float x, Half y) { return y->add(x,y); }
|
||||
|
||||
static inline Half operator-(Half x, Halfa y) { return x->sub(x,y); }
|
||||
static inline Half operator-(float x, Half y) { return y->sub(x,y); }
|
||||
|
||||
static inline Half operator*(Half x, Halfa y) { return x->mul(x,y); }
|
||||
static inline Half operator*(float x, Half y) { return y->mul(x,y); }
|
||||
|
||||
static inline Half min(Half x, Halfa y) { return x->min(x,y); }
|
||||
static inline Half min(float x, Half y) { return y->min(x,y); }
|
||||
|
||||
static inline Half max(Half x, Halfa y) { return x->max(x,y); }
|
||||
static inline Half max(float x, Half y) { return y->max(x,y); }
|
||||
|
||||
static inline HalfMask operator==(Half x, Half y) { return x->eq(x,y); }
|
||||
static inline HalfMask operator==(Half x, float y) { return x->eq(x,y); }
|
||||
static inline HalfMask operator==(float x, Half y) { return y->eq(x,y); }
|
||||
|
||||
static inline HalfMask operator!=(Half x, Half y) { return x->neq(x,y); }
|
||||
static inline HalfMask operator!=(Half x, float y) { return x->neq(x,y); }
|
||||
static inline HalfMask operator!=(float x, Half y) { return y->neq(x,y); }
|
||||
|
||||
static inline HalfMask operator< (Half x, Halfa y) { return x->lt(x,y); }
|
||||
static inline HalfMask operator< (float x, Half y) { return y->lt(x,y); }
|
||||
|
||||
static inline HalfMask operator<=(Half x, Halfa y) { return x->lte(x,y); }
|
||||
static inline HalfMask operator<=(float x, Half y) { return y->lte(x,y); }
|
||||
|
||||
static inline HalfMask operator> (Half x, Halfa y) { return x->gt(x,y); }
|
||||
static inline HalfMask operator> (float x, Half y) { return y->gt(x,y); }
|
||||
|
||||
static inline HalfMask operator>=(Half x, Halfa y) { return x->gte(x,y); }
|
||||
static inline HalfMask operator>=(float x, Half y) { return y->gte(x,y); }
|
||||
|
||||
static inline Half to_Half(F32 x) { return x->to_Half(x); }
|
||||
static inline F32 to_F32 (Half x) { return x->to_F32 (x); }
|
||||
|
||||
static inline void assert_true(I32 cond, I32 debug) { cond->assert_true(cond,debug); }
|
||||
static inline void assert_true(I32 cond, F32 debug) { cond->assert_true(cond,debug); }
|
||||
static inline void assert_true(I32 cond) { cond->assert_true(cond); }
|
||||
@ -1129,6 +1254,25 @@ namespace skvm {
|
||||
return poly(x, x*a+b, rest...);
|
||||
}
|
||||
}
|
||||
|
||||
static inline HalfColor to_Half(Color c) {
|
||||
return {
|
||||
c->to_Half(c.r),
|
||||
c->to_Half(c.g),
|
||||
c->to_Half(c.b),
|
||||
c->to_Half(c.a),
|
||||
};
|
||||
}
|
||||
|
||||
static inline Color to_F32(HalfColor c) {
|
||||
return {
|
||||
c->to_F32(c.r),
|
||||
c->to_F32(c.g),
|
||||
c->to_F32(c.b),
|
||||
c->to_F32(c.a),
|
||||
};
|
||||
}
|
||||
|
||||
} // namespace skvm
|
||||
|
||||
#endif//SkVM_DEFINED
|
||||
|
@ -15,6 +15,9 @@ namespace skvm {
|
||||
struct Arg;
|
||||
struct I32;
|
||||
struct F32;
|
||||
struct Half;
|
||||
struct HalfMask;
|
||||
struct HalfColor;
|
||||
struct Color;
|
||||
struct Coord;
|
||||
struct Uniforms;
|
||||
|
Loading…
Reference in New Issue
Block a user