/* * Copyright 2013 Google Inc. * * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ #ifndef GrPrimitiveProcessor_DEFINED #define GrPrimitiveProcessor_DEFINED #include "GrColor.h" #include "GrNonAtomicRef.h" #include "GrProcessor.h" #include "GrProxyRef.h" #include "GrShaderVar.h" class GrCoordTransform; /* * The GrPrimitiveProcessor represents some kind of geometric primitive. This includes the shape * of the primitive and the inherent color of the primitive. The GrPrimitiveProcessor is * responsible for providing a color and coverage input into the Ganesh rendering pipeline. Through * optimization, Ganesh may decide a different color, no color, and / or no coverage are required * from the GrPrimitiveProcessor, so the GrPrimitiveProcessor must be able to support this * functionality. * * There are two feedback loops between the GrFragmentProcessors, the GrXferProcessor, and the * GrPrimitiveProcessor. These loops run on the CPU and to determine known properties of the final * color and coverage inputs to the GrXferProcessor in order to perform optimizations that preserve * correctness. The GrDrawOp seeds these loops with initial color and coverage, in its * getProcessorAnalysisInputs implementation. These seed values are processed by the * subsequent * stages of the rendering pipeline and the output is then fed back into the GrDrawOp in * the applyPipelineOptimizations call, where the op can use the information to inform decisions * about GrPrimitiveProcessor creation. */ class GrGLSLPrimitiveProcessor; /** * GrPrimitiveProcessor defines an interface which all subclasses must implement. All * GrPrimitiveProcessors must proivide seed color and coverage for the Ganesh color / coverage * pipelines, and they must provide some notion of equality * * TODO: This class does not really need to be ref counted. Instances should be allocated using * GrOpFlushState's arena and destroyed when the arena is torn down. */ class GrPrimitiveProcessor : public GrProcessor, public GrNonAtomicRef { public: class TextureSampler; /** Describes a vertex or instance attribute. */ class Attribute { public: constexpr Attribute() = default; constexpr Attribute(const char* name, GrVertexAttribType cpuType, GrSLType gpuType) : fName(name), fCPUType(cpuType), fGPUType(gpuType) {} constexpr Attribute(const Attribute&) = default; Attribute& operator=(const Attribute&) = default; constexpr bool isInitialized() const { return SkToBool(fName); } constexpr const char* name() const { return fName; } constexpr GrVertexAttribType cpuType() const { return fCPUType; } constexpr GrSLType gpuType() const { return fGPUType; } inline constexpr size_t size() const; constexpr size_t sizeAlign4() const { return SkAlign4(this->size()); } GrShaderVar asShaderVar() const { return {fName, fGPUType, GrShaderVar::kIn_TypeModifier}; } private: const char* fName = nullptr; GrVertexAttribType fCPUType = kFloat_GrVertexAttribType; GrSLType fGPUType = kFloat_GrSLType; }; class Iter { public: Iter() : fCurr(nullptr), fRemaining(0) {} Iter(const Iter& iter) : fCurr(iter.fCurr), fRemaining(iter.fRemaining) {} Iter& operator= (const Iter& iter) { fCurr = iter.fCurr; fRemaining = iter.fRemaining; return *this; } Iter(const Attribute* attrs, int count) : fCurr(attrs), fRemaining(count) { this->skipUninitialized(); } bool operator!=(const Iter& that) const { return fCurr != that.fCurr; } const Attribute& operator*() const { return *fCurr; } void operator++() { if (fRemaining) { fRemaining--; fCurr++; this->skipUninitialized(); } } private: void skipUninitialized() { if (!fRemaining) { fCurr = nullptr; } else { while (!fCurr->isInitialized()) { ++fCurr; } } } const Attribute* fCurr; int fRemaining; }; class AttributeSet { public: Iter begin() const { return Iter(fAttributes, fCount); } Iter end() const { return Iter(); } private: friend class GrPrimitiveProcessor; void init(const Attribute* attrs, int count) { fAttributes = attrs; fRawCount = count; fCount = 0; fStride = 0; for (int i = 0; i < count; ++i) { if (attrs[i].isInitialized()) { fCount++; fStride += attrs[i].sizeAlign4(); } } } const Attribute* fAttributes = nullptr; int fRawCount = 0; int fCount = 0; size_t fStride = 0; }; GrPrimitiveProcessor(ClassID); int numTextureSamplers() const { return fTextureSamplerCnt; } const TextureSampler& textureSampler(int index) const; int numVertexAttributes() const { return fVertexAttributes.fCount; } const AttributeSet& vertexAttributes() const { return fVertexAttributes; } int numInstanceAttributes() const { return fInstanceAttributes.fCount; } const AttributeSet& instanceAttributes() const { return fInstanceAttributes; } bool hasVertexAttributes() const { return SkToBool(fVertexAttributes.fCount); } bool hasInstanceAttributes() const { return SkToBool(fInstanceAttributes.fCount); } /** * A common practice is to populate the the vertex/instance's memory using an implicit array of * structs. In this case, it is best to assert that: * stride == sizeof(struct) */ size_t vertexStride() const { return fVertexAttributes.fStride; } size_t instanceStride() const { return fInstanceAttributes.fStride; } // Only the GrGeometryProcessor subclass actually has a geo shader or vertex attributes, but // we put these calls on the base class to prevent having to cast virtual bool willUseGeoShader() const = 0; /** * Computes a transformKey from an array of coord transforms. Will only look at the first * transforms in the array. * * TODO: A better name for this function would be "compute" instead of "get". */ uint32_t getTransformKey(const SkTArray& coords, int numCoords) const; /** * Sets a unique key on the GrProcessorKeyBuilder that is directly associated with this geometry * processor's GL backend implementation. * * TODO: A better name for this function would be "compute" instead of "get". */ virtual void getGLSLProcessorKey(const GrShaderCaps&, GrProcessorKeyBuilder*) const = 0; void getAttributeKey(GrProcessorKeyBuilder* b) const { // Ensure that our CPU and GPU type fields fit together in a 32-bit value, and we never // collide with the "uninitialized" value. static_assert(kGrVertexAttribTypeCount < (1 << 8), ""); static_assert(kGrSLTypeCount < (1 << 8), ""); auto add_attributes = [=](const Attribute* attrs, int attrCount) { for (int i = 0; i < attrCount; ++i) { b->add32(attrs[i].isInitialized() ? (attrs[i].cpuType() << 16) | attrs[i].gpuType() : ~0); } }; add_attributes(fVertexAttributes.fAttributes, fVertexAttributes.fRawCount); add_attributes(fInstanceAttributes.fAttributes, fInstanceAttributes.fRawCount); } /** Returns a new instance of the appropriate *GL* implementation class for the given GrProcessor; caller is responsible for deleting the object. */ virtual GrGLSLPrimitiveProcessor* createGLSLInstance(const GrShaderCaps&) const = 0; virtual bool isPathRendering() const { return false; } protected: void setVertexAttributes(const Attribute* attrs, int attrCount) { fVertexAttributes.init(attrs, attrCount); } void setInstanceAttributes(const Attribute* attrs, int attrCount) { SkASSERT(attrCount >= 0); fInstanceAttributes.init(attrs, attrCount); } void setTextureSamplerCnt(int cnt) { SkASSERT(cnt >= 0); fTextureSamplerCnt = cnt; } /** * Helper for implementing onTextureSampler(). E.g.: * return IthTexureSampler(i, fMyFirstSampler, fMySecondSampler, fMyThirdSampler); */ template static const TextureSampler& IthTextureSampler(int i, const TextureSampler& samp0, const Args&... samps) { return (0 == i) ? samp0 : IthTextureSampler(i - 1, samps...); } inline static const TextureSampler& IthTextureSampler(int i); private: virtual const TextureSampler& onTextureSampler(int) const { return IthTextureSampler(0); } AttributeSet fVertexAttributes; AttributeSet fInstanceAttributes; int fTextureSamplerCnt = 0; typedef GrProcessor INHERITED; }; ////////////////////////////////////////////////////////////////////////////// /** * Used to represent a texture that is required by a GrPrimitiveProcessor. It holds a GrTextureProxy * along with an associated GrSamplerState. TextureSamplers don't perform any coord manipulation to * account for texture origin. */ class GrPrimitiveProcessor::TextureSampler { public: TextureSampler() = default; TextureSampler(GrTextureType, GrPixelConfig, const GrSamplerState&, uint32_t extraSamplerKey); explicit TextureSampler(GrTextureType, GrPixelConfig, GrSamplerState::Filter = GrSamplerState::Filter::kNearest, GrSamplerState::WrapMode wrapXAndY = GrSamplerState::WrapMode::kClamp); TextureSampler(const TextureSampler&) = delete; TextureSampler& operator=(const TextureSampler&) = delete; void reset(GrTextureType, GrPixelConfig, const GrSamplerState&, uint32_t extraSamplerKey = 0); void reset(GrTextureType, GrPixelConfig, GrSamplerState::Filter, GrSamplerState::WrapMode wrapXAndY); GrTextureType textureType() const { return fTextureType; } GrPixelConfig config() const { return fConfig; } const GrSamplerState& samplerState() const { return fSamplerState; } uint32_t extraSamplerKey() const { return fExtraSamplerKey; } bool isInitialized() const { return fConfig != kUnknown_GrPixelConfig; } private: GrSamplerState fSamplerState; GrTextureType fTextureType = GrTextureType::k2D; GrPixelConfig fConfig = kUnknown_GrPixelConfig; uint32_t fExtraSamplerKey = 0; }; const GrPrimitiveProcessor::TextureSampler& GrPrimitiveProcessor::IthTextureSampler(int i) { SK_ABORT("Illegal texture sampler index"); static const TextureSampler kBogus; return kBogus; } ////////////////////////////////////////////////////////////////////////////// /** * Returns the size of the attrib type in bytes. * This was moved from include/private/GrTypesPriv.h in service of Skia dependents that build * with C++11. */ static constexpr inline size_t GrVertexAttribTypeSize(GrVertexAttribType type) { switch (type) { case kFloat_GrVertexAttribType: return sizeof(float); case kFloat2_GrVertexAttribType: return 2 * sizeof(float); case kFloat3_GrVertexAttribType: return 3 * sizeof(float); case kFloat4_GrVertexAttribType: return 4 * sizeof(float); case kHalf_GrVertexAttribType: return sizeof(uint16_t); case kHalf2_GrVertexAttribType: return 2 * sizeof(uint16_t); case kHalf3_GrVertexAttribType: return 3 * sizeof(uint16_t); case kHalf4_GrVertexAttribType: return 4 * sizeof(uint16_t); case kInt2_GrVertexAttribType: return 2 * sizeof(int32_t); case kInt3_GrVertexAttribType: return 3 * sizeof(int32_t); case kInt4_GrVertexAttribType: return 4 * sizeof(int32_t); case kByte_GrVertexAttribType: return 1 * sizeof(char); case kByte2_GrVertexAttribType: return 2 * sizeof(char); case kByte3_GrVertexAttribType: return 3 * sizeof(char); case kByte4_GrVertexAttribType: return 4 * sizeof(char); case kUByte_GrVertexAttribType: return 1 * sizeof(char); case kUByte2_GrVertexAttribType: return 2 * sizeof(char); case kUByte3_GrVertexAttribType: return 3 * sizeof(char); case kUByte4_GrVertexAttribType: return 4 * sizeof(char); case kUByte_norm_GrVertexAttribType: return 1 * sizeof(char); case kUByte4_norm_GrVertexAttribType: return 4 * sizeof(char); case kShort2_GrVertexAttribType: return 2 * sizeof(int16_t); case kShort4_GrVertexAttribType: return 4 * sizeof(int16_t); case kUShort2_GrVertexAttribType: // fall through case kUShort2_norm_GrVertexAttribType: return 2 * sizeof(uint16_t); case kInt_GrVertexAttribType: return sizeof(int32_t); case kUint_GrVertexAttribType: return sizeof(uint32_t); } // GCC fails because SK_ABORT evaluates to non constexpr. clang and cl.exe think this is // unreachable and don't complain. #if defined(__clang__) || !defined(__GNUC__) SK_ABORT("Unsupported type conversion"); #endif return 0; } constexpr size_t GrPrimitiveProcessor::Attribute::size() const { return GrVertexAttribTypeSize(fCPUType); } #endif