diff options
author | joshualitt <joshualitt@chromium.org> | 2014-10-09 07:25:36 -0700 |
---|---|---|
committer | Commit bot <commit-bot@chromium.org> | 2014-10-09 07:25:36 -0700 |
commit | 651713408c5a5d9565665967ad09981250c7a8c9 (patch) | |
tree | b0e890cd8389dd1ee2876763527f25ef044677a2 | |
parent | 99d5915a02817659a0eec9f5addfdcf0a03d1aa2 (diff) |
gl programs rewrite
BUG=skia:
Committed: https://skia.googlesource.com/skia/+/07a255310aca9f3e83bf741dc663a58818ad681c
Review URL: https://codereview.chromium.org/628633003
-rw-r--r-- | include/gpu/GrProcessor.h | 2 | ||||
-rw-r--r-- | include/gpu/GrTypes.h | 4 | ||||
-rw-r--r-- | src/effects/SkColorFilters.cpp | 8 | ||||
-rw-r--r-- | src/gpu/GrDrawState.cpp | 7 | ||||
-rw-r--r-- | src/gpu/GrDrawState.h | 5 | ||||
-rw-r--r-- | src/gpu/GrDrawTarget.h | 15 | ||||
-rw-r--r-- | src/gpu/gl/GrGLProgramDesc.cpp | 150 | ||||
-rw-r--r-- | src/gpu/gl/GrGLProgramDesc.h | 45 | ||||
-rw-r--r-- | src/gpu/gl/GrGpuGL_program.cpp | 18 | ||||
-rw-r--r-- | tests/GLProgramsTest.cpp | 632 |
10 files changed, 510 insertions, 376 deletions
diff --git a/include/gpu/GrProcessor.h b/include/gpu/GrProcessor.h index 2dbead1c22..c1755a8954 100644 --- a/include/gpu/GrProcessor.h +++ b/include/gpu/GrProcessor.h @@ -42,6 +42,8 @@ public: uint32_t fValidFlags; bool fIsSingleComponent; + InvariantOutput() : fColor(0), fValidFlags(0), fIsSingleComponent(false) {} + bool isOpaque() const { return ((fValidFlags & kA_GrColorComponentFlag) && 0xFF == GrColorUnpackA(fColor)); } diff --git a/include/gpu/GrTypes.h b/include/gpu/GrTypes.h index 22b2e224f6..1f5f9179b9 100644 --- a/include/gpu/GrTypes.h +++ b/include/gpu/GrTypes.h @@ -222,8 +222,10 @@ enum GrBlendCoeff { kConstA_GrBlendCoeff, //<! constant color alpha kIConstA_GrBlendCoeff, //<! one minus constant color alpha - kPublicGrBlendCoeffCount + kFirstPublicGrBlendCoeff = kZero_GrBlendCoeff, + kLastPublicGrBlendCoeff = kIConstA_GrBlendCoeff, }; +static const int kPublicGrBlendCoeffCount = kLastPublicGrBlendCoeff + 1; /** * Formats for masks, used by the font cache. diff --git a/src/effects/SkColorFilters.cpp b/src/effects/SkColorFilters.cpp index ba62817f56..8e10d73a0e 100644 --- a/src/effects/SkColorFilters.cpp +++ b/src/effects/SkColorFilters.cpp @@ -406,7 +406,13 @@ GrFragmentProcessor* ModeColorFilterEffect::TestCreate(SkRandom* rand, while (SkXfermode::kDst_Mode == mode) { mode = static_cast<SkXfermode::Mode>(rand->nextRangeU(0, SkXfermode::kLastCoeffMode)); } - GrColor color = rand->nextU(); + + // pick a random premul color + uint8_t alpha = rand->nextULessThan(256); + GrColor color = GrColorPackRGBA(rand->nextRangeU(0, alpha), + rand->nextRangeU(0, alpha), + rand->nextRangeU(0, alpha), + alpha); return ModeColorFilterEffect::Create(color, mode); } diff --git a/src/gpu/GrDrawState.cpp b/src/gpu/GrDrawState.cpp index 6831fad1f9..5e177b8b12 100644 --- a/src/gpu/GrDrawState.cpp +++ b/src/gpu/GrDrawState.cpp @@ -404,7 +404,7 @@ bool GrDrawState::hasSolidCoverage() const { if (this->hasCoverageVertexAttribute()) { inout.fValidFlags = 0; } else { - inout.fColor = fCoverage; + inout.fColor = this->getCoverageColor(); inout.fValidFlags = kRGBA_GrColorComponentFlags; } @@ -413,6 +413,7 @@ bool GrDrawState::hasSolidCoverage() const { const GrGeometryProcessor* gp = fGeometryProcessor->getProcessor(); gp->computeInvariantOutput(&inout); } + for (int s = 0; s < this->numCoverageStages(); ++s) { const GrProcessor* processor = this->getCoverageStage(s).getProcessor(); processor->computeInvariantOutput(&inout); @@ -640,8 +641,8 @@ GrDrawState::~GrDrawState() { //////////////////////////////////////////////////////////////////////////////// GrDrawState::BlendOptFlags GrDrawState::getBlendOpts(bool forceCoverage, - GrBlendCoeff* srcCoeff, - GrBlendCoeff* dstCoeff) const { + GrBlendCoeff* srcCoeff, + GrBlendCoeff* dstCoeff) const { GrBlendCoeff bogusSrcCoeff, bogusDstCoeff; if (NULL == srcCoeff) { srcCoeff = &bogusSrcCoeff; diff --git a/src/gpu/GrDrawState.h b/src/gpu/GrDrawState.h index 5575b12966..03af7b532f 100644 --- a/src/gpu/GrDrawState.h +++ b/src/gpu/GrDrawState.h @@ -684,7 +684,10 @@ public: /// Hints that when provided can enable optimizations. //// - enum Hints { kVertexColorsAreOpaque_Hint = 0x1, }; + enum Hints { + kVertexColorsAreOpaque_Hint = 0x1, + kLast_Hint = kVertexColorsAreOpaque_Hint + }; void setHint(Hints hint, bool value) { fHints = value ? (fHints | hint) : (fHints & ~hint); } diff --git a/src/gpu/GrDrawTarget.h b/src/gpu/GrDrawTarget.h index dd2224bdbd..552314bca3 100644 --- a/src/gpu/GrDrawTarget.h +++ b/src/gpu/GrDrawTarget.h @@ -855,6 +855,14 @@ protected: GrDeviceCoordTexture fDstCopy; }; + // Makes a copy of the dst if it is necessary for the draw. Returns false if a copy is required + // but couldn't be made. Otherwise, returns true. This method needs to be protected because it + // needs to be accessed by GLPrograms to setup a correct drawstate + bool setupDstReadIfNecessary(DrawInfo* info) { + return this->setupDstReadIfNecessary(&info->fDstCopy, info->getDevBounds()); + } + bool setupDstReadIfNecessary(GrDeviceCoordTexture* dstCopy, const SkRect* drawBounds); + private: // A subclass can optionally overload this function to be notified before // vertex and index space is reserved. @@ -913,13 +921,6 @@ private: void releasePreviousVertexSource(); void releasePreviousIndexSource(); - // Makes a copy of the dst if it is necessary for the draw. Returns false if a copy is required - // but couldn't be made. Otherwise, returns true. - bool setupDstReadIfNecessary(DrawInfo* info) { - return this->setupDstReadIfNecessary(&info->fDstCopy, info->getDevBounds()); - } - bool setupDstReadIfNecessary(GrDeviceCoordTexture* dstCopy, const SkRect* drawBounds); - // Check to see if this set of draw commands has been sent out virtual bool isIssued(uint32_t drawID) { return true; } diff --git a/src/gpu/gl/GrGLProgramDesc.cpp b/src/gpu/gl/GrGLProgramDesc.cpp index ec09527182..0c85c99a8a 100644 --- a/src/gpu/gl/GrGLProgramDesc.cpp +++ b/src/gpu/gl/GrGLProgramDesc.cpp @@ -140,11 +140,11 @@ static uint32_t* get_processor_meta_key(const GrProcessorStage& processorStage, return key; } -bool GrGLProgramDesc::GetProcessorKey(const GrProcessorStage& stage, - const GrGLCaps& caps, - bool useExplicitLocalCoords, - GrProcessorKeyBuilder* b, - uint16_t* processorKeySize) { +static bool get_fp_key(const GrProcessorStage& stage, + const GrGLCaps& caps, + bool useExplicitLocalCoords, + GrProcessorKeyBuilder* b, + uint16_t* processorKeySize) { const GrProcessor& effect = *stage.getProcessor(); const GrBackendProcessorFactory& factory = effect.getFactory(); factory.getGLProcessorKey(effect, caps, b); @@ -160,11 +160,11 @@ bool GrGLProgramDesc::GetProcessorKey(const GrProcessorStage& stage, return true; } -bool GrGLProgramDesc::GetGeometryProcessorKey(const GrGeometryStage& stage, - const GrGLCaps& caps, - bool useExplicitLocalCoords, - GrProcessorKeyBuilder* b, - uint16_t* processorKeySize) { +static bool get_gp_key(const GrGeometryStage& stage, + const GrGLCaps& caps, + bool useExplicitLocalCoords, + GrProcessorKeyBuilder* b, + uint16_t* processorKeySize) { const GrProcessor& effect = *stage.getProcessor(); const GrBackendProcessorFactory& factory = effect.getFactory(); factory.getGLProcessorKey(effect, caps, b); @@ -191,6 +191,54 @@ bool GrGLProgramDesc::GetGeometryProcessorKey(const GrGeometryStage& stage, return true; } +struct GeometryProcessorKeyBuilder { + typedef GrGeometryStage StagedProcessor; + static bool GetProcessorKey(const GrGeometryStage& gpStage, + const GrGLCaps& caps, + bool requiresLocalCoordAttrib, + GrProcessorKeyBuilder* b, + uint16_t* processorKeySize) { + return get_gp_key(gpStage, caps, requiresLocalCoordAttrib, b, processorKeySize); + } +}; + +struct FragmentProcessorKeyBuilder { + typedef GrFragmentStage StagedProcessor; + static bool GetProcessorKey(const GrFragmentStage& fpStage, + const GrGLCaps& caps, + bool requiresLocalCoordAttrib, + GrProcessorKeyBuilder* b, + uint16_t* processorKeySize) { + return get_fp_key(fpStage, caps, requiresLocalCoordAttrib, b, processorKeySize); + } +}; + + +template <class ProcessorKeyBuilder> +bool +GrGLProgramDesc::BuildStagedProcessorKey(const typename ProcessorKeyBuilder::StagedProcessor& stage, + const GrGLCaps& caps, + bool requiresLocalCoordAttrib, + GrGLProgramDesc* desc, + int* offsetAndSizeIndex) { + GrProcessorKeyBuilder b(&desc->fKey); + uint16_t processorKeySize; + uint32_t processorOffset = desc->fKey.count(); + if (processorOffset > SK_MaxU16 || + !ProcessorKeyBuilder::GetProcessorKey(stage, caps, requiresLocalCoordAttrib, &b, + &processorKeySize)){ + desc->fKey.reset(); + return false; + } + + uint16_t* offsetAndSize = + reinterpret_cast<uint16_t*>(desc->fKey.begin() + kEffectKeyOffsetsAndLengthOffset + + *offsetAndSizeIndex * 2 * sizeof(uint16_t)); + offsetAndSize[0] = SkToU16(processorOffset); + offsetAndSize[1] = processorKeySize; + ++(*offsetAndSizeIndex); + return true; +} bool GrGLProgramDesc::Build(const GrOptDrawState& optState, GrGpu::DrawType drawType, @@ -224,90 +272,51 @@ bool GrGLProgramDesc::Build(const GrOptDrawState& optState, int offsetAndSizeIndex = 0; - KeyHeader* header = desc->header(); - // make sure any padding in the header is zeroed. - memset(desc->header(), 0, kHeaderSize); - // We can only have one effect which touches the vertex shader if (optState.hasGeometryProcessor()) { - uint16_t* offsetAndSize = - reinterpret_cast<uint16_t*>(desc->fKey.begin() + kEffectKeyOffsetsAndLengthOffset + - offsetAndSizeIndex * 2 * sizeof(uint16_t)); - - GrProcessorKeyBuilder b(&desc->fKey); - uint16_t processorKeySize; - uint32_t processorOffset = desc->fKey.count(); const GrGeometryStage& gpStage = *optState.getGeometryProcessor(); - if (processorOffset > SK_MaxU16 || - !GetGeometryProcessorKey(gpStage, gpu->glCaps(), requiresLocalCoordAttrib, &b, - &processorKeySize)) { - desc->fKey.reset(); + if (!BuildStagedProcessorKey<GeometryProcessorKeyBuilder>(gpStage, + gpu->glCaps(), + requiresLocalCoordAttrib, + desc, + &offsetAndSizeIndex)) { return false; } - - offsetAndSize[0] = SkToU16(processorOffset); - offsetAndSize[1] = processorKeySize; - ++offsetAndSizeIndex; *geometryProcessor = &gpStage; - header->fHasGeometryProcessor = true; } for (int s = 0; s < optState.numColorStages(); ++s) { - uint16_t* offsetAndSize = - reinterpret_cast<uint16_t*>(desc->fKey.begin() + kEffectKeyOffsetsAndLengthOffset + - offsetAndSizeIndex * 2 * sizeof(uint16_t)); - - GrProcessorKeyBuilder b(&desc->fKey); - uint16_t processorKeySize; - uint32_t processorOffset = desc->fKey.count(); - if (processorOffset > SK_MaxU16 || - !GetProcessorKey(optState.getColorStage(s), gpu->glCaps(), - requiresLocalCoordAttrib, &b, &processorKeySize)) { - desc->fKey.reset(); + if (!BuildStagedProcessorKey<FragmentProcessorKeyBuilder>(optState.getColorStage(s), + gpu->glCaps(), + requiresLocalCoordAttrib, + desc, + &offsetAndSizeIndex)) { return false; } - - offsetAndSize[0] = SkToU16(processorOffset); - offsetAndSize[1] = processorKeySize; - ++offsetAndSizeIndex; } for (int s = 0; s < optState.numCoverageStages(); ++s) { - uint16_t* offsetAndSize = - reinterpret_cast<uint16_t*>(desc->fKey.begin() + kEffectKeyOffsetsAndLengthOffset + - offsetAndSizeIndex * 2 * sizeof(uint16_t)); - - GrProcessorKeyBuilder b(&desc->fKey); - uint16_t processorKeySize; - uint32_t processorOffset = desc->fKey.count(); - if (processorOffset > SK_MaxU16 || - !GetProcessorKey(optState.getCoverageStage(s), gpu->glCaps(), - requiresLocalCoordAttrib, &b, &processorKeySize)) { - desc->fKey.reset(); + if (!BuildStagedProcessorKey<FragmentProcessorKeyBuilder>(optState.getCoverageStage(s), + gpu->glCaps(), + requiresLocalCoordAttrib, + desc, + &offsetAndSizeIndex)) { return false; } - - offsetAndSize[0] = SkToU16(processorOffset); - offsetAndSize[1] = processorKeySize; - ++offsetAndSizeIndex; } + // --------DO NOT MOVE HEADER ABOVE THIS LINE-------------------------------------------------- // Because header is a pointer into the dynamic array, we can't push any new data into the key // below here. + KeyHeader* header = desc->header(); + + // make sure any padding in the header is zeroed. + memset(header, 0, kHeaderSize); + header->fHasGeometryProcessor = optState.hasGeometryProcessor(); header->fEmitsPointSize = GrGpu::kDrawPoints_DrawType == drawType; - // Currently the experimental GS will only work with triangle prims (and it doesn't do anything - // other than pass through values from the VS to the FS anyway). -#if GR_GL_EXPERIMENTAL_GS -#if 0 - header->fExperimentalGS = gpu->caps().geometryShaderSupport(); -#else - header->fExperimentalGS = false; -#endif -#endif - if (gpu->caps()->pathRenderingSupport() && GrGpu::IsPathRenderingDrawType(drawType) && gpu->glPathRendering()->texturingMode() == GrGLPathRendering::FixedFunction_TexturingMode) { @@ -399,7 +408,6 @@ bool GrGLProgramDesc::Build(const GrOptDrawState& optState, header->fColorEffectCnt = colorStages->count(); header->fCoverageEffectCnt = coverageStages->count(); - desc->finalize(); return true; } diff --git a/src/gpu/gl/GrGLProgramDesc.h b/src/gpu/gl/GrGLProgramDesc.h index faa59f329f..9bf7553b61 100644 --- a/src/gpu/gl/GrGLProgramDesc.h +++ b/src/gpu/gl/GrGLProgramDesc.h @@ -15,15 +15,6 @@ class GrGpuGL; -#ifdef SK_DEBUG - // Optionally compile the experimental GS code. Set to SK_DEBUG so that debug build bots will - // execute the code. - #define GR_GL_EXPERIMENTAL_GS 1 -#else - #define GR_GL_EXPERIMENTAL_GS 0 -#endif - - /** This class describes a program to generate. It also serves as a program cache key. Very little of this is GL-specific. The GL-specific parts could be factored out into a subclass. */ class GrGLProgramDesc { @@ -44,18 +35,6 @@ public: // Gets the a checksum of the key. Can be used as a hash value for a fast lookup in a cache. uint32_t getChecksum() const { return *this->atOffset<uint32_t, kChecksumOffset>(); } - // For unit testing. - bool setRandom(SkRandom*, - GrGpuGL*, - const GrRenderTarget* dummyDstRenderTarget, - const GrTexture* dummyDstCopyTexture, - const GrGeometryStage* geometryProcessor, - const GrFragmentStage* stages[], - int numColorStages, - int numCoverageStages, - int currAttribIndex, - GrGpu::DrawType); - /** * Builds a program descriptor from a GrOptDrawState. Whether the primitive type is points, and * the caps of the GrGpuGL are also inputs. It also outputs the color and coverage stages @@ -129,13 +108,6 @@ private: GrOptDrawState::PrimaryOutputType fPrimaryOutputType : 8; GrOptDrawState::SecondaryOutputType fSecondaryOutputType : 8; - - // To enable experimental geometry shader code (not for use in - // production) -#if GR_GL_EXPERIMENTAL_GS - SkBool8 fExperimentalGS; -#endif - int8_t fPositionAttributeIndex; int8_t fLocalCoordAttributeIndex; int8_t fColorAttributeIndex; @@ -176,18 +148,13 @@ private: KeyHeader* header() { return this->atOffset<KeyHeader, kHeaderOffset>(); } - // Shared code between setRandom() and Build(). - static bool GetProcessorKey(const GrProcessorStage& stage, - const GrGLCaps& caps, - bool useExplicitLocalCoords, - GrProcessorKeyBuilder* b, - uint16_t* effectKeySize); - - static bool GetGeometryProcessorKey(const GrGeometryStage& stage, + // a helper class to handle getting an individual processor's key + template <class ProcessorKeyBuilder> + static bool BuildStagedProcessorKey(const typename ProcessorKeyBuilder::StagedProcessor& stage, const GrGLCaps& caps, - bool useExplicitLocalCoords, - GrProcessorKeyBuilder* b, - uint16_t* effectKeySize); + bool requiresLocalCoordAttrib, + GrGLProgramDesc* desc, + int* offsetAndSizeIndex); void finalize(); const KeyHeader& getHeader() const { return *this->atOffset<KeyHeader, kHeaderOffset>(); } diff --git a/src/gpu/gl/GrGpuGL_program.cpp b/src/gpu/gl/GrGpuGL_program.cpp index 891b49a1f8..7dba5316a4 100644 --- a/src/gpu/gl/GrGpuGL_program.cpp +++ b/src/gpu/gl/GrGpuGL_program.cpp @@ -242,15 +242,15 @@ bool GrGpuGL::flushGraphicsState(DrawType type, const GrDeviceCoordTexture* dstC SkSTArray<8, const GrFragmentStage*, true> coverageStages; GrGLProgramDesc desc; if (!GrGLProgramDesc::Build(*optState.get(), - type, - srcCoeff, - dstCoeff, - this, - dstCopy, - &geometryProcessor, - &colorStages, - &coverageStages, - &desc)) { + type, + srcCoeff, + dstCoeff, + this, + dstCopy, + &geometryProcessor, + &colorStages, + &coverageStages, + &desc)) { SkDEBUGFAIL("Failed to generate GL program descriptor"); return false; } diff --git a/tests/GLProgramsTest.cpp b/tests/GLProgramsTest.cpp index c31aa1e0da..4d1c59734b 100644 --- a/tests/GLProgramsTest.cpp +++ b/tests/GLProgramsTest.cpp @@ -12,7 +12,7 @@ #if SK_SUPPORT_GPU && SK_ALLOW_STATIC_GLOBAL_INITIALIZERS -#include "GrBackendProcessorFactory.h" +#include "GrTBackendProcessorFactory.h" #include "GrContextFactory.h" #include "GrOptDrawState.h" #include "effects/GrConfigConversionEffect.h" @@ -23,180 +23,122 @@ #include "SkRandom.h" #include "Test.h" -static void get_stage_stats(const GrFragmentStage stage, bool* readsDst, - bool* readsFragPosition, bool* requiresVertexShader) { - if (stage.getProcessor()->willReadDstColor()) { - *readsDst = true; - } - if (stage.getProcessor()->willReadFragmentPosition()) { - *readsFragPosition = true; - } -} +/* + * A dummy effect which just tries to insert a massive key and verify that it can retrieve the + * whole thing correctly + */ +static const uint32_t kMaxKeySize = 1024; -bool GrGLProgramDesc::setRandom(SkRandom* random, - GrGpuGL* gpu, - const GrRenderTarget* dstRenderTarget, - const GrTexture* dstCopyTexture, - const GrGeometryStage* geometryProcessor, - const GrFragmentStage* stages[], - int numColorStages, - int numCoverageStages, - int currAttribIndex, - GrGpu::DrawType drawType) { - bool isPathRendering = GrGpu::IsPathRenderingDrawType(drawType); - bool useLocalCoords = !isPathRendering && - random->nextBool() && - currAttribIndex < GrDrawState::kMaxVertexAttribCnt; - - int numStages = numColorStages + numCoverageStages; - fKey.reset(); - - GR_STATIC_ASSERT(0 == kEffectKeyOffsetsAndLengthOffset % sizeof(uint32_t)); - - // Make room for everything up to and including the array of offsets to effect keys. - fKey.push_back_n(kEffectKeyOffsetsAndLengthOffset + 2 * sizeof(uint16_t) * (numStages + - (geometryProcessor ? 1 : 0))); - - bool dstRead = false; - bool fragPos = false; - bool vertexShader = SkToBool(geometryProcessor); - int offset = 0; - if (geometryProcessor) { - const GrGeometryStage* stage = geometryProcessor; - uint16_t* offsetAndSize = reinterpret_cast<uint16_t*>(fKey.begin() + - kEffectKeyOffsetsAndLengthOffset + - offset * 2 * sizeof(uint16_t)); - uint32_t effectKeyOffset = fKey.count(); - if (effectKeyOffset > SK_MaxU16) { - fKey.reset(); - return false; - } - GrProcessorKeyBuilder b(&fKey); - uint16_t effectKeySize; - if (!GetProcessorKey(*stage, gpu->glCaps(), useLocalCoords, &b, &effectKeySize)) { - fKey.reset(); - return false; - } - vertexShader = true; - fragPos = stage->getProcessor()->willReadFragmentPosition(); - offsetAndSize[0] = effectKeyOffset; - offsetAndSize[1] = effectKeySize; - offset++; - } +class GLBigKeyProcessor; - for (int s = 0; s < numStages; ++s, ++offset) { - const GrFragmentStage* stage = stages[s]; - uint16_t* offsetAndSize = reinterpret_cast<uint16_t*>(fKey.begin() + - kEffectKeyOffsetsAndLengthOffset + - offset * 2 * sizeof(uint16_t)); - uint32_t effectKeyOffset = fKey.count(); - if (effectKeyOffset > SK_MaxU16) { - fKey.reset(); - return false; - } - GrProcessorKeyBuilder b(&fKey); - uint16_t effectKeySize; - if (!GetProcessorKey(*stages[s], gpu->glCaps(), useLocalCoords, &b, &effectKeySize)) { - fKey.reset(); - return false; - } - get_stage_stats(*stage, &dstRead, &fragPos, &vertexShader); - offsetAndSize[0] = effectKeyOffset; - offsetAndSize[1] = effectKeySize; +class BigKeyProcessor : public GrFragmentProcessor { +public: + static GrFragmentProcessor* Create() { + GR_CREATE_STATIC_FRAGMENT_PROCESSOR(gBigKeyProcessor, BigKeyProcessor, ()) + return SkRef(gBigKeyProcessor); } - KeyHeader* header = this->header(); - memset(header, 0, kHeaderSize); - header->fEmitsPointSize = random->nextBool(); + static const char* Name() { return "Big ol' Key"; } - header->fPositionAttributeIndex = 0; + virtual const GrBackendFragmentProcessorFactory& getFactory() const SK_OVERRIDE { + return GrTBackendFragmentProcessorFactory<BigKeyProcessor>::getInstance(); + } - // if the effects have used up all off the available attributes, - // don't try to use color or coverage attributes as input - do { - header->fColorInput = static_cast<GrGLProgramDesc::ColorInput>( - random->nextULessThan(kColorInputCnt)); - } while ((GrDrawState::kMaxVertexAttribCnt <= currAttribIndex || isPathRendering) && - kAttribute_ColorInput == header->fColorInput); - header->fColorAttributeIndex = (header->fColorInput == kAttribute_ColorInput) ? - currAttribIndex++ : - -1; + typedef GLBigKeyProcessor GLProcessor; - do { - header->fCoverageInput = static_cast<GrGLProgramDesc::ColorInput>( - random->nextULessThan(kColorInputCnt)); - } while ((GrDrawState::kMaxVertexAttribCnt <= currAttribIndex || isPathRendering) && - kAttribute_ColorInput == header->fCoverageInput); - header->fCoverageAttributeIndex = (header->fCoverageInput == kAttribute_ColorInput) ? - currAttribIndex++ : - -1; - bool useGS = random->nextBool(); -#if GR_GL_EXPERIMENTAL_GS - header->fExperimentalGS = gpu->caps()->geometryShaderSupport() && useGS; -#else - (void) useGS; -#endif +private: + BigKeyProcessor() { } + virtual bool onIsEqual(const GrProcessor&) const SK_OVERRIDE { return true; } + virtual void onComputeInvariantOutput(InvariantOutput* inout) const SK_OVERRIDE { } - header->fLocalCoordAttributeIndex = useLocalCoords ? currAttribIndex++ : -1; + GR_DECLARE_FRAGMENT_PROCESSOR_TEST; - header->fColorEffectCnt = numColorStages; - header->fCoverageEffectCnt = numCoverageStages; + typedef GrFragmentProcessor INHERITED; +}; - if (dstRead) { - header->fDstReadKey = SkToU8(GrGLFragmentShaderBuilder::KeyForDstRead(dstCopyTexture, - gpu->glCaps())); - } else { - header->fDstReadKey = 0; - } - if (fragPos) { - header->fFragPosKey = SkToU8(GrGLFragmentShaderBuilder::KeyForFragmentPosition(dstRenderTarget, - gpu->glCaps())); - } else { - header->fFragPosKey = 0; - } +GR_DEFINE_FRAGMENT_PROCESSOR_TEST(BigKeyProcessor); - header->fUseFragShaderOnly = isPathRendering && gpu->glPathRendering()->texturingMode() == - GrGLPathRendering::FixedFunction_TexturingMode; - header->fHasGeometryProcessor = vertexShader; +GrFragmentProcessor* BigKeyProcessor::TestCreate(SkRandom*, + GrContext*, + const GrDrawTargetCaps&, + GrTexture*[]) { + return BigKeyProcessor::Create(); +} - GrOptDrawState::PrimaryOutputType primaryOutput; - GrOptDrawState::SecondaryOutputType secondaryOutput; - if (!dstRead) { - primaryOutput = GrOptDrawState::kModulate_PrimaryOutputType; - } else { - primaryOutput = static_cast<GrOptDrawState::PrimaryOutputType>( - random->nextULessThan(GrOptDrawState::kPrimaryOutputTypeCnt)); +class GLBigKeyProcessor : public GrGLFragmentProcessor { +public: + GLBigKeyProcessor(const GrBackendProcessorFactory& factory, const GrProcessor&) + : INHERITED(factory) {} + + virtual void emitCode(GrGLProgramBuilder* builder, + const GrFragmentProcessor& fp, + const GrProcessorKey& key, + const char* outputColor, + const char* inputColor, + const TransformedCoordsArray&, + const TextureSamplerArray&) { + for (uint32_t i = 0; i < kMaxKeySize; i++) { + SkASSERT(key.get32(i) == i); + } } - if (GrOptDrawState::kCombineWithDst_PrimaryOutputType == primaryOutput || - !gpu->caps()->dualSourceBlendingSupport()) { - secondaryOutput = GrOptDrawState::kNone_SecondaryOutputType; - } else { - secondaryOutput = static_cast<GrOptDrawState::SecondaryOutputType>( - random->nextULessThan(GrOptDrawState::kSecondaryOutputTypeCnt)); + static void GenKey(const GrProcessor& processor, const GrGLCaps&, GrProcessorKeyBuilder* b) { + for (uint32_t i = 0; i < kMaxKeySize; i++) { + b->add32(i); + } } - header->fPrimaryOutputType = primaryOutput; - header->fSecondaryOutputType = secondaryOutput; +private: + typedef GrGLFragmentProcessor INHERITED; +}; - this->finalize(); - return true; +/* + * Begin test code + */ +static const int kRenderTargetHeight = 1; +static const int kRenderTargetWidth = 1; + +static GrRenderTarget* random_render_target(GrGpuGL* gpu, + const GrCacheID& cacheId, + SkRandom* random) { + // setup render target + GrTextureParams params; + GrTextureDesc texDesc; + texDesc.fWidth = kRenderTargetWidth; + texDesc.fHeight = kRenderTargetHeight; + texDesc.fFlags = kRenderTarget_GrTextureFlagBit; + texDesc.fConfig = kRGBA_8888_GrPixelConfig; + texDesc.fOrigin = random->nextBool() == true ? kTopLeft_GrSurfaceOrigin : + kBottomLeft_GrSurfaceOrigin; + + GrTexture* texture = gpu->getContext()->findAndRefTexture(texDesc, cacheId, ¶ms); + if (NULL == texture) { + texture = gpu->getContext()->createTexture(¶ms, texDesc, cacheId, 0, 0); + if (NULL == texture) { + return NULL; + } + } + return texture->asRenderTarget(); } // TODO clean this up, we have to do this to test geometry processors but there has got to be // a better way. In the mean time, we actually fill out these generic vertex attribs below with // the correct vertex attribs from the GP. We have to ensure, however, we don't try to add more -// than two attributes. -GrVertexAttrib genericVertexAttribs[] = { +// than two attributes. In addition, we 'pad' the below array with GPs up to 6 entries, 4 fixed +// function vertex attributes and 2 GP custom attributes. +GrVertexAttrib kGenericVertexAttribs[] = { { kVec2f_GrVertexAttribType, 0, kPosition_GrVertexAttribBinding }, { kVec2f_GrVertexAttribType, 0, kGeometryProcessor_GrVertexAttribBinding }, + { kVec2f_GrVertexAttribType, 0, kGeometryProcessor_GrVertexAttribBinding }, + { kVec2f_GrVertexAttribType, 0, kGeometryProcessor_GrVertexAttribBinding }, + { kVec2f_GrVertexAttribType, 0, kGeometryProcessor_GrVertexAttribBinding }, { kVec2f_GrVertexAttribType, 0, kGeometryProcessor_GrVertexAttribBinding } }; /* * convert sl type to vertexattrib type, not a complete implementation, only use for debugging */ -GrVertexAttribType convert_sltype_to_attribtype(GrSLType type) { +static GrVertexAttribType convert_sltype_to_attribtype(GrSLType type) { switch (type) { case kFloat_GrSLType: return kFloat_GrVertexAttribType; @@ -211,11 +153,227 @@ GrVertexAttribType convert_sltype_to_attribtype(GrSLType type) { return kFloat_GrVertexAttribType; } } -// TODO end test hack +// end test hack + +static void setup_random_ff_attribute(GrVertexAttribBinding binding, GrVertexAttribType type, + SkRandom* random, int* attribIndex, int* runningStride) { + if (random->nextBool()) { + kGenericVertexAttribs[*attribIndex].fType = type; + kGenericVertexAttribs[*attribIndex].fOffset = *runningStride; + kGenericVertexAttribs[*attribIndex].fBinding = binding; + *runningStride += GrVertexAttribTypeSize(kGenericVertexAttribs[(*attribIndex)++].fType); + } +} +static void set_random_gp(GrGpuGL* gpu, SkRandom* random, GrTexture* dummyTextures[]) { + GrProgramElementRef<const GrGeometryProcessor> gp( + GrProcessorTestFactory<GrGeometryProcessor>::CreateStage(random, + gpu->getContext(), + *gpu->caps(), + dummyTextures)); + SkASSERT(gp); + + // we have to set dummy vertex attributes, first we setup the fixed function attributes + // always leave the position attribute untouched in the array + int attribIndex = 1; + int runningStride = GrVertexAttribTypeSize(kGenericVertexAttribs[0].fType); + + // local coords + setup_random_ff_attribute(kLocalCoord_GrVertexAttribBinding, kVec2f_GrVertexAttribType, + random, &attribIndex, &runningStride); + + // color + setup_random_ff_attribute(kColor_GrVertexAttribBinding, kVec4f_GrVertexAttribType, + random, &attribIndex, &runningStride); + + // coverage + setup_random_ff_attribute(kCoverage_GrVertexAttribBinding, kVec4f_GrVertexAttribType, + random, &attribIndex, &runningStride); + + // Update the geometry processor attributes + const GrGeometryProcessor::VertexAttribArray& v = gp->getVertexAttribs(); + int numGPAttribs = v.count(); + SkASSERT(numGPAttribs <= GrGeometryProcessor::kMaxVertexAttribs && + GrGeometryProcessor::kMaxVertexAttribs == 2); + + // we actually can't overflow if kMaxVertexAttribs == 2, but GCC 4.8 wants more proof + int maxIndex = SK_ARRAY_COUNT(kGenericVertexAttribs); + for (int i = 0; i < numGPAttribs && i + attribIndex < maxIndex; i++) { + kGenericVertexAttribs[i + attribIndex].fType = + convert_sltype_to_attribtype(v[i].getType()); + kGenericVertexAttribs[i + attribIndex].fOffset = runningStride; + kGenericVertexAttribs[i + attribIndex].fBinding = kGeometryProcessor_GrVertexAttribBinding; + runningStride += GrVertexAttribTypeSize(kGenericVertexAttribs[i + attribIndex].fType); + } -bool GrGpuGL::programUnitTest(int maxStages) { + // update the vertex attributes with the ds + GrDrawState* ds = gpu->drawState(); + ds->setVertexAttribs<kGenericVertexAttribs>(attribIndex + numGPAttribs, runningStride); + ds->setGeometryProcessor(gp); +} + +static void set_random_color_coverage_stages(GrGpuGL* gpu, + int maxStages, + bool usePathRendering, + SkRandom* random, + GrTexture* dummyTextures[]) { + int numProcs = random->nextULessThan(maxStages + 1); + int numColorProcs = random->nextULessThan(numProcs + 1); + + int currTextureCoordSet = 0; + for (int s = 0; s < numProcs;) { + GrProgramElementRef<GrFragmentProcessor> fp( + GrProcessorTestFactory<GrFragmentProcessor>::CreateStage(random, + gpu->getContext(), + *gpu->caps(), + dummyTextures)); + SkASSERT(fp); + + // don't add dst color reads to coverage stage + if (s >= numColorProcs && fp->willReadDstColor()) { + continue; + } + + // If adding this effect would exceed the max texture coord set count then generate a + // new random effect. + if (usePathRendering && gpu->glPathRendering()->texturingMode() == + GrGLPathRendering::FixedFunction_TexturingMode) {; + int numTransforms = fp->numTransforms(); + if (currTextureCoordSet + numTransforms > + gpu->glCaps().maxFixedFunctionTextureCoords()) { + continue; + } + currTextureCoordSet += numTransforms; + } + + // finally add the stage to the correct pipeline in the drawstate + GrDrawState* ds = gpu->drawState(); + if (s < numColorProcs) { + ds->addColorProcessor(fp); + } else { + ds->addCoverageProcessor(fp); + } + ++s; + } +} + +// There are only a few cases of random colors which interest us +enum ColorMode { + kAllOnes_ColorMode, + kAllZeros_ColorMode, + kAlphaOne_ColorMode, + kRandom_ColorMode, + kLast_ColorMode = kRandom_ColorMode +}; + +static void set_random_color(GrGpuGL* gpu, SkRandom* random) { + ColorMode colorMode = ColorMode(random->nextULessThan(kLast_ColorMode + 1)); + GrColor color; + switch (colorMode) { + case kAllOnes_ColorMode: + color = GrColorPackRGBA(0xFF, 0xFF, 0xFF, 0xFF); + break; + case kAllZeros_ColorMode: + color = GrColorPackRGBA(0, 0, 0, 0); + break; + case kAlphaOne_ColorMode: + color = GrColorPackRGBA(random->nextULessThan(256), + random->nextULessThan(256), + random->nextULessThan(256), + 0xFF); + break; + case kRandom_ColorMode: + uint8_t alpha = random->nextULessThan(256); + color = GrColorPackRGBA(random->nextRangeU(0, alpha), + random->nextRangeU(0, alpha), + random->nextRangeU(0, alpha), + alpha); + break; + } + GrColorIsPMAssert(color); + gpu->drawState()->setColor(color); +} + +// There are only a few cases of random coverages which interest us +enum CoverageMode { + kZero_CoverageMode, + kFF_CoverageMode, + kRandom_CoverageMode, + kLast_CoverageMode = kRandom_CoverageMode +}; + +static void set_random_coverage(GrGpuGL* gpu, SkRandom* random) { + CoverageMode coverageMode = CoverageMode(random->nextULessThan(kLast_CoverageMode + 1)); + uint8_t coverage; + switch (coverageMode) { + case kZero_CoverageMode: + coverage = 0; + break; + case kFF_CoverageMode: + coverage = 0xFF; + break; + case kRandom_CoverageMode: + coverage = uint8_t(random->nextU()); + break; + } + gpu->drawState()->setCoverage(coverage); +} + +static void set_random_hints(GrGpuGL* gpu, SkRandom* random) { + for (int i = 1; i <= GrDrawState::kLast_Hint; i <<= 1) { + gpu->drawState()->setHint(GrDrawState::Hints(i), random->nextBool()); + } +} + +static void set_random_state(GrGpuGL* gpu, SkRandom* random) { + int state = 0; + for (int i = 1; i <= GrDrawState::kLastPublicStateBit; i <<= 1) { + state |= random->nextBool() * i; + } + gpu->drawState()->enableState(state); +} + +// this function will randomly pick non-self referencing blend modes +static void set_random_blend_func(GrGpuGL* gpu, SkRandom* random) { + GrBlendCoeff src; + do { + src = GrBlendCoeff(random->nextRangeU(kFirstPublicGrBlendCoeff, kLastPublicGrBlendCoeff)); + } while (GrBlendCoeffRefsSrc(src)); + GrBlendCoeff dst; + do { + dst = GrBlendCoeff(random->nextRangeU(kFirstPublicGrBlendCoeff, kLastPublicGrBlendCoeff)); + } while (GrBlendCoeffRefsDst(dst)); + + gpu->drawState()->setBlendFunc(src, dst); +} + +// right now, the only thing we seem to care about in drawState's stencil is 'doesWrite()' +static void set_random_stencil(GrGpuGL* gpu, SkRandom* random) { + GR_STATIC_CONST_SAME_STENCIL(kDoesWriteStencil, + kReplace_StencilOp, + kReplace_StencilOp, + kAlways_StencilFunc, + 0xffff, + 0xffff, + 0xffff); + GR_STATIC_CONST_SAME_STENCIL(kDoesNotWriteStencil, + kKeep_StencilOp, + kKeep_StencilOp, + kNever_StencilFunc, + 0xffff, + 0xffff, + 0xffff); + + if (random->nextBool()) { + gpu->drawState()->setStencil(kDoesWriteStencil); + } else { + gpu->drawState()->setStencil(kDoesNotWriteStencil); + } +} + +bool GrGpuGL::programUnitTest(int maxStages) { + // setup dummy textures GrTextureDesc dummyDesc; dummyDesc.fFlags = kRenderTarget_GrTextureFlagBit; dummyDesc.fConfig = kSkia8888_GrPixelConfig; @@ -229,130 +387,116 @@ bool GrGpuGL::programUnitTest(int maxStages) { SkAutoTUnref<GrTexture> dummyTexture2(this->createTexture(dummyDesc, NULL, 0)); if (!dummyTexture1 || ! dummyTexture2) { + SkDebugf("Could not allocate dummy textures"); return false; } - static const int NUM_TESTS = 512; + GrTexture* dummyTextures[] = {dummyTexture1.get(), dummyTexture2.get()}; - SkRandom random; - for (int t = 0; t < NUM_TESTS; ++t) { + // Setup texture cache id key + const GrCacheID::Domain glProgramsDomain = GrCacheID::GenerateDomain(); + GrCacheID::Key key; + memset(&key, 0, sizeof(key)); + key.fData32[0] = kRenderTargetWidth; + key.fData32[1] = kRenderTargetHeight; + GrCacheID glProgramsCacheID(glProgramsDomain, key); -#if 0 - GrPrintf("\nTest Program %d\n-------------\n", t); - static const int stop = -1; - if (t == stop) { - int breakpointhere = 9; - } -#endif + // setup clip + SkRect screen = + SkRect::MakeWH(SkIntToScalar(kRenderTargetWidth), SkIntToScalar(kRenderTargetHeight)); - GrGLProgramDesc pdesc; + SkClipStack stack; + stack.clipDevRect(screen, SkRegion::kReplace_Op, false); - int currAttribIndex = 1; // we need to always leave room for position - int currTextureCoordSet = 0; - GrTexture* dummyTextures[] = {dummyTexture1.get(), dummyTexture2.get()}; + // wrap the SkClipStack in a GrClipData + GrClipData clipData; + clipData.fClipStack = &stack; + this->setClip(&clipData); - int numStages = random.nextULessThan(maxStages + 1); - int numColorStages = random.nextULessThan(numStages + 1); - int numCoverageStages = numStages - numColorStages; + SkRandom random; + static const int NUM_TESTS = 512; + for (int t = 0; t < NUM_TESTS;) { + // setup random render target(can fail) + GrRenderTarget* rtPtr = random_render_target(this, glProgramsCacheID, &random); + if (!rtPtr) { + SkDebugf("Could not allocate render target"); + return false; + } + GrTGpuResourceRef<GrRenderTarget> rt(SkRef(rtPtr), kWrite_GrIOType); - SkAutoSTMalloc<8, const GrFragmentStage*> stages(numStages); + GrDrawState* ds = this->drawState(); + ds->setRenderTarget(rt.get()); + // if path rendering we have to setup a couple of things like the draw type bool usePathRendering = this->glCaps().pathRenderingSupport() && random.nextBool(); GrGpu::DrawType drawType = usePathRendering ? GrGpu::kDrawPath_DrawType : GrGpu::kDrawPoints_DrawType; - SkAutoTDelete<GrGeometryStage> geometryProcessor; + // twiddle drawstate knobs randomly bool hasGeometryProcessor = usePathRendering ? false : random.nextBool(); if (hasGeometryProcessor) { - while (true) { - SkAutoTUnref<const GrGeometryProcessor> effect( - GrProcessorTestFactory<GrGeometryProcessor>::CreateStage(&random, this->getContext(), *this->caps(), - dummyTextures)); - SkASSERT(effect); - // Only geometryProcessor can use vertex shader - GrGeometryStage* stage = SkNEW_ARGS(GrGeometryStage, (effect.get())); - geometryProcessor.reset(stage); - - // we have to set dummy vertex attribs - const GrGeometryProcessor::VertexAttribArray& v = effect->getVertexAttribs(); - int numVertexAttribs = v.count(); - - SkASSERT(GrGeometryProcessor::kMaxVertexAttribs == 2 && - GrGeometryProcessor::kMaxVertexAttribs >= numVertexAttribs); - size_t runningStride = GrVertexAttribTypeSize(genericVertexAttribs[0].fType); - for (int i = 0; i < numVertexAttribs; i++) { - genericVertexAttribs[i + 1].fOffset = runningStride; - genericVertexAttribs[i + 1].fType = - convert_sltype_to_attribtype(v[i].getType()); - runningStride += GrVertexAttribTypeSize(genericVertexAttribs[i + 1].fType); - } - - // update the vertex attributes with the ds - GrDrawState* ds = this->drawState(); - ds->setVertexAttribs<genericVertexAttribs>(numVertexAttribs + 1, runningStride); - currAttribIndex = numVertexAttribs + 1; - break; - } + set_random_gp(this, &random, dummyTextures); } - for (int s = 0; s < numStages;) { - SkAutoTUnref<const GrFragmentProcessor> effect( - GrProcessorTestFactory<GrFragmentProcessor>::CreateStage( - &random, - this->getContext(), - *this->caps(), - dummyTextures)); - SkASSERT(effect); - - // If adding this effect would exceed the max texture coord set count then generate a - // new random effect. - if (usePathRendering && this->glPathRendering()->texturingMode() == - GrGLPathRendering::FixedFunction_TexturingMode) {; - int numTransforms = effect->numTransforms(); - if (currTextureCoordSet + numTransforms > this->glCaps().maxFixedFunctionTextureCoords()) { - continue; - } - currTextureCoordSet += numTransforms; - } - GrFragmentStage* stage = SkNEW_ARGS(GrFragmentStage, (effect.get())); - - stages[s] = stage; - ++s; + set_random_color_coverage_stages(this, maxStages - hasGeometryProcessor, usePathRendering, + &random, dummyTextures); + set_random_color(this, &random); + set_random_coverage(this, &random); + set_random_hints(this, &random); + set_random_state(this, &random); + set_random_blend_func(this, &random); + set_random_stencil(this, &random); + + // create optimized draw state, setup readDst texture if required, and build a descriptor + // and program. ODS creation can fail, so we have to check + SkAutoTUnref<GrOptDrawState> ods(GrOptDrawState::Create(this->getDrawState(), + *this->caps(), + drawType)); + if (!ods.get()) { + ds->reset(); + continue; } - const GrTexture* dstTexture = random.nextBool() ? dummyTextures[0] : dummyTextures[1]; - if (!pdesc.setRandom(&random, - this, - dummyTextures[0]->asRenderTarget(), - dstTexture, - geometryProcessor.get(), - stages.get(), - numColorStages, - numCoverageStages, - currAttribIndex, - drawType)) { + const GrGeometryStage* geometryProcessor = NULL; + SkSTArray<8, const GrFragmentStage*, true> colorStages; + SkSTArray<8, const GrFragmentStage*, true> coverageStages; + GrGLProgramDesc desc; + GrDeviceCoordTexture dstCopy; + + if (!this->setupDstReadIfNecessary(&dstCopy, NULL)) { + SkDebugf("Couldn't setup dst read texture"); return false; } - - SkAutoTUnref<GrOptDrawState> optState(GrOptDrawState::Create(this->getDrawState(), - *this->caps(), - drawType)); - SkAutoTUnref<GrGLProgram> program( - GrGLProgramBuilder::CreateProgram(*optState, - pdesc, - drawType, - geometryProcessor, - stages, - stages + numColorStages, - this)); - for (int s = 0; s < numStages; ++s) { - SkDELETE(stages[s]); + if (!GrGLProgramDesc::Build(*ods, + drawType, + ods->getSrcBlendCoeff(), + ods->getDstBlendCoeff(), + this, + dstCopy.texture() ? &dstCopy : NULL, + &geometryProcessor, + &colorStages, + &coverageStages, + &desc)) { + SkDebugf("Failed to generate GL program descriptor"); + return false; } + SkAutoTUnref<GrGLProgram> program(GrGLProgramBuilder::CreateProgram(*ods, + desc, + drawType, + geometryProcessor, + colorStages.begin(), + coverageStages.begin(), + this)); if (NULL == program.get()) { + SkDebugf("Failed to create program!"); return false; } // We have to reset the drawstate because we might have added a gp - this->drawState()->reset(); + ds->reset(); + + // because occasionally optimized drawstate creation will fail for valid reasons, we only + // want to increment on success + ++t; } return true; } |