Color matrix effect rewritten as .fp

Also adds flexibility to unpremul the input, clamp the output, premul the
output or not.

Also fixes SkMatrix44 as a ctype.

The intent is to reuse this for rgb->yuv conversion in async rescale and
read.

Bug: skia:3962
Change-Id: I470d1cfebdbd79d8541b633c1747d510a5549ac4
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/217128
Reviewed-by: Brian Osman <brianosman@google.com>
Commit-Queue: Brian Salomon <bsalomon@google.com>
diff --git a/src/core/SkColorFilter_Matrix.cpp b/src/core/SkColorFilter_Matrix.cpp
index 0a648e5..c5e0679 100644
--- a/src/core/SkColorFilter_Matrix.cpp
+++ b/src/core/SkColorFilter_Matrix.cpp
@@ -65,121 +65,13 @@
 }
 
 #if SK_SUPPORT_GPU
-#include "src/gpu/GrFragmentProcessor.h"
-#include "src/gpu/glsl/GrGLSLFragmentProcessor.h"
-#include "src/gpu/glsl/GrGLSLFragmentShaderBuilder.h"
-#include "src/gpu/glsl/GrGLSLProgramDataManager.h"
-#include "src/gpu/glsl/GrGLSLUniformHandler.h"
-
-class ColorMatrixEffect : public GrFragmentProcessor {
-public:
-    static std::unique_ptr<GrFragmentProcessor> Make(const float matrix[20]) {
-        return std::unique_ptr<GrFragmentProcessor>(new ColorMatrixEffect(matrix));
-    }
-
-    const char* name() const override { return "Color Matrix"; }
-
-    GR_DECLARE_FRAGMENT_PROCESSOR_TEST
-
-    std::unique_ptr<GrFragmentProcessor> clone() const override { return Make(fMatrix); }
-
-private:
-    class GLSLProcessor : public GrGLSLFragmentProcessor {
-    public:
-        // this class always generates the same code.
-        static void GenKey(const GrProcessor&, const GrShaderCaps&, GrProcessorKeyBuilder*) {}
-
-        void emitCode(EmitArgs& args) override {
-            GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
-            fMatrixHandle = uniformHandler->addUniform(kFragment_GrShaderFlag, kHalf4x4_GrSLType,
-                                                       "ColorMatrix");
-            fVectorHandle = uniformHandler->addUniform(kFragment_GrShaderFlag, kHalf4_GrSLType,
-                                                       "ColorMatrixVector");
-
-            GrGLSLFragmentBuilder* fragBuilder = args.fFragBuilder;
-            // The max() is to guard against 0 / 0 during unpremul when the incoming color is
-            // transparent black.
-            fragBuilder->codeAppendf("\thalf nonZeroAlpha = max(%s.a, 0.00001);\n",
-                                     args.fInputColor);
-            fragBuilder->codeAppendf("\t%s = %s * half4(%s.rgb / nonZeroAlpha, nonZeroAlpha) + "
-                                     "%s;\n",
-                                     args.fOutputColor,
-                                     uniformHandler->getUniformCStr(fMatrixHandle),
-                                     args.fInputColor,
-                                     uniformHandler->getUniformCStr(fVectorHandle));
-            fragBuilder->codeAppendf("\t%s = saturate(%s);\n",
-                                     args.fOutputColor, args.fOutputColor);
-            fragBuilder->codeAppendf("\t%s.rgb *= %s.a;\n", args.fOutputColor, args.fOutputColor);
-        }
-
-    protected:
-        void onSetData(const GrGLSLProgramDataManager& uniManager,
-                       const GrFragmentProcessor& proc) override {
-            const ColorMatrixEffect& cme = proc.cast<ColorMatrixEffect>();
-            const float* m = cme.fMatrix;
-            // The GL matrix is transposed from SkColorMatrix.
-            float mt[]  = {
-                m[0], m[5], m[10], m[15],
-                m[1], m[6], m[11], m[16],
-                m[2], m[7], m[12], m[17],
-                m[3], m[8], m[13], m[18],
-            };
-            float vec[] = {
-                m[4], m[9], m[14], m[19],
-            };
-            uniManager.setMatrix4fv(fMatrixHandle, 1, mt);
-            uniManager.set4fv(fVectorHandle, 1, vec);
-        }
-
-    private:
-        GrGLSLProgramDataManager::UniformHandle fMatrixHandle;
-        GrGLSLProgramDataManager::UniformHandle fVectorHandle;
-
-        typedef GrGLSLFragmentProcessor INHERITED;
-    };
-
-    // We could implement the constant input->constant output optimization but haven't. Other
-    // optimizations would be matrix-dependent.
-    ColorMatrixEffect(const float matrix[20])
-    : INHERITED(kColorMatrixEffect_ClassID, kNone_OptimizationFlags) {
-        memcpy(fMatrix, matrix, sizeof(float) * 20);
-    }
-
-    GrGLSLFragmentProcessor* onCreateGLSLInstance() const override {
-        return new GLSLProcessor;
-    }
-
-    virtual void onGetGLSLProcessorKey(const GrShaderCaps& caps,
-                                       GrProcessorKeyBuilder* b) const override {
-        GLSLProcessor::GenKey(*this, caps, b);
-    }
-
-    bool onIsEqual(const GrFragmentProcessor& s) const override {
-        const ColorMatrixEffect& cme = s.cast<ColorMatrixEffect>();
-        return 0 == memcmp(fMatrix, cme.fMatrix, sizeof(fMatrix));
-    }
-
-    float fMatrix[20];
-
-    typedef GrFragmentProcessor INHERITED;
-};
-
-GR_DEFINE_FRAGMENT_PROCESSOR_TEST(ColorMatrixEffect);
-
-#if GR_TEST_UTILS
-std::unique_ptr<GrFragmentProcessor> ColorMatrixEffect::TestCreate(GrProcessorTestData* d) {
-    float colorMatrix[20];
-    for (size_t i = 0; i < SK_ARRAY_COUNT(colorMatrix); ++i) {
-        colorMatrix[i] = d->fRandom->nextSScalar1();
-    }
-    return ColorMatrixEffect::Make(colorMatrix);
-}
-
-#endif
-
+#include "src/gpu/effects/generated/GrColorMatrixFragmentProcessor.h"
 std::unique_ptr<GrFragmentProcessor> SkColorFilter_Matrix::asFragmentProcessor(
         GrRecordingContext*, const GrColorSpaceInfo&) const {
-    return ColorMatrixEffect::Make(fMatrix);
+    return GrColorMatrixFragmentProcessor::Make(fMatrix,
+                                                /* premulInput = */ true,
+                                                /* clampRGBOutput = */ true,
+                                                /* premulOutput = */ true);
 }
 
 #endif
diff --git a/src/gpu/GrProcessor.h b/src/gpu/GrProcessor.h
index ef245f8..656f418 100644
--- a/src/gpu/GrProcessor.h
+++ b/src/gpu/GrProcessor.h
@@ -66,7 +66,6 @@
         kCircleGeometryProcessor_ClassID,
         kCircularRRectEffect_ClassID,
         kClockwiseTestProcessor_ClassID,
-        kColorMatrixEffect_ClassID,
         kColorTableEffect_ClassID,
         kComposeOneFragmentProcessor_ClassID,
         kComposeTwoFragmentProcessor_ClassID,
@@ -94,6 +93,7 @@
         kGrCircleBlurFragmentProcessor_ClassID,
         kGrCircleEffect_ClassID,
         kGrClampedGradientEffect_ClassID,
+        kGrColorMatrixFragmentProcessor_ClassID,
         kGrColorSpaceXformEffect_ClassID,
         kGrComposeLerpEffect_ClassID,
         kGrComposeLerpRedEffect_ClassID,
diff --git a/src/gpu/effects/GrColorMatrixFragmentProcessor.fp b/src/gpu/effects/GrColorMatrixFragmentProcessor.fp
new file mode 100644
index 0000000..5bbfda7
--- /dev/null
+++ b/src/gpu/effects/GrColorMatrixFragmentProcessor.fp
@@ -0,0 +1,91 @@
+/*
+ * Copyright 2019 Google LLC
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+layout(ctype=SkMatrix44, tracked) in uniform half4x4 m;
+layout(ctype=SkRect, tracked) in uniform half4 v;
+layout(key) in bool unpremulInput;
+layout(key) in bool clampRGBOutput;
+layout(key) in bool premulOutput;
+
+@optimizationFlags {
+    kConstantOutputForConstantInput_OptimizationFlag
+}
+
+void main() {
+    half4 inputColor = sk_InColor;
+    @if (unpremulInput) {
+        // The max() is to guard against 0 / 0 during unpremul when the incoming color is
+        // transparent black.
+        half nonZeroAlpha = max(inputColor.a, 0.00001);
+        inputColor = half4(inputColor.rgb / nonZeroAlpha, nonZeroAlpha);
+    }
+    sk_OutColor = m * inputColor + v;
+    @if (clampRGBOutput) {
+        sk_OutColor = saturate(sk_OutColor);
+    } else {
+        sk_OutColor.a = saturate(sk_OutColor.a);
+    }
+    @if (premulOutput) {
+        sk_OutColor.rgb *= sk_OutColor.a;
+    }
+}
+
+@class {
+    SkPMColor4f constantOutputForConstantInput(const SkPMColor4f& input) const override {
+        SkColor4f color;
+        if (unpremulInput) {
+            color = input.unpremul();
+        } else {
+            color.fR = input.fR;
+            color.fG = input.fG;
+            color.fB = input.fB;
+            color.fA = input.fA;
+        }
+        m.mapScalars(color.vec());
+        color.fR += v.fLeft;
+        color.fG += v.fTop;
+        color.fB += v.fRight;
+        color.fA += v.fBottom;
+        color.fA = SkTPin(color.fA, 0.f, 1.f);
+        if (clampRGBOutput) {
+            color.fR = SkTPin(color.fR, 0.f, 1.f);
+            color.fG = SkTPin(color.fG, 0.f, 1.f);
+            color.fB = SkTPin(color.fB, 0.f, 1.f);
+        }
+        if (premulOutput) {
+            return color.premul();
+        } else {
+            return {color.fR, color.fG, color.fB, color.fA};
+        }
+    }
+}
+
+@make {
+    static std::unique_ptr<GrFragmentProcessor> Make(const float matrix[20], bool unpremulInput, bool clampRGBOutput, bool premulOutput) {
+        const float m[] {
+                matrix[0],  matrix[1],  matrix[2],  matrix[3],
+                matrix[5],  matrix[6],  matrix[7],  matrix[8],
+                matrix[10], matrix[11], matrix[12], matrix[13],
+                matrix[15], matrix[16], matrix[17], matrix[18]
+        };
+        SkMatrix44 m44;
+        m44.setRowMajorf(m);
+        SkRect v4 = SkRect::MakeLTRB(matrix[4], matrix[9], matrix[14], matrix[19]);
+        return std::unique_ptr<GrFragmentProcessor>(new GrColorMatrixFragmentProcessor(m44, v4, unpremulInput, clampRGBOutput, premulOutput));
+    }
+}
+
+@test(d) {
+    float m[20];
+    for (int i = 0; i < 20; ++i) {
+        m[i] = d->fRandom->nextRangeScalar(-10.f, 10.f);
+    }
+    bool unpremul = d->fRandom->nextBool();
+    bool clampRGB = d->fRandom->nextBool();
+    bool premul = d->fRandom->nextBool();
+    return Make(m, unpremul, clampRGB, premul);
+}
diff --git a/src/gpu/effects/generated/GrColorMatrixFragmentProcessor.cpp b/src/gpu/effects/generated/GrColorMatrixFragmentProcessor.cpp
new file mode 100644
index 0000000..c0cf275
--- /dev/null
+++ b/src/gpu/effects/generated/GrColorMatrixFragmentProcessor.cpp
@@ -0,0 +1,118 @@
+/*
+ * Copyright 2019 Google LLC
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+/**************************************************************************************************
+ *** This file was autogenerated from GrColorMatrixFragmentProcessor.fp; do not modify.
+ **************************************************************************************************/
+#include "GrColorMatrixFragmentProcessor.h"
+
+#include "include/gpu/GrTexture.h"
+#include "src/gpu/glsl/GrGLSLFragmentProcessor.h"
+#include "src/gpu/glsl/GrGLSLFragmentShaderBuilder.h"
+#include "src/gpu/glsl/GrGLSLProgramBuilder.h"
+#include "src/sksl/SkSLCPP.h"
+#include "src/sksl/SkSLUtil.h"
+class GrGLSLColorMatrixFragmentProcessor : public GrGLSLFragmentProcessor {
+public:
+    GrGLSLColorMatrixFragmentProcessor() {}
+    void emitCode(EmitArgs& args) override {
+        GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
+        const GrColorMatrixFragmentProcessor& _outer =
+                args.fFp.cast<GrColorMatrixFragmentProcessor>();
+        (void)_outer;
+        auto m = _outer.m;
+        (void)m;
+        auto v = _outer.v;
+        (void)v;
+        auto unpremulInput = _outer.unpremulInput;
+        (void)unpremulInput;
+        auto clampRGBOutput = _outer.clampRGBOutput;
+        (void)clampRGBOutput;
+        auto premulOutput = _outer.premulOutput;
+        (void)premulOutput;
+        mVar = args.fUniformHandler->addUniform(kFragment_GrShaderFlag, kHalf4x4_GrSLType, "m");
+        vVar = args.fUniformHandler->addUniform(kFragment_GrShaderFlag, kHalf4_GrSLType, "v");
+        fragBuilder->codeAppendf(
+                "half4 inputColor = %s;\n@if (%s) {\n    half nonZeroAlpha = max(inputColor.w, "
+                "1.0000000000000001e-05);\n    inputColor = half4(inputColor.xyz / nonZeroAlpha, "
+                "nonZeroAlpha);\n}\n%s = %s * inputColor + %s;\n@if (%s) {\n    %s = clamp(%s, "
+                "0.0, 1.0);\n} else {\n    %s.w = clamp(%s.w, 0.0, 1.0);\n}\n@if (%s) {\n    "
+                "%s.xyz *= %s.w;\n}\n",
+                args.fInputColor, (_outer.unpremulInput ? "true" : "false"), args.fOutputColor,
+                args.fUniformHandler->getUniformCStr(mVar),
+                args.fUniformHandler->getUniformCStr(vVar),
+                (_outer.clampRGBOutput ? "true" : "false"), args.fOutputColor, args.fOutputColor,
+                args.fOutputColor, args.fOutputColor, (_outer.premulOutput ? "true" : "false"),
+                args.fOutputColor, args.fOutputColor);
+    }
+
+private:
+    void onSetData(const GrGLSLProgramDataManager& pdman,
+                   const GrFragmentProcessor& _proc) override {
+        const GrColorMatrixFragmentProcessor& _outer = _proc.cast<GrColorMatrixFragmentProcessor>();
+        {
+            const SkMatrix44& mValue = _outer.m;
+            if (mPrev != (mValue)) {
+                mPrev = mValue;
+                pdman.setSkMatrix44(mVar, mValue);
+            }
+            const SkRect& vValue = _outer.v;
+            if (vPrev.isEmpty() || vPrev != vValue) {
+                vPrev = vValue;
+                pdman.set4fv(vVar, 1, reinterpret_cast<const float*>(&vValue));
+            }
+        }
+    }
+    SkMatrix44 mPrev = SkMatrix44(SkMatrix44::kNaN_Constructor);
+    SkRect vPrev = SkRect::MakeEmpty();
+    UniformHandle mVar;
+    UniformHandle vVar;
+};
+GrGLSLFragmentProcessor* GrColorMatrixFragmentProcessor::onCreateGLSLInstance() const {
+    return new GrGLSLColorMatrixFragmentProcessor();
+}
+void GrColorMatrixFragmentProcessor::onGetGLSLProcessorKey(const GrShaderCaps& caps,
+                                                           GrProcessorKeyBuilder* b) const {
+    b->add32((int32_t)unpremulInput);
+    b->add32((int32_t)clampRGBOutput);
+    b->add32((int32_t)premulOutput);
+}
+bool GrColorMatrixFragmentProcessor::onIsEqual(const GrFragmentProcessor& other) const {
+    const GrColorMatrixFragmentProcessor& that = other.cast<GrColorMatrixFragmentProcessor>();
+    (void)that;
+    if (m != that.m) return false;
+    if (v != that.v) return false;
+    if (unpremulInput != that.unpremulInput) return false;
+    if (clampRGBOutput != that.clampRGBOutput) return false;
+    if (premulOutput != that.premulOutput) return false;
+    return true;
+}
+GrColorMatrixFragmentProcessor::GrColorMatrixFragmentProcessor(
+        const GrColorMatrixFragmentProcessor& src)
+        : INHERITED(kGrColorMatrixFragmentProcessor_ClassID, src.optimizationFlags())
+        , m(src.m)
+        , v(src.v)
+        , unpremulInput(src.unpremulInput)
+        , clampRGBOutput(src.clampRGBOutput)
+        , premulOutput(src.premulOutput) {}
+std::unique_ptr<GrFragmentProcessor> GrColorMatrixFragmentProcessor::clone() const {
+    return std::unique_ptr<GrFragmentProcessor>(new GrColorMatrixFragmentProcessor(*this));
+}
+GR_DEFINE_FRAGMENT_PROCESSOR_TEST(GrColorMatrixFragmentProcessor);
+#if GR_TEST_UTILS
+std::unique_ptr<GrFragmentProcessor> GrColorMatrixFragmentProcessor::TestCreate(
+        GrProcessorTestData* d) {
+    float m[20];
+    for (int i = 0; i < 20; ++i) {
+        m[i] = d->fRandom->nextRangeScalar(-10.f, 10.f);
+    }
+    bool unpremul = d->fRandom->nextBool();
+    bool clampRGB = d->fRandom->nextBool();
+    bool premul = d->fRandom->nextBool();
+    return Make(m, unpremul, clampRGB, premul);
+}
+#endif
diff --git a/src/gpu/effects/generated/GrColorMatrixFragmentProcessor.h b/src/gpu/effects/generated/GrColorMatrixFragmentProcessor.h
new file mode 100644
index 0000000..3506e92
--- /dev/null
+++ b/src/gpu/effects/generated/GrColorMatrixFragmentProcessor.h
@@ -0,0 +1,83 @@
+/*
+ * Copyright 2019 Google LLC
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+/**************************************************************************************************
+ *** This file was autogenerated from GrColorMatrixFragmentProcessor.fp; do not modify.
+ **************************************************************************************************/
+#ifndef GrColorMatrixFragmentProcessor_DEFINED
+#define GrColorMatrixFragmentProcessor_DEFINED
+#include "include/core/SkTypes.h"
+
+#include "src/gpu/GrCoordTransform.h"
+#include "src/gpu/GrFragmentProcessor.h"
+class GrColorMatrixFragmentProcessor : public GrFragmentProcessor {
+public:
+    SkPMColor4f constantOutputForConstantInput(const SkPMColor4f& input) const override {
+        SkColor4f color;
+        if (unpremulInput) {
+            color = input.unpremul();
+        } else {
+            color.fR = input.fR;
+            color.fG = input.fG;
+            color.fB = input.fB;
+            color.fA = input.fA;
+        }
+        m.mapScalars(color.vec());
+        color.fR += v.fLeft;
+        color.fG += v.fTop;
+        color.fB += v.fRight;
+        color.fA += v.fBottom;
+        color.fA = SkTPin(color.fA, 0.f, 1.f);
+        if (clampRGBOutput) {
+            color.fR = SkTPin(color.fR, 0.f, 1.f);
+            color.fG = SkTPin(color.fG, 0.f, 1.f);
+            color.fB = SkTPin(color.fB, 0.f, 1.f);
+        }
+        if (premulOutput) {
+            return color.premul();
+        } else {
+            return {color.fR, color.fG, color.fB, color.fA};
+        }
+    }
+
+    static std::unique_ptr<GrFragmentProcessor> Make(const float matrix[20], bool unpremulInput,
+                                                     bool clampRGBOutput, bool premulOutput) {
+        const float m[]{matrix[0],  matrix[1],  matrix[2],  matrix[3],  matrix[5],  matrix[6],
+                        matrix[7],  matrix[8],  matrix[10], matrix[11], matrix[12], matrix[13],
+                        matrix[15], matrix[16], matrix[17], matrix[18]};
+        SkMatrix44 m44;
+        m44.setRowMajorf(m);
+        SkRect v4 = SkRect::MakeLTRB(matrix[4], matrix[9], matrix[14], matrix[19]);
+        return std::unique_ptr<GrFragmentProcessor>(new GrColorMatrixFragmentProcessor(
+                m44, v4, unpremulInput, clampRGBOutput, premulOutput));
+    }
+    GrColorMatrixFragmentProcessor(const GrColorMatrixFragmentProcessor& src);
+    std::unique_ptr<GrFragmentProcessor> clone() const override;
+    const char* name() const override { return "ColorMatrixFragmentProcessor"; }
+    SkMatrix44 m;
+    SkRect v;
+    bool unpremulInput;
+    bool clampRGBOutput;
+    bool premulOutput;
+
+private:
+    GrColorMatrixFragmentProcessor(SkMatrix44 m, SkRect v, bool unpremulInput, bool clampRGBOutput,
+                                   bool premulOutput)
+            : INHERITED(kGrColorMatrixFragmentProcessor_ClassID,
+                        (OptimizationFlags)kConstantOutputForConstantInput_OptimizationFlag)
+            , m(m)
+            , v(v)
+            , unpremulInput(unpremulInput)
+            , clampRGBOutput(clampRGBOutput)
+            , premulOutput(premulOutput) {}
+    GrGLSLFragmentProcessor* onCreateGLSLInstance() const override;
+    void onGetGLSLProcessorKey(const GrShaderCaps&, GrProcessorKeyBuilder*) const override;
+    bool onIsEqual(const GrFragmentProcessor&) const override;
+    GR_DECLARE_FRAGMENT_PROCESSOR_TEST
+    typedef GrFragmentProcessor INHERITED;
+};
+#endif
diff --git a/src/gpu/glsl/GrGLSLProgramDataManager.cpp b/src/gpu/glsl/GrGLSLProgramDataManager.cpp
index 08aa70d..395e325 100644
--- a/src/gpu/glsl/GrGLSLProgramDataManager.cpp
+++ b/src/gpu/glsl/GrGLSLProgramDataManager.cpp
@@ -25,3 +25,8 @@
     this->setMatrix3f(u, mt);
 }
 
+void GrGLSLProgramDataManager::setSkMatrix44(UniformHandle u, const SkMatrix44& matrix) const {
+    float mt[16];
+    matrix.asColMajorf(mt);
+    this->setMatrix4f(u, mt);
+}
diff --git a/src/gpu/glsl/GrGLSLProgramDataManager.h b/src/gpu/glsl/GrGLSLProgramDataManager.h
index 7348082..5db4ae5 100644
--- a/src/gpu/glsl/GrGLSLProgramDataManager.h
+++ b/src/gpu/glsl/GrGLSLProgramDataManager.h
@@ -55,6 +55,8 @@
 
     // convenience method for uploading a SkMatrix to a 3x3 matrix uniform
     void setSkMatrix(UniformHandle, const SkMatrix&) const;
+    // convenience method for uploading a SkMatrix to a 4x4 matrix uniform
+    void setSkMatrix44(UniformHandle, const SkMatrix44&) const;
 
     // for nvpr only
     GR_DEFINE_RESOURCE_HANDLE_CLASS(VaryingHandle);
diff --git a/src/sksl/SkSLCPPUniformCTypes.cpp b/src/sksl/SkSLCPPUniformCTypes.cpp
index c34cdac..1bd7b4c 100644
--- a/src/sksl/SkSLCPPUniformCTypes.cpp
+++ b/src/sksl/SkSLCPPUniformCTypes.cpp
@@ -176,8 +176,8 @@
 
     REGISTER(Layout::CType::kSkMatrix44,  { "half4x4", "float4x4", "double4x4" },
         "${pdman}.setSkMatrix44(${uniform}, ${var})",                              // to gpu
-        "SkMatrix::MakeScale(SK_FloatNaN)",                                        // default value
-        "!${oldVar}.cheapEqualTo(${newVar})"),                                     // dirty check
+        "SkMatrix44(SkMatrix44::kNaN_Constructor)",                                // default value
+        "${oldVar} != (${newVar})"),                                               // dirty check
 
     REGISTER(Layout::CType::kFloat,  { "half", "float", "double" },
         "${pdman}.set1f(${uniform}, ${var})",                                      // to gpu