1 /* 2 * Copyright 2016 Google Inc. 3 * 4 * Use of this source code is governed by a BSD-style license that can be 5 * found in the LICENSE file. 6 */ 7 8 #include "SkTypes.h" 9 #include "Test.h" 10 11 #if SK_SUPPORT_GPU 12 #include <random> 13 #include "GrClip.h" 14 #include "GrContext.h" 15 #include "GrGpuResource.h" 16 #include "GrRenderTargetContext.h" 17 #include "GrRenderTargetContextPriv.h" 18 #include "GrResourceProvider.h" 19 #include "glsl/GrGLSLFragmentProcessor.h" 20 #include "glsl/GrGLSLFragmentShaderBuilder.h" 21 #include "ops/GrMeshDrawOp.h" 22 #include "ops/GrRectOpFactory.h" 23 24 namespace { 25 class TestOp : public GrMeshDrawOp { 26 public: 27 DEFINE_OP_CLASS_ID 28 const char* name() const override { return "TestOp"; } 29 30 static std::unique_ptr<GrDrawOp> Make(sk_sp<GrFragmentProcessor> fp) { 31 return std::unique_ptr<GrDrawOp>(new TestOp(std::move(fp))); 32 } 33 34 FixedFunctionFlags fixedFunctionFlags() const override { return FixedFunctionFlags::kNone; } 35 36 RequiresDstTexture finalize(const GrCaps& caps, const GrAppliedClip* clip) override { 37 static constexpr GrProcessorAnalysisColor kUnknownColor; 38 GrColor overrideColor; 39 fProcessors.finalize(kUnknownColor, GrProcessorAnalysisCoverage::kNone, clip, false, caps, 40 &overrideColor); 41 return RequiresDstTexture::kNo; 42 } 43 44 private: 45 TestOp(sk_sp<GrFragmentProcessor> fp) : INHERITED(ClassID()), fProcessors(std::move(fp)) { 46 this->setBounds(SkRect::MakeWH(100, 100), HasAABloat::kNo, IsZeroArea::kNo); 47 } 48 49 void onPrepareDraws(Target* target) const override { return; } 50 51 bool onCombineIfPossible(GrOp* op, const GrCaps& caps) override { return false; } 52 53 GrProcessorSet fProcessors; 54 55 typedef GrMeshDrawOp INHERITED; 56 }; 57 58 /** 59 * FP used to test ref/IO counts on owned GrGpuResources. Can also be a parent FP to test counts 60 * of resources owned by child FPs. 61 */ 62 class TestFP : public GrFragmentProcessor { 63 public: 64 struct Image { 65 Image(sk_sp<GrTextureProxy> proxy, GrIOType ioType) : fProxy(proxy), fIOType(ioType) {} 66 sk_sp<GrTextureProxy> fProxy; 67 GrIOType fIOType; 68 }; 69 static sk_sp<GrFragmentProcessor> Make(sk_sp<GrFragmentProcessor> child) { 70 return sk_sp<GrFragmentProcessor>(new TestFP(std::move(child))); 71 } 72 static sk_sp<GrFragmentProcessor> Make(const SkTArray<sk_sp<GrTextureProxy>>& proxies, 73 const SkTArray<sk_sp<GrBuffer>>& buffers, 74 const SkTArray<Image>& images) { 75 return sk_sp<GrFragmentProcessor>(new TestFP(proxies, buffers, images)); 76 } 77 78 const char* name() const override { return "test"; } 79 80 void onGetGLSLProcessorKey(const GrShaderCaps&, GrProcessorKeyBuilder* b) const override { 81 // We don't really care about reusing these. 82 static int32_t gKey = 0; 83 b->add32(sk_atomic_inc(&gKey)); 84 } 85 86 private: 87 TestFP(const SkTArray<sk_sp<GrTextureProxy>>& proxies, 88 const SkTArray<sk_sp<GrBuffer>>& buffers, 89 const SkTArray<Image>& images) 90 : INHERITED(kNone_OptimizationFlags), fSamplers(4), fBuffers(4), fImages(4) { 91 for (const auto& proxy : proxies) { 92 this->addTextureSampler(&fSamplers.emplace_back(proxy)); 93 } 94 for (const auto& buffer : buffers) { 95 this->addBufferAccess(&fBuffers.emplace_back(kRGBA_8888_GrPixelConfig, buffer.get())); 96 } 97 for (const Image& image : images) { 98 fImages.emplace_back(image.fProxy, image.fIOType, 99 GrSLMemoryModel::kNone, GrSLRestrict::kNo); 100 this->addImageStorageAccess(&fImages.back()); 101 } 102 } 103 104 TestFP(sk_sp<GrFragmentProcessor> child) 105 : INHERITED(kNone_OptimizationFlags), fSamplers(4), fBuffers(4), fImages(4) { 106 this->registerChildProcessor(std::move(child)); 107 } 108 109 virtual GrGLSLFragmentProcessor* onCreateGLSLInstance() const override { 110 class TestGLSLFP : public GrGLSLFragmentProcessor { 111 public: 112 TestGLSLFP() {} 113 void emitCode(EmitArgs& args) override { 114 GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder; 115 fragBuilder->codeAppendf("%s = %s;", args.fOutputColor, args.fInputColor); 116 } 117 118 private: 119 }; 120 return new TestGLSLFP(); 121 } 122 123 bool onIsEqual(const GrFragmentProcessor&) const override { return false; } 124 125 GrTAllocator<TextureSampler> fSamplers; 126 GrTAllocator<BufferAccess> fBuffers; 127 GrTAllocator<ImageStorageAccess> fImages; 128 typedef GrFragmentProcessor INHERITED; 129 }; 130 } 131 132 template <typename T> 133 inline void testingOnly_getIORefCnts(const T* resource, int* refCnt, int* readCnt, int* writeCnt) { 134 *refCnt = resource->fRefCnt; 135 *readCnt = resource->fPendingReads; 136 *writeCnt = resource->fPendingWrites; 137 } 138 139 void testingOnly_getIORefCnts(GrTextureProxy* proxy, int* refCnt, int* readCnt, int* writeCnt) { 140 *refCnt = proxy->getBackingRefCnt_TestOnly(); 141 *readCnt = proxy->getPendingReadCnt_TestOnly(); 142 *writeCnt = proxy->getPendingWriteCnt_TestOnly(); 143 } 144 145 DEF_GPUTEST_FOR_ALL_CONTEXTS(ProcessorRefTest, reporter, ctxInfo) { 146 GrContext* context = ctxInfo.grContext(); 147 148 GrTextureDesc desc; 149 desc.fConfig = kRGBA_8888_GrPixelConfig; 150 desc.fWidth = 10; 151 desc.fHeight = 10; 152 153 for (int parentCnt = 0; parentCnt < 2; parentCnt++) { 154 sk_sp<GrRenderTargetContext> renderTargetContext(context->makeDeferredRenderTargetContext( 155 SkBackingFit::kApprox, 1, 1, kRGBA_8888_GrPixelConfig, nullptr)); 156 { 157 bool texelBufferSupport = context->caps()->shaderCaps()->texelBufferSupport(); 158 bool imageLoadStoreSupport = context->caps()->shaderCaps()->imageLoadStoreSupport(); 159 sk_sp<GrTextureProxy> proxy1(GrSurfaceProxy::MakeDeferred(context->resourceProvider(), 160 desc, SkBackingFit::kExact, 161 SkBudgeted::kYes)); 162 sk_sp<GrTextureProxy> proxy2(GrSurfaceProxy::MakeDeferred(context->resourceProvider(), 163 desc, SkBackingFit::kExact, 164 SkBudgeted::kYes)); 165 sk_sp<GrTextureProxy> proxy3(GrSurfaceProxy::MakeDeferred(context->resourceProvider(), 166 desc, SkBackingFit::kExact, 167 SkBudgeted::kYes)); 168 sk_sp<GrTextureProxy> proxy4(GrSurfaceProxy::MakeDeferred(context->resourceProvider(), 169 desc, SkBackingFit::kExact, 170 SkBudgeted::kYes)); 171 sk_sp<GrBuffer> buffer(texelBufferSupport 172 ? context->resourceProvider()->createBuffer( 173 1024, GrBufferType::kTexel_GrBufferType, 174 GrAccessPattern::kStatic_GrAccessPattern, 0) 175 : nullptr); 176 { 177 SkTArray<sk_sp<GrTextureProxy>> proxies; 178 SkTArray<sk_sp<GrBuffer>> buffers; 179 SkTArray<TestFP::Image> images; 180 proxies.push_back(proxy1); 181 if (texelBufferSupport) { 182 buffers.push_back(buffer); 183 } 184 if (imageLoadStoreSupport) { 185 images.emplace_back(proxy2, GrIOType::kRead_GrIOType); 186 images.emplace_back(proxy3, GrIOType::kWrite_GrIOType); 187 images.emplace_back(proxy4, GrIOType::kRW_GrIOType); 188 } 189 auto fp = TestFP::Make(std::move(proxies), std::move(buffers), std::move(images)); 190 for (int i = 0; i < parentCnt; ++i) { 191 fp = TestFP::Make(std::move(fp)); 192 } 193 std::unique_ptr<GrDrawOp> op(TestOp::Make(std::move(fp))); 194 renderTargetContext->priv().testingOnly_addDrawOp(std::move(op)); 195 } 196 int refCnt, readCnt, writeCnt; 197 198 testingOnly_getIORefCnts(proxy1.get(), &refCnt, &readCnt, &writeCnt); 199 REPORTER_ASSERT(reporter, 1 == refCnt); 200 REPORTER_ASSERT(reporter, 1 == readCnt); 201 REPORTER_ASSERT(reporter, 0 == writeCnt); 202 203 if (texelBufferSupport) { 204 testingOnly_getIORefCnts(buffer.get(), &refCnt, &readCnt, &writeCnt); 205 REPORTER_ASSERT(reporter, 1 == refCnt); 206 REPORTER_ASSERT(reporter, 1 == readCnt); 207 REPORTER_ASSERT(reporter, 0 == writeCnt); 208 } 209 210 if (imageLoadStoreSupport) { 211 testingOnly_getIORefCnts(proxy2.get(), &refCnt, &readCnt, &writeCnt); 212 REPORTER_ASSERT(reporter, 1 == refCnt); 213 REPORTER_ASSERT(reporter, 1 == readCnt); 214 REPORTER_ASSERT(reporter, 0 == writeCnt); 215 216 testingOnly_getIORefCnts(proxy3.get(), &refCnt, &readCnt, &writeCnt); 217 REPORTER_ASSERT(reporter, 1 == refCnt); 218 REPORTER_ASSERT(reporter, 0 == readCnt); 219 REPORTER_ASSERT(reporter, 1 == writeCnt); 220 221 testingOnly_getIORefCnts(proxy4.get(), &refCnt, &readCnt, &writeCnt); 222 REPORTER_ASSERT(reporter, 1 == refCnt); 223 REPORTER_ASSERT(reporter, 1 == readCnt); 224 REPORTER_ASSERT(reporter, 1 == writeCnt); 225 } 226 227 context->flush(); 228 229 testingOnly_getIORefCnts(proxy1.get(), &refCnt, &readCnt, &writeCnt); 230 REPORTER_ASSERT(reporter, 1 == refCnt); 231 REPORTER_ASSERT(reporter, 0 == readCnt); 232 REPORTER_ASSERT(reporter, 0 == writeCnt); 233 234 if (texelBufferSupport) { 235 testingOnly_getIORefCnts(buffer.get(), &refCnt, &readCnt, &writeCnt); 236 REPORTER_ASSERT(reporter, 1 == refCnt); 237 REPORTER_ASSERT(reporter, 0 == readCnt); 238 REPORTER_ASSERT(reporter, 0 == writeCnt); 239 } 240 241 if (texelBufferSupport) { 242 testingOnly_getIORefCnts(proxy2.get(), &refCnt, &readCnt, &writeCnt); 243 REPORTER_ASSERT(reporter, 1 == refCnt); 244 REPORTER_ASSERT(reporter, 0 == readCnt); 245 REPORTER_ASSERT(reporter, 0 == writeCnt); 246 247 testingOnly_getIORefCnts(proxy3.get(), &refCnt, &readCnt, &writeCnt); 248 REPORTER_ASSERT(reporter, 1 == refCnt); 249 REPORTER_ASSERT(reporter, 0 == readCnt); 250 REPORTER_ASSERT(reporter, 0 == writeCnt); 251 252 testingOnly_getIORefCnts(proxy4.get(), &refCnt, &readCnt, &writeCnt); 253 REPORTER_ASSERT(reporter, 1 == refCnt); 254 REPORTER_ASSERT(reporter, 0 == readCnt); 255 REPORTER_ASSERT(reporter, 0 == writeCnt); 256 } 257 } 258 } 259 } 260 261 // This test uses the random GrFragmentProcessor test factory, which relies on static initializers. 262 #if SK_ALLOW_STATIC_GLOBAL_INITIALIZERS 263 264 static GrColor texel_color(int i, int j) { 265 SkASSERT((unsigned)i < 256 && (unsigned)j < 256); 266 GrColor color = GrColorPackRGBA(j, (uint8_t)(i + j), (uint8_t)(2 * j - i), i); 267 return GrPremulColor(color); 268 } 269 270 static GrColor4f texel_color4f(int i, int j) { return GrColor4f::FromGrColor(texel_color(i, j)); } 271 272 void test_draw_op(GrRenderTargetContext* rtc, sk_sp<GrFragmentProcessor> fp, 273 sk_sp<GrTextureProxy> inputDataProxy) { 274 GrPaint paint; 275 paint.addColorTextureProcessor(std::move(inputDataProxy), nullptr, SkMatrix::I()); 276 paint.addColorFragmentProcessor(std::move(fp)); 277 paint.setPorterDuffXPFactory(SkBlendMode::kSrc); 278 279 auto op = GrRectOpFactory::MakeNonAAFill(std::move(paint), SkMatrix::I(), 280 SkRect::MakeWH(rtc->width(), rtc->height()), 281 GrAAType::kNone); 282 rtc->addDrawOp(GrNoClip(), std::move(op)); 283 } 284 285 #include "SkCommandLineFlags.h" 286 DEFINE_bool(randomProcessorTest, false, "Use non-deterministic seed for random processor tests?"); 287 288 #if GR_TEST_UTILS 289 DEF_GPUTEST_FOR_GL_RENDERING_CONTEXTS(ProcessorOptimizationValidationTest, reporter, ctxInfo) { 290 GrContext* context = ctxInfo.grContext(); 291 using FPFactory = GrProcessorTestFactory<GrFragmentProcessor>; 292 293 uint32_t seed = 0; 294 if (FLAGS_randomProcessorTest) { 295 std::random_device rd; 296 seed = rd(); 297 } 298 // If a non-deterministic bot fails this test, check the output to see what seed it used, then 299 // hard-code that value here: 300 SkRandom random(seed); 301 302 sk_sp<GrRenderTargetContext> rtc = context->makeDeferredRenderTargetContext( 303 SkBackingFit::kExact, 256, 256, kRGBA_8888_GrPixelConfig, nullptr); 304 GrSurfaceDesc desc; 305 desc.fWidth = 256; 306 desc.fHeight = 256; 307 desc.fFlags = kRenderTarget_GrSurfaceFlag; 308 desc.fConfig = kRGBA_8888_GrPixelConfig; 309 310 sk_sp<GrTextureProxy> proxies[2]; 311 312 // Put premul data into the RGBA texture that the test FPs can optionally use. 313 std::unique_ptr<GrColor[]> rgbaData(new GrColor[256 * 256]); 314 for (int y = 0; y < 256; ++y) { 315 for (int x = 0; x < 256; ++x) { 316 rgbaData.get()[256 * y + x] = 317 texel_color(random.nextULessThan(256), random.nextULessThan(256)); 318 } 319 } 320 proxies[0] = GrSurfaceProxy::MakeDeferred(context->resourceProvider(), desc, SkBudgeted::kYes, 321 rgbaData.get(), 256 * sizeof(GrColor)); 322 323 // Put random values into the alpha texture that the test FPs can optionally use. 324 desc.fConfig = kAlpha_8_GrPixelConfig; 325 std::unique_ptr<uint8_t[]> alphaData(new uint8_t[256 * 256]); 326 for (int y = 0; y < 256; ++y) { 327 for (int x = 0; x < 256; ++x) { 328 alphaData.get()[256 * y + x] = random.nextULessThan(256); 329 } 330 } 331 proxies[1] = GrSurfaceProxy::MakeDeferred(context->resourceProvider(), desc, SkBudgeted::kYes, 332 alphaData.get(), 256); 333 334 if (!proxies[0] || !proxies[1]) { 335 return; 336 } 337 338 GrProcessorTestData testData(&random, context, rtc.get(), proxies); 339 340 // Use a different array of premul colors for the output of the fragment processor that preceeds 341 // the fragment processor under test. 342 for (int y = 0; y < 256; ++y) { 343 for (int x = 0; x < 256; ++x) { 344 rgbaData.get()[256 * y + x] = texel_color(x, y); 345 } 346 } 347 desc.fConfig = kRGBA_8888_GrPixelConfig; 348 349 sk_sp<GrTextureProxy> dataProxy = GrSurfaceProxy::MakeDeferred(context->resourceProvider(), 350 desc, SkBudgeted::kYes, 351 rgbaData.get(), 352 256 * sizeof(GrColor)); 353 354 // Because processors factories configure themselves in random ways, this is not exhaustive. 355 for (int i = 0; i < FPFactory::Count(); ++i) { 356 int timesToInvokeFactory = 5; 357 // Increase the number of attempts if the FP has child FPs since optimizations likely depend 358 // on child optimizations being present. 359 sk_sp<GrFragmentProcessor> fp = FPFactory::MakeIdx(i, &testData); 360 for (int j = 0; j < fp->numChildProcessors(); ++j) { 361 // This value made a reasonable trade off between time and coverage when this test was 362 // written. 363 timesToInvokeFactory *= FPFactory::Count() / 2; 364 } 365 for (int j = 0; j < timesToInvokeFactory; ++j) { 366 fp = FPFactory::MakeIdx(i, &testData); 367 if (!fp->instantiate(context->resourceProvider())) { 368 continue; 369 } 370 371 if (!fp->hasConstantOutputForConstantInput() && !fp->preservesOpaqueInput() && 372 !fp->compatibleWithCoverageAsAlpha()) { 373 continue; 374 } 375 test_draw_op(rtc.get(), fp, dataProxy); 376 memset(rgbaData.get(), 0x0, sizeof(GrColor) * 256 * 256); 377 rtc->readPixels( 378 SkImageInfo::Make(256, 256, kRGBA_8888_SkColorType, kPremul_SkAlphaType), 379 rgbaData.get(), 0, 0, 0); 380 bool passing = true; 381 if (0) { // Useful to see what FPs are being tested. 382 SkString children; 383 for (int c = 0; c < fp->numChildProcessors(); ++c) { 384 if (!c) { 385 children.append("("); 386 } 387 children.append(fp->childProcessor(c).name()); 388 children.append(c == fp->numChildProcessors() - 1 ? ")" : ", "); 389 } 390 SkDebugf("%s %s\n", fp->name(), children.c_str()); 391 } 392 for (int y = 0; y < 256 && passing; ++y) { 393 for (int x = 0; x < 256 && passing; ++x) { 394 GrColor input = texel_color(x, y); 395 GrColor output = rgbaData.get()[y * 256 + x]; 396 if (fp->compatibleWithCoverageAsAlpha()) { 397 // A modulating processor is allowed to modulate either the input color or 398 // just the input alpha. 399 bool legalColorModulation = 400 GrColorUnpackA(output) <= GrColorUnpackA(input) && 401 GrColorUnpackR(output) <= GrColorUnpackR(input) && 402 GrColorUnpackG(output) <= GrColorUnpackG(input) && 403 GrColorUnpackB(output) <= GrColorUnpackB(input); 404 bool legalAlphaModulation = 405 GrColorUnpackA(output) <= GrColorUnpackA(input) && 406 GrColorUnpackR(output) <= GrColorUnpackA(input) && 407 GrColorUnpackG(output) <= GrColorUnpackA(input) && 408 GrColorUnpackB(output) <= GrColorUnpackA(input); 409 if (!legalColorModulation && !legalAlphaModulation) { 410 ERRORF(reporter, 411 "\"Modulating\" processor %s made color/alpha value larger. " 412 "Input: 0x%08x, Output: 0x%08x.", 413 fp->name(), input, output); 414 passing = false; 415 } 416 } 417 GrColor4f input4f = texel_color4f(x, y); 418 GrColor4f output4f = GrColor4f::FromGrColor(output); 419 GrColor4f expected4f; 420 if (fp->hasConstantOutputForConstantInput(input4f, &expected4f)) { 421 float rDiff = fabsf(output4f.fRGBA[0] - expected4f.fRGBA[0]); 422 float gDiff = fabsf(output4f.fRGBA[1] - expected4f.fRGBA[1]); 423 float bDiff = fabsf(output4f.fRGBA[2] - expected4f.fRGBA[2]); 424 float aDiff = fabsf(output4f.fRGBA[3] - expected4f.fRGBA[3]); 425 static constexpr float kTol = 4 / 255.f; 426 if (rDiff > kTol || gDiff > kTol || bDiff > kTol || aDiff > kTol) { 427 ERRORF(reporter, 428 "Processor %s claimed output for const input doesn't match " 429 "actual output. Error: %f, Tolerance: %f, input: (%f, %f, %f, " 430 "%f), actual: (%f, %f, %f, %f), expected(%f, %f, %f, %f)", 431 fp->name(), SkTMax(rDiff, SkTMax(gDiff, SkTMax(bDiff, aDiff))), 432 kTol, input4f.fRGBA[0], input4f.fRGBA[1], input4f.fRGBA[2], 433 input4f.fRGBA[3], output4f.fRGBA[0], output4f.fRGBA[1], 434 output4f.fRGBA[2], output4f.fRGBA[3], expected4f.fRGBA[0], 435 expected4f.fRGBA[1], expected4f.fRGBA[2], expected4f.fRGBA[3]); 436 passing = false; 437 } 438 } 439 if (GrColorIsOpaque(input) && fp->preservesOpaqueInput() && 440 !GrColorIsOpaque(output)) { 441 ERRORF(reporter, 442 "Processor %s claimed opaqueness is preserved but it is not. Input: " 443 "0x%08x, Output: 0x%08x.", 444 fp->name(), input, output); 445 passing = false; 446 } 447 if (!passing) { 448 ERRORF(reporter, "Seed: 0x%08x, Processor details: %s", 449 seed, fp->dumpInfo().c_str()); 450 } 451 } 452 } 453 } 454 } 455 } 456 457 #endif // GR_TEST_UTILS 458 #endif // SK_ALLOW_STATIC_GLOBAL_INITIALIZERS 459 #endif // SK_SUPPORT_GPU 460