diff --git a/gm/mesh.cpp b/gm/mesh.cpp index 3061a6bb3f..e23c1f34e4 100644 --- a/gm/mesh.cpp +++ b/gm/mesh.cpp @@ -16,6 +16,7 @@ #include "include/gpu/GrDirectContext.h" #include "src/core/SkCanvasPriv.h" #include "src/core/SkMeshPriv.h" +#include "tools/timer/TimeUtils.h" #include @@ -147,8 +148,9 @@ protected: mesh = SkMesh::Make(fSpecWithColor, SkMesh::Mode::kTriangleStrip, fColorVB, - /*vertexCount= */ 4, - /*vertexOffset=*/ 0, + /*vertexCount= */4, + /*vertexOffset=*/0, + /*uniforms= */nullptr, kRect); } else { mesh = SkMesh::Make(fSpecWithNoColor, @@ -156,6 +158,7 @@ protected: fNoColorVB, /*vertexCount=*/4, kNoColorOffset, + /*uniforms=*/nullptr, kRect); } } else { @@ -165,21 +168,23 @@ protected: mesh = SkMesh::MakeIndexed(fSpecWithColor, SkMesh::Mode::kTriangles, fColorIndexedVB, - /*vertexCount=*/ 6, + /*vertexCount=*/6, kColorIndexedOffset, std::move(ib), /*indexCount=*/6, kIndexOffset, + /*uniforms=*/nullptr, kRect); } else { mesh = SkMesh::MakeIndexed(fSpecWithNoColor, SkMesh::Mode::kTriangles, fNoColorIndexedVB, - /*vertexCount=*/ 6, + /*vertexCount=*/6, /*vertexOffset=*/0, std::move(ib), /*indexCount=*/6, kIndexOffset, + /*uniforms=*/nullptr, kRect); } } @@ -418,8 +423,9 @@ protected: SkMesh mesh = SkMesh::Make(fSpecs[SpecIndex(unpremul, spin)], SkMesh::Mode::kTriangleStrip, fVB, - /*vertexCount=*/ 4, + /*vertexCount= */4, /*vertexOffset=*/0, + /*uniforms= */nullptr, kRect); SkPaint paint; @@ -467,4 +473,161 @@ private: DEF_GM(return new MeshColorSpaceGM;) +class MeshUniformsGM : public skiagm::GM { +public: + MeshUniformsGM() { this->onAnimate(0); } + +protected: + using Attribute = SkMeshSpecification::Attribute; + using Varying = SkMeshSpecification::Varying; + + SkISize onISize() override { return {140, 250}; } + + void onOnceBeforeDraw() override { + static const Attribute kAttributes[]{ + {Attribute::Type::kFloat2, 0, SkString{"pos"} }, + {Attribute::Type::kFloat2, 8, SkString{"coords"}}, + }; + static const Varying kVaryings[]{ + {Varying::Type::kFloat2, SkString{"coords"}}, + }; + // To exercise shared VS/FS uniforms we have a matrix that is applied twice, once in each + // stage. + static constexpr char kVS[] = R"( + uniform float t[2]; + uniform half3x3 m; + float2 main(in Attributes attributes, out Varyings varyings) { + varyings.coords = (m*float3(attributes.coords + float2(t[0], t[1]), 1)).xy; + return attributes.pos; + } + )"; + static constexpr char kFS[] = R"( + uniform half3x3 m; + layout(color) uniform half4 color; + float2 main(Varyings varyings, out half4 c) { + c = color; + return (m*float3(varyings.coords, 1)).xy; + } + )"; + auto [spec, error] = + SkMeshSpecification::Make(SkMakeSpan(kAttributes, SK_ARRAY_COUNT(kAttributes)), + sizeof(Vertex), + SkMakeSpan(kVaryings, SK_ARRAY_COUNT(kVaryings)), + SkString(kVS), + SkString(kFS), + SkColorSpace::MakeSRGB(), + kPremul_SkAlphaType); + if (!spec) { + SkDebugf("%s\n", error.c_str()); + } + fSpec = std::move(spec); + + SkColor colors[] = {SK_ColorWHITE, SK_ColorBLACK}; + fShader = SkGradientShader::MakeRadial(kGradCenter, + .4f, + colors, + nullptr, + 2, + SkTileMode::kMirror); + + fVB = SkMesh::MakeVertexBuffer(nullptr, SkData::MakeWithoutCopy(kQuad, sizeof(kQuad))); + } + + SkString onShortName() override { return SkString("custommesh_uniforms"); } + + DrawResult onDraw(SkCanvas* canvas, SkString* error) override { + SkMatrix matrices[] { + SkMatrix::MakeAll(-1, 0, 0, // self inverse so no effect. + 0, -1, 0, + 0, 0, 1), + SkMatrix::RotateDeg(fDegrees/2.f, {0.5f, 0.5f}), + }; + for (const auto& m : matrices) { + auto unis = SkData::MakeUninitialized(fSpec->uniformSize()); + + SkPoint trans = -kCoordTrans; + static_assert(sizeof(SkPoint) == 2*sizeof(float)); + + const SkMeshSpecification::Uniform* u = fSpec->findUniform("t"); + SkASSERT(u); + std::memcpy(SkTAddOffset(unis->writable_data(), u->offset), + (void*)&trans, + 2*sizeof(float)); + + u = fSpec->findUniform("m"); + SkASSERT(u); + for (size_t offset = u->offset, col = 0; col < 3; ++col) { + for (size_t row = 0; row < 3; ++row, offset += sizeof(float)) { + *SkTAddOffset(unis->writable_data(), offset) = m.rc(row, col); + } + } + + u = fSpec->findUniform("color"); + SkASSERT(u); + std::memcpy(SkTAddOffset(unis->writable_data(), u->offset), + fColor.vec(), + 4*sizeof(float)); + + SkMesh mesh = SkMesh::Make(fSpec, + SkMesh::Mode::kTriangleStrip, + fVB, + /*vertexCount= */4, + /*vertexOffset=*/0, + /*uniforms= */std::move(unis), + kRect); + + SkPaint paint; + paint.setShader(fShader); + SkCanvasPriv::DrawMesh(canvas, mesh, SkBlender::Mode(SkBlendMode::kModulate), paint); + + canvas->translate(0, kRect.height() + 10); + } + return DrawResult::kOk; + } + + bool onAnimate(double nanos) override { + fDegrees = TimeUtils::NanosToSeconds(nanos) * 360.f/10.f + 45.f; + // prime number periods, like locusts. + fColor.fR = TimeUtils::SineWave(nanos, 13.f, 0.f, 0.f, 1.f); + fColor.fG = TimeUtils::SineWave(nanos, 23.f, 5.f, 0.f, 1.f); + fColor.fB = TimeUtils::SineWave(nanos, 11.f, 0.f, 0.f, 1.f); + fColor.fA = 1.f; + return true; + } + +private: + struct Vertex { + SkPoint pos; + SkPoint tex; + }; + + static constexpr auto kRect = SkRect::MakeLTRB(20, 20, 120, 120); + + // Our logical tex coords are [0..1] but we insert an arbitrary translation that gets undone + // with a uniform. + static constexpr SkPoint kCoordTrans = {75, -37}; + static constexpr auto kCoordRect = SkRect::MakeXYWH(kCoordTrans.x(), kCoordTrans.y(), 1, 1); + + static constexpr SkPoint kGradCenter = {0.3f, 0.2f}; + + static constexpr Vertex kQuad[] { + {{kRect.left() , kRect.top() }, {kCoordRect.left() , kCoordRect.top()} }, + {{kRect.right(), kRect.top() }, {kCoordRect.right(), kCoordRect.top()} }, + {{kRect.left() , kRect.bottom()}, {kCoordRect.left() , kCoordRect.bottom()}}, + {{kRect.right(), kRect.bottom()}, {kCoordRect.right(), kCoordRect.bottom()}}, + }; + + float fDegrees; + + SkColor4f fColor; + + sk_sp fVB; + + sk_sp fSpec; + + sk_sp fShader; +}; + +DEF_GM(return new MeshUniformsGM()) + } // namespace skiagm diff --git a/include/core/SkMesh.h b/include/core/SkMesh.h index 98e2038957..24d9efcc3e 100644 --- a/include/core/SkMesh.h +++ b/include/core/SkMesh.h @@ -143,8 +143,17 @@ public: SkSpan attributes() const { return SkMakeSpan(fAttributes); } + /** + * Combined size of all 'uniform' variables. When creating a SkMesh with this specification + * provide an SkData of this size, containing values for all of those variables. Use uniforms() + * to get the offset of each uniform within the SkData. + */ size_t uniformSize() const; + /** + * Provides info about individual uniforms including the offset into an SkData where each + * uniform value should be placed. + */ SkSpan uniforms() const { return SkMakeSpan(fUniforms); } /** Returns pointer to the named uniform variable's description, or nullptr if not found. */ @@ -215,6 +224,10 @@ private: * * If Make() is used the implicit index sequence is 0, 1, 2, 3, ... and vertexCount must be at least * 3. + * + * Both Make() and MakeIndexed() take a SkData with the uniform values. See + * SkMeshSpecification::uniformSize() and SkMeshSpecification::uniforms() for sizing and packing + * uniforms into the SkData. */ class SkMesh { public: @@ -263,6 +276,7 @@ public: sk_sp, size_t vertexCount, size_t vertexOffset, + sk_sp uniforms, const SkRect& bounds); static SkMesh MakeIndexed(sk_sp, @@ -273,6 +287,7 @@ public: sk_sp, size_t indexCount, size_t indexOffset, + sk_sp uniforms, const SkRect& bounds); sk_sp spec() const { return fSpec; } @@ -289,6 +304,8 @@ public: size_t indexOffset() const { return fIOffset; } size_t indexCount() const { return fICount; } + sk_sp uniforms() const { return fUniforms; } + SkRect bounds() const { return fBounds; } bool isValid() const; @@ -303,6 +320,8 @@ private: sk_sp fVB; sk_sp fIB; + sk_sp fUniforms; + size_t fVOffset = 0; // Must be a multiple of spec->stride() size_t fVCount = 0; diff --git a/include/core/SkRect.h b/include/core/SkRect.h index 99efe70bc7..8505216fd3 100644 --- a/include/core/SkRect.h +++ b/include/core/SkRect.h @@ -783,7 +783,7 @@ struct SK_API SkRect { @return midpoint on x-axis */ - SkScalar centerX() const { + constexpr SkScalar centerX() const { // don't use SkScalarHalf(fLeft + fBottom) as that might overflow before the 0.5 return SkScalarHalf(fLeft) + SkScalarHalf(fRight); } @@ -793,11 +793,16 @@ struct SK_API SkRect { @return midpoint on y-axis */ - SkScalar centerY() const { + constexpr SkScalar centerY() const { // don't use SkScalarHalf(fTop + fBottom) as that might overflow before the 0.5 return SkScalarHalf(fTop) + SkScalarHalf(fBottom); } + /** Returns the point this->centerX(), this->centerY(). + @return rectangle center + */ + constexpr SkPoint center() const { return {this->centerX(), this->centerY()}; } + /** Returns true if all members in a: fLeft, fTop, fRight, and fBottom; are equal to the corresponding members in b. diff --git a/src/core/SkMesh.cpp b/src/core/SkMesh.cpp index 4d40c88b20..939795ec54 100644 --- a/src/core/SkMesh.cpp +++ b/src/core/SkMesh.cpp @@ -501,14 +501,16 @@ SkMesh SkMesh::Make(sk_sp spec, sk_sp vb, size_t vertexCount, size_t vertexOffset, + sk_sp uniforms, const SkRect& bounds) { SkMesh cm; - cm.fSpec = std::move(spec); - cm.fMode = mode; - cm.fVB = std::move(vb); - cm.fVCount = vertexCount; - cm.fVOffset = vertexOffset; - cm.fBounds = bounds; + cm.fSpec = std::move(spec); + cm.fMode = mode; + cm.fVB = std::move(vb); + cm.fUniforms = std::move(uniforms); + cm.fVCount = vertexCount; + cm.fVOffset = vertexOffset; + cm.fBounds = bounds; return cm.validate() ? cm : SkMesh{}; } @@ -520,17 +522,19 @@ SkMesh SkMesh::MakeIndexed(sk_sp spec, sk_sp ib, size_t indexCount, size_t indexOffset, + sk_sp uniforms, const SkRect& bounds) { SkMesh cm; - cm.fSpec = std::move(spec); - cm.fMode = mode; - cm.fVB = std::move(vb); - cm.fVCount = vertexCount; - cm.fVOffset = vertexOffset; - cm.fIB = std::move(ib); - cm.fICount = indexCount; - cm.fIOffset = indexOffset; - cm.fBounds = bounds; + cm.fSpec = std::move(spec); + cm.fMode = mode; + cm.fVB = std::move(vb); + cm.fVCount = vertexCount; + cm.fVOffset = vertexOffset; + cm.fIB = std::move(ib); + cm.fUniforms = std::move(uniforms); + cm.fICount = indexCount; + cm.fIOffset = indexOffset; + cm.fBounds = bounds; return cm.validate() ? cm : SkMesh{}; } @@ -574,6 +578,12 @@ bool SkMesh::validate() const { return false; } + if (size_t uniformSize = fSpec->uniformSize()) { + if (!fUniforms || fUniforms->size() < uniformSize) { + return false; + } + } + if (ib) { if (fICount < min_vcount_for_mode(fMode)) { return false; diff --git a/src/core/SkRuntimeEffect.cpp b/src/core/SkRuntimeEffect.cpp index fbb2f04645..d85b4121ad 100644 --- a/src/core/SkRuntimeEffect.cpp +++ b/src/core/SkRuntimeEffect.cpp @@ -113,6 +113,59 @@ SkRuntimeEffect::Uniform SkRuntimeEffectPriv::VarAsUniform(const SkSL::Variable& SkASSERT(SkIsAlign4(*offset)); return uni; } +sk_sp SkRuntimeEffectPriv::TransformUniforms( + SkSpan uniforms, + sk_sp originalData, + const SkColorSpace* dstCS) { + SkColorSpaceXformSteps steps(sk_srgb_singleton(), kUnpremul_SkAlphaType, + dstCS, kUnpremul_SkAlphaType); + return TransformUniforms(uniforms, std::move(originalData), steps); +} + +sk_sp SkRuntimeEffectPriv::TransformUniforms( + SkSpan uniforms, + sk_sp originalData, + const SkColorSpaceXformSteps& steps) { + using Flags = SkRuntimeEffect::Uniform::Flags; + using Type = SkRuntimeEffect::Uniform::Type; + + sk_sp data = nullptr; + auto writableData = [&]() { + if (!data) { + data = SkData::MakeWithCopy(originalData->data(), originalData->size()); + } + return data->writable_data(); + }; + + for (const auto& u : uniforms) { + if (u.flags & Flags::kColor_Flag) { + SkASSERT(u.type == Type::kFloat3 || u.type == Type::kFloat4); + if (steps.flags.mask()) { + float* color = SkTAddOffset(writableData(), u.offset); + if (u.type == Type::kFloat4) { + // RGBA, easy case + for (int i = 0; i < u.count; ++i) { + steps.apply(color); + color += 4; + } + } else { + // RGB, need to pad out to include alpha. Technically, this isn't necessary, + // because steps shouldn't include unpremul or premul, and thus shouldn't + // read or write the fourth element. But let's be safe. + float rgba[4]; + for (int i = 0; i < u.count; ++i) { + memcpy(rgba, color, 3 * sizeof(float)); + rgba[3] = 1.0f; + steps.apply(rgba); + memcpy(color, rgba, 3 * sizeof(float)); + color += 3; + } + } + } + } + } + return data ? data : originalData; +} ////////////////////////////////////////////////////////////////////////////// @@ -788,52 +841,6 @@ const SkFilterColorProgram* SkRuntimeEffect::getFilterColorProgram() { /////////////////////////////////////////////////////////////////////////////////////////////////// -static sk_sp get_xformed_uniforms(const SkRuntimeEffect* effect, - sk_sp baseUniforms, - const SkColorSpace* dstCS) { - using Flags = SkRuntimeEffect::Uniform::Flags; - using Type = SkRuntimeEffect::Uniform::Type; - SkColorSpaceXformSteps steps(sk_srgb_singleton(), kUnpremul_SkAlphaType, - dstCS, kUnpremul_SkAlphaType); - - sk_sp uniforms = nullptr; - auto writableData = [&]() { - if (!uniforms) { - uniforms = SkData::MakeWithCopy(baseUniforms->data(), baseUniforms->size()); - } - return uniforms->writable_data(); - }; - - for (const auto& v : effect->uniforms()) { - if (v.flags & Flags::kColor_Flag) { - SkASSERT(v.type == Type::kFloat3 || v.type == Type::kFloat4); - if (steps.flags.mask()) { - float* color = SkTAddOffset(writableData(), v.offset); - if (v.type == Type::kFloat4) { - // RGBA, easy case - for (int i = 0; i < v.count; ++i) { - steps.apply(color); - color += 4; - } - } else { - // RGB, need to pad out to include alpha. Technically, this isn't necessary, - // because steps shouldn't include unpremul or premul, and thus shouldn't - // read or write the fourth element. But let's be safe. - float rgba[4]; - for (int i = 0; i < v.count; ++i) { - memcpy(rgba, color, 3 * sizeof(float)); - rgba[3] = 1.0f; - steps.apply(rgba); - memcpy(color, rgba, 3 * sizeof(float)); - color += 3; - } - } - } - } - } - return uniforms ? uniforms : baseUniforms; -} - #if SK_SUPPORT_GPU static GrFPResult make_effect_fp(sk_sp effect, const char* name, @@ -968,8 +975,10 @@ public: GrFPResult asFragmentProcessor(std::unique_ptr inputFP, GrRecordingContext* context, const GrColorInfo& colorInfo) const override { - sk_sp uniforms = - get_xformed_uniforms(fEffect.get(), fUniforms, colorInfo.colorSpace()); + sk_sp uniforms = SkRuntimeEffectPriv::TransformUniforms( + fEffect->uniforms(), + fUniforms, + colorInfo.colorSpace()); SkASSERT(uniforms); SkOverrideDeviceMatrixProvider matrixProvider(SkMatrix::I()); @@ -991,8 +1000,10 @@ public: skvm::Color onProgram(skvm::Builder* p, skvm::Color c, const SkColorInfo& colorInfo, skvm::Uniforms* uniforms, SkArenaAlloc* alloc) const override { - sk_sp inputs = - get_xformed_uniforms(fEffect.get(), fUniforms, colorInfo.colorSpace()); + sk_sp inputs = SkRuntimeEffectPriv::TransformUniforms( + fEffect->uniforms(), + fUniforms, + colorInfo.colorSpace()); SkASSERT(inputs); RuntimeEffectVMCallbacks callbacks(p, uniforms, alloc, fChildren, c, colorInfo); @@ -1017,7 +1028,10 @@ public: } // Get our specific uniform values - sk_sp inputs = get_xformed_uniforms(fEffect.get(), fUniforms, dstCS); + sk_sp inputs = SkRuntimeEffectPriv::TransformUniforms( + fEffect->uniforms(), + fUniforms, + dstCS); SkASSERT(inputs); auto evalChild = [&](int index, SkPMColor4f inColor) { @@ -1116,8 +1130,10 @@ public: return nullptr; } - sk_sp uniforms = - get_xformed_uniforms(fEffect.get(), fUniforms, args.fDstColorInfo->colorSpace()); + sk_sp uniforms = SkRuntimeEffectPriv::TransformUniforms( + fEffect->uniforms(), + fUniforms, + args.fDstColorInfo->colorSpace()); SkASSERT(uniforms); auto [success, fp] = make_effect_fp(fEffect, @@ -1144,8 +1160,9 @@ public: const SkMatrixProvider& matrices, const SkMatrix* localM, const SkColorInfo& colorInfo, skvm::Uniforms* uniforms, SkArenaAlloc* alloc) const override { - sk_sp inputs = - get_xformed_uniforms(fEffect.get(), fUniforms, colorInfo.colorSpace()); + sk_sp inputs = SkRuntimeEffectPriv::TransformUniforms(fEffect->uniforms(), + fUniforms, + colorInfo.colorSpace()); SkASSERT(inputs); SkMatrix inv; @@ -1252,8 +1269,9 @@ public: skvm::Color onProgram(skvm::Builder* p, skvm::Color src, skvm::Color dst, const SkColorInfo& colorInfo, skvm::Uniforms* uniforms, SkArenaAlloc* alloc) const override { - sk_sp inputs = get_xformed_uniforms(fEffect.get(), fUniforms, - colorInfo.colorSpace()); + sk_sp inputs = SkRuntimeEffectPriv::TransformUniforms(fEffect->uniforms(), + fUniforms, + colorInfo.colorSpace()); SkASSERT(inputs); RuntimeEffectVMCallbacks callbacks(p, uniforms, alloc, fChildren, src, colorInfo); @@ -1272,8 +1290,10 @@ public: std::unique_ptr srcFP, std::unique_ptr dstFP, const GrFPArgs& args) const override { - sk_sp uniforms = get_xformed_uniforms(fEffect.get(), fUniforms, - args.fDstColorInfo->colorSpace()); + sk_sp uniforms = SkRuntimeEffectPriv::TransformUniforms( + fEffect->uniforms(), + fUniforms, + args.fDstColorInfo->colorSpace()); SkASSERT(uniforms); auto [success, fp] = make_effect_fp(fEffect, "runtime_blender", @@ -1380,7 +1400,9 @@ sk_sp SkRuntimeEffect::makeImage(GrRecordingContext* rContext, if (!fillContext) { return nullptr; } - uniforms = get_xformed_uniforms(this, std::move(uniforms), resultInfo.colorSpace()); + uniforms = SkRuntimeEffectPriv::TransformUniforms(this->uniforms(), + std::move(uniforms), + resultInfo.colorSpace()); SkASSERT(uniforms); SkOverrideDeviceMatrixProvider matrixProvider(SkMatrix::I()); diff --git a/src/core/SkRuntimeEffectPriv.h b/src/core/SkRuntimeEffectPriv.h index 5354c5de48..42349c66c5 100644 --- a/src/core/SkRuntimeEffectPriv.h +++ b/src/core/SkRuntimeEffectPriv.h @@ -21,6 +21,8 @@ class Variable; class Context; } +struct SkColorSpaceXformSteps; + class SkRuntimeEffectPriv { public: // Helper function when creating an effect for a GrSkSLFP that verifies an effect will @@ -42,6 +44,15 @@ public: static SkRuntimeEffect::Uniform VarAsUniform(const SkSL::Variable&, const SkSL::Context&, size_t* offset); + + // If there are layout(color) uniforms then this performs color space transformation on the + // color values and returns a new SkData. Otherwise, the original data is returned. + static sk_sp TransformUniforms(SkSpan uniforms, + sk_sp originalData, + const SkColorSpaceXformSteps&); + static sk_sp TransformUniforms(SkSpan uniforms, + sk_sp originalData, + const SkColorSpace* dstCS); }; // These internal APIs for creating runtime effects vary from the public API in two ways: diff --git a/src/gpu/ganesh/ops/DrawMeshOp.cpp b/src/gpu/ganesh/ops/DrawMeshOp.cpp index 6dca687745..2b0888fe0d 100644 --- a/src/gpu/ganesh/ops/DrawMeshOp.cpp +++ b/src/gpu/ganesh/ops/DrawMeshOp.cpp @@ -11,6 +11,7 @@ #include "include/core/SkMesh.h" #include "src/core/SkArenaAlloc.h" #include "src/core/SkMeshPriv.h" +#include "src/core/SkRuntimeEffectPriv.h" #include "src/core/SkVerticesPriv.h" #include "src/gpu/BufferWriter.h" #include "src/gpu/KeyBuilder.h" @@ -24,6 +25,7 @@ #include "src/gpu/ganesh/ops/GrSimpleMeshDrawOpHelper.h" #include "src/sksl/codegen/SkSLPipelineStageCodeGenerator.h" #include "src/sksl/ir/SkSLProgram.h" +#include "src/sksl/ir/SkSLVarDeclarations.h" namespace { @@ -42,13 +44,15 @@ public: sk_sp colorSpaceXform, const SkMatrix& viewMatrix, const std::optional& color, - bool needsLocalCoords) { + bool needsLocalCoords, + sk_sp uniforms) { return arena->make([&](void* ptr) { return new (ptr) MeshGP(std::move(spec), std::move(colorSpaceXform), viewMatrix, std::move(color), - needsLocalCoords); + needsLocalCoords, + std::move(uniforms)); }); } @@ -73,11 +77,43 @@ private: void setData(const GrGLSLProgramDataManager& pdman, const GrShaderCaps& shaderCaps, const GrGeometryProcessor& geomProc) override { - const auto& cmgp = geomProc.cast(); - SetTransform(pdman, shaderCaps, fViewMatrixUniform, cmgp.fViewMatrix, &fViewMatrix); - fColorSpaceHelper.setData(pdman, cmgp.fColorSpaceXform.get()); + const auto& mgp = geomProc.cast(); + SetTransform(pdman, shaderCaps, fViewMatrixUniform, mgp.fViewMatrix, &fViewMatrix); + fColorSpaceHelper.setData(pdman, mgp.fColorSpaceXform.get()); if (fColorUniform.isValid()) { - pdman.set4fv(fColorUniform, 1, cmgp.fColor.vec()); + pdman.set4fv(fColorUniform, 1, mgp.fColor.vec()); + } + if (!mgp.fSpec->uniformSize()) { + return; + } + SkASSERT(mgp.fUniforms); + SkASSERT(mgp.fUniforms->size() >= mgp.fSpec->uniformSize()); + using Type = SkMeshSpecification::Uniform::Type; + const void* data = mgp.fUniforms->data(); + size_t i = 0; + for (const auto& u : mgp.fSpec->uniforms()) { + const UniformHandle& handle = fSpecUniformHandles[i++]; + auto floatData = [=] { return SkTAddOffset(data, u.offset); }; + auto intData = [=] { return SkTAddOffset(data, u.offset); }; + switch (u.type) { + case Type::kFloat: pdman.set1fv(handle, u.count, floatData()); break; + case Type::kFloat2: pdman.set2fv(handle, u.count, floatData()); break; + case Type::kFloat3: pdman.set3fv(handle, u.count, floatData()); break; + case Type::kFloat4: pdman.set4fv(handle, u.count, floatData()); break; + + case Type::kFloat2x2: pdman.setMatrix2fv(handle, u.count, floatData()); break; + case Type::kFloat3x3: pdman.setMatrix3fv(handle, u.count, floatData()); break; + case Type::kFloat4x4: pdman.setMatrix4fv(handle, u.count, floatData()); break; + + case Type::kInt: pdman.set1iv(handle, u.count, intData()); break; + case Type::kInt2: pdman.set2iv(handle, u.count, intData()); break; + case Type::kInt3: pdman.set3iv(handle, u.count, intData()); break; + case Type::kInt4: pdman.set4iv(handle, u.count, intData()); break; + + default: + SkDEBUGFAIL("Unsupported uniform type"); + break; + } } } @@ -85,13 +121,65 @@ private: class MeshCallbacks : public SkSL::PipelineStage::Callbacks { public: MeshCallbacks(Impl* self, + const MeshGP& gp, GrGLSLShaderBuilder* builder, + GrGLSLUniformHandler* uniformHandler, const char* mainName, const SkSL::Context& context) - : fSelf(self), fBuilder(builder), fMainName(mainName), fContext(context) {} + : fSelf(self) + , fGP(gp) + , fBuilder(builder) + , fUniformHandler(uniformHandler) + , fMainName(mainName) + , fContext(context) {} std::string declareUniform(const SkSL::VarDeclaration* decl) override { - SK_ABORT("uniforms not allowed"); + const SkSL::Variable& var = decl->var(); + SkASSERT(!var.type().isOpaque()); + + const SkSL::Type* type = &var.type(); + bool isArray = false; + if (type->isArray()) { + type = &type->componentType(); + isArray = true; + } + + SkSLType gpuType; + SkAssertResult(SkSL::type_to_sksltype(fContext, *type, &gpuType)); + + SkString name(var.name()); + const SkSpan uniforms = fGP.fSpec->uniforms(); + auto it = std::find_if(uniforms.begin(), + uniforms.end(), + [&name](SkMeshSpecification::Uniform uniform) { + return uniform.name == name; + }); + SkASSERT(it != uniforms.end()); + + UniformHandle* handle = &fSelf->fSpecUniformHandles[it - uniforms.begin()]; + if (handle->isValid()) { + const GrShaderVar& uniformVar = fUniformHandler->getUniformVariable(*handle); + return std::string(uniformVar.getName().c_str()); + } + + const SkMeshSpecification::Uniform& uniform = *it; + GrShaderFlags shaderFlags = kNone_GrShaderFlags; + if (uniform.flags & SkMeshSpecification::Uniform::Flags::kVertex_Flag) { + shaderFlags |= kVertex_GrShaderFlag; + } + if (uniform.flags & SkMeshSpecification::Uniform::Flags::kFragment_Flag) { + shaderFlags |= kFragment_GrShaderFlag; + } + SkASSERT(shaderFlags != kNone_GrShaderFlags); + + const char* mangledName = nullptr; + *handle = fUniformHandler->addUniformArray(&fGP, + shaderFlags, + gpuType, + name.c_str(), + isArray ? var.type().columns() : 0, + &mangledName); + return std::string(mangledName); } std::string getMangledName(const char* name) override { @@ -136,28 +224,38 @@ private: SK_ABORT("Color transform intrinsics not allowed."); } - Impl* fSelf; - GrGLSLShaderBuilder* fBuilder; - const char* fMainName; - const SkSL::Context& fContext; + Impl* fSelf; + const MeshGP& fGP; + GrGLSLShaderBuilder* fBuilder; + GrGLSLUniformHandler* fUniformHandler; + const char* fMainName; + const SkSL::Context& fContext; }; void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override { - const MeshGP& cmgp = args.fGeomProc.cast(); + const MeshGP& mgp = args.fGeomProc.cast(); GrGLSLVertexBuilder* vertBuilder = args.fVertBuilder; GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder; GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler; GrGLSLUniformHandler* uniformHandler = args.fUniformHandler; + SkASSERT(fSpecUniformHandles.empty()); + fSpecUniformHandles.resize(mgp.fSpec->uniforms().size()); + ////// VS // emit attributes - varyingHandler->emitAttributes(cmgp); + varyingHandler->emitAttributes(mgp); // Define the user's vert function. SkString userVertName = vertBuilder->getMangledFunctionName("custom_mesh_vs"); - const SkSL::Program* customVS = SkMeshSpecificationPriv::VS(*cmgp.fSpec); - MeshCallbacks vsCallbacks(this, vertBuilder, userVertName.c_str(), *customVS->fContext); + const SkSL::Program* customVS = SkMeshSpecificationPriv::VS(*mgp.fSpec); + MeshCallbacks vsCallbacks(this, + mgp, + vertBuilder, + uniformHandler, + userVertName.c_str(), + *customVS->fContext); SkSL::PipelineStage::ConvertProgram(*customVS, /*sampleCoords=*/"", /*inputColor=*/"", @@ -168,10 +266,10 @@ private: vertBuilder->codeAppendf("%s attributes;", vsCallbacks.getMangledName("Attributes").c_str()); size_t i = 0; - SkASSERT(cmgp.vertexAttributes().count() == (int)cmgp.fSpec->attributes().size()); - for (auto attr : cmgp.vertexAttributes()) { + SkASSERT(mgp.vertexAttributes().count() == (int)mgp.fSpec->attributes().size()); + for (auto attr : mgp.vertexAttributes()) { vertBuilder->codeAppendf("attributes.%s = %s;", - cmgp.fSpec->attributes()[i++].name.c_str(), + mgp.fSpec->attributes()[i++].name.c_str(), attr.name()); } @@ -183,8 +281,8 @@ private: // Unpack the varyings from the struct into individual varyings. std::vector varyings; - varyings.reserve(SkMeshSpecificationPriv::Varyings(*cmgp.fSpec).size()); - for (const auto& v : SkMeshSpecificationPriv::Varyings(*cmgp.fSpec)) { + varyings.reserve(SkMeshSpecificationPriv::Varyings(*mgp.fSpec).size()); + for (const auto& v : SkMeshSpecificationPriv::Varyings(*mgp.fSpec)) { varyings.emplace_back(SkMeshSpecificationPriv::VaryingTypeAsSLType(v.type)); varyingHandler->addVarying(v.name.c_str(), &varyings.back()); vertBuilder->codeAppendf("%s = varyings.%s;", @@ -198,7 +296,7 @@ private: *args.fShaderCaps, gpArgs, "pos", - cmgp.fViewMatrix, + mgp.fViewMatrix, &fViewMatrixUniform); ////// FS @@ -208,8 +306,13 @@ private: // Define the user's frag function. SkString userFragName = fragBuilder->getMangledFunctionName("custom_mesh_fs"); - const SkSL::Program* customFS = SkMeshSpecificationPriv::FS(*cmgp.fSpec); - MeshCallbacks fsCallbacks(this, fragBuilder, userFragName.c_str(), *customFS->fContext); + const SkSL::Program* customFS = SkMeshSpecificationPriv::FS(*mgp.fSpec); + MeshCallbacks fsCallbacks(this, + mgp, + fragBuilder, + uniformHandler, + userFragName.c_str(), + *customFS->fContext); SkSL::PipelineStage::ConvertProgram(*customFS, /*sampleCoords=*/"", /*inputColor=*/"", @@ -220,15 +323,15 @@ private: fragBuilder->codeAppendf("%s varyings;", fsCallbacks.getMangledName("Varyings").c_str()); i = 0; - for (const auto& varying : SkMeshSpecificationPriv::Varyings(*cmgp.fSpec)) { + for (const auto& varying : SkMeshSpecificationPriv::Varyings(*mgp.fSpec)) { fragBuilder->codeAppendf("varyings.%s = %s;", varying.name.c_str(), varyings[i++].vsOut()); } SkMeshSpecificationPriv::ColorType meshColorType = - SkMeshSpecificationPriv::GetColorType(*cmgp.fSpec); + SkMeshSpecificationPriv::GetColorType(*mgp.fSpec); const char* uniformColorName = nullptr; - if (cmgp.fColor != SK_PMColor4fILLEGAL) { + if (mgp.fColor != SK_PMColor4fILLEGAL) { fColorUniform = uniformHandler->addUniform(nullptr, kFragment_GrShaderFlag, SkSLType::kHalf4, @@ -236,7 +339,7 @@ private: &uniformColorName); } SkString localCoordAssignment; - if (SkMeshSpecificationPriv::HasLocalCoords(*cmgp.fSpec) && cmgp.fNeedsLocalCoords) { + if (SkMeshSpecificationPriv::HasLocalCoords(*mgp.fSpec) && mgp.fNeedsLocalCoords) { localCoordAssignment = "float2 local ="; } if (meshColorType == SkMeshSpecificationPriv::ColorType::kNone) { @@ -247,7 +350,7 @@ private: fragBuilder->codeAppendf("%s = %s;", args.fOutputColor, uniformColorName); } else { fColorSpaceHelper.emitCode(uniformHandler, - cmgp.fColorSpaceXform.get(), + mgp.fColorSpaceXform.get(), kFragment_GrShaderFlag); if (meshColorType == SkMeshSpecificationPriv::ColorType::kFloat4) { fragBuilder->codeAppendf("float4 color;"); @@ -266,8 +369,8 @@ private: fragBuilder->appendColorGamutXform(&xformedColor, color, &fColorSpaceHelper); fragBuilder->codeAppendf("%s = %s;", args.fOutputColor, xformedColor.c_str()); } - if (cmgp.fNeedsLocalCoords) { - if (SkMeshSpecificationPriv::HasLocalCoords(*cmgp.fSpec)) { + if (mgp.fNeedsLocalCoords) { + if (SkMeshSpecificationPriv::HasLocalCoords(*mgp.fSpec)) { gpArgs->fLocalCoordVar = GrShaderVar("local", SkSLType::kFloat2); gpArgs->fLocalCoordShader = kFragment_GrShaderType; } else { @@ -280,8 +383,9 @@ private: private: SkMatrix fViewMatrix = SkMatrix::InvalidMatrix(); - UniformHandle fViewMatrixUniform; - UniformHandle fColorUniform; + UniformHandle fViewMatrixUniform; + UniformHandle fColorUniform; + std::vector fSpecUniformHandles; GrGLSLColorSpaceXformHelper fColorSpaceHelper; }; @@ -290,9 +394,11 @@ private: sk_sp colorSpaceXform, const SkMatrix& viewMatrix, const std::optional& color, - bool needsLocalCoords) + bool needsLocalCoords, + sk_sp uniforms) : INHERITED(kVerticesGP_ClassID) , fSpec(std::move(spec)) + , fUniforms(std::move(uniforms)) , fViewMatrix(viewMatrix) , fColorSpaceXform(std::move(colorSpaceXform)) , fNeedsLocalCoords(needsLocalCoords) { @@ -308,6 +414,7 @@ private: } sk_sp fSpec; + sk_sp fUniforms; std::vector fAttributes; SkMatrix fViewMatrix; SkPMColor4f fColor; @@ -463,6 +570,7 @@ private: sk_sp fColorSpaceXform; SkPMColor4f fColor; // Used if no color from spec or analysis overrides. SkMatrix fViewMatrix; + sk_sp fUniforms; int fVertexCount; int fIndexCount; GrSimpleMesh* fMesh = nullptr; @@ -551,6 +659,13 @@ MeshOp::MeshOp(GrProcessorSet* processorSet, fMeshes.emplace_back(mesh); fSpecification = mesh.spec(); + if (fColorSpaceXform) { + fUniforms = SkRuntimeEffectPriv::TransformUniforms(mesh.spec()->uniforms(), + mesh.uniforms(), + fColorSpaceXform->steps()); + } else { + fUniforms = mesh.uniforms(); + } fVertexCount = fMeshes.back().vertexCount(); fIndexCount = fMeshes.back().indexCount(); @@ -704,7 +819,8 @@ GrGeometryProcessor* MeshOp::makeGP(SkArenaAlloc* arena) { fColorSpaceXform, vm, color, - fHelper.usesLocalCoords()); + fHelper.usesLocalCoords(), + fUniforms); } void MeshOp::onCreateProgramInfo(const GrCaps* caps, @@ -839,6 +955,17 @@ GrOp::CombineResult MeshOp::onCombineIfPossible(GrOp* t, SkArenaAlloc*, const Gr return CombineResult::kCannotCombine; } + if (fSpecification->uniformSize()) { + size_t size = fSpecification->uniformSize(); + SkASSERT(fUniforms); + SkASSERT(fUniforms->size() >= size); + SkASSERT(that->fUniforms); + SkASSERT(that->fUniforms->size() >= size); + if (memcmp(fUniforms->data(), that->fUniforms->data(), size) != 0) { + return GrOp::CombineResult::kCannotCombine; + } + } + if (!SkMeshSpecificationPriv::HasColors(*fSpecification) && fColor != that->fColor) { return CombineResult::kCannotCombine; }