From 97ae0c89025dfd791047f5701e57d58da37c125c Mon Sep 17 00:00:00 2001 From: Ethan Nicholas Date: Thu, 12 Jul 2018 14:02:00 -0400 Subject: Revert "Revert "added GrSkSLFP and converted DitherEffect to use it"" This reverts commit f2030783094e502fb74221077a5ee7cb41287fe4. Bug: skia: Change-Id: Icaaa8b3ea652a8f126bfbcc788a360493a7ebe3e Reviewed-on: https://skia-review.googlesource.com/137391 Commit-Queue: Ethan Nicholas Reviewed-by: Brian Salomon --- src/gpu/GrContext.cpp | 9 +- src/gpu/GrContextPriv.h | 5 + src/gpu/GrContextThreadSafeProxyPriv.h | 1 + src/gpu/GrDDLContext.cpp | 2 + src/gpu/GrDirectContext.cpp | 6 +- src/gpu/GrProcessor.h | 1 + src/gpu/SkGr.cpp | 86 ++++++++++- src/gpu/SkGr.h | 2 + src/gpu/effects/GrDitherEffect.cpp | 75 --------- src/gpu/effects/GrDitherEffect.fp | 85 ----------- src/gpu/effects/GrDitherEffect.h | 67 --------- src/gpu/effects/GrRectBlurEffect.cpp | 34 ++--- src/gpu/effects/GrSkSLFP.cpp | 268 +++++++++++++++++++++++++++++++++ src/gpu/effects/GrSkSLFP.h | 147 ++++++++++++++++++ src/gpu/glsl/GrGLSLShaderBuilder.h | 2 + 15 files changed, 537 insertions(+), 253 deletions(-) delete mode 100644 src/gpu/effects/GrDitherEffect.cpp delete mode 100644 src/gpu/effects/GrDitherEffect.fp delete mode 100644 src/gpu/effects/GrDitherEffect.h create mode 100644 src/gpu/effects/GrSkSLFP.cpp create mode 100644 src/gpu/effects/GrSkSLFP.h (limited to 'src/gpu') diff --git a/src/gpu/GrContext.cpp b/src/gpu/GrContext.cpp index caed7298ae..8380f0d711 100644 --- a/src/gpu/GrContext.cpp +++ b/src/gpu/GrContext.cpp @@ -37,7 +37,9 @@ #include "SkTaskGroup.h" #include "SkUnPreMultiplyPriv.h" #include "effects/GrConfigConversionEffect.h" +#include "effects/GrSkSLFP.h" #include "text/GrTextBlobCache.h" +#include #define ASSERT_OWNED_PROXY(P) \ SkASSERT(!(P) || !((P)->priv().peekTexture()) || (P)->priv().peekTexture()->getContext() == this) @@ -160,7 +162,6 @@ GrContext::~GrContext() { if (fDrawingManager) { fDrawingManager->cleanup(); } - fTextureStripAtlasManager = nullptr; delete fResourceProvider; delete fResourceCache; @@ -172,11 +173,13 @@ GrContext::~GrContext() { GrContextThreadSafeProxy::GrContextThreadSafeProxy(sk_sp caps, uint32_t uniqueID, GrBackend backend, - const GrContextOptions& options) + const GrContextOptions& options, + sk_sp cache) : fCaps(std::move(caps)) , fContextUniqueID(uniqueID) , fBackend(backend) - , fOptions(options) {} + , fOptions(options) + , fFPFactoryCache(std::move(cache)) {} GrContextThreadSafeProxy::~GrContextThreadSafeProxy() = default; diff --git a/src/gpu/GrContextPriv.h b/src/gpu/GrContextPriv.h index 4f392f88b3..1b90dafa37 100644 --- a/src/gpu/GrContextPriv.h +++ b/src/gpu/GrContextPriv.h @@ -16,6 +16,7 @@ class GrBackendRenderTarget; class GrOpMemoryPool; class GrOnFlushCallbackObject; class GrSemaphore; +class GrSkSLFPFactory; class GrSurfaceProxy; class GrTextureContext; @@ -279,6 +280,10 @@ public: GrContextOptions::PersistentCache* getPersistentCache() { return fContext->fPersistentCache; } + sk_sp getFPFactoryCache() { + return fContext->fFPFactoryCache; + } + /** This is only useful for debug purposes */ SkDEBUGCODE(GrSingleOwner* debugSingleOwner() const { return &fContext->fSingleOwner; } ) diff --git a/src/gpu/GrContextThreadSafeProxyPriv.h b/src/gpu/GrContextThreadSafeProxyPriv.h index 8e299c8180..b3a4eab021 100644 --- a/src/gpu/GrContextThreadSafeProxyPriv.h +++ b/src/gpu/GrContextThreadSafeProxyPriv.h @@ -23,6 +23,7 @@ public: sk_sp refCaps() const { return fProxy->fCaps; } uint32_t contextUniqueID() const { return fProxy->fContextUniqueID; } GrBackend backend() const { return fProxy->fBackend; } + sk_sp fpFactoryCache() const { return fProxy->fFPFactoryCache; } private: explicit GrContextThreadSafeProxyPriv(GrContextThreadSafeProxy* proxy) : fProxy(proxy) {} diff --git a/src/gpu/GrDDLContext.cpp b/src/gpu/GrDDLContext.cpp index 1ae640c3e3..eb5ed29e6b 100644 --- a/src/gpu/GrDDLContext.cpp +++ b/src/gpu/GrDDLContext.cpp @@ -19,6 +19,8 @@ public: GrDDLContext(sk_sp proxy) : INHERITED(proxy->priv().backend(), proxy->priv().contextUniqueID()) { fCaps = proxy->priv().refCaps(); + fFPFactoryCache = proxy->priv().fpFactoryCache(); + SkASSERT(fFPFactoryCache); fThreadSafeProxy = std::move(proxy); } diff --git a/src/gpu/GrDirectContext.cpp b/src/gpu/GrDirectContext.cpp index 3e4b8ef5fa..c87078b98b 100644 --- a/src/gpu/GrDirectContext.cpp +++ b/src/gpu/GrDirectContext.cpp @@ -10,6 +10,7 @@ #include "GrContextPriv.h" #include "GrGpu.h" +#include "effects/GrSkSLFP.h" #include "gl/GrGLGpu.h" #include "mock/GrMockGpu.h" #include "text/GrGlyphCache.h" @@ -58,9 +59,10 @@ protected: bool init(const GrContextOptions& options) override { SkASSERT(fCaps); // should've been set in ctor SkASSERT(!fThreadSafeProxy); - + SkASSERT(!fFPFactoryCache); + fFPFactoryCache.reset(new GrSkSLFPFactoryCache()); fThreadSafeProxy.reset(new GrContextThreadSafeProxy(fCaps, this->uniqueID(), - fBackend, options)); + fBackend, options, fFPFactoryCache)); if (!INHERITED::initCommon(options)) { return false; diff --git a/src/gpu/GrProcessor.h b/src/gpu/GrProcessor.h index 038ce16717..34f51935af 100644 --- a/src/gpu/GrProcessor.h +++ b/src/gpu/GrProcessor.h @@ -131,6 +131,7 @@ public: kGrRRectBlurEffect_ClassID, kGrRRectShadowGeoProc_ClassID, kGrSimpleTextureEffect_ClassID, + kGrSkSLFP_ClassID, kGrSpecularLightingEffect_ClassID, kGrSRGBEffect_ClassID, kGrSweepGradient_ClassID, diff --git a/src/gpu/SkGr.cpp b/src/gpu/SkGr.cpp index 5baf0d4017..9082fc4222 100644 --- a/src/gpu/SkGr.cpp +++ b/src/gpu/SkGr.cpp @@ -37,9 +37,49 @@ #include "SkTraceEvent.h" #include "effects/GrBicubicEffect.h" #include "effects/GrConstColorProcessor.h" -#include "effects/GrDitherEffect.h" #include "effects/GrPorterDuffXferProcessor.h" #include "effects/GrXfermodeFragmentProcessor.h" +#include "effects/GrSkSLFP.h" + +const char* SKSL_DITHER_SRC = R"( +// This controls the range of values added to color channels +layout(key) in int rangeType; + +void main(int x, int y, inout half4 color) { + half value; + half range; + @switch (rangeType) { + case 0: + range = 1.0 / 255.0; + break; + case 1: + range = 1.0 / 63.0; + break; + default: + // Experimentally this looks better than the expected value of 1/15. + range = 1.0 / 15.0; + break; + } + @if (sk_Caps.integerSupport) { + // This ordered-dither code is lifted from the cpu backend. + uint x = uint(x); + uint y = uint(y); + uint m = (y & 1) << 5 | (x & 1) << 4 | + (y & 2) << 2 | (x & 2) << 1 | + (y & 4) >> 1 | (x & 4) >> 2; + value = half(m) * 1.0 / 64.0 - 63.0 / 128.0; + } else { + // Simulate the integer effect used above using step/mod. For speed, simulates a 4x4 + // dither pattern rather than an 8x8 one. + half4 modValues = mod(float4(x, y, x, y), half4(2.0, 2.0, 4.0, 4.0)); + half4 stepValues = step(modValues, half4(1.0, 1.0, 2.0, 2.0)); + value = dot(stepValues, half4(8.0 / 16.0, 4.0 / 16.0, 2.0 / 16.0, 1.0 / 16.0)) - 15.0 / 32.0; + } + // For each color channel, add the random offset to the channel value and then clamp + // between 0 and alpha to keep the color premultiplied. + color = half4(clamp(color.rgb + value * range, 0.0, color.a), color.a); +} +)"; GrSurfaceDesc GrImageInfoToSurfaceDesc(const SkImageInfo& info) { GrSurfaceDesc desc; @@ -284,6 +324,39 @@ static inline bool blend_requires_shader(const SkBlendMode mode) { return SkBlendMode::kDst != mode; } +#ifndef SK_IGNORE_GPU_DITHER +static inline int32_t dither_range_type_for_config(GrPixelConfig dstConfig) { + switch (dstConfig) { + case kGray_8_GrPixelConfig: + case kGray_8_as_Lum_GrPixelConfig: + case kGray_8_as_Red_GrPixelConfig: + case kRGBA_8888_GrPixelConfig: + case kRGB_888_GrPixelConfig: + case kBGRA_8888_GrPixelConfig: + return 0; + case kRGB_565_GrPixelConfig: + return 1; + case kRGBA_4444_GrPixelConfig: + return 2; + case kUnknown_GrPixelConfig: + case kSRGBA_8888_GrPixelConfig: + case kSBGRA_8888_GrPixelConfig: + case kRGBA_1010102_GrPixelConfig: + case kAlpha_half_GrPixelConfig: + case kAlpha_half_as_Red_GrPixelConfig: + case kRGBA_float_GrPixelConfig: + case kRG_float_GrPixelConfig: + case kRGBA_half_GrPixelConfig: + case kAlpha_8_GrPixelConfig: + case kAlpha_8_as_Alpha_GrPixelConfig: + case kAlpha_8_as_Red_GrPixelConfig: + return -1; + } + SkASSERT(false); + return 0; +} +#endif + static inline bool skpaint_to_grpaint_impl(GrContext* context, const GrColorSpaceInfo& colorSpaceInfo, const SkPaint& skPaint, @@ -411,9 +484,14 @@ static inline bool skpaint_to_grpaint_impl(GrContext* context, SkColorType ct = SkColorType::kRGB_565_SkColorType; GrPixelConfigToColorType(colorSpaceInfo.config(), &ct); if (SkPaintPriv::ShouldDither(skPaint, ct) && grPaint->numColorFragmentProcessors() > 0) { - auto ditherFP = GrDitherEffect::Make(colorSpaceInfo.config()); - if (ditherFP) { - grPaint->addColorFragmentProcessor(std::move(ditherFP)); + int32_t ditherRange = dither_range_type_for_config(colorSpaceInfo.config()); + if (ditherRange >= 0) { + static int ditherIndex = GrSkSLFP::NewIndex(); + auto ditherFP = GrSkSLFP::Make(context, ditherIndex, "Dither", SKSL_DITHER_SRC, + &ditherRange, sizeof(ditherRange)); + if (ditherFP) { + grPaint->addColorFragmentProcessor(std::move(ditherFP)); + } } } #endif diff --git a/src/gpu/SkGr.h b/src/gpu/SkGr.h index ec9f30a3bf..394b2f2156 100644 --- a/src/gpu/SkGr.h +++ b/src/gpu/SkGr.h @@ -38,6 +38,8 @@ class SkPixelRef; class SkPixmap; struct SkIRect; +extern const char* SKSL_DITHER_SRC; + //////////////////////////////////////////////////////////////////////////////// // Color type conversions diff --git a/src/gpu/effects/GrDitherEffect.cpp b/src/gpu/effects/GrDitherEffect.cpp deleted file mode 100644 index 17c8776524..0000000000 --- a/src/gpu/effects/GrDitherEffect.cpp +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Copyright 2018 Google Inc. - * - * 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 GrDitherEffect.fp; do not modify. - **************************************************************************************************/ -#include "GrDitherEffect.h" -#include "glsl/GrGLSLFragmentProcessor.h" -#include "glsl/GrGLSLFragmentShaderBuilder.h" -#include "glsl/GrGLSLProgramBuilder.h" -#include "GrTexture.h" -#include "SkSLCPP.h" -#include "SkSLUtil.h" -class GrGLSLDitherEffect : public GrGLSLFragmentProcessor { -public: - GrGLSLDitherEffect() {} - void emitCode(EmitArgs& args) override { - GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder; - const GrDitherEffect& _outer = args.fFp.cast(); - (void)_outer; - auto rangeType = _outer.rangeType(); - (void)rangeType; - fragBuilder->codeAppendf( - "half value;\nhalf range;\n@switch (%d) {\n case 0:\n range = " - "0.0039215686274509803;\n break;\n case 1:\n range = " - "0.015873015873015872;\n break;\n default:\n range = " - "0.066666666666666666;\n break;\n}\n@if (sk_Caps.integerSupport) {\n " - "uint x = uint(sk_FragCoord.x);\n uint y = uint(sk_FragCoord.y);\n uint m = " - "(((((y & 1) << 5 | (x & 1) << 4) | (y & 2) << 2) | (x & 2) << 1) | (y & 4) >> 1) " - "| (x & 4) >> 2;\n value = float(float(half(m)) / 64.0) - 0.4", - _outer.rangeType()); - fragBuilder->codeAppendf( - "921875;\n} else {\n half4 modValues = half4(mod(sk_FragCoord.xyxy, " - "float4(half4(2.0, 2.0, 4.0, 4.0))));\n half4 stepValues = " - "half4(step(float4(modValues), float4(half4(1.0, 1.0, 2.0, 2.0))));\n value = " - "float(dot(stepValues, half4(0.5, 0.25, 0.125, 0.0625))) - 0.46875;\n}\n%s = " - "half4(clamp(float3(%s.xyz + value * range), 0.0, float(%s.w)), %s.w);\n", - args.fOutputColor, args.fInputColor ? args.fInputColor : "half4(1)", - args.fInputColor ? args.fInputColor : "half4(1)", - args.fInputColor ? args.fInputColor : "half4(1)"); - } - -private: - void onSetData(const GrGLSLProgramDataManager& pdman, - const GrFragmentProcessor& _proc) override {} -}; -GrGLSLFragmentProcessor* GrDitherEffect::onCreateGLSLInstance() const { - return new GrGLSLDitherEffect(); -} -void GrDitherEffect::onGetGLSLProcessorKey(const GrShaderCaps& caps, - GrProcessorKeyBuilder* b) const { - b->add32((int32_t)fRangeType); -} -bool GrDitherEffect::onIsEqual(const GrFragmentProcessor& other) const { - const GrDitherEffect& that = other.cast(); - (void)that; - if (fRangeType != that.fRangeType) return false; - return true; -} -GrDitherEffect::GrDitherEffect(const GrDitherEffect& src) - : INHERITED(kGrDitherEffect_ClassID, src.optimizationFlags()), fRangeType(src.fRangeType) {} -std::unique_ptr GrDitherEffect::clone() const { - return std::unique_ptr(new GrDitherEffect(*this)); -} -GR_DEFINE_FRAGMENT_PROCESSOR_TEST(GrDitherEffect); -#if GR_TEST_UTILS -std::unique_ptr GrDitherEffect::TestCreate(GrProcessorTestData* testData) { - float range = testData->fRandom->nextRangeF(0.001f, 0.05f); - return std::unique_ptr(new GrDitherEffect(range)); -} -#endif diff --git a/src/gpu/effects/GrDitherEffect.fp b/src/gpu/effects/GrDitherEffect.fp deleted file mode 100644 index ed6c0e6b70..0000000000 --- a/src/gpu/effects/GrDitherEffect.fp +++ /dev/null @@ -1,85 +0,0 @@ -/* - * Copyright 2018 Google Inc. - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -// This controls the range of values added to color channels -layout(key) in int rangeType; - -@make { - static std::unique_ptr Make(GrPixelConfig dstConfig) { - int rangeType; - switch (dstConfig) { - case kGray_8_GrPixelConfig: - case kGray_8_as_Lum_GrPixelConfig: - case kGray_8_as_Red_GrPixelConfig: - case kRGBA_8888_GrPixelConfig: - case kRGB_888_GrPixelConfig: - case kBGRA_8888_GrPixelConfig: - rangeType = 0; - break; - case kRGB_565_GrPixelConfig: - rangeType = 1; - break; - case kRGBA_4444_GrPixelConfig: - rangeType = 2; - break; - case kUnknown_GrPixelConfig: - case kSRGBA_8888_GrPixelConfig: - case kSBGRA_8888_GrPixelConfig: - case kRGBA_1010102_GrPixelConfig: - case kAlpha_half_GrPixelConfig: - case kAlpha_half_as_Red_GrPixelConfig: - case kRGBA_float_GrPixelConfig: - case kRG_float_GrPixelConfig: - case kRGBA_half_GrPixelConfig: - case kAlpha_8_GrPixelConfig: - case kAlpha_8_as_Alpha_GrPixelConfig: - case kAlpha_8_as_Red_GrPixelConfig: - return nullptr; - } - return std::unique_ptr(new GrDitherEffect(rangeType)); - } -} - -void main() { - half value; - half range; - @switch (rangeType) { - case 0: - range = 1.0 / 255.0; - break; - case 1: - range = 1.0 / 63.0; - break; - default: - // Experimentally this looks better than the expected value of 1/15. - range = 1.0 / 15.0; - break; - } - @if (sk_Caps.integerSupport) { - // This ordered-dither code is lifted from the cpu backend. - uint x = uint(sk_FragCoord.x); - uint y = uint(sk_FragCoord.y); - uint m = (y & 1) << 5 | (x & 1) << 4 | - (y & 2) << 2 | (x & 2) << 1 | - (y & 4) >> 1 | (x & 4) >> 2; - value = half(m) * 1.0 / 64.0 - 63.0 / 128.0; - } else { - // Simulate the integer effect used above using step/mod. For speed, simulates a 4x4 - // dither pattern rather than an 8x8 one. - half4 modValues = mod(sk_FragCoord.xyxy, half4(2.0, 2.0, 4.0, 4.0)); - half4 stepValues = step(modValues, half4(1.0, 1.0, 2.0, 2.0)); - value = dot(stepValues, half4(8.0 / 16.0, 4.0 / 16.0, 2.0 / 16.0, 1.0 / 16.0)) - 15.0 / 32.0; - } - // For each color channel, add the random offset to the channel value and then clamp - // between 0 and alpha to keep the color premultiplied. - sk_OutColor = half4(clamp(sk_InColor.rgb + value * range, 0, sk_InColor.a), sk_InColor.a); -} - -@test(testData) { - float range = testData->fRandom->nextRangeF(0.001f, 0.05f); - return std::unique_ptr(new GrDitherEffect(range)); -} diff --git a/src/gpu/effects/GrDitherEffect.h b/src/gpu/effects/GrDitherEffect.h deleted file mode 100644 index 70adc45e6a..0000000000 --- a/src/gpu/effects/GrDitherEffect.h +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright 2018 Google Inc. - * - * 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 GrDitherEffect.fp; do not modify. - **************************************************************************************************/ -#ifndef GrDitherEffect_DEFINED -#define GrDitherEffect_DEFINED -#include "SkTypes.h" -#include "GrFragmentProcessor.h" -#include "GrCoordTransform.h" -class GrDitherEffect : public GrFragmentProcessor { -public: - int rangeType() const { return fRangeType; } - - static std::unique_ptr Make(GrPixelConfig dstConfig) { - int rangeType; - switch (dstConfig) { - case kGray_8_GrPixelConfig: - case kGray_8_as_Lum_GrPixelConfig: - case kGray_8_as_Red_GrPixelConfig: - case kRGBA_8888_GrPixelConfig: - case kRGB_888_GrPixelConfig: - case kBGRA_8888_GrPixelConfig: - rangeType = 0; - break; - case kRGB_565_GrPixelConfig: - rangeType = 1; - break; - case kRGBA_4444_GrPixelConfig: - rangeType = 2; - break; - case kUnknown_GrPixelConfig: - case kSRGBA_8888_GrPixelConfig: - case kSBGRA_8888_GrPixelConfig: - case kRGBA_1010102_GrPixelConfig: - case kAlpha_half_GrPixelConfig: - case kAlpha_half_as_Red_GrPixelConfig: - case kRGBA_float_GrPixelConfig: - case kRG_float_GrPixelConfig: - case kRGBA_half_GrPixelConfig: - case kAlpha_8_GrPixelConfig: - case kAlpha_8_as_Alpha_GrPixelConfig: - case kAlpha_8_as_Red_GrPixelConfig: - return nullptr; - } - return std::unique_ptr(new GrDitherEffect(rangeType)); - } - GrDitherEffect(const GrDitherEffect& src); - std::unique_ptr clone() const override; - const char* name() const override { return "DitherEffect"; } - -private: - GrDitherEffect(int rangeType) - : INHERITED(kGrDitherEffect_ClassID, kNone_OptimizationFlags), fRangeType(rangeType) {} - GrGLSLFragmentProcessor* onCreateGLSLInstance() const override; - void onGetGLSLProcessorKey(const GrShaderCaps&, GrProcessorKeyBuilder*) const override; - bool onIsEqual(const GrFragmentProcessor&) const override; - GR_DECLARE_FRAGMENT_PROCESSOR_TEST - int fRangeType; - typedef GrFragmentProcessor INHERITED; -}; -#endif diff --git a/src/gpu/effects/GrRectBlurEffect.cpp b/src/gpu/effects/GrRectBlurEffect.cpp index 219fefa713..d423b786ce 100644 --- a/src/gpu/effects/GrRectBlurEffect.cpp +++ b/src/gpu/effects/GrRectBlurEffect.cpp @@ -46,13 +46,13 @@ public: fProfileSizeVar = args.fUniformHandler->addUniform(kFragment_GrShaderFlag, kHalf_GrSLType, kDefault_GrSLPrecision, "profileSize"); fragBuilder->codeAppendf( - "bool highPrecision = %s;\n@if (highPrecision) {\n float2 translatedPos = " - "sk_FragCoord.xy - %s.xy;\n float width = %s.z - %s.x;\n float height = %s.w " - "- %s.y;\n float2 smallDims = float2(width - float(%s), height - float(%s));\n " - " float center = 2.0 * floor(float(float(%s / 2.0) + 0.25)) - 1.0;\n float2 wh " - "= smallDims - float2(center, center);\n half hcoord = " - "half((abs(translatedPos.x - 0.5 * width) - 0.5 * wh.x) / float(%s));\n half " - "hlookup = texture(%s, float2(float(hcoord), 0.5)).%s.w", + "/* key */ bool highPrecision = %s;\n@if (highPrecision) {\n float2 " + "translatedPos = sk_FragCoord.xy - %s.xy;\n float width = %s.z - %s.x;\n " + "float height = %s.w - %s.y;\n float2 smallDims = float2(width - float(%s), " + "height - float(%s));\n float center = 2.0 * floor(float(float(%s / 2.0) + " + "0.25)) - 1.0;\n float2 wh = smallDims - float2(center, center);\n half " + "hcoord = half((abs(translatedPos.x - 0.5 * width) - 0.5 * wh.x) / float(%s));\n " + " half hlookup = texture(%s, float2(float(hcoord), ", (highPrecision ? "true" : "false"), args.fUniformHandler->getUniformCStr(fRectVar), args.fUniformHandler->getUniformCStr(fRectVar), args.fUniformHandler->getUniformCStr(fRectVar), @@ -62,16 +62,16 @@ public: args.fUniformHandler->getUniformCStr(fProfileSizeVar), args.fUniformHandler->getUniformCStr(fProfileSizeVar), args.fUniformHandler->getUniformCStr(fProfileSizeVar), - fragBuilder->getProgramBuilder()->samplerVariable(args.fTexSamplers[0]).c_str(), - fragBuilder->getProgramBuilder()->samplerSwizzle(args.fTexSamplers[0]).c_str()); + fragBuilder->getProgramBuilder()->samplerVariable(args.fTexSamplers[0]).c_str()); fragBuilder->codeAppendf( - ";\n half vcoord = half((abs(translatedPos.y - 0.5 * height) - 0.5 * wh.y) / " - "float(%s));\n half vlookup = texture(%s, float2(float(vcoord), 0.5)).%s.w;\n " - " %s = (%s * hlookup) * vlookup;\n} else {\n half2 translatedPos = " - "half2(sk_FragCoord.xy - %s.xy);\n half width = half(%s.z - %s.x);\n half " - "height = half(%s.w - %s.y);\n half2 smallDims = half2(width - %s, height - " - "%s);\n half center = half(2.0 * floor(float(float(%s / 2.0) + 0.25)) - 1.0);\n " - " half2 wh = smallDims - half2(float2(floa", + "0.5)).%s.w;\n half vcoord = half((abs(translatedPos.y - 0.5 * height) - 0.5 * " + "wh.y) / float(%s));\n half vlookup = texture(%s, float2(float(vcoord), " + "0.5)).%s.w;\n %s = (%s * hlookup) * vlookup;\n} else {\n half2 " + "translatedPos = half2(sk_FragCoord.xy - %s.xy);\n half width = half(%s.z - " + "%s.x);\n half height = half(%s.w - %s.y);\n half2 smallDims = half2(width - " + "%s, height - %s);\n half center = half(2.0 * floor(float(float(%s / 2.0) + " + "0.25)) - 1.0);\n half2 wh = smallDims - half2(f", + fragBuilder->getProgramBuilder()->samplerSwizzle(args.fTexSamplers[0]).c_str(), args.fUniformHandler->getUniformCStr(fProfileSizeVar), fragBuilder->getProgramBuilder()->samplerVariable(args.fTexSamplers[0]).c_str(), fragBuilder->getProgramBuilder()->samplerSwizzle(args.fTexSamplers[0]).c_str(), @@ -85,7 +85,7 @@ public: args.fUniformHandler->getUniformCStr(fProfileSizeVar), args.fUniformHandler->getUniformCStr(fProfileSizeVar)); fragBuilder->codeAppendf( - "t(center), float(center)));\n half hcoord = " + "loat2(float(center), float(center)));\n half hcoord = " "half((abs(float(float(translatedPos.x) - 0.5 * float(width))) - 0.5 * " "float(wh.x)) / float(%s));\n half hlookup = texture(%s, float2(float(hcoord), " "0.5)).%s.w;\n half vcoord = half((abs(float(float(translatedPos.y) - 0.5 * " diff --git a/src/gpu/effects/GrSkSLFP.cpp b/src/gpu/effects/GrSkSLFP.cpp new file mode 100644 index 0000000000..bc84c833d7 --- /dev/null +++ b/src/gpu/effects/GrSkSLFP.cpp @@ -0,0 +1,268 @@ +/* + * Copyright 2018 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "GrSkSLFP.h" +#include "glsl/GrGLSLFragmentProcessor.h" +#include "glsl/GrGLSLFragmentShaderBuilder.h" +#include "glsl/GrGLSLProgramBuilder.h" +#include "GrContext.h" +#include "GrContextPriv.h" +#include "GrTexture.h" +#include "SkSLUtil.h" + +GrSkSLFPFactory::GrSkSLFPFactory(const char* name, const GrShaderCaps* shaderCaps, const char* sksl) + : fName(name) { + SkSL::Program::Settings settings; + settings.fCaps = shaderCaps; + fBaseProgram = fCompiler.convertProgram(SkSL::Program::kPipelineStage_Kind, + SkSL::String(sksl), + settings); + if (fCompiler.errorCount()) { + SkDebugf("%s\n", fCompiler.errorText().c_str()); + } + SkASSERT(fBaseProgram); + SkASSERT(!fCompiler.errorCount()); + for (const auto& e : *fBaseProgram) { + if (e.fKind == SkSL::ProgramElement::kVar_Kind) { + SkSL::VarDeclarations& v = (SkSL::VarDeclarations&) e; + for (const auto& varStatement : v.fVars) { + const SkSL::Variable& var = *((SkSL::VarDeclaration&) *varStatement).fVar; + if (var.fModifiers.fFlags & SkSL::Modifiers::kIn_Flag) { + fInputVars.push_back(&var); + } + if (var.fModifiers.fLayout.fKey) { + fKeyVars.push_back(&var); + } + } + } + } +} + +const SkSL::Program* GrSkSLFPFactory::getSpecialization(const SkSL::String& key, const void* inputs, + size_t inputSize) { + const auto& found = fSpecializations.find(key); + if (found != fSpecializations.end()) { + return found->second.get(); + } + + std::unordered_map inputMap; + size_t offset = 0; + for (const auto& v : fInputVars) { + SkSL::String name(v->fName); + if (&v->fType == fCompiler.context().fInt_Type.get()) { + offset = SkAlign4(offset); + int32_t v = *(int32_t*) (((uint8_t*) inputs) + offset); + inputMap.insert(std::make_pair(name, SkSL::Program::Settings::Value(v))); + offset += sizeof(int32_t); + } + } + SkASSERT(offset == inputSize); + + std::unique_ptr specialized = fCompiler.specialize(*fBaseProgram, inputMap); + SkAssertResult(fCompiler.optimize(*specialized)); + const SkSL::Program* result = specialized.get(); + fSpecializations.insert(std::make_pair(key, std::move(specialized))); + return result; +} + +class GrGLSLSkSLFP : public GrGLSLFragmentProcessor { +public: + GrGLSLSkSLFP(SkSL::String glsl, std::vector formatArgs) + : fGLSL(glsl) + , fFormatArgs(formatArgs) {} + + void emitCode(EmitArgs& args) override { + GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder; + int substringStartIndex = 0; + int formatArgIndex = 0; + for (size_t i = 0; i < fGLSL.length(); ++i) { + char c = fGLSL[i]; + if (c == '%') { + fragBuilder->codeAppend(fGLSL.c_str() + substringStartIndex, + i - substringStartIndex); + ++i; + c = fGLSL[i]; + switch (c) { + case 's': + switch (fFormatArgs[formatArgIndex++]) { + case SkSL::Compiler::FormatArg::kInput: + fragBuilder->codeAppend(args.fInputColor ? args.fInputColor + : "half4(1)"); + break; + case SkSL::Compiler::FormatArg::kOutput: + fragBuilder->codeAppend(args.fOutputColor); + break; + } + break; + default: + fragBuilder->codeAppendf("%c", c); + } + substringStartIndex = i + 1; + } + } + fragBuilder->codeAppend(fGLSL.c_str() + substringStartIndex, + fGLSL.length() - substringStartIndex); + } + + // nearly-finished GLSL; still contains printf-style "%s" format tokens + const SkSL::String fGLSL; + std::vector fFormatArgs; +}; + +std::unique_ptr GrSkSLFP::Make(GrContext* context, int index, const char* name, + const char* sksl, const void* inputs, + size_t inputSize) { + return std::unique_ptr(new GrSkSLFP( + context->contextPriv().getFPFactoryCache(), + context->contextPriv().caps()->shaderCaps(), + index, name, sksl, inputs, inputSize)); +} + + +GrSkSLFP::GrSkSLFP(sk_sp factoryCache, const GrShaderCaps* shaderCaps, + int index, const char* name, const char* sksl, const void* inputs, + size_t inputSize) + : INHERITED(kGrSkSLFP_ClassID, kNone_OptimizationFlags) + , fFactoryCache(factoryCache) + , fShaderCaps(sk_ref_sp(shaderCaps)) + , fIndex(index) + , fName(name) + , fSkSL(sksl) + , fInputs(new int8_t[inputSize]) + , fInputSize(inputSize) { + memcpy(fInputs.get(), inputs, inputSize); +} + +GrSkSLFP::GrSkSLFP(const GrSkSLFP& other) + : INHERITED(kGrSkSLFP_ClassID, kNone_OptimizationFlags) + , fFactoryCache(other.fFactoryCache) + , fShaderCaps(other.fShaderCaps) + , fFactory(other.fFactory) + , fIndex(other.fIndex) + , fName(other.fName) + , fSkSL(other.fSkSL) + , fInputs(new int8_t[other.fInputSize]) + , fInputSize(other.fInputSize) { + memcpy(fInputs.get(), other.fInputs.get(), fInputSize); +} + +const char* GrSkSLFP::name() const { + return fName; +} + +void GrSkSLFP::createFactory() const { + if (!fFactory) { + fFactory = fFactoryCache->get(fIndex); + if (!fFactory) { + fFactory = sk_sp(new GrSkSLFPFactory(fName, fShaderCaps.get(), fSkSL)); + fFactoryCache->set(fIndex, fFactory); + } + } +} + +GrGLSLFragmentProcessor* GrSkSLFP::onCreateGLSLInstance() const { + this->createFactory(); + const SkSL::Program* specialized = fFactory->getSpecialization(fKey, fInputs.get(), fInputSize); + SkSL::String glsl; + std::vector formatArgs; + if (!fFactory->fCompiler.toPipelineStage(*specialized, &glsl, &formatArgs)) { + printf("%s\n", fFactory->fCompiler.errorText().c_str()); + abort(); + } + return new GrGLSLSkSLFP(glsl, formatArgs); +} + +void GrSkSLFP::onGetGLSLProcessorKey(const GrShaderCaps& caps, + GrProcessorKeyBuilder* b) const { + this->createFactory(); + size_t offset = 0; + char* inputs = (char*) fInputs.get(); + for (const auto& v : fFactory->fInputVars) { + if (&v->fType == fFactory->fCompiler.context().fInt_Type.get()) { + offset = SkAlign4(offset); + if (v->fModifiers.fLayout.fKey) { + fKey += inputs[offset + 0]; + fKey += inputs[offset + 1]; + fKey += inputs[offset + 2]; + fKey += inputs[offset + 3]; + b->add32(*(int32_t*) (inputs + offset)); + } + offset += sizeof(int32_t); + } + else { + // unsupported input var type + SkASSERT(false); + } + } + SkASSERT(offset == fInputSize); +} + +bool GrSkSLFP::onIsEqual(const GrFragmentProcessor& other) const { + const GrSkSLFP& sk = other.cast(); + SkASSERT(fIndex != sk.fIndex || fInputSize == sk.fInputSize); + return fIndex == sk.fIndex && + !memcmp(fInputs.get(), sk.fInputs.get(), fInputSize); +} + +std::unique_ptr GrSkSLFP::clone() const { + return std::unique_ptr(new GrSkSLFP(*this)); +} + +// We have to do a bit of manual refcounting in the cache methods below. Ideally, we could just +// define fFactories to contain sk_sp rather than GrSkSLFPFactory*, but that would +// require GrContext to include GrSkSLFP, which creates much bigger headaches than a few manual +// refcounts. + +sk_sp GrSkSLFPFactoryCache::get(int index) { + if (index >= (int) fFactories.size()) { + return nullptr; + } + GrSkSLFPFactory* result = fFactories[index]; + result->ref(); + return sk_sp(result); +} + +void GrSkSLFPFactoryCache::set(int index, sk_sp factory) { + while (index >= (int) fFactories.size()) { + fFactories.emplace_back(); + } + factory->ref(); + SkASSERT(!fFactories[index]); + fFactories[index] = factory.get(); +} + +GrSkSLFPFactoryCache::~GrSkSLFPFactoryCache() { + for (GrSkSLFPFactory* factory : fFactories) { + if (factory) { + factory->unref(); + } + } +} + +GR_DEFINE_FRAGMENT_PROCESSOR_TEST(GrSkSLFP); + +#if GR_TEST_UTILS + +#include "SkGr.h" + +using Value = SkSL::Program::Settings::Value; + +std::unique_ptr GrSkSLFP::TestCreate(GrProcessorTestData* d) { + int type = d->fRandom->nextULessThan(1); + switch (type) { + case 0: { + static int ditherIndex = NewIndex(); + int rangeType = d->fRandom->nextULessThan(3); + return GrSkSLFP::Make(d->context(), ditherIndex, "Dither", SKSL_DITHER_SRC, &rangeType, + sizeof(rangeType)); + } + } + SK_ABORT("unreachable"); + return nullptr; +} + +#endif diff --git a/src/gpu/effects/GrSkSLFP.h b/src/gpu/effects/GrSkSLFP.h new file mode 100644 index 0000000000..428e0892e0 --- /dev/null +++ b/src/gpu/effects/GrSkSLFP.h @@ -0,0 +1,147 @@ +/* + * Copyright 2018 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef GrSkSLFP_DEFINED +#define GrSkSLFP_DEFINED + +#include "GrCaps.h" +#include "GrFragmentProcessor.h" +#include "GrCoordTransform.h" +#include "GrShaderCaps.h" +#include "SkSLCompiler.h" +#include "SkSLPipelineStageCodeGenerator.h" +#include "SkRefCnt.h" +#include "../private/GrSkSLFPFactoryCache.h" + +class GrContext; +class GrSkSLFPFactory; + +class GrSkSLFP : public GrFragmentProcessor { +public: + /** + * Returns a new unique identifier. Each different SkSL fragment processor should call + * NewIndex once, statically, and use this index for all calls to Make. + */ + static int NewIndex() { + static int index = 0; + return sk_atomic_inc(&index); + } + + /** + * Creates a new fragment processor from an SkSL source string and a struct of inputs to the + * program. The input struct's type is derived from the 'in' variables in the SkSL source, so + * e.g. the shader: + * + * in bool dither; + * in float x; + * in float y; + * .... + * + * would expect a pointer to a struct set up like: + * + * struct { + * bool dither; + * float x; + * float y; + * }; + * + * As turning SkSL into GLSL / SPIR-V / etc. is fairly expensive, and the output may differ + * based on the inputs, internally the process is divided into two steps: we first parse and + * semantically analyze the SkSL into an internal representation, and then "specialize" this + * internal representation based on the inputs. The unspecialized internal representation of + * the program is cached, so further specializations of the same code are much faster than the + * first call. + * + * This caching is based on the 'index' parameter, which should be derived by statically calling + * 'NewIndex()'. Each given SkSL string should have a single, statically defined index + * associated with it. + */ + static std::unique_ptr Make( + GrContext* context, + int index, + const char* name, + const char* sksl, + const void* inputs, + size_t inputSize); + + const char* name() const override; + + std::unique_ptr clone() const override; + +private: + GrSkSLFP(sk_sp factoryCache, const GrShaderCaps* shaderCaps, int fIndex, + const char* name, const char* sksl, const void* inputs, size_t inputSize); + + GrSkSLFP(const GrSkSLFP& other); + + GrGLSLFragmentProcessor* onCreateGLSLInstance() const override; + + void onGetGLSLProcessorKey(const GrShaderCaps&, GrProcessorKeyBuilder*) const override; + + bool onIsEqual(const GrFragmentProcessor&) const override; + + void createFactory() const; + + sk_sp fFactoryCache; + + const sk_sp fShaderCaps; + + mutable sk_sp fFactory; + + int fIndex; + + const char* fName; + + const char* fSkSL; + + const std::unique_ptr fInputs; + + size_t fInputSize; + + mutable SkSL::String fKey; + + GR_DECLARE_FRAGMENT_PROCESSOR_TEST + + typedef GrFragmentProcessor INHERITED; + + friend class GrSkSLFPFactory; +}; + +/** + * Produces GrFragmentProcessors from SkSL code. As the shader code produced from the SkSL depends + * upon the inputs to the SkSL (static if's, etc.) we first create a factory for a given SkSL + * string, then use that to create the actual GrFragmentProcessor. + */ +class GrSkSLFPFactory : public SkNVRefCnt { +public: + /** + * Constructs a GrSkSLFPFactory for a given SkSL source string. Creating a factory will + * preprocess the SkSL and determine which of its inputs are declared "key" (meaning they cause + * the produced shaders to differ), so it is important to reuse the same factory instance for + * the same shader in order to avoid repeatedly re-parsing the SkSL. + */ + GrSkSLFPFactory(const char* name, const GrShaderCaps* shaderCaps, const char* sksl); + + const SkSL::Program* getSpecialization(const SkSL::String& key, const void* inputs, + size_t inputSize); + + const char* fName; + + SkSL::Compiler fCompiler; + + std::shared_ptr fBaseProgram; + + std::vector fInputVars; + + std::vector fKeyVars; + + std::unordered_map> fSpecializations; + + friend class GrSkSLFP; +}; + +#endif diff --git a/src/gpu/glsl/GrGLSLShaderBuilder.h b/src/gpu/glsl/GrGLSLShaderBuilder.h index 538300d95a..f61174f2d7 100644 --- a/src/gpu/glsl/GrGLSLShaderBuilder.h +++ b/src/gpu/glsl/GrGLSLShaderBuilder.h @@ -102,6 +102,8 @@ public: void codeAppend(const char* str) { this->code().append(str); } + void codeAppend(const char* str, size_t length) { this->code().append(str, length); } + void codePrependf(const char format[], ...) SK_PRINTF_LIKE(2, 3) { va_list args; va_start(args, format); -- cgit v1.2.3