Finish uniform support for SkMesh.

SkMesh now takes SkData that contains the uniform values.

Change-Id: I286e7559ba692ce15925a54f59c2e829b6b5448f
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/546096
Commit-Queue: Brian Salomon <bsalomon@google.com>
Reviewed-by: Brian Osman <brianosman@google.com>
This commit is contained in:
Brian Salomon 2022-06-03 14:00:12 -04:00 committed by SkCQ
parent d29d446a7d
commit 3e08ec878d
7 changed files with 474 additions and 117 deletions

View File

@ -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 <memory>
@ -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<void>(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<float>(unis->writable_data(), offset) = m.rc(row, col);
}
}
u = fSpec->findUniform("color");
SkASSERT(u);
std::memcpy(SkTAddOffset<void>(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<SkMesh::VertexBuffer> fVB;
sk_sp<SkMeshSpecification> fSpec;
sk_sp<SkShader> fShader;
};
DEF_GM(return new MeshUniformsGM())
} // namespace skiagm

View File

@ -143,8 +143,17 @@ public:
SkSpan<const Attribute> 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<const Uniform> 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<VertexBuffer>,
size_t vertexCount,
size_t vertexOffset,
sk_sp<const SkData> uniforms,
const SkRect& bounds);
static SkMesh MakeIndexed(sk_sp<SkMeshSpecification>,
@ -273,6 +287,7 @@ public:
sk_sp<IndexBuffer>,
size_t indexCount,
size_t indexOffset,
sk_sp<const SkData> uniforms,
const SkRect& bounds);
sk_sp<SkMeshSpecification> spec() const { return fSpec; }
@ -289,6 +304,8 @@ public:
size_t indexOffset() const { return fIOffset; }
size_t indexCount() const { return fICount; }
sk_sp<const SkData> uniforms() const { return fUniforms; }
SkRect bounds() const { return fBounds; }
bool isValid() const;
@ -303,6 +320,8 @@ private:
sk_sp<VertexBuffer> fVB;
sk_sp<IndexBuffer> fIB;
sk_sp<const SkData> fUniforms;
size_t fVOffset = 0; // Must be a multiple of spec->stride()
size_t fVCount = 0;

View File

@ -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.

View File

@ -501,14 +501,16 @@ SkMesh SkMesh::Make(sk_sp<SkMeshSpecification> spec,
sk_sp<VertexBuffer> vb,
size_t vertexCount,
size_t vertexOffset,
sk_sp<const SkData> 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<SkMeshSpecification> spec,
sk_sp<IndexBuffer> ib,
size_t indexCount,
size_t indexOffset,
sk_sp<const SkData> 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;

View File

@ -113,6 +113,59 @@ SkRuntimeEffect::Uniform SkRuntimeEffectPriv::VarAsUniform(const SkSL::Variable&
SkASSERT(SkIsAlign4(*offset));
return uni;
}
sk_sp<const SkData> SkRuntimeEffectPriv::TransformUniforms(
SkSpan<const SkRuntimeEffect::Uniform> uniforms,
sk_sp<const SkData> originalData,
const SkColorSpace* dstCS) {
SkColorSpaceXformSteps steps(sk_srgb_singleton(), kUnpremul_SkAlphaType,
dstCS, kUnpremul_SkAlphaType);
return TransformUniforms(uniforms, std::move(originalData), steps);
}
sk_sp<const SkData> SkRuntimeEffectPriv::TransformUniforms(
SkSpan<const SkRuntimeEffect::Uniform> uniforms,
sk_sp<const SkData> originalData,
const SkColorSpaceXformSteps& steps) {
using Flags = SkRuntimeEffect::Uniform::Flags;
using Type = SkRuntimeEffect::Uniform::Type;
sk_sp<SkData> 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<float>(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<const SkData> get_xformed_uniforms(const SkRuntimeEffect* effect,
sk_sp<const SkData> 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<SkData> 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<float>(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<SkRuntimeEffect> effect,
const char* name,
@ -968,8 +975,10 @@ public:
GrFPResult asFragmentProcessor(std::unique_ptr<GrFragmentProcessor> inputFP,
GrRecordingContext* context,
const GrColorInfo& colorInfo) const override {
sk_sp<const SkData> uniforms =
get_xformed_uniforms(fEffect.get(), fUniforms, colorInfo.colorSpace());
sk_sp<const SkData> 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<const SkData> inputs =
get_xformed_uniforms(fEffect.get(), fUniforms, colorInfo.colorSpace());
sk_sp<const SkData> 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<const SkData> inputs = get_xformed_uniforms(fEffect.get(), fUniforms, dstCS);
sk_sp<const SkData> inputs = SkRuntimeEffectPriv::TransformUniforms(
fEffect->uniforms(),
fUniforms,
dstCS);
SkASSERT(inputs);
auto evalChild = [&](int index, SkPMColor4f inColor) {
@ -1116,8 +1130,10 @@ public:
return nullptr;
}
sk_sp<const SkData> uniforms =
get_xformed_uniforms(fEffect.get(), fUniforms, args.fDstColorInfo->colorSpace());
sk_sp<const SkData> 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<const SkData> inputs =
get_xformed_uniforms(fEffect.get(), fUniforms, colorInfo.colorSpace());
sk_sp<const SkData> 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<const SkData> inputs = get_xformed_uniforms(fEffect.get(), fUniforms,
colorInfo.colorSpace());
sk_sp<const SkData> 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<GrFragmentProcessor> srcFP,
std::unique_ptr<GrFragmentProcessor> dstFP,
const GrFPArgs& args) const override {
sk_sp<const SkData> uniforms = get_xformed_uniforms(fEffect.get(), fUniforms,
args.fDstColorInfo->colorSpace());
sk_sp<const SkData> 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<SkImage> 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());

View File

@ -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<const SkData> TransformUniforms(SkSpan<const SkRuntimeEffect::Uniform> uniforms,
sk_sp<const SkData> originalData,
const SkColorSpaceXformSteps&);
static sk_sp<const SkData> TransformUniforms(SkSpan<const SkRuntimeEffect::Uniform> uniforms,
sk_sp<const SkData> originalData,
const SkColorSpace* dstCS);
};
// These internal APIs for creating runtime effects vary from the public API in two ways:

View File

@ -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<GrColorSpaceXform> colorSpaceXform,
const SkMatrix& viewMatrix,
const std::optional<SkPMColor4f>& color,
bool needsLocalCoords) {
bool needsLocalCoords,
sk_sp<const SkData> 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<MeshGP>();
SetTransform(pdman, shaderCaps, fViewMatrixUniform, cmgp.fViewMatrix, &fViewMatrix);
fColorSpaceHelper.setData(pdman, cmgp.fColorSpaceXform.get());
const auto& mgp = geomProc.cast<MeshGP>();
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<const float>(data, u.offset); };
auto intData = [=] { return SkTAddOffset<const int>(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<const SkMeshSpecification::Uniform> 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<MeshGP>();
const MeshGP& mgp = args.fGeomProc.cast<MeshGP>();
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<GrGLSLVarying> 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<UniformHandle> fSpecUniformHandles;
GrGLSLColorSpaceXformHelper fColorSpaceHelper;
};
@ -290,9 +394,11 @@ private:
sk_sp<GrColorSpaceXform> colorSpaceXform,
const SkMatrix& viewMatrix,
const std::optional<SkPMColor4f>& color,
bool needsLocalCoords)
bool needsLocalCoords,
sk_sp<const SkData> 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<SkMeshSpecification> fSpec;
sk_sp<const SkData> fUniforms;
std::vector<Attribute> fAttributes;
SkMatrix fViewMatrix;
SkPMColor4f fColor;
@ -463,6 +570,7 @@ private:
sk_sp<GrColorSpaceXform> fColorSpaceXform;
SkPMColor4f fColor; // Used if no color from spec or analysis overrides.
SkMatrix fViewMatrix;
sk_sp<const SkData> 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;
}