Support integer uniforms in CanvasKit runtime effects

These were added to SkRuntimeEffects, but we need to do a bit of fixup
in the CK bindings. Note that 'getUniformFloatCount' is now poorly
named, but the idea still stands: it's how many total scalar values are
required.

Bug: skia:11803
Change-Id: If464156d8e6240736e324ef833e57ba7d53f55a0
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/394476
Reviewed-by: Kevin Lubick <kjlubick@google.com>
Reviewed-by: Joe Gregorio <jcgregorio@google.com>
Commit-Queue: Brian Osman <brianosman@google.com>
This commit is contained in:
Brian Osman 2021-04-08 14:51:29 -04:00 committed by Skia Commit-Bot
parent 48d13b2e2c
commit 9be7683222
3 changed files with 57 additions and 17 deletions

View File

@ -9,6 +9,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Added
- Add `drawPatch` to `Canvas`.
- Add `Strut` as a `RectHeightStyle` enum.
- `CanvasKit.RuntimeEffect` now supports integer uniforms in the SkSL. These are still passed
to `RuntimeEffect.makeShader` as floats (like all other uniforms), and will be converted to
integers internally, to match the expectations of the shader.
## [0.25.1] - 2021-03-30

View File

@ -631,24 +631,51 @@ struct RuntimeEffectUniform {
int columns;
int rows;
int slot; // the index into the uniforms array that this uniform begins.
bool isInteger;
};
RuntimeEffectUniform fromUniform(const SkRuntimeEffect::Uniform& u) {
RuntimeEffectUniform su;
su.rows = u.count; // arrayLength
su.columns = 1;
su.rows = u.count; // arrayLength
su.columns = 1;
su.isInteger = false;
using Type = SkRuntimeEffect::Uniform::Type;
switch (u.type) {
case SkRuntimeEffect::Uniform::Type::kFloat: break;
case SkRuntimeEffect::Uniform::Type::kFloat2: su.columns = 2; break;
case SkRuntimeEffect::Uniform::Type::kFloat3: su.columns = 3; break;
case SkRuntimeEffect::Uniform::Type::kFloat4: su.columns = 4; break;
case SkRuntimeEffect::Uniform::Type::kFloat2x2: su.columns = 2; su.rows *= 2; break;
case SkRuntimeEffect::Uniform::Type::kFloat3x3: su.columns = 3; su.rows *= 3; break;
case SkRuntimeEffect::Uniform::Type::kFloat4x4: su.columns = 4; su.rows *= 4; break;
case Type::kFloat: break;
case Type::kFloat2: su.columns = 2; break;
case Type::kFloat3: su.columns = 3; break;
case Type::kFloat4: su.columns = 4; break;
case Type::kFloat2x2: su.columns = 2; su.rows *= 2; break;
case Type::kFloat3x3: su.columns = 3; su.rows *= 3; break;
case Type::kFloat4x4: su.columns = 4; su.rows *= 4; break;
case Type::kInt: su.isInteger = true; break;
case Type::kInt2: su.columns = 2; su.isInteger = true; break;
case Type::kInt3: su.columns = 3; su.isInteger = true; break;
case Type::kInt4: su.columns = 4; su.isInteger = true; break;
}
su.slot = u.offset / sizeof(float);
return su;
}
void castUniforms(void* data, size_t dataLen, const SkRuntimeEffect& effect) {
if (dataLen != effect.uniformSize()) {
// Incorrect number of uniforms. Our code below could read/write off the end of the buffer.
// However, shader creation is going to fail anyway, so just do nothing.
return;
}
float* fltData = reinterpret_cast<float*>(data);
for (const auto& u : effect.uniforms()) {
RuntimeEffectUniform reu = fromUniform(u);
if (reu.isInteger) {
// The SkSL is expecting integers in the uniform data
for (int i = 0; i < reu.columns * reu.rows; ++i) {
int numAsInt = static_cast<int>(fltData[reu.slot + i]);
fltData[reu.slot + i] = SkBits2Float(numAsInt);
}
}
}
}
#endif
// These objects have private destructors / delete methods - I don't think
@ -1663,6 +1690,7 @@ EMSCRIPTEN_BINDINGS(Skia) {
.function("_makeShader", optional_override([](SkRuntimeEffect& self, uintptr_t fPtr, size_t fLen, bool isOpaque,
uintptr_t /* SkScalar* */ mPtr)->sk_sp<SkShader> {
void* inputData = reinterpret_cast<void*>(fPtr);
castUniforms(inputData, fLen, self);
sk_sp<SkData> inputs = SkData::MakeFromMalloc(inputData, fLen);
OptionalMatrix localMatrix(mPtr);
@ -1672,6 +1700,7 @@ EMSCRIPTEN_BINDINGS(Skia) {
uintptr_t /** SkShader*[] */cPtrs, size_t cLen,
uintptr_t /* SkScalar* */ mPtr)->sk_sp<SkShader> {
void* inputData = reinterpret_cast<void*>(fPtr);
castUniforms(inputData, fLen, self);
sk_sp<SkData> inputs = SkData::MakeFromMalloc(inputData, fLen);
sk_sp<SkShader>* children = new sk_sp<SkShader>[cLen];
@ -1703,9 +1732,10 @@ EMSCRIPTEN_BINDINGS(Skia) {
}));
value_object<RuntimeEffectUniform>("RuntimeEffectUniform")
.field("columns", &RuntimeEffectUniform::columns)
.field("rows", &RuntimeEffectUniform::rows)
.field("slot", &RuntimeEffectUniform::slot);
.field("columns", &RuntimeEffectUniform::columns)
.field("rows", &RuntimeEffectUniform::rows)
.field("slot", &RuntimeEffectUniform::slot)
.field("isInteger", &RuntimeEffectUniform::isInteger);
constant("rt_effect", true);
#endif

View File

@ -18,12 +18,12 @@ describe('Runtime shader effects', () => {
// it will draw blank.
const spiralSkSL = `
uniform float rad_scale;
uniform float2 in_center;
uniform int2 in_center;
uniform float4 in_colors0;
uniform float4 in_colors1;
half4 main(float2 p) {
float2 pp = p - in_center;
float2 pp = p - float2(in_center);
float radius = sqrt(dot(pp, pp));
radius = sqrt(radius);
float angle = atan(pp.y / pp.x);
@ -45,11 +45,18 @@ half4 main(float2 p) {
expect(spiral.getUniformCount() ).toEqual(4);
expect(spiral.getUniformFloatCount()).toEqual(11);
const center = spiral.getUniform(1);
expect(center).toBeTruthy('could not fetch numbered uniform');
expect(center.slot ).toEqual(1);
expect(center.columns ).toEqual(2);
expect(center.rows ).toEqual(1);
expect(center.isInteger).toEqual(true);
const color_0 = spiral.getUniform(2);
expect(color_0).toBeTruthy('could not fetch numbered uniform');
expect(color_0.slot ).toEqual(3);
expect(color_0.columns).toEqual(4);
expect(color_0.rows ).toEqual(1);
expect(color_0.slot ).toEqual(3);
expect(color_0.columns ).toEqual(4);
expect(color_0.rows ).toEqual(1);
expect(color_0.isInteger).toEqual(false);
expect(spiral.getUniformName(2)).toEqual('in_colors0');
const canvas = surface.getCanvas();