simpler immediates
That last patch was too ambitious: the existing logic is error-prone, and to fix it would require implementing a whole side algebra for immediates. I don't really want to have a second side system, even if we work out how to make it correct. If instead we just require a few more splats, we can come up with a simple sane system that at least can't blow up on you unexpectedly. - Strip F32/I32 back down to a Builder pointer and Val ID, but create F32a/I32a for use as method arguments that can auto-construct from an immediate or the normal type. - Add method overloads using F32a/I32a. - Add operators using F32a/I32a and int/float primitive types, guaranteeing there's at least one F32/I32 to get a Builder from. - To keep things simple, drop all the operator forward declarations. They're only needed for inline methods on Builder, and there are few enough of them that calling methods is fine. - TODO: some of the inline helpers become simple enough when written with operators they might not need to exist, e.g. inv(x) -> (1-x). Change-Id: I193b0e5bca5617ab564c769b916473434ad6d56d Reviewed-on: https://skia-review.googlesource.com/c/skia/+/280276 Reviewed-by: Herb Derby <herb@google.com> Reviewed-by: Mike Reed <reed@google.com> Commit-Queue: Mike Klein <mtklein@google.com>
This commit is contained in:
parent
e9bc857b39
commit
46de36f993
@ -722,22 +722,6 @@ namespace skvm {
|
||||
return id;
|
||||
}
|
||||
|
||||
Val I32::resolve(Builder* b) {
|
||||
if (!fBuilder) {
|
||||
*this = b->splat(fImm);
|
||||
}
|
||||
SkASSERT(fBuilder == b);
|
||||
return fID;
|
||||
}
|
||||
|
||||
Val F32::resolve(Builder* b) {
|
||||
if (!fBuilder) {
|
||||
*this = b->splat(fImm);
|
||||
}
|
||||
SkASSERT(fBuilder == b);
|
||||
return fID;
|
||||
}
|
||||
|
||||
bool Builder::allImm() const { return true; }
|
||||
|
||||
template <typename T, typename... Rest>
|
||||
@ -759,14 +743,14 @@ namespace skvm {
|
||||
void Builder::assert_true(I32 cond, I32 debug) {
|
||||
#ifdef SK_DEBUG
|
||||
int imm;
|
||||
if (this->allImm(id(cond),&imm)) { SkASSERT(imm); return; }
|
||||
(void)push(Op::assert_true, id(cond),id(debug),NA);
|
||||
if (this->allImm(cond.id,&imm)) { SkASSERT(imm); return; }
|
||||
(void)push(Op::assert_true, cond.id,debug.id,NA);
|
||||
#endif
|
||||
}
|
||||
|
||||
void Builder::store8 (Arg ptr, I32 val) { (void)push(Op::store8 , id(val),NA,NA, ptr.ix); }
|
||||
void Builder::store16(Arg ptr, I32 val) { (void)push(Op::store16, id(val),NA,NA, ptr.ix); }
|
||||
void Builder::store32(Arg ptr, I32 val) { (void)push(Op::store32, id(val),NA,NA, ptr.ix); }
|
||||
void Builder::store8 (Arg ptr, I32 val) { (void)push(Op::store8 , val.id,NA,NA, ptr.ix); }
|
||||
void Builder::store16(Arg ptr, I32 val) { (void)push(Op::store16, val.id,NA,NA, ptr.ix); }
|
||||
void Builder::store32(Arg ptr, I32 val) { (void)push(Op::store32, val.id,NA,NA, ptr.ix); }
|
||||
|
||||
I32 Builder::index() { return {this, push(Op::index , NA,NA,NA,0) }; }
|
||||
|
||||
@ -775,13 +759,13 @@ namespace skvm {
|
||||
I32 Builder::load32(Arg ptr) { return {this, push(Op::load32, NA,NA,NA, ptr.ix) }; }
|
||||
|
||||
I32 Builder::gather8 (Arg ptr, int offset, I32 index) {
|
||||
return {this, push(Op::gather8 , id(index),NA,NA, ptr.ix,offset)};
|
||||
return {this, push(Op::gather8 , index.id,NA,NA, ptr.ix,offset)};
|
||||
}
|
||||
I32 Builder::gather16(Arg ptr, int offset, I32 index) {
|
||||
return {this, push(Op::gather16, id(index),NA,NA, ptr.ix,offset)};
|
||||
return {this, push(Op::gather16, index.id,NA,NA, ptr.ix,offset)};
|
||||
}
|
||||
I32 Builder::gather32(Arg ptr, int offset, I32 index) {
|
||||
return {this, push(Op::gather32, id(index),NA,NA, ptr.ix,offset)};
|
||||
return {this, push(Op::gather32, index.id,NA,NA, ptr.ix,offset)};
|
||||
}
|
||||
|
||||
I32 Builder::uniform8(Arg ptr, int offset) {
|
||||
@ -831,55 +815,55 @@ namespace skvm {
|
||||
|
||||
F32 Builder::add(F32 x, F32 y) {
|
||||
float X,Y;
|
||||
if (this->allImm(id(x),&X, id(y),&Y)) { return this->splat(X+Y); }
|
||||
if (this->isImm(id(y), 0.0f)) { return x; } // x+0 == x
|
||||
if (this->isImm(id(x), 0.0f)) { return y; } // 0+y == y
|
||||
if (this->allImm(x.id,&X, y.id,&Y)) { return this->splat(X+Y); }
|
||||
if (this->isImm(y.id, 0.0f)) { return x; } // x+0 == x
|
||||
if (this->isImm(x.id, 0.0f)) { return y; } // 0+y == y
|
||||
|
||||
if (fma_supported()) {
|
||||
if (fProgram[id(x)].op == Op::mul_f32) {
|
||||
return {this, push(Op::fma_f32, fProgram[id(x)].x, fProgram[id(x)].y, id(y))};
|
||||
if (fProgram[x.id].op == Op::mul_f32) {
|
||||
return {this, push(Op::fma_f32, fProgram[x.id].x, fProgram[x.id].y, y.id)};
|
||||
}
|
||||
if (fProgram[id(y)].op == Op::mul_f32) {
|
||||
return {this, push(Op::fma_f32, fProgram[id(y)].x, fProgram[id(y)].y, id(x))};
|
||||
if (fProgram[y.id].op == Op::mul_f32) {
|
||||
return {this, push(Op::fma_f32, fProgram[y.id].x, fProgram[y.id].y, x.id)};
|
||||
}
|
||||
}
|
||||
return {this, push(Op::add_f32, id(x), id(y))};
|
||||
return {this, push(Op::add_f32, x.id, y.id)};
|
||||
}
|
||||
|
||||
F32 Builder::sub(F32 x, F32 y) {
|
||||
float X,Y;
|
||||
if (this->allImm(id(x),&X, id(y),&Y)) { return this->splat(X-Y); }
|
||||
if (this->isImm(id(y), 0.0f)) { return x; } // x-0 == x
|
||||
if (this->allImm(x.id,&X, y.id,&Y)) { return this->splat(X-Y); }
|
||||
if (this->isImm(y.id, 0.0f)) { return x; } // x-0 == x
|
||||
if (fma_supported()) {
|
||||
if (fProgram[id(x)].op == Op::mul_f32) {
|
||||
return {this, push(Op::fms_f32, fProgram[id(x)].x, fProgram[id(x)].y, id(y))};
|
||||
if (fProgram[x.id].op == Op::mul_f32) {
|
||||
return {this, push(Op::fms_f32, fProgram[x.id].x, fProgram[x.id].y, y.id)};
|
||||
}
|
||||
if (fProgram[id(y)].op == Op::mul_f32) {
|
||||
return {this, push(Op::fnma_f32, fProgram[id(y)].x, fProgram[id(y)].y, id(x))};
|
||||
if (fProgram[y.id].op == Op::mul_f32) {
|
||||
return {this, push(Op::fnma_f32, fProgram[y.id].x, fProgram[y.id].y, x.id)};
|
||||
}
|
||||
}
|
||||
return {this, push(Op::sub_f32, id(x), id(y))};
|
||||
return {this, push(Op::sub_f32, x.id, y.id)};
|
||||
}
|
||||
|
||||
F32 Builder::mul(F32 x, F32 y) {
|
||||
float X,Y;
|
||||
if (this->allImm(id(x),&X, id(y),&Y)) { return this->splat(X*Y); }
|
||||
if (this->isImm(id(y), 1.0f)) { return x; } // x*1 == x
|
||||
if (this->isImm(id(x), 1.0f)) { return y; } // 1*y == y
|
||||
return {this, push(Op::mul_f32, id(x), id(y))};
|
||||
if (this->allImm(x.id,&X, y.id,&Y)) { return this->splat(X*Y); }
|
||||
if (this->isImm(y.id, 1.0f)) { return x; } // x*1 == x
|
||||
if (this->isImm(x.id, 1.0f)) { return y; } // 1*y == y
|
||||
return {this, push(Op::mul_f32, x.id, y.id)};
|
||||
}
|
||||
|
||||
F32 Builder::div(F32 x, F32 y) {
|
||||
float X,Y;
|
||||
if (this->allImm(id(x),&X, id(y),&Y)) { return this->splat(X/Y); }
|
||||
if (this->isImm(id(y), 1.0f)) { return x; } // x/1 == x
|
||||
return {this, push(Op::div_f32, id(x), id(y))};
|
||||
if (this->allImm(x.id,&X, y.id,&Y)) { return this->splat(X/Y); }
|
||||
if (this->isImm(y.id, 1.0f)) { return x; } // x/1 == x
|
||||
return {this, push(Op::div_f32, x.id, y.id)};
|
||||
}
|
||||
|
||||
F32 Builder::sqrt(F32 x) {
|
||||
float X;
|
||||
if (this->allImm(id(x),&X)) { return this->splat(std::sqrt(X)); }
|
||||
return {this, push(Op::sqrt_f32, id(x),NA,NA)};
|
||||
if (this->allImm(x.id,&X)) { return this->splat(std::sqrt(X)); }
|
||||
return {this, push(Op::sqrt_f32, x.id,NA,NA)};
|
||||
}
|
||||
|
||||
// See http://www.machinedlearnings.com/2011/06/fast-approximate-logarithm-exponential.html.
|
||||
@ -914,167 +898,167 @@ namespace skvm {
|
||||
|
||||
F32 Builder::min(F32 x, F32 y) {
|
||||
float X,Y;
|
||||
if (this->allImm(id(x),&X, id(y),&Y)) { return this->splat(std::min(X,Y)); }
|
||||
return {this, push(Op::min_f32, id(x), id(y))};
|
||||
if (this->allImm(x.id,&X, y.id,&Y)) { return this->splat(std::min(X,Y)); }
|
||||
return {this, push(Op::min_f32, x.id, y.id)};
|
||||
}
|
||||
F32 Builder::max(F32 x, F32 y) {
|
||||
float X,Y;
|
||||
if (this->allImm(id(x),&X, id(y),&Y)) { return this->splat(std::max(X,Y)); }
|
||||
return {this, push(Op::max_f32, id(x), id(y))};
|
||||
if (this->allImm(x.id,&X, y.id,&Y)) { return this->splat(std::max(X,Y)); }
|
||||
return {this, push(Op::max_f32, x.id, y.id)};
|
||||
}
|
||||
|
||||
I32 Builder::add(I32 x, I32 y) { return {this, push(Op::add_i32, id(x), id(y))}; }
|
||||
I32 Builder::sub(I32 x, I32 y) { return {this, push(Op::sub_i32, id(x), id(y))}; }
|
||||
I32 Builder::mul(I32 x, I32 y) { return {this, push(Op::mul_i32, id(x), id(y))}; }
|
||||
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)}; }
|
||||
I32 Builder::mul(I32 x, I32 y) { return {this, push(Op::mul_i32, x.id, y.id)}; }
|
||||
|
||||
I32 Builder::add_16x2(I32 x, I32 y) { return {this, push(Op::add_i16x2, id(x), id(y))}; }
|
||||
I32 Builder::sub_16x2(I32 x, I32 y) { return {this, push(Op::sub_i16x2, id(x), id(y))}; }
|
||||
I32 Builder::mul_16x2(I32 x, I32 y) { return {this, push(Op::mul_i16x2, id(x), id(y))}; }
|
||||
I32 Builder::add_16x2(I32 x, I32 y) { return {this, push(Op::add_i16x2, x.id, y.id)}; }
|
||||
I32 Builder::sub_16x2(I32 x, I32 y) { return {this, push(Op::sub_i16x2, x.id, y.id)}; }
|
||||
I32 Builder::mul_16x2(I32 x, I32 y) { return {this, push(Op::mul_i16x2, x.id, y.id)}; }
|
||||
|
||||
I32 Builder::shl(I32 x, int bits) {
|
||||
if (bits == 0) { return x; }
|
||||
int X;
|
||||
if (this->allImm(id(x),&X)) { return this->splat(X << bits); }
|
||||
return {this, push(Op::shl_i32, id(x),NA,NA, bits)};
|
||||
if (this->allImm(x.id,&X)) { return this->splat(X << bits); }
|
||||
return {this, push(Op::shl_i32, x.id,NA,NA, bits)};
|
||||
}
|
||||
I32 Builder::shr(I32 x, int bits) {
|
||||
if (bits == 0) { return x; }
|
||||
int X;
|
||||
if (this->allImm(id(x),&X)) { return this->splat(unsigned(X) >> bits); }
|
||||
return {this, push(Op::shr_i32, id(x),NA,NA, bits)};
|
||||
if (this->allImm(x.id,&X)) { return this->splat(unsigned(X) >> bits); }
|
||||
return {this, push(Op::shr_i32, x.id,NA,NA, bits)};
|
||||
}
|
||||
I32 Builder::sra(I32 x, int bits) {
|
||||
if (bits == 0) { return x; }
|
||||
int X;
|
||||
if (this->allImm(id(x),&X)) { return this->splat(X >> bits); }
|
||||
return {this, push(Op::sra_i32, id(x),NA,NA, bits)};
|
||||
if (this->allImm(x.id,&X)) { return this->splat(X >> bits); }
|
||||
return {this, push(Op::sra_i32, x.id,NA,NA, bits)};
|
||||
}
|
||||
|
||||
I32 Builder::shl_16x2(I32 x, int k) { return {this, push(Op::shl_i16x2, id(x),NA,NA, k)}; }
|
||||
I32 Builder::shr_16x2(I32 x, int k) { return {this, push(Op::shr_i16x2, id(x),NA,NA, k)}; }
|
||||
I32 Builder::sra_16x2(I32 x, int k) { return {this, push(Op::sra_i16x2, id(x),NA,NA, k)}; }
|
||||
I32 Builder::shl_16x2(I32 x, int k) { return {this, push(Op::shl_i16x2, x.id,NA,NA, k)}; }
|
||||
I32 Builder::shr_16x2(I32 x, int k) { return {this, push(Op::shr_i16x2, x.id,NA,NA, k)}; }
|
||||
I32 Builder::sra_16x2(I32 x, int k) { return {this, push(Op::sra_i16x2, x.id,NA,NA, k)}; }
|
||||
|
||||
I32 Builder:: eq(F32 x, F32 y) {
|
||||
float X,Y;
|
||||
if (this->allImm(id(x),&X, id(y),&Y)) { return this->splat(X==Y ? ~0 : 0); }
|
||||
return {this, push(Op::eq_f32, id(x), id(y))};
|
||||
if (this->allImm(x.id,&X, y.id,&Y)) { return this->splat(X==Y ? ~0 : 0); }
|
||||
return {this, push(Op::eq_f32, x.id, y.id)};
|
||||
}
|
||||
I32 Builder::neq(F32 x, F32 y) {
|
||||
float X,Y;
|
||||
if (this->allImm(id(x),&X, id(y),&Y)) { return this->splat(X!=Y ? ~0 : 0); }
|
||||
return {this, push(Op::neq_f32, id(x), id(y))};
|
||||
if (this->allImm(x.id,&X, y.id,&Y)) { return this->splat(X!=Y ? ~0 : 0); }
|
||||
return {this, push(Op::neq_f32, x.id, y.id)};
|
||||
}
|
||||
I32 Builder::lt(F32 x, F32 y) {
|
||||
float X,Y;
|
||||
if (this->allImm(id(x),&X, id(y),&Y)) { return this->splat(Y> X ? ~0 : 0); }
|
||||
return {this, push(Op::gt_f32, id(y), id(x))};
|
||||
if (this->allImm(x.id,&X, y.id,&Y)) { return this->splat(Y> X ? ~0 : 0); }
|
||||
return {this, push(Op::gt_f32, y.id, x.id)};
|
||||
}
|
||||
I32 Builder::lte(F32 x, F32 y) {
|
||||
float X,Y;
|
||||
if (this->allImm(id(x),&X, id(y),&Y)) { return this->splat(Y>=X ? ~0 : 0); }
|
||||
return {this, push(Op::gte_f32, id(y), id(x))};
|
||||
if (this->allImm(x.id,&X, y.id,&Y)) { return this->splat(Y>=X ? ~0 : 0); }
|
||||
return {this, push(Op::gte_f32, y.id, x.id)};
|
||||
}
|
||||
I32 Builder::gt(F32 x, F32 y) {
|
||||
float X,Y;
|
||||
if (this->allImm(id(x),&X, id(y),&Y)) { return this->splat(X> Y ? ~0 : 0); }
|
||||
return {this, push(Op::gt_f32, id(x), id(y))};
|
||||
if (this->allImm(x.id,&X, y.id,&Y)) { return this->splat(X> Y ? ~0 : 0); }
|
||||
return {this, push(Op::gt_f32, x.id, y.id)};
|
||||
}
|
||||
I32 Builder::gte(F32 x, F32 y) {
|
||||
float X,Y;
|
||||
if (this->allImm(id(x),&X, id(y),&Y)) { return this->splat(X>=Y ? ~0 : 0); }
|
||||
return {this, push(Op::gte_f32, id(x), id(y))};
|
||||
if (this->allImm(x.id,&X, y.id,&Y)) { return this->splat(X>=Y ? ~0 : 0); }
|
||||
return {this, push(Op::gte_f32, x.id, y.id)};
|
||||
}
|
||||
|
||||
I32 Builder:: eq(I32 x, I32 y) { return {this, push(Op:: eq_i32, id(x), id(y))}; }
|
||||
I32 Builder::neq(I32 x, I32 y) { return {this, push(Op::neq_i32, id(x), id(y))}; }
|
||||
I32 Builder:: lt(I32 x, I32 y) { return {this, push(Op:: gt_i32, id(y), id(x))}; }
|
||||
I32 Builder::lte(I32 x, I32 y) { return {this, push(Op::gte_i32, id(y), id(x))}; }
|
||||
I32 Builder:: gt(I32 x, I32 y) { return {this, push(Op:: gt_i32, id(x), id(y))}; }
|
||||
I32 Builder::gte(I32 x, I32 y) { return {this, push(Op::gte_i32, id(x), id(y))}; }
|
||||
I32 Builder:: eq(I32 x, I32 y) { return {this, push(Op:: eq_i32, x.id, y.id)}; }
|
||||
I32 Builder::neq(I32 x, I32 y) { return {this, push(Op::neq_i32, x.id, y.id)}; }
|
||||
I32 Builder:: lt(I32 x, I32 y) { return {this, push(Op:: gt_i32, y.id, x.id)}; }
|
||||
I32 Builder::lte(I32 x, I32 y) { return {this, push(Op::gte_i32, y.id, x.id)}; }
|
||||
I32 Builder:: gt(I32 x, I32 y) { return {this, push(Op:: gt_i32, x.id, y.id)}; }
|
||||
I32 Builder::gte(I32 x, I32 y) { return {this, push(Op::gte_i32, x.id, y.id)}; }
|
||||
|
||||
I32 Builder:: eq_16x2(I32 x, I32 y) { return {this, push(Op:: eq_i16x2, id(x), id(y))}; }
|
||||
I32 Builder::neq_16x2(I32 x, I32 y) { return {this, push(Op::neq_i16x2, id(x), id(y))}; }
|
||||
I32 Builder:: lt_16x2(I32 x, I32 y) { return {this, push(Op:: gt_i16x2, id(y), id(x))}; }
|
||||
I32 Builder::lte_16x2(I32 x, I32 y) { return {this, push(Op::gte_i16x2, id(y), id(x))}; }
|
||||
I32 Builder:: gt_16x2(I32 x, I32 y) { return {this, push(Op:: gt_i16x2, id(x), id(y))}; }
|
||||
I32 Builder::gte_16x2(I32 x, I32 y) { return {this, push(Op::gte_i16x2, id(x), id(y))}; }
|
||||
I32 Builder:: eq_16x2(I32 x, I32 y) { return {this, push(Op:: eq_i16x2, x.id, y.id)}; }
|
||||
I32 Builder::neq_16x2(I32 x, I32 y) { return {this, push(Op::neq_i16x2, x.id, y.id)}; }
|
||||
I32 Builder:: lt_16x2(I32 x, I32 y) { return {this, push(Op:: gt_i16x2, y.id, x.id)}; }
|
||||
I32 Builder::lte_16x2(I32 x, I32 y) { return {this, push(Op::gte_i16x2, y.id, x.id)}; }
|
||||
I32 Builder:: gt_16x2(I32 x, I32 y) { return {this, push(Op:: gt_i16x2, x.id, y.id)}; }
|
||||
I32 Builder::gte_16x2(I32 x, I32 y) { return {this, push(Op::gte_i16x2, x.id, y.id)}; }
|
||||
|
||||
I32 Builder::bit_and(I32 x, I32 y) {
|
||||
int X,Y;
|
||||
if (this->allImm(id(x),&X, id(y),&Y)) { return this->splat(X&Y); }
|
||||
if (this->isImm(id(y), 0)) { return this->splat(0); } // (x & false) == false
|
||||
if (this->isImm(id(x), 0)) { return this->splat(0); } // (false & y) == false
|
||||
if (this->isImm(id(y),~0)) { return x; } // (x & true) == x
|
||||
if (this->isImm(id(x),~0)) { return y; } // (true & y) == y
|
||||
return {this, push(Op::bit_and, id(x), id(y))};
|
||||
if (this->allImm(x.id,&X, y.id,&Y)) { return this->splat(X&Y); }
|
||||
if (this->isImm(y.id, 0)) { return this->splat(0); } // (x & false) == false
|
||||
if (this->isImm(x.id, 0)) { return this->splat(0); } // (false & y) == false
|
||||
if (this->isImm(y.id,~0)) { return x; } // (x & true) == x
|
||||
if (this->isImm(x.id,~0)) { return y; } // (true & y) == y
|
||||
return {this, push(Op::bit_and, x.id, y.id)};
|
||||
}
|
||||
I32 Builder::bit_or(I32 x, I32 y) {
|
||||
int X,Y;
|
||||
if (this->allImm(id(x),&X, id(y),&Y)) { return this->splat(X|Y); }
|
||||
if (this->isImm(id(y), 0)) { return x; } // (x | false) == x
|
||||
if (this->isImm(id(x), 0)) { return y; } // (false | y) == y
|
||||
if (this->isImm(id(y),~0)) { return this->splat(~0); } // (x | true) == true
|
||||
if (this->isImm(id(x),~0)) { return this->splat(~0); } // (true | y) == true
|
||||
return {this, push(Op::bit_or, id(x), id(y))};
|
||||
if (this->allImm(x.id,&X, y.id,&Y)) { return this->splat(X|Y); }
|
||||
if (this->isImm(y.id, 0)) { return x; } // (x | false) == x
|
||||
if (this->isImm(x.id, 0)) { return y; } // (false | y) == y
|
||||
if (this->isImm(y.id,~0)) { return this->splat(~0); } // (x | true) == true
|
||||
if (this->isImm(x.id,~0)) { return this->splat(~0); } // (true | y) == true
|
||||
return {this, push(Op::bit_or, x.id, y.id)};
|
||||
}
|
||||
I32 Builder::bit_xor(I32 x, I32 y) {
|
||||
int X,Y;
|
||||
if (this->allImm(id(x),&X, id(y),&Y)) { return this->splat(X^Y); }
|
||||
if (this->isImm(id(y), 0)) { return x; } // (x ^ false) == x
|
||||
if (this->isImm(id(x), 0)) { return y; } // (false ^ y) == y
|
||||
return {this, push(Op::bit_xor, id(x), id(y))};
|
||||
if (this->allImm(x.id,&X, y.id,&Y)) { return this->splat(X^Y); }
|
||||
if (this->isImm(y.id, 0)) { return x; } // (x ^ false) == x
|
||||
if (this->isImm(x.id, 0)) { return y; } // (false ^ y) == y
|
||||
return {this, push(Op::bit_xor, x.id, y.id)};
|
||||
}
|
||||
I32 Builder::bit_clear(I32 x, I32 y) {
|
||||
int X,Y;
|
||||
if (this->allImm(id(x),&X, id(y),&Y)) { return this->splat(X&~Y); }
|
||||
if (this->isImm(id(y), 0)) { return x; } // (x & ~false) == x
|
||||
if (this->isImm(id(y),~0)) { return this->splat(0); } // (x & ~true) == false
|
||||
if (this->isImm(id(x), 0)) { return this->splat(0); } // (false & ~y) == false
|
||||
return {this, push(Op::bit_clear, id(x), id(y))};
|
||||
if (this->allImm(x.id,&X, y.id,&Y)) { return this->splat(X&~Y); }
|
||||
if (this->isImm(y.id, 0)) { return x; } // (x & ~false) == x
|
||||
if (this->isImm(y.id,~0)) { return this->splat(0); } // (x & ~true) == false
|
||||
if (this->isImm(x.id, 0)) { return this->splat(0); } // (false & ~y) == false
|
||||
return {this, push(Op::bit_clear, x.id, y.id)};
|
||||
}
|
||||
|
||||
I32 Builder::select(I32 x, I32 y, I32 z) {
|
||||
int X,Y,Z;
|
||||
if (this->allImm(id(x),&X, id(y),&Y, id(z),&Z)) { return this->splat(X?Y:Z); }
|
||||
if (this->allImm(x.id,&X, y.id,&Y, z.id,&Z)) { return this->splat(X?Y:Z); }
|
||||
// TODO: some cases to reduce to bit_and when y == 0 or z == 0?
|
||||
return {this, push(Op::select, id(x), id(y), id(z))};
|
||||
return {this, push(Op::select, x.id, y.id, z.id)};
|
||||
}
|
||||
|
||||
I32 Builder::extract(I32 x, int bits, I32 z) {
|
||||
int Z;
|
||||
if (this->allImm(id(z),&Z) && (~0u>>bits) == (unsigned)Z) { return this->shr(x, bits); }
|
||||
if (this->allImm(z.id,&Z) && (~0u>>bits) == (unsigned)Z) { return this->shr(x, bits); }
|
||||
return this->bit_and(z, this->shr(x, bits));
|
||||
}
|
||||
|
||||
I32 Builder::pack(I32 x, I32 y, int bits) {
|
||||
int X,Y;
|
||||
if (this->allImm(id(x),&X, id(y),&Y)) { return this->splat(X|(Y<<bits)); }
|
||||
return {this, push(Op::pack, id(x),id(y),NA, 0,bits)};
|
||||
if (this->allImm(x.id,&X, y.id,&Y)) { return this->splat(X|(Y<<bits)); }
|
||||
return {this, push(Op::pack, x.id,y.id,NA, 0,bits)};
|
||||
}
|
||||
|
||||
I32 Builder::bytes(I32 x, int control) {
|
||||
return {this, push(Op::bytes, id(x),NA,NA, control)};
|
||||
return {this, push(Op::bytes, x.id,NA,NA, control)};
|
||||
}
|
||||
|
||||
F32 Builder::floor(F32 x) {
|
||||
float X;
|
||||
if (this->allImm(id(x),&X)) { return this->splat(floorf(X)); }
|
||||
return {this, push(Op::floor, id(x))};
|
||||
if (this->allImm(x.id,&X)) { return this->splat(floorf(X)); }
|
||||
return {this, push(Op::floor, x.id)};
|
||||
}
|
||||
F32 Builder::to_f32(I32 x) {
|
||||
int X;
|
||||
if (this->allImm(id(x),&X)) { return this->splat((float)X); }
|
||||
return {this, push(Op::to_f32, id(x))};
|
||||
if (this->allImm(x.id,&X)) { return this->splat((float)X); }
|
||||
return {this, push(Op::to_f32, x.id)};
|
||||
}
|
||||
I32 Builder::trunc(F32 x) {
|
||||
float X;
|
||||
if (this->allImm(id(x),&X)) { return this->splat((int)X); }
|
||||
return {this, push(Op::trunc, id(x))};
|
||||
if (this->allImm(x.id,&X)) { return this->splat((int)X); }
|
||||
return {this, push(Op::trunc, x.id)};
|
||||
}
|
||||
I32 Builder::round(F32 x) {
|
||||
float X;
|
||||
if (this->allImm(id(x),&X)) { return this->splat((int)lrintf(X)); }
|
||||
return {this, push(Op::round, id(x))};
|
||||
if (this->allImm(x.id,&X)) { return this->splat((int)lrintf(X)); }
|
||||
return {this, push(Op::round, x.id)};
|
||||
}
|
||||
|
||||
F32 Builder::from_unorm(int bits, I32 x) {
|
||||
@ -1107,7 +1091,7 @@ namespace skvm {
|
||||
from_unorm(5, extract(bgr, 11, 0b011'111)),
|
||||
from_unorm(6, extract(bgr, 5, 0b111'111)),
|
||||
from_unorm(5, extract(bgr, 0, 0b011'111)),
|
||||
1.0f,
|
||||
splat(1.0f),
|
||||
};
|
||||
}
|
||||
|
||||
@ -1289,7 +1273,7 @@ namespace skvm {
|
||||
switch (mode) {
|
||||
default: SkASSERT(false); /*but also, for safety, fallthrough*/
|
||||
|
||||
case SkBlendMode::kClear: return { 0.0f, 0.0f, 0.0f, 0.0f };
|
||||
case SkBlendMode::kClear: return { splat(0.0f), splat(0.0f), splat(0.0f), splat(0.0f) };
|
||||
|
||||
case SkBlendMode::kSrc: return src;
|
||||
case SkBlendMode::kDst: return dst;
|
||||
|
385
src/core/SkVM.h
385
src/core/SkVM.h
@ -339,88 +339,53 @@ namespace skvm {
|
||||
};
|
||||
|
||||
using Val = int;
|
||||
// We reserve impossibe Val IDs as a sentinels:
|
||||
// - NA meaning none, n/a, null, nil, etc.
|
||||
// - IMM meaning an unresolved immediate.
|
||||
static const Val NA = -1,
|
||||
IMM = -2;
|
||||
// We reserve an impossibe Val ID as a sentinel
|
||||
// NA meaning none, n/a, null, nil, etc.
|
||||
static const Val NA = -1;
|
||||
|
||||
struct Arg { int ix; };
|
||||
|
||||
struct I32 {
|
||||
public:
|
||||
I32() : fBuilder(nullptr), fID( NA), fImm(0) {}
|
||||
I32(int x) : fBuilder(nullptr), fID(IMM), fImm(x) {}
|
||||
I32(Builder* builder, Val id) : fBuilder(builder), fID( id), fImm(0) {}
|
||||
|
||||
explicit operator bool() const { return fID != NA; }
|
||||
Builder* builder() const { return fBuilder; }
|
||||
|
||||
// Gets our ID, resolving any immediate to a splat() on b. Mostly for internal use.
|
||||
Val resolve(Builder* b);
|
||||
|
||||
private:
|
||||
Builder* fBuilder = nullptr;
|
||||
Val fID = NA;
|
||||
int fImm = 0;
|
||||
Builder* builder = nullptr;
|
||||
Val id = NA;
|
||||
explicit operator bool() const { return id != NA; }
|
||||
};
|
||||
|
||||
static inline I32& operator+=(I32&, I32);
|
||||
static inline I32& operator-=(I32&, I32);
|
||||
static inline I32& operator*=(I32&, I32);
|
||||
|
||||
static inline I32 operator+(I32, I32);
|
||||
static inline I32 operator-(I32, I32);
|
||||
static inline I32 operator*(I32, I32);
|
||||
|
||||
static inline I32 min(I32, I32);
|
||||
static inline I32 max(I32, I32);
|
||||
|
||||
static inline I32 operator==(I32, I32);
|
||||
static inline I32 operator!=(I32, I32);
|
||||
static inline I32 operator< (I32, I32);
|
||||
static inline I32 operator<=(I32, I32);
|
||||
static inline I32 operator> (I32, I32);
|
||||
static inline I32 operator>=(I32, I32);
|
||||
|
||||
struct F32 {
|
||||
public:
|
||||
F32() : fBuilder(nullptr), fID( NA), fImm(0) {}
|
||||
F32(float x) : fBuilder(nullptr), fID(IMM), fImm(x) {}
|
||||
F32(Builder* builder, Val id) : fBuilder(builder), fID( id), fImm(0) {}
|
||||
|
||||
explicit operator bool() const { return fID != NA; }
|
||||
Builder* builder() const { return fBuilder; }
|
||||
|
||||
// Gets our ID, resolving any immediate to a splat() on b. Mostly for internal use.
|
||||
Val resolve(Builder* b);
|
||||
|
||||
private:
|
||||
Builder* fBuilder = nullptr;
|
||||
Val fID = NA;
|
||||
float fImm = 0;
|
||||
Builder* builder = nullptr;
|
||||
Val id = NA;
|
||||
explicit operator bool() const { return id != NA; }
|
||||
};
|
||||
|
||||
static inline F32& operator+=(F32&, F32);
|
||||
static inline F32& operator-=(F32&, F32);
|
||||
static inline F32& operator*=(F32&, F32);
|
||||
static inline F32& operator/=(F32&, F32);
|
||||
// Some operations make sense with immediate arguments,
|
||||
// so we use I32a and F32a 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,
|
||||
// and even sometimes not for binary operations, e.g.
|
||||
//
|
||||
// div(x,y) -- normal every day divide
|
||||
// div(3.0f,y) -- yep, makes sense
|
||||
// div(x,3.0f) -- omitted as a reminder you probably want mul(x, 1/3.0f).
|
||||
//
|
||||
// You can of course always splat() to override these opinions.
|
||||
struct I32a {
|
||||
I32a(I32 v) : SkDEBUGCODE(builder(v.builder),) id(v.id) {}
|
||||
I32a(int v) : imm(v) {}
|
||||
|
||||
static inline F32 operator+(F32, F32);
|
||||
static inline F32 operator-(F32, F32);
|
||||
static inline F32 operator*(F32, F32);
|
||||
static inline F32 operator/(F32, F32);
|
||||
SkDEBUGCODE(Builder* builder = nullptr;)
|
||||
Val id = NA;
|
||||
int imm = 0;
|
||||
};
|
||||
|
||||
static inline F32 min(F32, F32);
|
||||
static inline F32 max(F32, F32);
|
||||
|
||||
static inline I32 operator==(F32, F32);
|
||||
static inline I32 operator!=(F32, F32);
|
||||
static inline I32 operator< (F32, F32);
|
||||
static inline I32 operator<=(F32, F32);
|
||||
static inline I32 operator> (F32, F32);
|
||||
static inline I32 operator>=(F32, F32);
|
||||
struct F32a {
|
||||
F32a(F32 v) : SkDEBUGCODE(builder(v.builder),) id(v.id) {}
|
||||
F32a(float v) : imm(v) {}
|
||||
|
||||
SkDEBUGCODE(Builder* builder = nullptr;)
|
||||
Val id = NA;
|
||||
float imm = 0;
|
||||
};
|
||||
|
||||
struct Color {
|
||||
skvm::F32 r,g,b,a;
|
||||
@ -473,8 +438,8 @@ namespace skvm {
|
||||
|
||||
// Assert cond is true, printing debug when not.
|
||||
void assert_true(I32 cond, I32 debug);
|
||||
void assert_true(I32 cond, F32 debug) { this->assert_true(cond, this->bit_cast(debug)); }
|
||||
void assert_true(I32 cond) { this->assert_true(cond, cond); }
|
||||
void assert_true(I32 cond, F32 debug) { assert_true(cond, bit_cast(debug)); }
|
||||
void assert_true(I32 cond) { assert_true(cond, cond); }
|
||||
|
||||
// Store {8,16,32}-bit varying.
|
||||
void store8 (Arg ptr, I32 val);
|
||||
@ -529,103 +494,102 @@ namespace skvm {
|
||||
F32 splat(float f);
|
||||
|
||||
// float math, comparisons, etc.
|
||||
F32 add(F32 x, F32 y);
|
||||
F32 sub(F32 x, F32 y);
|
||||
F32 mul(F32 x, F32 y);
|
||||
F32 div(F32 x, F32 y);
|
||||
F32 min(F32 x, F32 y);
|
||||
F32 max(F32 x, F32 y);
|
||||
F32 mad(F32 x, F32 y, F32 z) { return add(mul(x,y), z); }
|
||||
F32 sqrt(F32 x);
|
||||
F32 add(F32, F32); F32 add(F32a x, F32a y) { return add(_(x), _(y)); }
|
||||
F32 sub(F32, F32); F32 sub(F32a x, F32a y) { return sub(_(x), _(y)); }
|
||||
F32 mul(F32, F32); F32 mul(F32a x, F32a y) { return mul(_(x), _(y)); }
|
||||
F32 div(F32, F32); F32 div(F32a x, F32 y) { return div(_(x), y ); }
|
||||
F32 min(F32, F32); F32 min(F32a x, F32a y) { return min(_(x), _(y)); }
|
||||
F32 max(F32, F32); F32 max(F32a x, F32a y) { return max(_(x), _(y)); }
|
||||
|
||||
F32 mad(F32 x, F32 y, F32 z) { return add(mul(x,y), z); }
|
||||
F32 mad(F32a x, F32a y, F32a z) { return mad(_(x), _(y), _(z)); }
|
||||
|
||||
F32 inv(F32 x) { return sub(1.0f, x); }
|
||||
F32 negate(F32 x) { return sub(0.0f, x); }
|
||||
F32 sqrt(F32);
|
||||
|
||||
F32 approx_log2(F32);
|
||||
F32 approx_pow2(F32);
|
||||
F32 approx_powf(F32 base, F32 exp);
|
||||
F32 approx_log (F32 x) { return mul(0.69314718f, approx_log2(x)); }
|
||||
F32 approx_exp (F32 x) { return approx_pow2(mul(x, 1.4426950408889634074f)); }
|
||||
|
||||
F32 approx_log(F32 x) { // return ln(2) * log2(x)
|
||||
return 0.69314718f * approx_log2(x);
|
||||
}
|
||||
F32 approx_exp(F32 x) { // 2^(x * log2(e))
|
||||
return approx_pow2(x * 1.4426950408889634074f);
|
||||
}
|
||||
F32 approx_powf(F32 base, F32 exp);
|
||||
F32 approx_powf(F32a base, F32a exp) { return approx_powf(_(base), _(exp)); }
|
||||
|
||||
F32 inv(F32 x) { return 1-x; }
|
||||
F32 negate(F32 x) { return 0-x; }
|
||||
F32 lerp(F32 lo, F32 hi, F32 t) { return mad(sub(hi, lo), t, lo); }
|
||||
F32 lerp(F32a lo, F32a hi, F32a t) { return lerp(_(lo), _(hi), _(t)); }
|
||||
|
||||
F32 lerp(F32 lo, F32 hi, F32 t) {
|
||||
return mad(hi-lo, t, lo);
|
||||
}
|
||||
F32 clamp(F32 x, F32 lo, F32 hi) {
|
||||
return max(lo, min(x, hi));
|
||||
}
|
||||
F32 clamp01(F32 x) {
|
||||
return clamp(x, 0.0f, 1.0f);
|
||||
}
|
||||
F32 abs(F32 x) {
|
||||
return bit_cast(bit_and(bit_cast(x), 0x7fff'ffff));
|
||||
}
|
||||
F32 fract(F32 x) {
|
||||
return x - floor(x);
|
||||
}
|
||||
F32 norm(F32 x, F32 y) {
|
||||
return sqrt(x*x + y*y);
|
||||
}
|
||||
|
||||
I32 eq (F32 x, F32 y);
|
||||
I32 neq(F32 x, F32 y);
|
||||
I32 lt (F32 x, F32 y);
|
||||
I32 lte(F32 x, F32 y);
|
||||
I32 gt (F32 x, F32 y);
|
||||
I32 gte(F32 x, F32 y);
|
||||
F32 clamp(F32 x, F32 lo, F32 hi) { return max(lo, min(x, hi)); }
|
||||
F32 clamp(F32a x, F32a lo, F32a hi) { return clamp(_(x), _(lo), _(hi)); }
|
||||
F32 clamp01(F32 x) { return clamp(x, 0.0f, 1.0f); }
|
||||
|
||||
F32 abs(F32 x) { return bit_cast(bit_and(bit_cast(x), 0x7fff'ffff)); }
|
||||
F32 fract(F32 x) { return sub(x, floor(x)); }
|
||||
F32 floor(F32);
|
||||
|
||||
I32 trunc(F32 x);
|
||||
I32 round(F32 x); // Round to int using current rounding mode (as if lrintf()).
|
||||
I32 bit_cast(F32 x) { return {this, id(x)}; }
|
||||
I32 bit_cast(F32 x) { return {x.builder, x.id}; }
|
||||
|
||||
F32 norm(F32 x, F32 y) {
|
||||
return sqrt(add(mul(x,x),
|
||||
mul(y,y)));
|
||||
}
|
||||
F32 norm(F32a x, F32a y) { return norm(_(x), _(y)); }
|
||||
|
||||
I32 eq(F32, F32); I32 eq(F32a x, F32a y) { return eq(_(x), _(y)); }
|
||||
I32 neq(F32, F32); I32 neq(F32a x, F32a y) { return neq(_(x), _(y)); }
|
||||
I32 lt (F32, F32); I32 lt (F32a x, F32a y) { return lt (_(x), _(y)); }
|
||||
I32 lte(F32, F32); I32 lte(F32a x, F32a y) { return lte(_(x), _(y)); }
|
||||
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)); }
|
||||
|
||||
// int math, comparisons, etc.
|
||||
I32 add(I32 x, I32 y);
|
||||
I32 sub(I32 x, I32 y);
|
||||
I32 mul(I32 x, I32 y);
|
||||
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)); }
|
||||
I32 mul(I32, I32); I32 mul(I32a x, I32a y) { return mul(_(x), _(y)); }
|
||||
|
||||
I32 shl(I32 x, int bits);
|
||||
I32 shr(I32 x, int bits);
|
||||
I32 sra(I32 x, int bits);
|
||||
|
||||
I32 eq (I32 x, I32 y);
|
||||
I32 neq(I32 x, I32 y);
|
||||
I32 lt (I32 x, I32 y);
|
||||
I32 lte(I32 x, I32 y);
|
||||
I32 gt (I32 x, I32 y);
|
||||
I32 gte(I32 x, I32 y);
|
||||
I32 eq (I32 x, I32 y); I32 eq(I32a x, I32a y) { return eq(_(x), _(y)); }
|
||||
I32 neq(I32 x, I32 y); I32 neq(I32a x, I32a y) { return neq(_(x), _(y)); }
|
||||
I32 lt (I32 x, I32 y); I32 lt (I32a x, I32a y) { return lt (_(x), _(y)); }
|
||||
I32 lte(I32 x, I32 y); I32 lte(I32a x, I32a y) { return lte(_(x), _(y)); }
|
||||
I32 gt (I32 x, I32 y); I32 gt (I32a x, I32a y) { return gt (_(x), _(y)); }
|
||||
I32 gte(I32 x, I32 y); I32 gte(I32a x, I32a y) { return gte(_(x), _(y)); }
|
||||
|
||||
F32 to_f32(I32 x);
|
||||
F32 bit_cast(I32 x) { return {this, id(x)}; }
|
||||
F32 bit_cast(I32 x) { return {x.builder, x.id}; }
|
||||
|
||||
// Treat each 32-bit lane as a pair of 16-bit ints.
|
||||
I32 add_16x2(I32 x, I32 y);
|
||||
I32 sub_16x2(I32 x, I32 y);
|
||||
I32 mul_16x2(I32 x, I32 y);
|
||||
I32 add_16x2(I32, I32); I32 add_16x2(I32a x, I32a y) { return add_16x2(_(x), _(y)); }
|
||||
I32 sub_16x2(I32, I32); I32 sub_16x2(I32a x, I32a y) { return sub_16x2(_(x), _(y)); }
|
||||
I32 mul_16x2(I32, I32); I32 mul_16x2(I32a x, I32a y) { return mul_16x2(_(x), _(y)); }
|
||||
|
||||
I32 shl_16x2(I32 x, int bits);
|
||||
I32 shr_16x2(I32 x, int bits);
|
||||
I32 sra_16x2(I32 x, int bits);
|
||||
|
||||
I32 eq_16x2(I32 x, I32 y);
|
||||
I32 neq_16x2(I32 x, I32 y);
|
||||
I32 lt_16x2(I32 x, I32 y);
|
||||
I32 lte_16x2(I32 x, I32 y);
|
||||
I32 gt_16x2(I32 x, I32 y);
|
||||
I32 gte_16x2(I32 x, I32 y);
|
||||
I32 eq_16x2(I32, I32); I32 eq_16x2(I32a x, I32a y) { return eq_16x2(_(x), _(y)); }
|
||||
I32 neq_16x2(I32, I32); I32 neq_16x2(I32a x, I32a y) { return neq_16x2(_(x), _(y)); }
|
||||
I32 lt_16x2(I32, I32); I32 lt_16x2(I32a x, I32a y) { return lt_16x2(_(x), _(y)); }
|
||||
I32 lte_16x2(I32, I32); I32 lte_16x2(I32a x, I32a y) { return lte_16x2(_(x), _(y)); }
|
||||
I32 gt_16x2(I32, I32); I32 gt_16x2(I32a x, I32a y) { return gt_16x2(_(x), _(y)); }
|
||||
I32 gte_16x2(I32, I32); I32 gte_16x2(I32a x, I32a y) { return gte_16x2(_(x), _(y)); }
|
||||
|
||||
// Bitwise operations.
|
||||
I32 bit_and (I32 x, I32 y);
|
||||
I32 bit_or (I32 x, I32 y);
|
||||
I32 bit_xor (I32 x, I32 y);
|
||||
I32 bit_clear(I32 x, I32 y); // x & ~y
|
||||
I32 bit_and (I32, I32); I32 bit_and (I32a x, I32a y) { return bit_and (_(x), _(y)); }
|
||||
I32 bit_or (I32, I32); I32 bit_or (I32a x, I32a y) { return bit_or (_(x), _(y)); }
|
||||
I32 bit_xor (I32, I32); I32 bit_xor (I32a x, I32a y) { return bit_xor (_(x), _(y)); }
|
||||
I32 bit_clear(I32, I32); I32 bit_clear(I32a x, I32a y) { return bit_clear(_(x), _(y)); }
|
||||
|
||||
I32 min(I32 x, I32 y) { return select(x<y, x, y); }
|
||||
I32 max(I32 x, I32 y) { return select(x>y, x, y); }
|
||||
I32 min(I32 x, I32 y) { return select(lt(x,y), x, y); }
|
||||
I32 max(I32 x, I32 y) { return select(gt(x,y), x, y); }
|
||||
|
||||
I32 min(I32a x, I32a y) { return min(_(x), _(y)); }
|
||||
I32 max(I32a x, I32a y) { return max(_(x), _(y)); }
|
||||
|
||||
I32 select(I32 cond, I32 t, I32 f); // cond ? t : f
|
||||
F32 select(I32 cond, F32 t, F32 f) {
|
||||
@ -633,6 +597,9 @@ namespace skvm {
|
||||
, this->bit_cast(f)));
|
||||
}
|
||||
|
||||
I32 select(I32a cond, I32a t, I32a f) { return select(_(cond), _(t), _(f)); }
|
||||
F32 select(I32a cond, F32a t, F32a f) { return select(_(cond), _(t), _(f)); }
|
||||
|
||||
// More complex operations...
|
||||
|
||||
// Shuffle the bytes in x according to each nibble of control, as if
|
||||
@ -659,6 +626,10 @@ namespace skvm {
|
||||
I32 extract(I32 x, int bits, I32 z); // (x>>bits) & z
|
||||
I32 pack (I32 x, I32 y, int bits); // x | (y << bits), assuming (x & (y << bits)) == 0
|
||||
|
||||
I32 extract(I32a x, int bits, I32a z) { return extract(_(x), bits, _(z)); }
|
||||
I32 pack (I32a x, I32a y, int bits) { return pack (_(x), _(y), bits); }
|
||||
|
||||
|
||||
// Common idioms used in several places, worth centralizing for consistency.
|
||||
F32 from_unorm(int bits, I32); // E.g. from_unorm(8, x) -> x * (1/255.0f)
|
||||
I32 to_unorm(int bits, F32); // E.g. to_unorm(8, x) -> round(x * 255)
|
||||
@ -690,8 +661,21 @@ namespace skvm {
|
||||
|
||||
Val push(Op, Val x, Val y=NA, Val z=NA, int immy=0, int immz=0);
|
||||
|
||||
Val id(I32 x) { return x.resolve(this); }
|
||||
Val id(F32 x) { return x.resolve(this); }
|
||||
I32 _(I32a x) {
|
||||
if (x.id != NA) {
|
||||
SkASSERT(x.builder == this);
|
||||
return {this, x.id};
|
||||
}
|
||||
return this->splat(x.imm);
|
||||
}
|
||||
|
||||
F32 _(F32a x) {
|
||||
if (x.id != NA) {
|
||||
SkASSERT(x.builder == this);
|
||||
return {this, x.id};
|
||||
}
|
||||
return this->splat(x.imm);
|
||||
}
|
||||
|
||||
bool allImm() const;
|
||||
|
||||
@ -806,63 +790,84 @@ namespace skvm {
|
||||
// TODO: control flow
|
||||
// TODO: 64-bit values?
|
||||
|
||||
static Builder* common_builder(I32 x, I32 y) {
|
||||
Builder *X = x.builder(),
|
||||
*Y = y.builder();
|
||||
SkASSERT(X || Y);
|
||||
if (X && Y) { SkASSERT(X==Y); return X; }
|
||||
if (X ) { return X; }
|
||||
if (Y ) { return Y; }
|
||||
return nullptr;
|
||||
}
|
||||
static inline I32 operator+(I32 x, I32a y) { return x.builder->add(x,y); }
|
||||
static inline I32 operator+(int x, I32 y) { return y.builder->add(x,y); }
|
||||
|
||||
static inline I32& operator+=(I32& x, I32 y) { return (x = common_builder(x,y)->add(x,y)); }
|
||||
static inline I32& operator-=(I32& x, I32 y) { return (x = common_builder(x,y)->sub(x,y)); }
|
||||
static inline I32& operator*=(I32& x, I32 y) { return (x = common_builder(x,y)->mul(x,y)); }
|
||||
static inline I32 operator-(I32 x, I32a y) { return x.builder->sub(x,y); }
|
||||
static inline I32 operator-(int x, I32 y) { return y.builder->sub(x,y); }
|
||||
|
||||
static inline I32 operator+(I32 x, I32 y) { return x += y; }
|
||||
static inline I32 operator-(I32 x, I32 y) { return x -= y; }
|
||||
static inline I32 operator*(I32 x, I32 y) { return x *= y; }
|
||||
static inline I32 operator*(I32 x, I32a y) { return x.builder->mul(x,y); }
|
||||
static inline I32 operator*(int x, I32 y) { return y.builder->mul(x,y); }
|
||||
|
||||
static inline I32 min(I32 x, I32 y) { return common_builder(x,y)->min(x,y); }
|
||||
static inline I32 max(I32 x, I32 y) { return common_builder(x,y)->max(x,y); }
|
||||
static inline I32 min(I32 x, I32a y) { return x.builder->min(x,y); }
|
||||
static inline I32 min(int x, I32 y) { return y.builder->min(x,y); }
|
||||
|
||||
static inline I32 operator==(I32 x, I32 y) { return common_builder(x,y)-> eq(x,y); }
|
||||
static inline I32 operator!=(I32 x, I32 y) { return common_builder(x,y)->neq(x,y); }
|
||||
static inline I32 operator< (I32 x, I32 y) { return common_builder(x,y)->lt (x,y); }
|
||||
static inline I32 operator<=(I32 x, I32 y) { return common_builder(x,y)->lte(x,y); }
|
||||
static inline I32 operator> (I32 x, I32 y) { return common_builder(x,y)->gt (x,y); }
|
||||
static inline I32 operator>=(I32 x, I32 y) { return common_builder(x,y)->gte(x,y); }
|
||||
static inline I32 max(I32 x, I32a y) { return x.builder->max(x,y); }
|
||||
static inline I32 max(int x, I32 y) { return y.builder->max(x,y); }
|
||||
|
||||
static Builder* common_builder(F32 x, F32 y) {
|
||||
Builder *X = x.builder(),
|
||||
*Y = y.builder();
|
||||
SkASSERT(X || Y);
|
||||
if (X && Y) { SkASSERT(X==Y); return X; }
|
||||
if (X ) { return X; }
|
||||
if (Y ) { return Y; }
|
||||
return nullptr;
|
||||
}
|
||||
static inline I32 operator==(I32 x, I32a y) { return x.builder->eq(x,y); }
|
||||
static inline I32 operator==(int x, I32 y) { return y.builder->eq(x,y); }
|
||||
|
||||
static inline F32& operator+=(F32& x, F32 y) { return (x = common_builder(x,y)->add(x,y)); }
|
||||
static inline F32& operator-=(F32& x, F32 y) { return (x = common_builder(x,y)->sub(x,y)); }
|
||||
static inline F32& operator*=(F32& x, F32 y) { return (x = common_builder(x,y)->mul(x,y)); }
|
||||
static inline F32& operator/=(F32& x, F32 y) { return (x = common_builder(x,y)->div(x,y)); }
|
||||
static inline I32 operator!=(I32 x, I32a y) { return x.builder->neq(x,y); }
|
||||
static inline I32 operator!=(int x, I32 y) { return y.builder->neq(x,y); }
|
||||
|
||||
static inline F32 operator+(F32 x, F32 y) { return x += y; }
|
||||
static inline F32 operator-(F32 x, F32 y) { return x -= y; }
|
||||
static inline F32 operator*(F32 x, F32 y) { return x *= y; }
|
||||
static inline F32 operator/(F32 x, F32 y) { return x /= y; }
|
||||
static inline I32 operator< (I32 x, I32a y) { return x.builder->lt(x,y); }
|
||||
static inline I32 operator< (int x, I32 y) { return y.builder->lt(x,y); }
|
||||
|
||||
static inline F32 min(F32 x, F32 y) { return common_builder(x,y)->min(x,y); }
|
||||
static inline F32 max(F32 x, F32 y) { return common_builder(x,y)->max(x,y); }
|
||||
static inline I32 operator<=(I32 x, I32a y) { return x.builder->lte(x,y); }
|
||||
static inline I32 operator<=(int x, I32 y) { return y.builder->lte(x,y); }
|
||||
|
||||
static inline I32 operator==(F32 x, F32 y) { return common_builder(x,y)-> eq(x,y); }
|
||||
static inline I32 operator!=(F32 x, F32 y) { return common_builder(x,y)->neq(x,y); }
|
||||
static inline I32 operator< (F32 x, F32 y) { return common_builder(x,y)->lt (x,y); }
|
||||
static inline I32 operator<=(F32 x, F32 y) { return common_builder(x,y)->lte(x,y); }
|
||||
static inline I32 operator> (F32 x, F32 y) { return common_builder(x,y)->gt (x,y); }
|
||||
static inline I32 operator>=(F32 x, F32 y) { return common_builder(x,y)->gte(x,y); }
|
||||
static inline I32 operator> (I32 x, I32a y) { return x.builder->gt(x,y); }
|
||||
static inline I32 operator> (int x, I32 y) { return y.builder->gt(x,y); }
|
||||
|
||||
static inline I32 operator>=(I32 x, I32a y) { return x.builder->gte(x,y); }
|
||||
static inline I32 operator>=(int x, I32 y) { return y.builder->gte(x,y); }
|
||||
|
||||
|
||||
static inline F32 operator+(F32 x, F32a y) { return x.builder->add(x,y); }
|
||||
static inline F32 operator+(float x, F32 y) { return y.builder->add(x,y); }
|
||||
|
||||
static inline F32 operator-(F32 x, F32a y) { return x.builder->sub(x,y); }
|
||||
static inline F32 operator-(float x, F32 y) { return y.builder->sub(x,y); }
|
||||
|
||||
static inline F32 operator*(F32 x, F32a y) { return x.builder->mul(x,y); }
|
||||
static inline F32 operator*(float x, F32 y) { return y.builder->mul(x,y); }
|
||||
|
||||
static inline F32 operator/(F32 x, F32 y) { return x.builder->div(x,y); }
|
||||
static inline F32 operator/(float x, F32 y) { return y.builder->div(x,y); }
|
||||
|
||||
static inline F32 min(F32 x, F32a y) { return x.builder->min(x,y); }
|
||||
static inline F32 min(float x, F32 y) { return y.builder->min(x,y); }
|
||||
|
||||
static inline F32 max(F32 x, F32a y) { return x.builder->max(x,y); }
|
||||
static inline F32 max(float x, F32 y) { return y.builder->max(x,y); }
|
||||
|
||||
static inline I32 operator==(F32 x, F32a y) { return x.builder->eq(x,y); }
|
||||
static inline I32 operator==(float x, F32 y) { return y.builder->eq(x,y); }
|
||||
|
||||
static inline I32 operator!=(F32 x, F32a y) { return x.builder->neq(x,y); }
|
||||
static inline I32 operator!=(float x, F32 y) { return y.builder->neq(x,y); }
|
||||
|
||||
static inline I32 operator< (F32 x, F32a y) { return x.builder->lt(x,y); }
|
||||
static inline I32 operator< (float x, F32 y) { return y.builder->lt(x,y); }
|
||||
|
||||
static inline I32 operator<=(F32 x, F32a y) { return x.builder->lte(x,y); }
|
||||
static inline I32 operator<=(float x, F32 y) { return y.builder->lte(x,y); }
|
||||
|
||||
static inline I32 operator> (F32 x, F32a y) { return x.builder->gt(x,y); }
|
||||
static inline I32 operator> (float x, F32 y) { return y.builder->gt(x,y); }
|
||||
|
||||
static inline I32 operator>=(F32 x, F32a y) { return x.builder->gte(x,y); }
|
||||
static inline I32 operator>=(float x, F32 y) { return y.builder->gte(x,y); }
|
||||
|
||||
|
||||
static inline I32& operator+=(I32& x, I32a y) { return (x = x + y); }
|
||||
static inline I32& operator-=(I32& x, I32a y) { return (x = x - y); }
|
||||
static inline I32& operator*=(I32& x, I32a y) { return (x = x * y); }
|
||||
|
||||
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 F32& operator*=(F32& x, F32a y) { return (x = x * y); }
|
||||
|
||||
}
|
||||
|
||||
|
@ -141,12 +141,7 @@ namespace {
|
||||
// precisely which value we'll treat as which channel. Imagine the shader
|
||||
// called std::swap(*r,*b)... it draws differently, but p.hash() is unchanged.
|
||||
// We'll fold the hash of their IDs in order to disambiguate.
|
||||
const int outputs[] = {
|
||||
c.r.resolve(&p),
|
||||
c.g.resolve(&p),
|
||||
c.b.resolve(&p),
|
||||
c.a.resolve(&p),
|
||||
};
|
||||
const int outputs[] = { c.r.id, c.g.id, c.b.id, c.a.id };
|
||||
hash ^= SkOpts::hash(outputs, sizeof(outputs));
|
||||
} else {
|
||||
*ok = false;
|
||||
@ -259,7 +254,7 @@ namespace {
|
||||
auto load_coverage = [&](skvm::Color* cov) {
|
||||
bool partial_coverage = true;
|
||||
switch (params.coverage) {
|
||||
case Coverage::Full: cov->r = cov->g = cov->b = cov->a = 1.0f;
|
||||
case Coverage::Full: cov->r = cov->g = cov->b = cov->a = splat(1.0f);
|
||||
partial_coverage = false;
|
||||
break;
|
||||
|
||||
@ -340,7 +335,7 @@ namespace {
|
||||
// When a destination is known opaque, we may assume it both starts and stays fully
|
||||
// opaque, ignoring any math that disagrees. This sometimes trims a little work.
|
||||
if (params.dst.isOpaque()) {
|
||||
dst.a = 1.0f;
|
||||
dst.a = splat(1.0f);
|
||||
} else if (params.dst.alphaType() == kUnpremul_SkAlphaType) {
|
||||
premul(&dst.r, &dst.g, &dst.b, dst.a);
|
||||
}
|
||||
@ -356,7 +351,7 @@ namespace {
|
||||
}
|
||||
|
||||
if (params.dst.isOpaque()) {
|
||||
src.a = 1.0f;
|
||||
src.a = splat(1.0f);
|
||||
} else if (params.dst.alphaType() == kUnpremul_SkAlphaType) {
|
||||
unpremul(&src.r, &src.g, &src.b, src.a);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user