1
2/*
3 * Copyright 2011 Google Inc.
4 *
5 * Use of this source code is governed by a BSD-style license that can be
6 * found in the LICENSE file.
7 */
8
9// This is a GPU-backend specific test. It relies on static intializers to work
10
11#include "SkTypes.h"
12
13#if SK_SUPPORT_GPU && SK_ALLOW_STATIC_GLOBAL_INITIALIZERS
14
15#include "GrBackendProcessorFactory.h"
16#include "GrContextFactory.h"
17#include "GrOptDrawState.h"
18#include "effects/GrConfigConversionEffect.h"
19#include "gl/GrGLPathRendering.h"
20#include "gl/GrGpuGL.h"
21#include "SkChecksum.h"
22#include "SkRandom.h"
23#include "Test.h"
24
25static void get_stage_stats(const GrFragmentStage stage, bool* readsDst,
26                            bool* readsFragPosition, bool* requiresVertexShader) {
27    if (stage.getFragmentProcessor()->willReadDstColor()) {
28        *readsDst = true;
29    }
30    if (stage.getProcessor()->willReadFragmentPosition()) {
31        *readsFragPosition = true;
32    }
33}
34
35bool GrGLProgramDesc::setRandom(SkRandom* random,
36                                GrGpuGL* gpu,
37                                const GrRenderTarget* dstRenderTarget,
38                                const GrTexture* dstCopyTexture,
39                                const GrGeometryStage* geometryProcessor,
40                                const GrFragmentStage* stages[],
41                                int numColorStages,
42                                int numCoverageStages,
43                                int currAttribIndex,
44                                GrGpu::DrawType drawType) {
45    bool isPathRendering = GrGpu::IsPathRenderingDrawType(drawType);
46    bool useLocalCoords = !isPathRendering &&
47                          random->nextBool() &&
48                          currAttribIndex < GrDrawState::kMaxVertexAttribCnt;
49
50    int numStages = numColorStages + numCoverageStages;
51    fKey.reset();
52
53    GR_STATIC_ASSERT(0 == kEffectKeyOffsetsAndLengthOffset % sizeof(uint32_t));
54
55    // Make room for everything up to and including the array of offsets to effect keys.
56    fKey.push_back_n(kEffectKeyOffsetsAndLengthOffset + 2 * sizeof(uint16_t) * (numStages +
57            (geometryProcessor ? 1 : 0)));
58
59    bool dstRead = false;
60    bool fragPos = false;
61    bool vertexShader = SkToBool(geometryProcessor);
62    int offset = 0;
63    if (geometryProcessor) {
64        const GrGeometryStage* stage = geometryProcessor;
65        uint16_t* offsetAndSize = reinterpret_cast<uint16_t*>(fKey.begin() +
66                                                              kEffectKeyOffsetsAndLengthOffset +
67                                                              offset * 2 * sizeof(uint16_t));
68        uint32_t effectKeyOffset = fKey.count();
69        if (effectKeyOffset > SK_MaxU16) {
70            fKey.reset();
71            return false;
72        }
73        GrProcessorKeyBuilder b(&fKey);
74        uint16_t effectKeySize;
75        if (!GetProcessorKey(*stage, gpu->glCaps(), useLocalCoords, &b, &effectKeySize)) {
76            fKey.reset();
77            return false;
78        }
79        vertexShader = true;
80        fragPos = stage->getProcessor()->willReadFragmentPosition();
81        offsetAndSize[0] = effectKeyOffset;
82        offsetAndSize[1] = effectKeySize;
83        offset++;
84    }
85
86    for (int s = 0; s < numStages; ++s, ++offset) {
87        const GrFragmentStage* stage = stages[s];
88        uint16_t* offsetAndSize = reinterpret_cast<uint16_t*>(fKey.begin() +
89                                                              kEffectKeyOffsetsAndLengthOffset +
90                                                              offset * 2 * sizeof(uint16_t));
91        uint32_t effectKeyOffset = fKey.count();
92        if (effectKeyOffset > SK_MaxU16) {
93            fKey.reset();
94            return false;
95        }
96        GrProcessorKeyBuilder b(&fKey);
97        uint16_t effectKeySize;
98        if (!GetProcessorKey(*stages[s], gpu->glCaps(), useLocalCoords, &b, &effectKeySize)) {
99            fKey.reset();
100            return false;
101        }
102        get_stage_stats(*stage, &dstRead, &fragPos, &vertexShader);
103        offsetAndSize[0] = effectKeyOffset;
104        offsetAndSize[1] = effectKeySize;
105    }
106
107    KeyHeader* header = this->header();
108    memset(header, 0, kHeaderSize);
109    header->fEmitsPointSize = random->nextBool();
110
111    header->fPositionAttributeIndex = 0;
112
113    // if the effects have used up all off the available attributes,
114    // don't try to use color or coverage attributes as input
115    do {
116        header->fColorInput = static_cast<GrGLProgramDesc::ColorInput>(
117                                     random->nextULessThan(kColorInputCnt));
118    } while ((GrDrawState::kMaxVertexAttribCnt <= currAttribIndex || isPathRendering) &&
119             kAttribute_ColorInput == header->fColorInput);
120    header->fColorAttributeIndex = (header->fColorInput == kAttribute_ColorInput) ?
121                                        currAttribIndex++ :
122                                        -1;
123
124    do {
125        header->fCoverageInput = static_cast<GrGLProgramDesc::ColorInput>(
126                                     random->nextULessThan(kColorInputCnt));
127    } while ((GrDrawState::kMaxVertexAttribCnt <= currAttribIndex || isPathRendering)  &&
128             kAttribute_ColorInput == header->fCoverageInput);
129    header->fCoverageAttributeIndex = (header->fCoverageInput == kAttribute_ColorInput) ?
130                                        currAttribIndex++ :
131                                        -1;
132    bool useGS = random->nextBool();
133#if GR_GL_EXPERIMENTAL_GS
134    header->fExperimentalGS = gpu->caps()->geometryShaderSupport() && useGS;
135#else
136    (void) useGS;
137#endif
138
139    header->fLocalCoordAttributeIndex = useLocalCoords ? currAttribIndex++ : -1;
140
141    header->fColorEffectCnt = numColorStages;
142    header->fCoverageEffectCnt = numCoverageStages;
143
144    if (dstRead) {
145        header->fDstReadKey = SkToU8(GrGLFragmentShaderBuilder::KeyForDstRead(dstCopyTexture,
146                                                                      gpu->glCaps()));
147    } else {
148        header->fDstReadKey = 0;
149    }
150    if (fragPos) {
151        header->fFragPosKey = SkToU8(GrGLFragmentShaderBuilder::KeyForFragmentPosition(dstRenderTarget,
152                                                                               gpu->glCaps()));
153    } else {
154        header->fFragPosKey = 0;
155    }
156
157    header->fUseFragShaderOnly = isPathRendering && gpu->glPathRendering()->texturingMode() ==
158                                                    GrGLPathRendering::FixedFunction_TexturingMode;
159    header->fHasGeometryProcessor = vertexShader;
160
161    GrOptDrawState::PrimaryOutputType primaryOutput;
162    GrOptDrawState::SecondaryOutputType secondaryOutput;
163    if (!dstRead) {
164        primaryOutput = GrOptDrawState::kModulate_PrimaryOutputType;
165    } else {
166        primaryOutput = static_cast<GrOptDrawState::PrimaryOutputType>(
167            random->nextULessThan(GrOptDrawState::kPrimaryOutputTypeCnt));
168    }
169
170    if (GrOptDrawState::kCombineWithDst_PrimaryOutputType == primaryOutput ||
171        !gpu->caps()->dualSourceBlendingSupport()) {
172        secondaryOutput = GrOptDrawState::kNone_SecondaryOutputType;
173    } else {
174        secondaryOutput = static_cast<GrOptDrawState::SecondaryOutputType>(
175            random->nextULessThan(GrOptDrawState::kSecondaryOutputTypeCnt));
176    }
177
178    header->fPrimaryOutputType = primaryOutput;
179    header->fSecondaryOutputType = secondaryOutput;
180
181    this->finalize();
182    return true;
183}
184
185// TODO clean this up, we have to do this to test geometry processors but there has got to be
186// a better way.  In the mean time, we actually fill out these generic vertex attribs below with
187// the correct vertex attribs from the GP.  We have to ensure, however, we don't try to add more
188// than two attributes.
189GrVertexAttrib genericVertexAttribs[] = {
190    { kVec2f_GrVertexAttribType, 0,   kPosition_GrVertexAttribBinding },
191    { kVec2f_GrVertexAttribType, 0,   kGeometryProcessor_GrVertexAttribBinding },
192    { kVec2f_GrVertexAttribType, 0,   kGeometryProcessor_GrVertexAttribBinding }
193};
194
195/*
196 * convert sl type to vertexattrib type, not a complete implementation, only use for debugging
197 */
198GrVertexAttribType convert_sltype_to_attribtype(GrSLType type) {
199    switch (type) {
200        case kFloat_GrSLType:
201            return kFloat_GrVertexAttribType;
202        case kVec2f_GrSLType:
203            return kVec2f_GrVertexAttribType;
204        case kVec3f_GrSLType:
205            return kVec3f_GrVertexAttribType;
206        case kVec4f_GrSLType:
207            return kVec4f_GrVertexAttribType;
208        default:
209            SkFAIL("Type isn't convertible");
210            return kFloat_GrVertexAttribType;
211    }
212}
213// TODO end test hack
214
215
216bool GrGpuGL::programUnitTest(int maxStages) {
217
218    GrTextureDesc dummyDesc;
219    dummyDesc.fFlags = kRenderTarget_GrTextureFlagBit;
220    dummyDesc.fConfig = kSkia8888_GrPixelConfig;
221    dummyDesc.fWidth = 34;
222    dummyDesc.fHeight = 18;
223    SkAutoTUnref<GrTexture> dummyTexture1(this->createTexture(dummyDesc, NULL, 0));
224    dummyDesc.fFlags = kNone_GrTextureFlags;
225    dummyDesc.fConfig = kAlpha_8_GrPixelConfig;
226    dummyDesc.fWidth = 16;
227    dummyDesc.fHeight = 22;
228    SkAutoTUnref<GrTexture> dummyTexture2(this->createTexture(dummyDesc, NULL, 0));
229
230    if (!dummyTexture1 || ! dummyTexture2) {
231        return false;
232    }
233
234    static const int NUM_TESTS = 512;
235
236    SkRandom random;
237    for (int t = 0; t < NUM_TESTS; ++t) {
238
239#if 0
240        GrPrintf("\nTest Program %d\n-------------\n", t);
241        static const int stop = -1;
242        if (t == stop) {
243            int breakpointhere = 9;
244        }
245#endif
246
247        GrGLProgramDesc pdesc;
248
249        int currAttribIndex = 1;  // we need to always leave room for position
250        int currTextureCoordSet = 0;
251        GrTexture* dummyTextures[] = {dummyTexture1.get(), dummyTexture2.get()};
252
253        int numStages = random.nextULessThan(maxStages + 1);
254        int numColorStages = random.nextULessThan(numStages + 1);
255        int numCoverageStages = numStages - numColorStages;
256
257        SkAutoSTMalloc<8, const GrFragmentStage*> stages(numStages);
258
259        bool usePathRendering = this->glCaps().pathRenderingSupport() && random.nextBool();
260
261        GrGpu::DrawType drawType = usePathRendering ? GrGpu::kDrawPath_DrawType :
262                                                      GrGpu::kDrawPoints_DrawType;
263
264        SkAutoTDelete<GrGeometryStage> geometryProcessor;
265        bool hasGeometryProcessor = usePathRendering ? false : random.nextBool();
266        if (hasGeometryProcessor) {
267            while (true) {
268                SkAutoTUnref<const GrGeometryProcessor> effect(
269                        GrProcessorTestFactory<GrGeometryProcessor>::CreateStage(&random, this->getContext(), *this->caps(),
270                                                         dummyTextures));
271                SkASSERT(effect);
272                // Only geometryProcessor can use vertex shader
273                GrGeometryStage* stage = SkNEW_ARGS(GrGeometryStage, (effect.get()));
274                geometryProcessor.reset(stage);
275
276                // we have to set dummy vertex attribs
277                const GrGeometryProcessor::VertexAttribArray& v = effect->getVertexAttribs();
278                int numVertexAttribs = v.count();
279
280                SkASSERT(GrGeometryProcessor::kMaxVertexAttribs == 2 &&
281                         GrGeometryProcessor::kMaxVertexAttribs >= numVertexAttribs);
282                size_t runningStride = GrVertexAttribTypeSize(genericVertexAttribs[0].fType);
283                for (int i = 0; i < numVertexAttribs; i++) {
284                    genericVertexAttribs[i + 1].fOffset = runningStride;
285                    genericVertexAttribs[i + 1].fType =
286                            convert_sltype_to_attribtype(v[i].getType());
287                    runningStride += GrVertexAttribTypeSize(genericVertexAttribs[i + 1].fType);
288                }
289
290                // update the vertex attributes with the ds
291                GrDrawState* ds = this->drawState();
292                ds->setVertexAttribs<genericVertexAttribs>(numVertexAttribs + 1, runningStride);
293                currAttribIndex = numVertexAttribs + 1;
294                break;
295            }
296        }
297        for (int s = 0; s < numStages;) {
298            SkAutoTUnref<const GrFragmentProcessor> effect(
299                    GrProcessorTestFactory<GrFragmentProcessor>::CreateStage(
300                                                                            &random,
301                                                                            this->getContext(),
302                                                                            *this->caps(),
303                                                                            dummyTextures));
304            SkASSERT(effect);
305
306            // If adding this effect would exceed the max texture coord set count then generate a
307            // new random effect.
308            if (usePathRendering && this->glPathRendering()->texturingMode() ==
309                                    GrGLPathRendering::FixedFunction_TexturingMode) {;
310                int numTransforms = effect->numTransforms();
311                if (currTextureCoordSet + numTransforms > this->glCaps().maxFixedFunctionTextureCoords()) {
312                    continue;
313                }
314                currTextureCoordSet += numTransforms;
315            }
316            GrFragmentStage* stage = SkNEW_ARGS(GrFragmentStage, (effect.get()));
317
318            stages[s] = stage;
319            ++s;
320        }
321        const GrTexture* dstTexture = random.nextBool() ? dummyTextures[0] : dummyTextures[1];
322        if (!pdesc.setRandom(&random,
323                             this,
324                             dummyTextures[0]->asRenderTarget(),
325                             dstTexture,
326                             geometryProcessor.get(),
327                             stages.get(),
328                             numColorStages,
329                             numCoverageStages,
330                             currAttribIndex,
331                             drawType)) {
332            return false;
333        }
334
335        SkAutoTUnref<GrGLProgram> program(GrGLProgram::Create(this,
336                                                              pdesc,
337                                                              geometryProcessor.get(),
338                                                              stages,
339                                                              stages + numColorStages));
340        for (int s = 0; s < numStages; ++s) {
341            SkDELETE(stages[s]);
342        }
343        if (NULL == program.get()) {
344            return false;
345        }
346
347        // We have to reset the drawstate because we might have added a gp
348        this->drawState()->reset();
349    }
350    return true;
351}
352
353DEF_GPUTEST(GLPrograms, reporter, factory) {
354    for (int type = 0; type < GrContextFactory::kLastGLContextType; ++type) {
355        GrContext* context = factory->get(static_cast<GrContextFactory::GLContextType>(type));
356        if (context) {
357            GrGpuGL* gpu = static_cast<GrGpuGL*>(context->getGpu());
358            int maxStages = 6;
359#if SK_ANGLE
360            // Some long shaders run out of temporary registers in the D3D compiler on ANGLE.
361            if (type == GrContextFactory::kANGLE_GLContextType) {
362                maxStages = 3;
363            }
364#endif
365            REPORTER_ASSERT(reporter, gpu->programUnitTest(maxStages));
366        }
367    }
368}
369
370// This is evil evil evil. The linker may throw away whole translation units as dead code if it
371// thinks none of the functions are called. It will do this even if there are static initializers
372// in the unit that could pass pointers to functions from the unit out to other translation units!
373// We force some of the effects that would otherwise be discarded to link here.
374
375#include "SkAlphaThresholdFilter.h"
376#include "SkColorMatrixFilter.h"
377#include "SkLightingImageFilter.h"
378#include "SkMagnifierImageFilter.h"
379
380void forceLinking();
381
382void forceLinking() {
383    SkLightingImageFilter::CreateDistantLitDiffuse(SkPoint3(0,0,0), 0, 0, 0);
384    SkAlphaThresholdFilter::Create(SkRegion(), .5f, .5f);
385    SkAutoTUnref<SkImageFilter> mag(SkMagnifierImageFilter::Create(
386        SkRect::MakeWH(SK_Scalar1, SK_Scalar1), SK_Scalar1));
387    GrConfigConversionEffect::Create(NULL,
388                                     false,
389                                     GrConfigConversionEffect::kNone_PMConversion,
390                                     SkMatrix::I());
391    SkScalar matrix[20];
392    SkAutoTUnref<SkColorMatrixFilter> cmf(SkColorMatrixFilter::Create(matrix));
393}
394
395#endif
396