GLProgramsTest.cpp revision 71e236c03e65ff6b48a3d0eb091f814dd3e3a928
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 "GrContextFactory.h"
16#include "GrInvariantOutput.h"
17#include "GrOptDrawState.h"
18#include "GrTest.h"
19#include "GrXferProcessor.h"
20#include "SkChecksum.h"
21#include "SkRandom.h"
22#include "Test.h"
23#include "effects/GrConfigConversionEffect.h"
24#include "effects/GrPorterDuffXferProcessor.h"
25#include "gl/GrGLGpu.h"
26#include "gl/GrGLPathRendering.h"
27#include "gl/builders/GrGLProgramBuilder.h"
28
29/*
30 * A dummy processor which just tries to insert a massive key and verify that it can retrieve the
31 * whole thing correctly
32 */
33static const uint32_t kMaxKeySize = 1024;
34
35class GLBigKeyProcessor : public GrGLFragmentProcessor {
36public:
37    GLBigKeyProcessor(const GrProcessor&) {}
38
39    virtual void emitCode(GrGLFPBuilder* builder,
40                          const GrFragmentProcessor& fp,
41                          const char* outputColor,
42                          const char* inputColor,
43                          const TransformedCoordsArray&,
44                          const TextureSamplerArray&) {}
45
46    static void GenKey(const GrProcessor& processor, const GrGLCaps&, GrProcessorKeyBuilder* b) {
47        for (uint32_t i = 0; i < kMaxKeySize; i++) {
48            b->add32(i);
49        }
50    }
51
52private:
53    typedef GrGLFragmentProcessor INHERITED;
54};
55
56class BigKeyProcessor : public GrFragmentProcessor {
57public:
58    static GrFragmentProcessor* Create() {
59        GR_CREATE_STATIC_PROCESSOR(gBigKeyProcessor, BigKeyProcessor, ())
60        return SkRef(gBigKeyProcessor);
61    }
62
63    const char* name() const SK_OVERRIDE { return "Big Ole Key"; }
64
65    virtual void getGLProcessorKey(const GrGLCaps& caps,
66                                   GrProcessorKeyBuilder* b) const SK_OVERRIDE {
67        GLBigKeyProcessor::GenKey(*this, caps, b);
68    }
69
70    GrGLFragmentProcessor* createGLInstance() const SK_OVERRIDE {
71        return SkNEW_ARGS(GLBigKeyProcessor, (*this));
72    }
73
74private:
75    BigKeyProcessor() {
76        this->initClassID<BigKeyProcessor>();
77    }
78    bool onIsEqual(const GrFragmentProcessor&) const SK_OVERRIDE { return true; }
79    void onComputeInvariantOutput(GrInvariantOutput* inout) const SK_OVERRIDE { }
80
81    GR_DECLARE_FRAGMENT_PROCESSOR_TEST;
82
83    typedef GrFragmentProcessor INHERITED;
84};
85
86GR_DEFINE_FRAGMENT_PROCESSOR_TEST(BigKeyProcessor);
87
88GrFragmentProcessor* BigKeyProcessor::TestCreate(SkRandom*,
89                                                 GrContext*,
90                                                 const GrDrawTargetCaps&,
91                                                 GrTexture*[]) {
92    return BigKeyProcessor::Create();
93}
94
95/*
96 * Begin test code
97 */
98static const int kRenderTargetHeight = 1;
99static const int kRenderTargetWidth = 1;
100
101static GrRenderTarget* random_render_target(GrContext* context,
102                                            const GrCacheID& cacheId,
103                                            SkRandom* random) {
104    // setup render target
105    GrTextureParams params;
106    GrSurfaceDesc texDesc;
107    texDesc.fWidth = kRenderTargetWidth;
108    texDesc.fHeight = kRenderTargetHeight;
109    texDesc.fFlags = kRenderTarget_GrSurfaceFlag;
110    texDesc.fConfig = kRGBA_8888_GrPixelConfig;
111    texDesc.fOrigin = random->nextBool() == true ? kTopLeft_GrSurfaceOrigin :
112                                                   kBottomLeft_GrSurfaceOrigin;
113
114    SkAutoTUnref<GrTexture> texture(context->findAndRefTexture(texDesc, cacheId, &params));
115    if (!texture) {
116        texture.reset(context->createTexture(&params, texDesc, cacheId, 0, 0));
117        if (!texture) {
118            return NULL;
119        }
120    }
121    return SkRef(texture->asRenderTarget());
122}
123
124static void set_random_xpf(GrContext* context, const GrDrawTargetCaps& caps, GrDrawState* ds,
125                           SkRandom* random, GrTexture* dummyTextures[]) {
126    SkAutoTUnref<const GrXPFactory> xpf(
127        GrProcessorTestFactory<GrXPFactory>::CreateStage(random, context, caps, dummyTextures));
128    SkASSERT(xpf);
129    ds->setXPFactory(xpf.get());
130}
131
132static const GrGeometryProcessor* get_random_gp(GrContext* context,
133                                                const GrDrawTargetCaps& caps,
134                                                SkRandom* random,
135                                                GrTexture* dummyTextures[]) {
136    return GrProcessorTestFactory<GrGeometryProcessor>::CreateStage(random,
137                                                                    context,
138                                                                    caps,
139                                                                    dummyTextures);
140}
141
142static void set_random_color_coverage_stages(GrGLGpu* gpu,
143                                             GrDrawState* ds,
144                                             int maxStages,
145                                             bool usePathRendering,
146                                             SkRandom* random,
147                                             GrTexture* dummyTextures[]) {
148    int numProcs = random->nextULessThan(maxStages + 1);
149    int numColorProcs = random->nextULessThan(numProcs + 1);
150
151    int currTextureCoordSet = 0;
152    for (int s = 0; s < numProcs;) {
153        SkAutoTUnref<const GrFragmentProcessor> fp(
154                GrProcessorTestFactory<GrFragmentProcessor>::CreateStage(random,
155                                                                         gpu->getContext(),
156                                                                         *gpu->caps(),
157                                                                         dummyTextures));
158        SkASSERT(fp);
159
160        // If adding this effect would exceed the max texture coord set count then generate a
161        // new random effect.
162        if (usePathRendering && gpu->glPathRendering()->texturingMode() ==
163                                GrGLPathRendering::FixedFunction_TexturingMode) {;
164            int numTransforms = fp->numTransforms();
165            if (currTextureCoordSet + numTransforms >
166                gpu->glCaps().maxFixedFunctionTextureCoords()) {
167                continue;
168            }
169            currTextureCoordSet += numTransforms;
170        }
171
172        // finally add the stage to the correct pipeline in the drawstate
173        if (s < numColorProcs) {
174            ds->addColorProcessor(fp);
175        } else {
176            ds->addCoverageProcessor(fp);
177        }
178        ++s;
179    }
180}
181
182static void set_random_state(GrDrawState* ds, SkRandom* random) {
183    int state = 0;
184    for (int i = 1; i <= GrDrawState::kLast_StateBit; i <<= 1) {
185        state |= random->nextBool() * i;
186    }
187    ds->enableState(state);
188}
189
190// right now, the only thing we seem to care about in drawState's stencil is 'doesWrite()'
191static void set_random_stencil(GrDrawState* ds, SkRandom* random) {
192    GR_STATIC_CONST_SAME_STENCIL(kDoesWriteStencil,
193                                 kReplace_StencilOp,
194                                 kReplace_StencilOp,
195                                 kAlways_StencilFunc,
196                                 0xffff,
197                                 0xffff,
198                                 0xffff);
199    GR_STATIC_CONST_SAME_STENCIL(kDoesNotWriteStencil,
200                                 kKeep_StencilOp,
201                                 kKeep_StencilOp,
202                                 kNever_StencilFunc,
203                                 0xffff,
204                                 0xffff,
205                                 0xffff);
206
207    if (random->nextBool()) {
208        ds->setStencil(kDoesWriteStencil);
209    } else {
210        ds->setStencil(kDoesNotWriteStencil);
211    }
212}
213
214bool GrDrawTarget::programUnitTest(int maxStages) {
215    GrGLGpu* gpu = static_cast<GrGLGpu*>(fContext->getGpu());
216    // setup dummy textures
217    GrSurfaceDesc dummyDesc;
218    dummyDesc.fFlags = kRenderTarget_GrSurfaceFlag;
219    dummyDesc.fConfig = kSkia8888_GrPixelConfig;
220    dummyDesc.fWidth = 34;
221    dummyDesc.fHeight = 18;
222    SkAutoTUnref<GrTexture> dummyTexture1(gpu->createTexture(dummyDesc, false, NULL, 0));
223    dummyDesc.fFlags = kNone_GrSurfaceFlags;
224    dummyDesc.fConfig = kAlpha_8_GrPixelConfig;
225    dummyDesc.fWidth = 16;
226    dummyDesc.fHeight = 22;
227    SkAutoTUnref<GrTexture> dummyTexture2(gpu->createTexture(dummyDesc, false, NULL, 0));
228
229    if (!dummyTexture1 || ! dummyTexture2) {
230        SkDebugf("Could not allocate dummy textures");
231        return false;
232    }
233
234    GrTexture* dummyTextures[] = {dummyTexture1.get(), dummyTexture2.get()};
235
236    // dummy scissor state
237    GrScissorState scissor;
238
239    // Setup texture cache id key
240    const GrCacheID::Domain glProgramsDomain = GrCacheID::GenerateDomain();
241    GrCacheID::Key key;
242    memset(&key, 0, sizeof(key));
243    key.fData32[0] = kRenderTargetWidth;
244    key.fData32[1] = kRenderTargetHeight;
245    GrCacheID glProgramsCacheID(glProgramsDomain, key);
246
247    // setup clip
248    SkRect screen = SkRect::MakeWH(SkIntToScalar(kRenderTargetWidth),
249                                   SkIntToScalar(kRenderTargetHeight));
250
251    SkClipStack stack;
252    stack.clipDevRect(screen, SkRegion::kReplace_Op, false);
253
254    // wrap the SkClipStack in a GrClipData
255    GrClipData clipData;
256    clipData.fClipStack = &stack;
257    this->setClip(&clipData);
258
259    SkRandom random;
260    static const int NUM_TESTS = 512;
261    for (int t = 0; t < NUM_TESTS;) {
262        // setup random render target(can fail)
263        SkAutoTUnref<GrRenderTarget> rt(random_render_target(fContext, glProgramsCacheID, &random));
264        if (!rt.get()) {
265            SkDebugf("Could not allocate render target");
266            return false;
267        }
268
269        GrDrawState ds;
270        ds.setRenderTarget(rt.get());
271
272        // if path rendering we have to setup a couple of things like the draw type
273        bool usePathRendering = gpu->glCaps().pathRenderingSupport() && random.nextBool();
274
275        GrGpu::DrawType drawType = usePathRendering ? GrGpu::kDrawPath_DrawType :
276                                                      GrGpu::kDrawPoints_DrawType;
277
278        // twiddle drawstate knobs randomly
279        bool hasGeometryProcessor = !usePathRendering;
280        SkAutoTUnref<const GrGeometryProcessor> gp;
281        SkAutoTUnref<const GrPathProcessor> pathProc;
282        if (hasGeometryProcessor) {
283            gp.reset(get_random_gp(fContext, gpu->glCaps(), &random, dummyTextures));
284        } else {
285            pathProc.reset(GrPathProcessor::Create(GrColor_WHITE));
286        }
287        set_random_color_coverage_stages(gpu,
288                                         &ds,
289                                         maxStages - hasGeometryProcessor,
290                                         usePathRendering,
291                                         &random,
292                                         dummyTextures);
293
294        // creates a random xfer processor factory on the draw state
295        set_random_xpf(fContext, gpu->glCaps(), &ds, &random, dummyTextures);
296
297        set_random_state(&ds, &random);
298        set_random_stencil(&ds, &random);
299
300        GrDeviceCoordTexture dstCopy;
301
302        const GrPrimitiveProcessor* primProc;
303        if (hasGeometryProcessor) {
304            primProc = gp.get();
305        } else {
306            primProc = pathProc.get();
307        }
308        if (!this->setupDstReadIfNecessary(&ds, &dstCopy, NULL)) {
309            SkDebugf("Couldn't setup dst read texture");
310            return false;
311        }
312
313        // create optimized draw state, setup readDst texture if required, and build a descriptor
314        // and program.  ODS creation can fail, so we have to check
315        GrOptDrawState ods(ds, primProc, *gpu->caps(), scissor, &dstCopy, drawType);
316        if (ods.mustSkip()) {
317            continue;
318        }
319        ods.finalize(gpu);
320        SkAutoTUnref<GrGLProgram> program(GrGLProgramBuilder::CreateProgram(ods, gpu));
321        if (NULL == program.get()) {
322            SkDebugf("Failed to create program!");
323            return false;
324        }
325
326        // because occasionally optimized drawstate creation will fail for valid reasons, we only
327        // want to increment on success
328        ++t;
329    }
330    return true;
331}
332
333DEF_GPUTEST(GLPrograms, reporter, factory) {
334    for (int type = 0; type < GrContextFactory::kLastGLContextType; ++type) {
335        GrContext* context = factory->get(static_cast<GrContextFactory::GLContextType>(type));
336        if (context) {
337            GrGLGpu* gpu = static_cast<GrGLGpu*>(context->getGpu());
338
339            /*
340             * For the time being, we only support the test with desktop GL or for android on
341             * ARM platforms
342             * TODO When we run ES 3.00 GLSL in more places, test again
343             */
344            int maxStages;
345            if (kGL_GrGLStandard == gpu->glStandard() ||
346                kARM_GrGLVendor == gpu->ctxInfo().vendor()) {
347                maxStages = 6;
348            } else if (kTegra3_GrGLRenderer == gpu->ctxInfo().renderer() ||
349                       kOther_GrGLRenderer == gpu->ctxInfo().renderer()) {
350                maxStages = 1;
351            } else {
352                return;
353            }
354#if SK_ANGLE
355            // Some long shaders run out of temporary registers in the D3D compiler on ANGLE.
356            if (type == GrContextFactory::kANGLE_GLContextType) {
357                maxStages = 3;
358            }
359#endif
360            GrTestTarget target;
361            context->getTestTarget(&target);
362            REPORTER_ASSERT(reporter, target.target()->programUnitTest(maxStages));
363        }
364    }
365}
366
367#endif
368