aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/core/SkNormalSource.cpp
diff options
context:
space:
mode:
authorGravatar dvonbeck <dvonbeck@google.com>2016-06-27 11:40:45 -0700
committerGravatar Commit bot <commit-bot@chromium.org>2016-06-27 11:40:45 -0700
commit12c4fc2579017a162668db077e8067512fd968ca (patch)
tree5337853b1101385452b5cd0c473c8fd9e2bb125e /src/core/SkNormalSource.cpp
parent8bad3acce52a24fb99d3201a0cefa54eacf9ee57 (diff)
SkLightingShader normal vector CPU computation refactor.
The purpose of this change is to refactor the handling of normal maps out of SkLightingShader, laying the groundwork to eventually allow for multiple normal sources. This CL's base was the CL for GPU handling: https://codereview.chromium.org/2043393002/ What this CL includes: - A refactor of the SkLightingShader context's code that deals with reading normals off of a normal map. This is now abstracted out into a NormalSource::Provider class that the context uses. BUG=skia: GOLD_TRYBOT_URL= https://gold.skia.org/search?issue=2050773002 Committed: https://skia.googlesource.com/skia/+/790a70118327a129cb6b48fabe80f4e184c1e67c Review-Url: https://codereview.chromium.org/2050773002
Diffstat (limited to 'src/core/SkNormalSource.cpp')
-rw-r--r--src/core/SkNormalSource.cpp294
1 files changed, 294 insertions, 0 deletions
diff --git a/src/core/SkNormalSource.cpp b/src/core/SkNormalSource.cpp
new file mode 100644
index 0000000000..2f52530382
--- /dev/null
+++ b/src/core/SkNormalSource.cpp
@@ -0,0 +1,294 @@
+/*
+ * Copyright 2016 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "SkError.h"
+#include "SkErrorInternals.h"
+#include "SkLightingShader.h"
+#include "SkNormalSource.h"
+#include "SkReadBuffer.h"
+#include "SkWriteBuffer.h"
+
+// Genretating vtable
+SkNormalSource::~SkNormalSource() {}
+
+///////////////////////////////////////////////////////////////////////////////
+
+class NormalMapSourceImpl : public SkNormalSource {
+public:
+ NormalMapSourceImpl(sk_sp<SkShader> mapShader, const SkVector &normRotation)
+ : fMapShader(std::move(mapShader))
+ , fNormRotation(normRotation) {}
+
+#if SK_SUPPORT_GPU
+ sk_sp<GrFragmentProcessor> asFragmentProcessor(GrContext*,
+ const SkMatrix& viewM,
+ const SkMatrix* localMatrix,
+ SkFilterQuality,
+ SkSourceGammaTreatment) const override;
+#endif
+
+ SkNormalSource::Provider* asProvider(const SkShader::ContextRec& rec,
+ void* storage) const override;
+
+ size_t providerSize(const SkShader::ContextRec& rec) const override;
+ SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(NormalMapSourceImpl)
+
+protected:
+ void flatten(SkWriteBuffer& buf) const override;
+
+ bool computeNormTotalInverse(const SkShader::ContextRec& rec, SkMatrix* normTotalInverse) const;
+
+private:
+ class Provider : public SkNormalSource::Provider {
+ public:
+ Provider(const NormalMapSourceImpl& source, SkShader::Context* fMapContext);
+
+ virtual ~Provider() override;
+
+ void fillScanLine(int x, int y, SkPoint3 output[], int count) const override;
+ private:
+ const NormalMapSourceImpl& fSource;
+ SkShader::Context* fMapContext;
+
+ typedef SkNormalSource::Provider INHERITED;
+ };
+
+ sk_sp<SkShader> fMapShader;
+ SkVector fNormRotation;
+
+ friend class SkNormalSource;
+
+ typedef SkNormalSource INHERITED;
+};
+
+////////////////////////////////////////////////////////////////////////////
+
+#if SK_SUPPORT_GPU
+
+#include "GrCoordTransform.h"
+#include "GrInvariantOutput.h"
+#include "GrTextureParams.h"
+#include "glsl/GrGLSLFragmentProcessor.h"
+#include "glsl/GrGLSLFragmentShaderBuilder.h"
+#include "SkGr.h"
+
+class NormalMapFP : public GrFragmentProcessor {
+public:
+ NormalMapFP(sk_sp<GrFragmentProcessor> mapFP, const SkVector& normRotation)
+ : fNormRotation(normRotation) {
+ this->registerChildProcessor(mapFP);
+
+ this->initClassID<NormalMapFP>();
+ }
+
+ class GLSLNormalMapFP : public GrGLSLFragmentProcessor {
+ public:
+ GLSLNormalMapFP() {
+ fNormRotation.set(0.0f, 0.0f);
+ }
+
+ void emitCode(EmitArgs& args) override {
+
+ GrGLSLFragmentBuilder* fragBuilder = args.fFragBuilder;
+ GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
+
+ // add uniform
+ const char* xformUniName = nullptr;
+ fXformUni = uniformHandler->addUniform(kFragment_GrShaderFlag,
+ kVec2f_GrSLType, kDefault_GrSLPrecision,
+ "Xform", &xformUniName);
+
+ SkString dstNormalColorName("dstNormalColor");
+ this->emitChild(0, nullptr, &dstNormalColorName, args);
+ fragBuilder->codeAppendf("vec3 normal = %s.rgb - vec3(0.5);",
+ dstNormalColorName.c_str());
+
+ // TODO: inverse map the light direction vectors in the vertex shader rather than
+ // transforming all the normals here!
+ fragBuilder->codeAppendf(
+ "mat3 m = mat3(%s.x, -%s.y, 0.0, %s.y, %s.x, 0.0, 0.0, 0.0, 1.0);",
+ xformUniName, xformUniName, xformUniName, xformUniName);
+
+ fragBuilder->codeAppend("normal = normalize(m*normal);");
+ fragBuilder->codeAppendf("%s = vec4(normal, 0);", args.fOutputColor);
+ }
+
+ static void GenKey(const GrProcessor& proc, const GrGLSLCaps&,
+ GrProcessorKeyBuilder* b) {
+ b->add32(0x0);
+ }
+
+ protected:
+ void onSetData(const GrGLSLProgramDataManager& pdman, const GrProcessor& proc) override {
+ const NormalMapFP& normalMapFP = proc.cast<NormalMapFP>();
+
+ const SkVector& normRotation = normalMapFP.normRotation();
+ if (normRotation != fNormRotation) {
+ pdman.set2fv(fXformUni, 1, &normRotation.fX);
+ fNormRotation = normRotation;
+ }
+ }
+
+ private:
+ SkVector fNormRotation;
+ GrGLSLProgramDataManager::UniformHandle fXformUni;
+ };
+
+ void onGetGLSLProcessorKey(const GrGLSLCaps& caps, GrProcessorKeyBuilder* b) const override {
+ GLSLNormalMapFP::GenKey(*this, caps, b);
+ }
+
+ const char* name() const override { return "NormalMapFP"; }
+
+ void onComputeInvariantOutput(GrInvariantOutput* inout) const override {
+ inout->setToUnknown(GrInvariantOutput::ReadInput::kWillNot_ReadInput);
+ }
+
+ const SkVector& normRotation() const { return fNormRotation; }
+
+private:
+ GrGLSLFragmentProcessor* onCreateGLSLInstance() const override { return new GLSLNormalMapFP; }
+
+ bool onIsEqual(const GrFragmentProcessor& proc) const override {
+ const NormalMapFP& normalMapFP = proc.cast<NormalMapFP>();
+ return fNormRotation == normalMapFP.fNormRotation;
+ }
+
+ SkVector fNormRotation;
+};
+
+sk_sp<GrFragmentProcessor> NormalMapSourceImpl::asFragmentProcessor(
+ GrContext *context,
+ const SkMatrix &viewM,
+ const SkMatrix *localMatrix,
+ SkFilterQuality filterQuality,
+ SkSourceGammaTreatment gammaTreatment) const {
+
+ sk_sp<GrFragmentProcessor> mapFP = fMapShader->asFragmentProcessor(context, viewM,
+ localMatrix, filterQuality, gammaTreatment);
+
+ return sk_make_sp<NormalMapFP>(std::move(mapFP), fNormRotation);
+}
+
+#endif // SK_SUPPORT_GPU
+
+////////////////////////////////////////////////////////////////////////////
+
+NormalMapSourceImpl::Provider::Provider(const NormalMapSourceImpl& source,
+ SkShader::Context* mapContext)
+ : fSource(source)
+ , fMapContext(mapContext) {
+}
+
+NormalMapSourceImpl::Provider::~Provider() {
+ fMapContext->~Context();
+}
+
+SkNormalSource::Provider* NormalMapSourceImpl::asProvider(
+ const SkShader::ContextRec &rec, void *storage) const {
+ SkMatrix normTotalInv;
+ if (!this->computeNormTotalInverse(rec, &normTotalInv)) {
+ return nullptr;
+ }
+
+ void* mapContextStorage = (char*)storage + sizeof(Provider);
+ SkShader::Context* context = fMapShader->createContext(rec, mapContextStorage);
+ if (!context) {
+ return nullptr;
+ }
+
+ return new (storage) Provider(*this, context);
+}
+
+size_t NormalMapSourceImpl::providerSize(const SkShader::ContextRec& rec) const {
+ return sizeof(Provider) + fMapShader->contextSize(rec);
+}
+
+bool NormalMapSourceImpl::computeNormTotalInverse(const SkShader::ContextRec& rec,
+ SkMatrix* normTotalInverse) const {
+ SkMatrix total;
+ total.setConcat(*rec.fMatrix, fMapShader->getLocalMatrix());
+
+ const SkMatrix* m = &total;
+ if (rec.fLocalMatrix) {
+ total.setConcat(*m, *rec.fLocalMatrix);
+ m = &total;
+ }
+ return m->invert(normTotalInverse);
+}
+
+#define BUFFER_MAX 16
+void NormalMapSourceImpl::Provider::fillScanLine(int x, int y, SkPoint3 output[],
+ int count) const {
+ SkPMColor tmpNormalColors[BUFFER_MAX];
+
+ do {
+ int n = SkTMin(count, BUFFER_MAX);
+
+ fMapContext->shadeSpan(x, y, tmpNormalColors, n);
+
+ for (int i = 0; i < n; i++) {
+ SkPoint3 tempNorm;
+
+ tempNorm.set(SkIntToScalar(SkGetPackedR32(tmpNormalColors[i])) - 127.0f,
+ SkIntToScalar(SkGetPackedG32(tmpNormalColors[i])) - 127.0f,
+ SkIntToScalar(SkGetPackedB32(tmpNormalColors[i])) - 127.0f);
+ tempNorm.normalize();
+
+ output[i].fX = fSource.fNormRotation.fX * tempNorm.fX +
+ fSource.fNormRotation.fY * tempNorm.fY;
+ output[i].fY = -fSource.fNormRotation.fY * tempNorm.fX +
+ fSource.fNormRotation.fX * tempNorm.fY;
+ output[i].fZ = tempNorm.fZ;
+ }
+
+ output += n;
+ x += n;
+ count -= n;
+ } while (count > 0);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+sk_sp<SkFlattenable> NormalMapSourceImpl::CreateProc(SkReadBuffer& buf) {
+
+ sk_sp<SkShader> mapShader = buf.readFlattenable<SkShader>();
+
+ SkVector normRotation = {1,0};
+ if (!buf.isVersionLT(SkReadBuffer::kLightingShaderWritesInvNormRotation)) {
+ normRotation = buf.readPoint();
+ }
+
+ return sk_make_sp<NormalMapSourceImpl>(std::move(mapShader), normRotation);
+}
+
+void NormalMapSourceImpl::flatten(SkWriteBuffer& buf) const {
+ this->INHERITED::flatten(buf);
+
+ buf.writeFlattenable(fMapShader.get());
+ buf.writePoint(fNormRotation);
+}
+
+////////////////////////////////////////////////////////////////////////////
+
+sk_sp<SkNormalSource> SkNormalSource::MakeFromNormalMap(sk_sp<SkShader> map,
+ const SkVector &normRotation) {
+ SkASSERT(SkScalarNearlyEqual(normRotation.lengthSqd(), SK_Scalar1));
+ if (!map) {
+ return nullptr;
+ }
+
+ return sk_make_sp<NormalMapSourceImpl>(std::move(map), normRotation);
+}
+
+////////////////////////////////////////////////////////////////////////////
+
+SK_DEFINE_FLATTENABLE_REGISTRAR_GROUP_START(SkNormalSource)
+ SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(NormalMapSourceImpl)
+SK_DEFINE_FLATTENABLE_REGISTRAR_GROUP_END
+
+////////////////////////////////////////////////////////////////////////////