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, ¶ms)); 115 if (!texture) { 116 texture.reset(context->createTexture(¶ms, 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