GrFP can express distance vector field req., program builder declares variable for it

This update allows fragment processors to require a field of vectors to the nearest edge. This requirement propagates:

- from child FPs to their parent
- from parent FPs to the GrPaint
- from GrPaint through the PipelineBuilder into GrPipeline
- acessed from GrPipeline by GrGLSLProgramBuilder

GrGLSL generates a variable for the distance vector and passes it down to the GeometryProcessor->emitCode() method.

This CL's base is the CL for adding the BevelNormalSource API: https://codereview.chromium.org/2080993002

BUG=skia:
GOLD_TRYBOT_URL= https://gold.skia.org/search?issue=2114993002

Committed: https://skia.googlesource.com/skia/+/4ef6dfa7089c092c67b0d5ec34e89c1e319af196
Review-Url: https://codereview.chromium.org/2114993002
This commit is contained in:
dvonbeck 2016-08-01 11:01:56 -07:00 committed by Commit bot
parent 6be452c800
commit 9b03e7b29d
22 changed files with 158 additions and 21 deletions

View File

@ -197,6 +197,7 @@
'<(skia_src_path)/core/SkNormalFlatSource.h',
'<(skia_src_path)/core/SkNormalSource.cpp',
'<(skia_src_path)/core/SkNormalSource.h',
'<(skia_src_path)/core/SkNormalSourcePriv.h',
'<(skia_src_path)/core/SkNx.h',
'<(skia_src_path)/core/SkOpts.cpp',
'<(skia_src_path)/core/SkOpts.h',

View File

@ -16,12 +16,14 @@
'include_dirs': [
'../include/effects',
'../include/client/android',
'../include/gpu',
'../include/images',
'../include/ports',
'../include/private',
'../include/utils',
'../include/utils/win',
'../src/core',
'../src/gpu',
'../src/image',
'../src/lazy',
'../src/ports',

View File

@ -65,6 +65,7 @@ public:
GrFragmentProcessor()
: INHERITED()
, fUsesDistanceVectorField(false)
, fUsesLocalCoords(false)
, fNumTexturesExclChildren(0)
, fNumBuffersExclChildren(0)
@ -110,6 +111,9 @@ public:
/** Do any of the coordtransforms for this processor require local coords? */
bool usesLocalCoords() const { return fUsesLocalCoords; }
/** Does this FP need a vector to the nearest edge? */
bool usesDistanceVectorField() const { return fUsesDistanceVectorField; }
/** Returns true if this and other processor conservatively draw identically. It can only return
true when the two processor are of the same subclass (i.e. they return the same object from
from getFactory()).
@ -173,6 +177,11 @@ protected:
*/
virtual void onComputeInvariantOutput(GrInvariantOutput* inout) const = 0;
/* Sub-classes should set this to true in their constructors if they need access to a distance
* vector field to the nearest edge
*/
bool fUsesDistanceVectorField;
private:
void notifyRefCntIsZero() const final;

View File

@ -77,6 +77,11 @@ public:
void setAllowSRGBInputs(bool allowSRGBInputs) { fAllowSRGBInputs = allowSRGBInputs; }
bool getAllowSRGBInputs() const { return fAllowSRGBInputs; }
/**
* Does one of the fragment processors need a field of distance vectors to the nearest edge?
*/
bool usesDistanceVectorField() const { return fUsesDistanceVectorField; }
/**
* Should rendering be gamma-correct, end-to-end. Causes sRGB render targets to behave
* as such (with linear blending), and sRGB inputs to be filtered and decoded correctly.
@ -101,6 +106,7 @@ public:
*/
void addColorFragmentProcessor(sk_sp<GrFragmentProcessor> fp) {
SkASSERT(fp);
fUsesDistanceVectorField |= fp->usesDistanceVectorField();
fColorFragmentProcessors.push_back(std::move(fp));
}
@ -109,6 +115,7 @@ public:
*/
void addCoverageFragmentProcessor(sk_sp<GrFragmentProcessor> fp) {
SkASSERT(fp);
fUsesDistanceVectorField |= fp->usesDistanceVectorField();
fCoverageFragmentProcessors.push_back(std::move(fp));
}
@ -142,6 +149,7 @@ public:
fAntiAlias = paint.fAntiAlias;
fDisableOutputConversionToSRGB = paint.fDisableOutputConversionToSRGB;
fAllowSRGBInputs = paint.fAllowSRGBInputs;
fUsesDistanceVectorField = paint.fUsesDistanceVectorField;
fColor = paint.fColor;
fColorFragmentProcessors = paint.fColorFragmentProcessors;
@ -168,6 +176,7 @@ private:
bool fAntiAlias;
bool fDisableOutputConversionToSRGB;
bool fAllowSRGBInputs;
bool fUsesDistanceVectorField;
GrColor4f fColor;
};

View File

@ -8,6 +8,7 @@
#include "SkNormalBevelSource.h"
#include "SkNormalSource.h"
#include "SkNormalSourcePriv.h"
#include "SkPoint3.h"
#include "SkReadBuffer.h"
#include "SkWriteBuffer.h"
@ -25,17 +26,19 @@ public:
, fWidth(width)
, fHeight(height) {
this->initClassID<NormalBevelFP>();
fUsesDistanceVectorField = true;
}
class GLSLNormalBevelFP : public GrGLSLFragmentProcessor {
class GLSLNormalBevelFP : public GLSLNormalFP {
public:
GLSLNormalBevelFP() {
fPrevWidth = SkFloatToScalar(0.0f);
fPrevHeight = SkFloatToScalar(0.0f);
}
void emitCode(EmitArgs& args) override {
GrGLSLFragmentBuilder* fragBuilder = args.fFragBuilder;
void onEmitCode(EmitArgs& args) override {
GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
const char* widthUniName = nullptr;
@ -46,7 +49,7 @@ public:
fHeightUni = uniformHandler->addUniform(kFragment_GrShaderFlag, kFloat_GrSLType,
kDefault_GrSLPrecision, "Height", &heightUniName);
fragBuilder->codeAppendf("%s = vec4(0, 0, 1, 0);", args.fOutputColor);
fragBuilder->codeAppendf("%s = vec4(0.0, 0.0, 1.0, 0.0);", args.fOutputColor);
}
static void GenKey(const GrProcessor& proc, const GrGLSLCaps&,
@ -56,7 +59,8 @@ public:
}
protected:
void onSetData(const GrGLSLProgramDataManager& pdman, const GrProcessor& proc) override {
void setNormalData(const GrGLSLProgramDataManager& pdman,
const GrProcessor& proc) override {
const NormalBevelFP& normalBevelFP = proc.cast<NormalBevelFP>();
if (fPrevWidth != normalBevelFP.fWidth) {

View File

@ -8,6 +8,7 @@
#include "SkNormalFlatSource.h"
#include "SkNormalSource.h"
#include "SkNormalSourcePriv.h"
#include "SkPoint3.h"
#include "SkReadBuffer.h"
#include "SkWriteBuffer.h"
@ -23,12 +24,12 @@ public:
this->initClassID<NormalFlatFP>();
}
class GLSLNormalFlatFP : public GrGLSLFragmentProcessor {
class GLSLNormalFlatFP : public GLSLNormalFP {
public:
GLSLNormalFlatFP() {}
void emitCode(EmitArgs& args) override {
GrGLSLFragmentBuilder* fragBuilder = args.fFragBuilder;
void onEmitCode(EmitArgs& args) override {
GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
fragBuilder->codeAppendf("%s = vec4(0, 0, 1, 0);", args.fOutputColor);
}
@ -39,7 +40,8 @@ public:
}
protected:
void onSetData(const GrGLSLProgramDataManager& pdman, const GrProcessor& proc) override {}
void setNormalData(const GrGLSLProgramDataManager& pdman,
const GrProcessor& proc) override {}
};
void onGetGLSLProcessorKey(const GrGLSLCaps& caps, GrProcessorKeyBuilder* b) const override {

View File

@ -10,6 +10,7 @@
#include "SkLightingShader.h"
#include "SkMatrix.h"
#include "SkNormalSource.h"
#include "SkNormalSourcePriv.h"
#include "SkPM4f.h"
#include "SkReadBuffer.h"
#include "SkWriteBuffer.h"
@ -31,13 +32,13 @@ public:
this->initClassID<NormalMapFP>();
}
class GLSLNormalMapFP : public GrGLSLFragmentProcessor {
class GLSLNormalMapFP : public GLSLNormalFP {
public:
GLSLNormalMapFP()
: fColumnMajorInvCTM22{0.0f} {}
void emitCode(EmitArgs& args) override {
GrGLSLFragmentBuilder* fragBuilder = args.fFragBuilder;
void onEmitCode(EmitArgs& args) override {
GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
// add uniform
@ -78,7 +79,8 @@ public:
}
protected:
void onSetData(const GrGLSLProgramDataManager& pdman, const GrProcessor& proc) override {
void setNormalData(const GrGLSLProgramDataManager& pdman,
const GrProcessor& proc) override {
const NormalMapFP& normalMapFP = proc.cast<NormalMapFP>();
const SkMatrix& invCTM = normalMapFP.invCTM();

View File

@ -111,7 +111,7 @@ public:
/** Returns a normal source that generates a bevel for the given shape. UNIMPLEMENTED: Will
return straight-up normals only.
@param type the type of bevel to add
@param type the type of bevel to add.
@param width the width of the bevel, in source space. Must be positive.
@param height the height of the plateau, in source space. Can be positive, negative,
or zero. A negative height means the simulated bevels slope downwards.

View File

@ -0,0 +1,57 @@
/*
* Copyright 2016 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#ifndef SkNormalSourcePriv_DEFINED
#define SkNormalSourcePriv_DEFINED
#if SK_SUPPORT_GPU
#include "glsl/GrGLSLFragmentProcessor.h"
#include "glsl/GrGLSLFragmentShaderBuilder.h"
/* GLSLFragmentProcessors for NormalSourceImpls must sub-class this class and override onEmitCode,
* and setNormalData calls, as well as all other calls FPs normally override, except for the 2
* defined in this superclass.
* This class exists to intercept emitCode calls and emit <0, 0, 1> if the FP requires a distance
* vector but the GP doesn't provide it. onSetData calls need to be intercepted too because
* uniform handlers will be invalid in subclasses where onEmitCode isn't called.
* We don't need to adjust the key here since the use of a given GP (through its class ID already in
* the key), will determine what code gets emitted here.
*/
class GLSLNormalFP : public GrGLSLFragmentProcessor {
public:
GLSLNormalFP()
: fDidIntercept(false) {}
void emitCode(EmitArgs& args) final override {
if (args.fFp.usesDistanceVectorField() && !args.fGpImplementsDistanceVector) {
GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
fragBuilder->codeAppendf("// GLSLNormalFP intercepted emitCode call, GP does not "
"implement required distance vector feature\n");
fragBuilder->codeAppendf("%s = vec4(0, 0, 1, 0);", args.fOutputColor);
fDidIntercept = true;
} else {
this->onEmitCode(args);
}
}
void onSetData(const GrGLSLProgramDataManager& pdman, const GrProcessor& proc) final override {
if (!fDidIntercept) {
this->setNormalData(pdman, proc);
}
}
protected:
virtual void onEmitCode(EmitArgs& args) = 0;
virtual void setNormalData(const GrGLSLProgramDataManager& pdman, const GrProcessor& proc) = 0;
private:
bool fDidIntercept;
};
#endif
#endif

View File

@ -106,6 +106,9 @@ int GrFragmentProcessor::registerChildProcessor(sk_sp<GrFragmentProcessor> child
if (child->usesLocalCoords()) {
fUsesLocalCoords = true;
}
if (child->usesDistanceVectorField()) {
fUsesDistanceVectorField = true;
}
int index = fChildProcessors.count();
fChildProcessors.push_back(child.release());

View File

@ -16,6 +16,7 @@ GrPaint::GrPaint()
: fAntiAlias(false)
, fDisableOutputConversionToSRGB(false)
, fAllowSRGBInputs(false)
, fUsesDistanceVectorField(false)
, fColor(GrColor4f::FromGrColor(GrColor_WHITE)) {}
void GrPaint::setCoverageSetOpXPFactory(SkRegion::Op regionOp, bool invertCoverage) {

View File

@ -48,6 +48,9 @@ GrPipeline* GrPipeline::CreateAt(void* memory, const CreateArgs& args,
if (builder.getAllowSRGBInputs()) {
pipeline->fFlags |= kAllowSRGBInputs_Flag;
}
if (builder.getUsesDistanceVectorField()) {
pipeline->fFlags |= kUsesDistanceVectorField_Flag;
}
if (args.fHasStencilClip) {
pipeline->fFlags |= kHasStencilClip_Flag;
}

View File

@ -160,6 +160,9 @@ public:
bool getAllowSRGBInputs() const {
return SkToBool(fFlags & kAllowSRGBInputs_Flag);
}
bool usesDistanceVectorField() const {
return SkToBool(fFlags & kUsesDistanceVectorField_Flag);
}
bool hasStencilClip() const {
return SkToBool(fFlags & kHasStencilClip_Flag);
}
@ -206,7 +209,8 @@ private:
kSnapVertices_Flag = 0x2,
kDisableOutputConversionToSRGB_Flag = 0x4,
kAllowSRGBInputs_Flag = 0x8,
kHasStencilClip_Flag = 0x10
kUsesDistanceVectorField_Flag = 0x10,
kHasStencilClip_Flag = 0x20,
};
typedef GrPendingIOResource<GrRenderTarget, kWrite_GrIOType> RenderTarget;

View File

@ -41,6 +41,8 @@ GrPipelineBuilder::GrPipelineBuilder(const GrPaint& paint, bool useHWAA)
paint.getDisableOutputConversionToSRGB());
this->setState(GrPipelineBuilder::kAllowSRGBInputs_Flag,
paint.getAllowSRGBInputs());
this->setState(GrPipelineBuilder::kUsesDistanceVectorField_Flag,
paint.usesDistanceVectorField());
}
//////////////////////////////////////////////////////////////////////////////s

View File

@ -221,7 +221,13 @@ public:
*/
kAllowSRGBInputs_Flag = 0x08,
kLast_Flag = kAllowSRGBInputs_Flag,
/**
* Signals that one or more FPs need access to the distance vector field to the nearest
* edge
*/
kUsesDistanceVectorField_Flag = 0x10,
kLast_Flag = kUsesDistanceVectorField_Flag,
};
bool isHWAntialias() const { return SkToBool(fFlags & kHWAntialias_Flag); }
@ -231,6 +237,8 @@ public:
return SkToBool(fFlags & kDisableOutputConversionToSRGB_Flag); }
bool getAllowSRGBInputs() const {
return SkToBool(fFlags & kAllowSRGBInputs_Flag); }
bool getUsesDistanceVectorField() const {
return SkToBool(fFlags & kUsesDistanceVectorField_Flag); }
/**
* Enable render state settings.

View File

@ -221,6 +221,10 @@ public:
return 0.0;
}
/* Sub-class should override and return true if this primitive processor implements the distance
* vector field, a field of vectors to the nearest point in the edge of the shape. */
virtual bool implementsDistanceVector() const { return false; }
protected:
GrPrimitiveProcessor() : fVertexStride(0) {}

View File

@ -107,7 +107,8 @@ void GrGLSLFragmentProcessor::internalEmitChild(int childIndex, const char* inpu
inputColor,
childCoords,
childTexSamplers,
childBufferSamplers);
childBufferSamplers,
args.fGpImplementsDistanceVector);
this->childProcessor(childIndex)->emitCode(childArgs);
fragBuilder->codeAppend("}\n");

View File

@ -61,7 +61,8 @@ public:
const char* inputColor,
const GrGLSLTransformedCoordsArray& coords,
const SamplerHandle* texSamplers,
const SamplerHandle* bufferSamplers)
const SamplerHandle* bufferSamplers,
bool gpImplementsDistanceVector)
: fFragBuilder(fragBuilder)
, fUniformHandler(uniformHandler)
, fGLSLCaps(caps)
@ -70,7 +71,8 @@ public:
, fInputColor(inputColor)
, fCoords(coords)
, fTexSamplers(texSamplers)
, fBufferSamplers(bufferSamplers) {}
, fBufferSamplers(bufferSamplers)
, fGpImplementsDistanceVector(gpImplementsDistanceVector){}
GrGLSLFPFragmentBuilder* fFragBuilder;
GrGLSLUniformHandler* fUniformHandler;
const GrGLSLCaps* fGLSLCaps;
@ -80,6 +82,7 @@ public:
const GrGLSLTransformedCoordsArray& fCoords;
const SamplerHandle* fTexSamplers;
const SamplerHandle* fBufferSamplers;
bool fGpImplementsDistanceVector;
};
virtual void emitCode(EmitArgs&) = 0;

View File

@ -189,6 +189,10 @@ const char* GrGLSLFragmentShaderBuilder::fragmentPosition() {
}
}
const char* GrGLSLFragmentShaderBuilder::distanceVectorName() const {
return "fsDistanceVector";
}
void GrGLSLFragmentShaderBuilder::appendOffsetToSample(const char* sampleIdx, Coordinates coords) {
SkASSERT(fProgramBuilder->header().fSamplePatternKey);
SkDEBUGCODE(fUsedProcessorFeatures |= GrProcessor::kSampleLocations_RequiredFeature);

View File

@ -96,6 +96,10 @@ public:
*/
virtual void maskSampleCoverage(const char* mask, bool invert = false) = 0;
/** Returns a variable name that represents a vector to the nearest edge of the shape, in source
space coordinates. */
virtual const char* distanceVectorName() const = 0;
/**
* Fragment procs with child procs should call these functions before/after calling emitCode
* on a child proc.
@ -166,6 +170,7 @@ public:
virtual SkString ensureFSCoords2D(const GrGLSLTransformedCoordsArray& coords,
int index) override;
const char* fragmentPosition() override;
const char* distanceVectorName() const override;
// GrGLSLFPFragmentBuilder interface.
void appendOffsetToSample(const char* sampleIdx, Coordinates) override;
@ -235,6 +240,7 @@ private:
bool fHasSecondaryOutput;
uint8_t fUsedSampleOffsetArrays;
bool fHasInitializedSampleMask;
SkString fDistanceVectorOutput;
#ifdef SK_DEBUG
// some state to verify shaders and effects are consistent, this is reset between effects by

View File

@ -42,6 +42,7 @@ public:
const GrPrimitiveProcessor& gp,
const char* outputColor,
const char* outputCoverage,
const char* distanceVectorName,
const SamplerHandle* texSamplers,
const SamplerHandle* bufferSamplers,
const TransformsIn& transformsIn,
@ -54,6 +55,7 @@ public:
, fGP(gp)
, fOutputColor(outputColor)
, fOutputCoverage(outputCoverage)
, fDistanceVectorName(distanceVectorName)
, fTexSamplers(texSamplers)
, fBufferSamplers(bufferSamplers)
, fTransformsIn(transformsIn)
@ -66,6 +68,7 @@ public:
const GrPrimitiveProcessor& fGP;
const char* fOutputColor;
const char* fOutputCoverage;
const char* fDistanceVectorName;
const SamplerHandle* fTexSamplers;
const SamplerHandle* fBufferSamplers;
const TransformsIn& fTransformsIn;
@ -78,7 +81,6 @@ public:
*/
virtual void emitCode(EmitArgs&) = 0;
/** A GrGLSLPrimitiveProcessor instance can be reused with any GrGLSLPrimitiveProcessor that
produces the same stage key; this function reads data from a GrGLSLPrimitiveProcessor and
uploads any uniform variables required by the shaders created in emitCode(). The

View File

@ -87,6 +87,13 @@ void GrGLSLProgramBuilder::emitAndInstallPrimProc(const GrPrimitiveProcessor& pr
this->nameExpression(outputColor, "outputColor");
this->nameExpression(outputCoverage, "outputCoverage");
const char* distanceVectorName = nullptr;
if (this->fPipeline.usesDistanceVectorField() && proc.implementsDistanceVector()) {
distanceVectorName = fFS.distanceVectorName();
fFS.codeAppend( "// Un-normalized vector to the closed geometric edge (in source space)\n");
fFS.codeAppendf("vec2 %s;", distanceVectorName);
}
// Enclose custom code in a block to avoid namespace conflicts
SkString openBrace;
openBrace.printf("{ // Stage %d, %s\n", fStageIndex, proc.name());
@ -108,6 +115,7 @@ void GrGLSLProgramBuilder::emitAndInstallPrimProc(const GrPrimitiveProcessor& pr
proc,
outputColor->c_str(),
outputCoverage->c_str(),
distanceVectorName,
texSamplers.begin(),
bufferSamplers.begin(),
fCoordTransforms,
@ -161,7 +169,9 @@ void GrGLSLProgramBuilder::emitAndInstallFragProc(const GrFragmentProcessor& fp,
input.isOnes() ? nullptr : input.c_str(),
fOutCoords[index],
texSamplers.begin(),
bufferSamplers.begin());
bufferSamplers.begin(),
this->primitiveProcessor().implementsDistanceVector());
fragProc->emitCode(args);
// We have to check that effects and the code they emit are consistent, ie if an effect