| /* |
| * Copyright 2014 Google Inc. |
| * |
| * Use of this source code is governed by a BSD-style license that can be |
| * found in the LICENSE file. |
| */ |
| |
| #ifndef GrXferProcessor_DEFINED |
| #define GrXferProcessor_DEFINED |
| |
| #include "include/core/SkRefCnt.h" |
| #include "include/gpu/GrTypes.h" |
| #include "include/private/base/SkAssert.h" |
| #include "include/private/base/SkTo.h" |
| #include "src/gpu/Blend.h" |
| #include "src/gpu/Swizzle.h" |
| #include "src/gpu/ganesh/GrCaps.h" |
| #include "src/gpu/ganesh/GrNonAtomicRef.h" |
| #include "src/gpu/ganesh/GrProcessor.h" |
| #include "src/gpu/ganesh/GrProcessorAnalysis.h" |
| #include "src/gpu/ganesh/glsl/GrGLSLUniformHandler.h" |
| |
| #include <memory> |
| |
| class GrGLSLProgramDataManager; |
| class GrGLSLXPFragmentBuilder; |
| enum class GrClampType; |
| enum class SkBlendMode; |
| namespace skgpu { class KeyBuilder; } |
| struct GrShaderCaps; |
| |
| /** |
| * Barriers for blending. When a shader reads the dst directly, an Xfer barrier is sometimes |
| * required after a pixel has been written, before it can be safely read again. |
| */ |
| enum GrXferBarrierType { |
| kNone_GrXferBarrierType = 0, //<! No barrier is required |
| kTexture_GrXferBarrierType, //<! Required when a shader reads and renders to the same texture. |
| kBlend_GrXferBarrierType, //<! Required by certain blend extensions. |
| }; |
| /** Should be able to treat kNone as false in boolean expressions */ |
| static_assert(SkToBool(kNone_GrXferBarrierType) == false); |
| |
| // Flag version of the above enum. |
| enum class GrXferBarrierFlags { |
| kNone = 0, |
| kTexture = 1 << 0, |
| kBlend = 1 << 1, |
| }; |
| |
| GR_MAKE_BITFIELD_CLASS_OPS(GrXferBarrierFlags) |
| |
| /** |
| * GrXferProcessor is responsible for implementing the xfer mode that blends the src color and dst |
| * color, and for applying any coverage. It does this by emitting fragment shader code and |
| * controlling the fixed-function blend state. When dual-source blending is available, it may also |
| * write a secondary fragment shader output color. GrXferProcessor has two modes of operation: |
| * |
| * Dst read: When allowed by the backend API, or when supplied a texture of the destination, the |
| * GrXferProcessor may read the destination color. While operating in this mode, the subclass only |
| * provides shader code that blends the src and dst colors, and the base class applies coverage. |
| * |
| * No dst read: When not performing a dst read, the subclass is given full control of the fixed- |
| * function blend state and/or secondary output, and is responsible to apply coverage on its own. |
| * |
| * A GrXferProcessor is never installed directly into our draw state, but instead is created from a |
| * GrXPFactory once we have finalized the state of our draw. |
| */ |
| class GrXferProcessor : public GrProcessor, public GrNonAtomicRef<GrXferProcessor> { |
| public: |
| /** |
| * Every GrXferProcessor must be capable of creating a subclass of ProgramImpl. The ProgramImpl |
| * emits the shader code combines determines the fragment shader output(s) from the color and |
| * coverage FP outputs, is attached to the generated backend API pipeline/program, and used to |
| * extract uniform data from GrXferProcessor instances. |
| */ |
| class ProgramImpl; |
| |
| /** |
| * Adds a key on the skgpu::KeyBuilder calls onAddToKey(...) to get the specific subclass's key. |
| */ |
| void addToKey(const GrShaderCaps&, |
| skgpu::KeyBuilder*, |
| const GrSurfaceOrigin* originIfDstTexture, |
| bool usesInputAttachmentForDstRead) const; |
| |
| /** Returns a new instance of the appropriate *GL* implementation class |
| for the given GrXferProcessor; caller is responsible for deleting |
| the object. */ |
| virtual std::unique_ptr<ProgramImpl> makeProgramImpl() const = 0; |
| |
| /** |
| * Returns the barrier type, if any, that this XP will require. Note that the possibility |
| * that a kTexture type barrier is required is handled by the GrPipeline and need not be |
| * considered by subclass overrides of this function. |
| */ |
| virtual GrXferBarrierType xferBarrierType(const GrCaps& caps) const { |
| return kNone_GrXferBarrierType; |
| } |
| |
| inline skgpu::BlendInfo getBlendInfo() const { |
| skgpu::BlendInfo blendInfo; |
| if (!this->willReadDstColor()) { |
| this->onGetBlendInfo(&blendInfo); |
| } |
| return blendInfo; |
| } |
| |
| bool willReadDstColor() const { return fWillReadDstColor; } |
| |
| /** |
| * Returns whether or not this xferProcossor will set a secondary output to be used with dual |
| * source blending. |
| */ |
| bool hasSecondaryOutput() const; |
| |
| bool isLCD() const { return fIsLCD; } |
| |
| /** 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()). |
| |
| A return value of true from isEqual() should not be used to test whether the processor would |
| generate the same shader code. To test for identical code generation use addToKey. |
| */ |
| |
| bool isEqual(const GrXferProcessor& that) const { |
| if (this->classID() != that.classID()) { |
| return false; |
| } |
| if (this->fWillReadDstColor != that.fWillReadDstColor) { |
| return false; |
| } |
| if (fIsLCD != that.fIsLCD) { |
| return false; |
| } |
| return this->onIsEqual(that); |
| } |
| |
| protected: |
| GrXferProcessor(ClassID classID); |
| GrXferProcessor(ClassID classID, bool willReadDstColor, GrProcessorAnalysisCoverage); |
| |
| private: |
| /** |
| * Adds a key on the skgpu::KeyBuilder that reflects any variety in the code that may be emitted |
| * by the xfer processor subclass. |
| */ |
| virtual void onAddToKey(const GrShaderCaps&, skgpu::KeyBuilder*) const = 0; |
| |
| /** |
| * If we are not performing a dst read, returns whether the subclass will set a secondary |
| * output. When using dst reads, the base class controls the secondary output and this method |
| * will not be called. |
| */ |
| virtual bool onHasSecondaryOutput() const { return false; } |
| |
| /** |
| * If we are not performing a dst read, retrieves the fixed-function blend state required by the |
| * subclass. When using dst reads, the base class controls the fixed-function blend state and |
| * this method will not be called. The BlendInfo struct comes initialized to "no blending". |
| */ |
| virtual void onGetBlendInfo(skgpu::BlendInfo*) const {} |
| |
| virtual bool onIsEqual(const GrXferProcessor&) const = 0; |
| |
| bool fWillReadDstColor; |
| bool fIsLCD; |
| |
| using INHERITED = GrProcessor; |
| }; |
| |
| /** |
| * We install a GrXPFactory (XPF) early on in the pipeline before all the final draw information is |
| * known (e.g. whether there is fractional pixel coverage, will coverage be 1 or 4 channel, is the |
| * draw opaque, etc.). Once the state of the draw is finalized, we use the XPF along with all the |
| * draw information to create a GrXferProcessor (XP) which can implement the desired blending for |
| * the draw. |
| * |
| * Before the XP is created, the XPF is able to answer queries about what functionality the XPs it |
| * creates will have. For example, can it create an XP that supports RGB coverage or will the XP |
| * blend with the destination color. |
| * |
| * GrXPFactories are intended to be static immutable objects. We pass them around as raw pointers |
| * and expect the pointers to always be valid and for the factories to be reusable and thread safe. |
| * Equality is tested for using pointer comparison. GrXPFactory destructors must be no-ops. |
| */ |
| |
| // In order to construct GrXPFactory subclass instances as constexpr the subclass, and therefore |
| // GrXPFactory, must be a literal type. One requirement is having a trivial destructor. This is ok |
| // since these objects have no need for destructors. However, GCC and clang throw a warning when a |
| // class has virtual functions and a non-virtual destructor. We suppress that warning here and |
| // for the subclasses. |
| #if defined(__GNUC__) |
| #pragma GCC diagnostic push |
| #pragma GCC diagnostic ignored "-Wnon-virtual-dtor" |
| #endif |
| #if defined(__clang__) |
| #pragma clang diagnostic push |
| #pragma clang diagnostic ignored "-Wnon-virtual-dtor" |
| #endif |
| class GrXPFactory { |
| public: |
| enum class AnalysisProperties : unsigned { |
| kNone = 0x0, |
| /** |
| * The fragment shader will require the destination color. |
| */ |
| kReadsDstInShader = 0x1, |
| /** |
| * The op may apply coverage as alpha and still blend correctly. |
| */ |
| kCompatibleWithCoverageAsAlpha = 0x2, |
| /** |
| * The color input to the GrXferProcessor will be ignored. |
| */ |
| kIgnoresInputColor = 0x4, |
| /** |
| * The destination color will be provided to the fragment processor using a texture. This is |
| * additional information about the implementation of kReadsDstInShader. |
| */ |
| kRequiresDstTexture = 0x10, |
| /** |
| * If set, each pixel can only be touched once during a draw (e.g., because we have a dst |
| * texture or because we need an xfer barrier). |
| */ |
| kRequiresNonOverlappingDraws = 0x20, |
| /** |
| * If set the draw will use fixed function non coherent advanced blends. |
| */ |
| kUsesNonCoherentHWBlending = 0x40, |
| /** |
| * If set, the existing dst value has no effect on the final output. |
| */ |
| kUnaffectedByDstValue = 0x80, |
| }; |
| GR_DECL_BITFIELD_CLASS_OPS_FRIENDS(AnalysisProperties); |
| |
| static sk_sp<const GrXferProcessor> MakeXferProcessor(const GrXPFactory*, |
| const GrProcessorAnalysisColor&, |
| GrProcessorAnalysisCoverage, |
| const GrCaps& caps, |
| GrClampType); |
| |
| static AnalysisProperties GetAnalysisProperties(const GrXPFactory*, |
| const GrProcessorAnalysisColor&, |
| const GrProcessorAnalysisCoverage&, |
| const GrCaps&, |
| GrClampType); |
| |
| static const GrXPFactory* FromBlendMode(SkBlendMode); |
| |
| protected: |
| constexpr GrXPFactory() {} |
| |
| private: |
| virtual sk_sp<const GrXferProcessor> makeXferProcessor(const GrProcessorAnalysisColor&, |
| GrProcessorAnalysisCoverage, |
| const GrCaps&, |
| GrClampType) const = 0; |
| |
| /** |
| * Subclass analysis implementation. This should not return kNeedsDstInTexture as that will be |
| * inferred by the base class based on kReadsDstInShader and the caps. |
| */ |
| virtual AnalysisProperties analysisProperties(const GrProcessorAnalysisColor&, |
| const GrProcessorAnalysisCoverage&, |
| const GrCaps&, |
| GrClampType) const = 0; |
| }; |
| #if defined(__GNUC__) |
| #pragma GCC diagnostic pop |
| #endif |
| #if defined(__clang__) |
| #pragma clang diagnostic pop |
| #endif |
| |
| GR_MAKE_BITFIELD_CLASS_OPS(GrXPFactory::AnalysisProperties) |
| |
| ////////////////////////////////////////////////////////////////////////////// |
| |
| class GrXferProcessor::ProgramImpl { |
| public: |
| virtual ~ProgramImpl() = default; |
| |
| using SamplerHandle = GrGLSLUniformHandler::SamplerHandle; |
| |
| struct EmitArgs { |
| EmitArgs(GrGLSLXPFragmentBuilder* fragBuilder, |
| GrGLSLUniformHandler* uniformHandler, |
| const GrShaderCaps* caps, |
| const GrXferProcessor& xp, |
| const char* inputColor, |
| const char* inputCoverage, |
| const char* outputPrimary, |
| const char* outputSecondary, |
| const SamplerHandle dstTextureSamplerHandle, |
| GrSurfaceOrigin dstTextureOrigin, |
| const skgpu::Swizzle& writeSwizzle) |
| : fXPFragBuilder(fragBuilder) |
| , fUniformHandler(uniformHandler) |
| , fShaderCaps(caps) |
| , fXP(xp) |
| , fInputColor(inputColor ? inputColor : "half4(1.0)") |
| , fInputCoverage(inputCoverage) |
| , fOutputPrimary(outputPrimary) |
| , fOutputSecondary(outputSecondary) |
| , fDstTextureSamplerHandle(dstTextureSamplerHandle) |
| , fDstTextureOrigin(dstTextureOrigin) |
| , fWriteSwizzle(writeSwizzle) {} |
| GrGLSLXPFragmentBuilder* fXPFragBuilder; |
| GrGLSLUniformHandler* fUniformHandler; |
| const GrShaderCaps* fShaderCaps; |
| const GrXferProcessor& fXP; |
| const char* fInputColor; |
| const char* fInputCoverage; |
| const char* fOutputPrimary; |
| const char* fOutputSecondary; |
| const SamplerHandle fDstTextureSamplerHandle; |
| GrSurfaceOrigin fDstTextureOrigin; |
| skgpu::Swizzle fWriteSwizzle; |
| }; |
| /** |
| * This is similar to emitCode() in the base class, except it takes a full shader builder. |
| * This allows the effect subclass to emit vertex code. |
| */ |
| void emitCode(const EmitArgs&); |
| |
| /** A ProgramImpl instance can be reused with any GrXferProcessor that produces the same key. |
| This function reads data from a GrXferProcessor and uploads any uniform variables required |
| by the shaders created in emitCode(). The GrXferProcessor parameter is guaranteed to be of |
| the same type that created this ProgramImpl and to have an identical processor key as the |
| one that created this ProgramImpl. This function calls onSetData on the subclass of |
| ProgramImpl. |
| */ |
| void setData(const GrGLSLProgramDataManager& pdm, const GrXferProcessor& xp); |
| |
| protected: |
| ProgramImpl() = default; |
| |
| static void DefaultCoverageModulation(GrGLSLXPFragmentBuilder* fragBuilder, |
| const char* srcCoverage, |
| const char* dstColor, |
| const char* outColor, |
| const char* outColorSecondary, |
| const GrXferProcessor& proc); |
| |
| private: |
| /** |
| * Called by emitCode() when the XP will not be performing a dst read. This method is |
| * responsible for both blending and coverage. A subclass only needs to implement this method if |
| * it can construct a GrXferProcessor that will not read the dst color. |
| */ |
| virtual void emitOutputsForBlendState(const EmitArgs&) { |
| SK_ABORT("emitOutputsForBlendState not implemented."); |
| } |
| |
| /** |
| * Called by emitCode() when the XP will perform a dst read. This method only needs to supply |
| * the blending logic. The base class applies coverage. A subclass only needs to implement this |
| * method if it can construct a GrXferProcessor that reads the dst color. |
| */ |
| virtual void emitBlendCodeForDstRead(GrGLSLXPFragmentBuilder*, |
| GrGLSLUniformHandler*, |
| const char* srcColor, |
| const char* srcCoverage, |
| const char* dstColor, |
| const char* outColor, |
| const char* outColorSecondary, |
| const GrXferProcessor&) { |
| SK_ABORT("emitBlendCodeForDstRead not implemented."); |
| } |
| |
| virtual void emitWriteSwizzle(GrGLSLXPFragmentBuilder*, |
| const skgpu::Swizzle&, |
| const char* outColor, |
| const char* outColorSecondary) const; |
| |
| virtual void onSetData(const GrGLSLProgramDataManager&, const GrXferProcessor&) {} |
| }; |
| |
| #endif |