diff options
author | dvonbeck <dvonbeck@google.com> | 2016-07-20 11:20:30 -0700 |
---|---|---|
committer | Commit bot <commit-bot@chromium.org> | 2016-07-20 11:20:30 -0700 |
commit | c526da94e4f2dc0c8521099dad2118c5d6b8da4a (patch) | |
tree | cacbe403042f6a407fc3ae052c0110a88942af09 /src | |
parent | f2944815e5e490c843c5a364fcaae2471526562f (diff) |
SkLS now accepting nullptr for diffuse shader and normal source, now accurately handling alpha
This CL's base is the CL for taking in a diffuse shader into SkLS on the API side: https://codereview.chromium.org/2064153002
BUG=skia:5502,skia:5517
GOLD_TRYBOT_URL= https://gold.skia.org/search?issue=2132113002
Review-Url: https://codereview.chromium.org/2132113002
Diffstat (limited to 'src')
-rw-r--r-- | src/core/SkLightingShader.cpp | 98 | ||||
-rw-r--r-- | src/core/SkLightingShader.h | 6 | ||||
-rw-r--r-- | src/core/SkNormalSource.cpp | 180 | ||||
-rw-r--r-- | src/core/SkNormalSource.h | 4 | ||||
-rw-r--r-- | src/gpu/GrFragmentProcessor.cpp | 40 |
5 files changed, 280 insertions, 48 deletions
diff --git a/src/core/SkLightingShader.cpp b/src/core/SkLightingShader.cpp index 02f14b34dd..92f41ad8c8 100644 --- a/src/core/SkLightingShader.cpp +++ b/src/core/SkLightingShader.cpp @@ -21,15 +21,11 @@ /* SkLightingShader TODOs: - support other than clamp mode - allow 'diffuse' & 'normal' to be of different dimensions? support different light types support multiple lights - enforce normal map is 4 channel - use SkImages instead if SkBitmaps + fix non-opaque diffuse textures To Test: - non-opaque diffuse textures A8 diffuse textures down & upsampled draws */ @@ -81,6 +77,7 @@ public: private: SkShader::Context* fDiffuseContext; SkNormalSource::Provider* fNormalProvider; + SkColor fPaintColor; uint32_t fFlags; void* fHeapAllocated; @@ -121,6 +118,9 @@ private: #include "SkGr.h" #include "SkGrPriv.h" +// This FP expects a premul'd color input for its diffuse color. Premul'ing of the paint's color is +// handled by the asFragmentProcessor() factory, but shaders providing diffuse color must output it +// premul'd. class LightingFP : public GrFragmentProcessor { public: LightingFP(sk_sp<GrFragmentProcessor> normalFP, sk_sp<SkLights> lights) { @@ -180,9 +180,13 @@ public: lightDirUniName); // diffuse light fragBuilder->codeAppendf("vec3 result = %s*diffuseColor.rgb*NdotL;", lightColorUniName); - // ambient light - fragBuilder->codeAppendf("result += %s;", ambientColorUniName); - fragBuilder->codeAppendf("%s = vec4(result.rgb, diffuseColor.a);", args.fOutputColor); + // ambient light (multiplied by input color's alpha because we're working in premul'd + // space) + fragBuilder->codeAppendf("result += diffuseColor.a * %s;", ambientColorUniName); + + // Clamping to alpha (equivalent to an unpremul'd clamp to 1.0) + fragBuilder->codeAppendf("%s = vec4(clamp(result.rgb, 0.0, diffuseColor.a), " + "diffuseColor.a);", args.fOutputColor); } static void GenKey(const GrProcessor& proc, const GrGLSLCaps&, @@ -270,18 +274,25 @@ sk_sp<GrFragmentProcessor> SkLightingShaderImpl::asFragmentProcessor( return nullptr; } - sk_sp<GrFragmentProcessor> fpPipeline[] = { + if (fDiffuseShader) { + sk_sp<GrFragmentProcessor> fpPipeline[] = { fDiffuseShader->asFragmentProcessor(context, viewM, localMatrix, filterQuality, gammaTreatment), sk_make_sp<LightingFP>(std::move(normalFP), fLights) - }; - if(!fpPipeline[0]) { - return nullptr; - } - - sk_sp<GrFragmentProcessor> inner(GrFragmentProcessor::RunInSeries(fpPipeline, 2)); + }; + if(!fpPipeline[0]) { + return nullptr; + } - return GrFragmentProcessor::MulOutputByInputAlpha(std::move(inner)); + sk_sp<GrFragmentProcessor> innerLightFP = GrFragmentProcessor::RunInSeries(fpPipeline, 2); + // FP is wrapped because paint's alpha needs to be applied to output + return GrFragmentProcessor::MulOutputByInputAlpha(std::move(innerLightFP)); + } else { + // FP is wrapped because paint comes in unpremul'd to fragment shader, but LightingFP + // expects premul'd color. + return GrFragmentProcessor::PremulInput(sk_make_sp<LightingFP>(std::move(normalFP), + fLights)); + } } #endif @@ -289,7 +300,7 @@ sk_sp<GrFragmentProcessor> SkLightingShaderImpl::asFragmentProcessor( //////////////////////////////////////////////////////////////////////////// bool SkLightingShaderImpl::isOpaque() const { - return fDiffuseShader->isOpaque(); + return (fDiffuseShader ? fDiffuseShader->isOpaque() : false); } SkLightingShaderImpl::LightingShaderContext::LightingShaderContext( @@ -308,13 +319,16 @@ SkLightingShaderImpl::LightingShaderContext::LightingShaderContext( flags |= kOpaqueAlpha_Flag; } + fPaintColor = rec.fPaint->getColor(); fFlags = flags; } SkLightingShaderImpl::LightingShaderContext::~LightingShaderContext() { // The dependencies have been created outside of the context on memory that was allocated by // the onCreateContext() method. Call the destructors and free the memory. - fDiffuseContext->~Context(); + if (fDiffuseContext) { + fDiffuseContext->~Context(); + } fNormalProvider->~Provider(); sk_free(fHeapAllocated); @@ -352,15 +366,21 @@ void SkLightingShaderImpl::LightingShaderContext::shadeSpan(int x, int y, SkPMColor diffuse[BUFFER_MAX]; SkPoint3 normals[BUFFER_MAX]; + SkColor diffColor = fPaintColor; + do { int n = SkTMin(count, BUFFER_MAX); - fDiffuseContext->shadeSpan(x, y, diffuse, n); fNormalProvider->fillScanLine(x, y, normals, n); - for (int i = 0; i < n; ++i) { + if (fDiffuseContext) { + fDiffuseContext->shadeSpan(x, y, diffuse, n); + } - SkColor diffColor = SkUnPreMultiply::PMColorToColor(diffuse[i]); + for (int i = 0; i < n; ++i) { + if (fDiffuseContext) { + diffColor = SkUnPreMultiply::PMColorToColor(diffuse[i]); + } SkColor3f accum = SkColor3f::Make(0.0f, 0.0f, 0.0f); // This is all done in linear unpremul color space (each component 0..255.0f though) @@ -381,6 +401,7 @@ void SkLightingShaderImpl::LightingShaderContext::shadeSpan(int x, int y, } } + // convert() premultiplies the accumulate color with alpha result[i] = convert(accum, SkColorGetA(diffColor)); } @@ -430,7 +451,12 @@ sk_sp<SkFlattenable> SkLightingShaderImpl::CreateProc(SkReadBuffer& buf) { sk_sp<SkLights> lights(builder.finish()); sk_sp<SkNormalSource> normalSource(buf.readFlattenable<SkNormalSource>()); - sk_sp<SkShader> diffuseShader(buf.readFlattenable<SkShader>()); + + bool hasDiffuse = buf.readBool(); + sk_sp<SkShader> diffuseShader = nullptr; + if (hasDiffuse) { + diffuseShader = buf.readFlattenable<SkShader>(); + } return sk_make_sp<SkLightingShaderImpl>(std::move(diffuseShader), std::move(normalSource), std::move(lights)); @@ -453,7 +479,10 @@ void SkLightingShaderImpl::flatten(SkWriteBuffer& buf) const { } buf.writeFlattenable(fNormalSource.get()); - buf.writeFlattenable(fDiffuseShader.get()); + buf.writeBool(fDiffuseShader); + if (fDiffuseShader) { + buf.writeFlattenable(fDiffuseShader.get()); + } } size_t SkLightingShaderImpl::onContextSize(const ContextRec& rec) const { @@ -462,18 +491,23 @@ size_t SkLightingShaderImpl::onContextSize(const ContextRec& rec) const { SkShader::Context* SkLightingShaderImpl::onCreateContext(const ContextRec& rec, void* storage) const { - size_t heapRequired = fDiffuseShader->contextSize(rec) + + size_t heapRequired = (fDiffuseShader ? fDiffuseShader->contextSize(rec) : 0) + fNormalSource->providerSize(rec); void* heapAllocated = sk_malloc_throw(heapRequired); void* diffuseContextStorage = heapAllocated; - SkShader::Context* diffuseContext = fDiffuseShader->createContext(rec, diffuseContextStorage); - if (!diffuseContext) { - sk_free(heapAllocated); - return nullptr; + void* normalProviderStorage = (char*) diffuseContextStorage + + (fDiffuseShader ? fDiffuseShader->contextSize(rec) : 0); + + SkShader::Context *diffuseContext = nullptr; + if (fDiffuseShader) { + diffuseContext = fDiffuseShader->createContext(rec, diffuseContextStorage); + if (!diffuseContext) { + sk_free(heapAllocated); + return nullptr; + } } - void* normalProviderStorage = (char*)heapAllocated + fDiffuseShader->contextSize(rec); SkNormalSource::Provider* normalProvider = fNormalSource->asProvider(rec, normalProviderStorage); if (!normalProvider) { @@ -491,10 +525,8 @@ SkShader::Context* SkLightingShaderImpl::onCreateContext(const ContextRec& rec, sk_sp<SkShader> SkLightingShader::Make(sk_sp<SkShader> diffuseShader, sk_sp<SkNormalSource> normalSource, sk_sp<SkLights> lights) { - if (!diffuseShader || !normalSource) { - // TODO: Use paint's color in absence of a diffuseShader - // TODO: Use a default implementation of normalSource instead - return nullptr; + if (!normalSource) { + normalSource = SkNormalSource::MakeFlat(); } return sk_make_sp<SkLightingShaderImpl>(std::move(diffuseShader), std::move(normalSource), diff --git a/src/core/SkLightingShader.h b/src/core/SkLightingShader.h index bb642615a5..41e3ca2394 100644 --- a/src/core/SkLightingShader.h +++ b/src/core/SkLightingShader.h @@ -23,8 +23,10 @@ public: It returns a shader with a reference count of 1. The caller should decrement the shader's reference count when done with the shader. It is an error for count to be < 2. - @param diffuseShader the shader that provides the colors - @param normalSource the source for the shape's normals + @param diffuseShader the shader that provides the colors. If nullptr, uses the paint's + color. + @param normalSource the source for the shape's normals. If nullptr, assumes straight + up normals (<0,0,1>). @param lights the lights applied to the normals The lighting equation is currently: diff --git a/src/core/SkNormalSource.cpp b/src/core/SkNormalSource.cpp index ce72532d23..c082d843c1 100644 --- a/src/core/SkNormalSource.cpp +++ b/src/core/SkNormalSource.cpp @@ -10,6 +10,7 @@ #include "SkLightingShader.h" #include "SkMatrix.h" #include "SkNormalSource.h" +#include "SkPM4f.h" #include "SkReadBuffer.h" #include "SkWriteBuffer.h" @@ -46,15 +47,19 @@ protected: private: class Provider : public SkNormalSource::Provider { public: - Provider(const NormalMapSourceImpl& source, SkShader::Context* fMapContext); + Provider(const NormalMapSourceImpl& source, SkShader::Context* mapContext, + SkPaint* overridePaint); virtual ~Provider() override; void fillScanLine(int x, int y, SkPoint3 output[], int count) const override; + private: const NormalMapSourceImpl& fSource; SkShader::Context* fMapContext; + SkPaint* fOverridePaint; + typedef SkNormalSource::Provider INHERITED; }; @@ -105,17 +110,14 @@ public: fragBuilder->codeAppendf("vec3 normal = normalize(%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! - // If there's no x & y components, return (0, 0, +/- 1) instead to avoid division by 0 fragBuilder->codeAppend( "if (abs(normal.z) > 0.999) {"); fragBuilder->codeAppendf(" %s = normalize(vec4(0.0, 0.0, normal.z, 0.0));", args.fOutputColor); // Else, Normalizing the transformed X and Y, while keeping constant both Z and the // vector's angle in the XY plane. This maintains the "slope" for the surface while - // appropriately rotating the normal for any anisotropic scaling that occurs. - // Here, we call scaling factor the number that must divide the transformed X and Y so + // appropriately rotating the normal regardless of any anisotropic scaling that occurs. + // Here, we call 'scaling factor' the number that must divide the transformed X and Y so // that the normal's length remains equal to 1. fragBuilder->codeAppend( "} else {"); fragBuilder->codeAppendf(" vec2 transformed = %s * normal.xy;", @@ -195,13 +197,15 @@ sk_sp<GrFragmentProcessor> NormalMapSourceImpl::asFragmentProcessor( //////////////////////////////////////////////////////////////////////////// NormalMapSourceImpl::Provider::Provider(const NormalMapSourceImpl& source, - SkShader::Context* mapContext) + SkShader::Context* mapContext, + SkPaint* overridePaint) : fSource(source) - , fMapContext(mapContext) { -} + , fMapContext(mapContext) + , fOverridePaint(overridePaint) {} NormalMapSourceImpl::Provider::~Provider() { fMapContext->~Context(); + fOverridePaint->~SkPaint(); } SkNormalSource::Provider* NormalMapSourceImpl::asProvider( @@ -211,17 +215,24 @@ SkNormalSource::Provider* NormalMapSourceImpl::asProvider( return nullptr; } - void* mapContextStorage = (char*)storage + sizeof(Provider); - SkShader::Context* context = fMapShader->createContext(rec, mapContextStorage); + // Overriding paint's alpha because we need the normal map's RGB channels to be unpremul'd + void* paintStorage = (char*)storage + sizeof(Provider); + SkPaint* overridePaint = new (paintStorage) SkPaint(*(rec.fPaint)); + overridePaint->setAlpha(0xFF); + SkShader::ContextRec overrideRec(*overridePaint, *(rec.fMatrix), rec.fLocalMatrix, + rec.fPreferredDstType); + + void* mapContextStorage = (char*) paintStorage + sizeof(SkPaint); + SkShader::Context* context = fMapShader->createContext(overrideRec, mapContextStorage); if (!context) { return nullptr; } - return new (storage) Provider(*this, context); + return new (storage) Provider(*this, context, overridePaint); } size_t NormalMapSourceImpl::providerSize(const SkShader::ContextRec& rec) const { - return sizeof(Provider) + fMapShader->contextSize(rec); + return sizeof(Provider) + sizeof(SkPaint) + fMapShader->contextSize(rec); } bool NormalMapSourceImpl::computeNormTotalInverse(const SkShader::ContextRec& rec, @@ -253,8 +264,10 @@ void NormalMapSourceImpl::Provider::fillScanLine(int x, int y, SkPoint3 output[] tempNorm.set(SkIntToScalar(SkGetPackedR32(tmpNormalColors[i])) - 127.0f, SkIntToScalar(SkGetPackedG32(tmpNormalColors[i])) - 127.0f, SkIntToScalar(SkGetPackedB32(tmpNormalColors[i])) - 127.0f); + tempNorm.normalize(); + if (!SkScalarNearlyEqual(SkScalarAbs(tempNorm.fZ), 1.0f)) { SkVector transformed = fSource.fInvCTM.mapVector(tempNorm.fX, tempNorm.fY); @@ -316,10 +329,151 @@ sk_sp<SkNormalSource> SkNormalSource::MakeFromNormalMap(sk_sp<SkShader> map, con return sk_make_sp<NormalMapSourceImpl>(std::move(map), invCTM); } +/////////////////////////////////////////////////////////////////////////////// + +class SK_API NormalFlatSourceImpl : public SkNormalSource { +public: + NormalFlatSourceImpl(){} + +#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(NormalFlatSourceImpl) + +protected: + void flatten(SkWriteBuffer& buf) const override; + +private: + class Provider : public SkNormalSource::Provider { + public: + Provider(); + + virtual ~Provider(); + + void fillScanLine(int x, int y, SkPoint3 output[], int count) const override; + + private: + typedef SkNormalSource::Provider INHERITED; + }; + + friend class SkNormalSource; + + typedef SkNormalSource INHERITED; +}; + +//////////////////////////////////////////////////////////////////////////// + +#if SK_SUPPORT_GPU + +class NormalFlatFP : public GrFragmentProcessor { +public: + NormalFlatFP() { + this->initClassID<NormalFlatFP>(); + } + + class GLSLNormalFlatFP : public GrGLSLFragmentProcessor { + public: + GLSLNormalFlatFP() {} + + void emitCode(EmitArgs& args) override { + GrGLSLFragmentBuilder* fragBuilder = args.fFragBuilder; + + fragBuilder->codeAppendf("%s = vec4(0, 0, 1, 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 {} + }; + + void onGetGLSLProcessorKey(const GrGLSLCaps& caps, GrProcessorKeyBuilder* b) const override { + GLSLNormalFlatFP::GenKey(*this, caps, b); + } + + const char* name() const override { return "NormalFlatFP"; } + + void onComputeInvariantOutput(GrInvariantOutput* inout) const override { + inout->setToUnknown(GrInvariantOutput::ReadInput::kWillNot_ReadInput); + } + +private: + GrGLSLFragmentProcessor* onCreateGLSLInstance() const override { return new GLSLNormalFlatFP; } + + bool onIsEqual(const GrFragmentProcessor& proc) const override { + return true; + } +}; + +sk_sp<GrFragmentProcessor> NormalFlatSourceImpl::asFragmentProcessor( + GrContext *context, + const SkMatrix &viewM, + const SkMatrix *localMatrix, + SkFilterQuality filterQuality, + SkSourceGammaTreatment gammaTreatment) const { + + return sk_make_sp<NormalFlatFP>(); +} + +#endif // SK_SUPPORT_GPU + +//////////////////////////////////////////////////////////////////////////// + +NormalFlatSourceImpl::Provider::Provider() {} + +NormalFlatSourceImpl::Provider::~Provider() {} + +SkNormalSource::Provider* NormalFlatSourceImpl::asProvider(const SkShader::ContextRec &rec, + void *storage) const { + return new (storage) Provider(); +} + +size_t NormalFlatSourceImpl::providerSize(const SkShader::ContextRec&) const { + return sizeof(Provider); +} + +void NormalFlatSourceImpl::Provider::fillScanLine(int x, int y, SkPoint3 output[], + int count) const { + for (int i = 0; i < count; i++) { + output[i] = {0.0f, 0.0f, 1.0f}; + } +} + +//////////////////////////////////////////////////////////////////////////////// + +sk_sp<SkFlattenable> NormalFlatSourceImpl::CreateProc(SkReadBuffer& buf) { + return sk_make_sp<NormalFlatSourceImpl>(); +} + +void NormalFlatSourceImpl::flatten(SkWriteBuffer& buf) const { + this->INHERITED::flatten(buf); +} + +//////////////////////////////////////////////////////////////////////////// + +sk_sp<SkNormalSource> SkNormalSource::MakeFlat() { + return sk_make_sp<NormalFlatSourceImpl>(); +} + +//////////////////////////////////////////////////////////////////////////// + //////////////////////////////////////////////////////////////////////////// SK_DEFINE_FLATTENABLE_REGISTRAR_GROUP_START(SkNormalSource) SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(NormalMapSourceImpl) + SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(NormalFlatSourceImpl) SK_DEFINE_FLATTENABLE_REGISTRAR_GROUP_END //////////////////////////////////////////////////////////////////////////// diff --git a/src/core/SkNormalSource.h b/src/core/SkNormalSource.h index 7517770800..e46e2dae34 100644 --- a/src/core/SkNormalSource.h +++ b/src/core/SkNormalSource.h @@ -65,6 +65,10 @@ public: */ static sk_sp<SkNormalSource> MakeFromNormalMap(sk_sp<SkShader> map, const SkMatrix& ctm); + /** Returns a normal source that provides straight-up normals only (0, 0, 1). + */ + static sk_sp<SkNormalSource> MakeFlat(); + SK_DEFINE_FLATTENABLE_TYPE(SkNormalSource) SK_DECLARE_FLATTENABLE_REGISTRAR_GROUP() }; diff --git a/src/gpu/GrFragmentProcessor.cpp b/src/gpu/GrFragmentProcessor.cpp index bad7ebe036..fdba610356 100644 --- a/src/gpu/GrFragmentProcessor.cpp +++ b/src/gpu/GrFragmentProcessor.cpp @@ -143,6 +143,46 @@ sk_sp<GrFragmentProcessor> GrFragmentProcessor::MulOutputByInputAlpha( SkXfermode::kDstIn_Mode); } +sk_sp<GrFragmentProcessor> GrFragmentProcessor::PremulInput(sk_sp<GrFragmentProcessor> fp) { + + class PremulInputFragmentProcessor : public GrFragmentProcessor { + public: + PremulInputFragmentProcessor() { + this->initClassID<PremulInputFragmentProcessor>(); + } + + const char* name() const override { return "PremultiplyInput"; } + + private: + GrGLSLFragmentProcessor* onCreateGLSLInstance() const override { + class GLFP : public GrGLSLFragmentProcessor { + public: + void emitCode(EmitArgs& args) override { + GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder; + + fragBuilder->codeAppendf("%s = %s;", args.fOutputColor, args.fInputColor); + fragBuilder->codeAppendf("%s.rgb *= %s.a;", + args.fOutputColor, args.fInputColor); + } + }; + return new GLFP; + } + + void onGetGLSLProcessorKey(const GrGLSLCaps&, GrProcessorKeyBuilder*) const override {} + + bool onIsEqual(const GrFragmentProcessor&) const override { return true; } + + void onComputeInvariantOutput(GrInvariantOutput* inout) const override { + inout->premulFourChannelColor(); + } + }; + if (!fp) { + return nullptr; + } + sk_sp<GrFragmentProcessor> fpPipeline[] = { sk_make_sp<PremulInputFragmentProcessor>(), fp}; + return GrFragmentProcessor::RunInSeries(fpPipeline, 2); +} + sk_sp<GrFragmentProcessor> GrFragmentProcessor::MulOutputByInputUnpremulColor( sk_sp<GrFragmentProcessor> fp) { |