friendly wrapper to allocate uniforms

This basically wraps up the old `uniforms` and `buf` params
into a new type that has push() and pushF() methods that return
a value you can pass directly to Builder::uniform32() and co.

I think this has uniforms about as streamlined as they can get.

Change-Id: I8f611f91b4a2d7cdb8f05ce0333669d2e3930be8
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/252937
Reviewed-by: Mike Reed <reed@google.com>
Commit-Queue: Mike Klein <mtklein@google.com>
This commit is contained in:
Mike Klein 2019-11-05 14:19:58 -06:00 committed by Skia Commit-Bot
parent 23607a9a77
commit 1cc6067757
13 changed files with 105 additions and 81 deletions

View File

@ -30,11 +30,11 @@ struct Fade : public SkShaderBase {
bool onProgram(skvm::Builder* p,
SkColorSpace* dstCS,
skvm::Arg uniforms, SkTDArray<uint32_t>* buf,
skvm::Uniforms* uniforms,
skvm::F32 x, skvm::F32 y,
skvm::I32* r, skvm::I32* g, skvm::I32* b, skvm::I32* a) const override {
if (as_SB(fShader)->program(p, dstCS,
uniforms, buf,
uniforms,
x,y, r,g,b,a)) {
// In this GM `y` will range over 0-50 and `x` over 50-100.
*r = p->to_i32(p->mul(y, p->splat(255/ 50.0f)));

View File

@ -12,7 +12,6 @@
#include "include/core/SkColor.h"
#include "include/core/SkFlattenable.h"
#include "include/core/SkRefCnt.h"
#include "include/private/SkTDArray.h"
class GrColorInfo;
class GrFragmentProcessor;
@ -28,6 +27,7 @@ namespace skvm {
struct Arg;
struct I32;
struct F32;
struct Uniforms;
}
/**
@ -66,7 +66,7 @@ public:
virtual bool program(skvm::Builder*,
SkColorSpace* dstCS,
skvm::Arg uniforms, SkTDArray<uint32_t>*,
skvm::Uniforms* uniforms,
skvm::I32* r, skvm::I32* g, skvm::I32* b, skvm::I32* a) const;
enum Flags {

View File

@ -46,7 +46,7 @@ bool SkColorFilter::appendStages(const SkStageRec& rec, bool shaderIsOpaque) con
bool SkColorFilter::program(skvm::Builder*,
SkColorSpace* dstCS,
skvm::Arg uniforms, SkTDArray<uint32_t>* buf,
skvm::Uniforms* uniforms,
skvm::I32* r, skvm::I32* g, skvm::I32* b, skvm::I32* a) const {
return false;
}

View File

@ -81,7 +81,7 @@ bool SkColorFilter_Matrix::onAppendStages(const SkStageRec& rec, bool shaderIsOp
bool SkColorFilter_Matrix::program(skvm::Builder* p,
SkColorSpace* /*dstCS*/,
skvm::Arg uniforms, SkTDArray<uint32_t>* buf,
skvm::Uniforms* uniforms,
skvm::I32* r, skvm::I32* g, skvm::I32* b, skvm::I32* a) const {
// TODO: specialize generated code on the 0/1 values of fMatrix?
if (fDomain == Domain::kRGBA) {
@ -99,9 +99,8 @@ bool SkColorFilter_Matrix::program(skvm::Builder* p,
B = p->mul(B, invA);
// Apply matrix.
const size_t offset = buf->bytes();
memcpy(buf->append(20), &fMatrix, sizeof(fMatrix));
auto m = [&](int i) { return p->bit_cast(p->uniform32(uniforms, offset + 4*i)); };
skvm::Builder::Uniform u = uniforms->pushF(fMatrix, 20);
auto m = [&](int i) { return p->bit_cast(p->uniform32(u.ptr, u.offset + 4*i)); };
skvm::F32 rgba[4];
for (int j = 0; j < 4; j++) {

View File

@ -35,7 +35,7 @@ private:
bool onAppendStages(const SkStageRec& rec, bool shaderIsOpaque) const override;
bool program(skvm::Builder*,
SkColorSpace* dstCS,
skvm::Arg uniforms, SkTDArray<uint32_t>*,
skvm::Uniforms* uniforms,
skvm::I32* r, skvm::I32* g, skvm::I32* b, skvm::I32* a) const override;
float fMatrix[20];

View File

@ -338,6 +338,14 @@ namespace skvm {
I32 uniform16(Arg ptr, int offset=0);
I32 uniform32(Arg ptr, int offset=0);
struct Uniform {
Arg ptr;
int offset;
};
I32 uniform8 (Uniform u) { return this->uniform8 (u.ptr, u.offset); }
I32 uniform16(Uniform u) { return this->uniform16(u.ptr, u.offset); }
I32 uniform32(Uniform u) { return this->uniform32(u.ptr, u.offset); }
// Load an immediate constant.
I32 splat(int n);
I32 splat(unsigned u) { return this->splat((int)u); }
@ -473,6 +481,27 @@ namespace skvm {
uint32_t fHash{0};
};
// Helper to streamline allocating and working with uniforms.
struct Uniforms {
Arg ptr;
std::vector<int> buf;
explicit Uniforms(int init) : ptr(Arg{0}), buf(init) {}
Builder::Uniform push(const int* vals, int n) {
int offset = sizeof(int)*buf.size();
buf.insert(buf.end(), vals, vals+n);
return {ptr, offset};
}
Builder::Uniform pushF(const float* vals, int n) {
return this->push((const int*)vals, n);
}
Builder::Uniform push (int val) { return this->push (&val, 1); }
Builder::Uniform pushF(float val) { return this->pushF(&val, 1); }
};
using Reg = int;
class Program {

View File

@ -17,14 +17,13 @@
namespace {
// Uniforms set by the Blitter itself,
// rather than by the Shader, which follow this struct in the buffer.
struct Uniforms {
// rather than by the Shader, which follow this struct in the skvm::Uniforms buffer.
struct BlitterUniforms {
int right; // First device x + blit run length n, used to get device x coordiate.
int y; // Device y coordiate.
};
static_assert(SkIsAlign4(sizeof(Uniforms)), "");
static constexpr int kUniformsCount = sizeof(Uniforms) / sizeof(uint32_t);
static SkTDArray<uint32_t> uniforms_init() { return {0,0}; }
static_assert(SkIsAlign4(sizeof(BlitterUniforms)), "");
static constexpr int kBlitterUniformsCount = sizeof(BlitterUniforms) / 4;
enum class Coverage { Full, UniformA8, MaskA8, MaskLCD16, Mask3D };
@ -143,20 +142,21 @@ namespace {
}
// If Builder can't build this program, CacheKey() sets *ok to false.
static Key CacheKey(const Params& params, SkTDArray<uint32_t>* buf, bool* ok) {
static Key CacheKey(const Params& params, skvm::Uniforms* uniforms, bool* ok) {
SkASSERT(params.shader);
uint32_t shaderHash = 0;
{
const SkShaderBase* shader = as_SB(params.shader);
skvm::Builder p;
skvm::Arg uniforms = skvm::Arg{0};
skvm::F32 x = p.to_f32(p.sub(p.uniform32(uniforms, offsetof(Uniforms, right)),
skvm::F32 x = p.to_f32(p.sub(p.uniform32(uniforms->ptr,
offsetof(BlitterUniforms, right)),
p.index())),
y = p.to_f32(p.uniform32(uniforms, offsetof(Uniforms, y)));
y = p.to_f32(p.uniform32(uniforms->ptr,
offsetof(BlitterUniforms, y)));
skvm::I32 r,g,b,a;
if (shader->program(&p,
params.colorSpace.get(),
uniforms, buf,
uniforms,
x,y, &r,&g,&b,&a)) {
shaderHash = p.hash();
} else {
@ -189,29 +189,29 @@ namespace {
};
}
Builder(const Params& params, SkTDArray<uint32_t>* buf) {
Builder(const Params& params, skvm::Uniforms* uniforms) {
#define TODO SkUNREACHABLE
SkDEBUGCODE(
bool ok = true;
SkTDArray<uint32_t> dummy = uniforms_init();
(void)CacheKey(params, &dummy, &ok);
SkASSERT(ok);
)
skvm::Arg uniforms = uniform(),
dst_ptr = arg(SkColorTypeBytesPerPixel(params.colorType));
// If coverage is Mask3D there'll next come two varyings for mul and add planes,
// and then finally if coverage is any Mask?? format, a varying for the mask.
// First two arguments are always uniforms and the destination buffer.
uniforms->ptr = uniform();
skvm::Arg dst_ptr = arg(SkColorTypeBytesPerPixel(params.colorType));
// Other arguments depend on params.coverage:
// - Full: (no more arguments)
// - Mask3D: mul varying, add varying, 8-bit coverage varying
// - MaskA8: 8-bit coverage varying
// - MaskLCD16: 565 coverage varying
// - UniformA8: 8-bit coverage uniform
Color src;
SkASSERT(params.shader);
skvm::F32 x = to_f32(sub(uniform32(uniforms, offsetof(Uniforms, right)),
skvm::F32 x = to_f32(sub(uniform32(uniforms->ptr,
offsetof(BlitterUniforms, right)),
index())),
y = to_f32(uniform32(uniforms, offsetof(Uniforms, y)));
y = to_f32(uniform32(uniforms->ptr,
offsetof(BlitterUniforms, y)));
SkAssertResult(as_SB(params.shader)->program(this,
params.colorSpace.get(),
uniforms, buf,
uniforms,
x,y, &src.r, &src.g, &src.b, &src.a));
SkASSERT(buf->size() == dummy.size());
if (params.coverage == Coverage::Mask3D) {
skvm::I32 M = load8(varying<uint8_t>()),
@ -339,14 +339,13 @@ namespace {
bool onProgram(skvm::Builder* p,
SkColorSpace* dstCS,
skvm::Arg uniforms, SkTDArray<uint32_t>* buf,
skvm::Uniforms* uniforms,
skvm::F32 x, skvm::F32 y,
skvm::I32* r, skvm::I32* g, skvm::I32* b, skvm::I32* a) const override {
if (as_SB(fShader)->program(p, dstCS,
uniforms, buf,
uniforms,
x,y, r,g,b,a)) {
skvm::I32 A = p->uniform32(uniforms, buf->bytes());
buf->push_back(fAlpha);
skvm::I32 A = p->uniform32(uniforms->push(fAlpha));
*r = p->scale_unorm8(*r, A);
*g = p->scale_unorm8(*g, A);
*b = p->scale_unorm8(*b, A);
@ -406,7 +405,7 @@ namespace {
public:
Blitter(const SkPixmap& device, const SkPaint& paint, bool* ok)
: fDevice(device)
, fUniforms(uniforms_init())
, fUniforms(kBlitterUniformsCount)
, fParams(effective_params(device, paint))
, fKey(Builder::CacheKey(fParams, &fUniforms, ok))
{}
@ -434,15 +433,15 @@ namespace {
}
private:
SkPixmap fDevice; // TODO: can this be const&?
SkTDArray<uint32_t> fUniforms;
const Params fParams;
const Key fKey;
skvm::Program fBlitH,
fBlitAntiH,
fBlitMaskA8,
fBlitMask3D,
fBlitMaskLCD16;
SkPixmap fDevice; // TODO: can this be const&?
skvm::Uniforms fUniforms;
const Params fParams;
const Key fKey;
skvm::Program fBlitH,
fBlitAntiH,
fBlitMaskA8,
fBlitMask3D,
fBlitMaskLCD16;
skvm::Program buildProgram(Coverage coverage) {
Key key = fKey.withCoverage(coverage);
@ -468,10 +467,10 @@ namespace {
// It's just more natural to have effects unconditionally emit them,
// and more natural to rebuild fUniforms than to emit them into a dummy buffer.
// fUniforms should reuse the exact same memory, so this is very cheap.
SkDEBUGCODE(size_t prev = fUniforms.size();)
fUniforms.setCount(kUniformsCount);
SkDEBUGCODE(size_t prev = fUniforms.buf.size();)
fUniforms.buf.resize(kBlitterUniformsCount);
Builder builder{fParams.withCoverage(coverage), &fUniforms};
SkASSERT(fUniforms.size() == prev);
SkASSERT(fUniforms.buf.size() == prev);
skvm::Program program = builder.done(debug_name(key).c_str());
if (!program.hasJIT() && debug_dump(key)) {
@ -483,8 +482,8 @@ namespace {
}
void updateUniforms(int right, int y) {
Uniforms uniforms{right, y};
memcpy(fUniforms.begin(), &uniforms, sizeof(Uniforms));
BlitterUniforms uniforms{right, y};
memcpy(fUniforms.buf.data(), &uniforms, sizeof(BlitterUniforms));
}
void blitH(int x, int y, int w) override {
@ -492,7 +491,7 @@ namespace {
fBlitH = this->buildProgram(Coverage::Full);
}
this->updateUniforms(x+w, y);
fBlitH.eval(w, fUniforms.begin(), fDevice.addr(x,y));
fBlitH.eval(w, fUniforms.buf.data(), fDevice.addr(x,y));
}
void blitAntiH(int x, int y, const SkAlpha cov[], const int16_t runs[]) override {
@ -501,7 +500,7 @@ namespace {
}
for (int16_t run = *runs; run > 0; run = *runs) {
this->updateUniforms(x+run, y);
fBlitAntiH.eval(run, fUniforms.begin(), fDevice.addr(x,y), cov);
fBlitAntiH.eval(run, fUniforms.buf.data(), fDevice.addr(x,y), cov);
x += run;
runs += run;
@ -552,11 +551,11 @@ namespace {
if (program == &fBlitMask3D) {
size_t plane = mask.computeImageSize();
program->eval(w, fUniforms.begin(), dptr, mptr + 1*plane
, mptr + 2*plane
, mptr + 0*plane);
program->eval(w, fUniforms.buf.data(), dptr, mptr + 1*plane
, mptr + 2*plane
, mptr + 0*plane);
} else {
program->eval(w, fUniforms.begin(), dptr, mptr);
program->eval(w, fUniforms.buf.data(), dptr, mptr);
}
}
}

View File

@ -62,18 +62,18 @@ bool SkColorFilterShader::onAppendStages(const SkStageRec& rec) const {
bool SkColorFilterShader::onProgram(skvm::Builder* p,
SkColorSpace* dstCS,
skvm::Arg uniforms, SkTDArray<uint32_t>* buf,
skvm::Uniforms* uniforms,
skvm::F32 x, skvm::F32 y,
skvm::I32* r, skvm::I32* g, skvm::I32* b, skvm::I32* a) const {
// Run the shader.
if (!as_SB(fShader)->program(p, dstCS, uniforms,buf, x,y, r,g,b,a)) {
if (!as_SB(fShader)->program(p, dstCS, uniforms, x,y, r,g,b,a)) {
return false;
}
// Scale that by alpha.
if (fAlpha != 1.0f) {
skvm::I32 A = p->uniform32(uniforms, buf->bytes());
buf->push_back(fAlpha*255 + 0.5f);
int alpha = fAlpha*255 + 0.5f;
skvm::I32 A = p->uniform32(uniforms->push(alpha));
*r = p->scale_unorm8(*r, A);
*g = p->scale_unorm8(*g, A);
*b = p->scale_unorm8(*b, A);
@ -81,7 +81,7 @@ bool SkColorFilterShader::onProgram(skvm::Builder* p,
}
// Finally run that through the color filter.
if (!fFilter->program(p, dstCS, uniforms,buf, r,g,b,a)) {
if (!fFilter->program(p, dstCS, uniforms, r,g,b,a)) {
return false;
}

View File

@ -28,7 +28,7 @@ private:
bool onProgram(skvm::Builder*,
SkColorSpace* dstCS,
skvm::Arg uniforms, SkTDArray<uint32_t>*,
skvm::Uniforms* uniforms,
skvm::F32 x, skvm::F32 y,
skvm::I32* r, skvm::I32* g, skvm::I32* b, skvm::I32* a) const override;

View File

@ -93,15 +93,13 @@ bool SkColor4Shader::onAppendStages(const SkStageRec& rec) const {
static bool common_program(SkColor4f color, SkColorSpace* cs,
skvm::Builder* p,
SkColorSpace* dstCS,
skvm::Arg uniforms, SkTDArray<uint32_t>* buf,
skvm::Uniforms* uniforms,
skvm::I32* r, skvm::I32* g, skvm::I32* b, skvm::I32* a) {
SkColorSpaceXformSteps( cs, kUnpremul_SkAlphaType,
dstCS, kUnpremul_SkAlphaType).apply(color.vec());
if (color.fitsInBytes()) {
skvm::I32 rgba = p->uniform32(uniforms, buf->bytes());
buf->push_back(color.premul().toBytes_RGBA());
skvm::I32 rgba = p->uniform32(uniforms->push(color.premul().toBytes_RGBA()));
*r = p->extract(rgba, 0, p->splat(0xff));
*g = p->extract(rgba, 8, p->splat(0xff));
*b = p->extract(rgba, 16, p->splat(0xff));
@ -113,19 +111,19 @@ static bool common_program(SkColor4f color, SkColorSpace* cs,
bool SkColorShader::onProgram(skvm::Builder* p,
SkColorSpace* dstCS,
skvm::Arg uniforms, SkTDArray<uint32_t>* buf,
skvm::Uniforms* uniforms,
skvm::F32 /*x*/, skvm::F32 /*y*/,
skvm::I32* r, skvm::I32* g, skvm::I32* b, skvm::I32* a) const {
return common_program(SkColor4f::FromColor(fColor), sk_srgb_singleton(),
p, dstCS, uniforms,buf, r,g,b,a);
p, dstCS, uniforms, r,g,b,a);
}
bool SkColor4Shader::onProgram(skvm::Builder* p,
SkColorSpace* dstCS,
skvm::Arg uniforms, SkTDArray<uint32_t>* buf,
skvm::Uniforms* uniforms,
skvm::F32 /*x*/, skvm::F32 /*y*/,
skvm::I32* r, skvm::I32* g, skvm::I32* b, skvm::I32* a) const {
return common_program(fColor, fColorSpace.get(),
p, dstCS, uniforms,buf, r,g,b,a);
p, dstCS, uniforms, r,g,b,a);
}
#if SK_SUPPORT_GPU

View File

@ -46,7 +46,7 @@ private:
bool onProgram(skvm::Builder*,
SkColorSpace* dstCS,
skvm::Arg uniforms, SkTDArray<uint32_t>* buf,
skvm::Uniforms* uniforms,
skvm::F32 x, skvm::F32 y,
skvm::I32* r, skvm::I32* g, skvm::I32* b, skvm::I32* a) const override;
@ -72,7 +72,7 @@ private:
bool onProgram(skvm::Builder*,
SkColorSpace* dstCS,
skvm::Arg uniforms, SkTDArray<uint32_t>* buf,
skvm::Uniforms* uniforms,
skvm::F32 x, skvm::F32 y,
skvm::I32* r, skvm::I32* g, skvm::I32* b, skvm::I32* a) const override;

View File

@ -194,7 +194,7 @@ bool SkShaderBase::onAppendStages(const SkStageRec& rec) const {
bool SkShaderBase::program(skvm::Builder* p,
SkColorSpace* dstCS,
skvm::Arg uniforms, SkTDArray<uint32_t>* buf,
skvm::Uniforms* uniforms,
skvm::F32 x, skvm::F32 y,
skvm::I32* r, skvm::I32* g, skvm::I32* b, skvm::I32* a) const {
// Force opaque alpha for all opaque shaders.
@ -209,7 +209,7 @@ bool SkShaderBase::program(skvm::Builder* p,
// shader program hash and blitter Key. This makes it safe for us to use
// that bit to make decisions when constructing an SkVMBlitter, like doing
// SrcOver -> Src strength reduction.
if (this->onProgram(p, dstCS, uniforms,buf, x,y, r,g,b,a)) {
if (this->onProgram(p, dstCS, uniforms, x,y, r,g,b,a)) {
if (this->isOpaque()) {
*a = p->splat(0xff);
}

View File

@ -12,7 +12,6 @@
#include "include/core/SkMatrix.h"
#include "include/core/SkShader.h"
#include "include/private/SkNoncopyable.h"
#include "include/private/SkTDArray.h"
#include "src/core/SkEffectPriv.h"
#include "src/core/SkMask.h"
#include "src/core/SkTLazy.h"
@ -210,13 +209,13 @@ public:
bool program(skvm::Builder*,
SkColorSpace* dstCS,
skvm::Arg uniforms, SkTDArray<uint32_t>*,
skvm::Uniforms* uniforms,
skvm::F32 x, skvm::F32 y,
skvm::I32* r, skvm::I32* g, skvm::I32* b, skvm::I32* a) const;
virtual bool onProgram(skvm::Builder*,
SkColorSpace* dstCS,
skvm::Arg uniforms, SkTDArray<uint32_t>*,
skvm::Uniforms* uniforms,
skvm::F32 x, skvm::F32 y,
skvm::I32* r, skvm::I32* g, skvm::I32* b, skvm::I32* a) const {
return false;