/* * Copyright 2017 Google Inc. * * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ #include "SkTypes.h" #if SK_SUPPORT_GPU #include "GrBackendSurface.h" #include "GrGpu.h" #include "SkCanvas.h" #include "SkDeferredDisplayListRecorder.h" #include "SkGpuDevice.h" #include "SkSurface.h" #include "SkSurface_Gpu.h" #include "SkSurfaceCharacterization.h" #include "SkSurfaceProps.h" #include "Test.h" class SurfaceParameters { public: static const int kNumParams = 8; static const int kSampleCount = 5; SurfaceParameters() : fWidth(64) , fHeight(64) , fOrigin(kTopLeft_GrSurfaceOrigin) , fColorType(kRGBA_8888_SkColorType) , fColorSpace(SkColorSpace::MakeSRGB()) , fSampleCount(0) , fSurfaceProps(0x0, kUnknown_SkPixelGeometry) { } int sampleCount() const { return fSampleCount; } // Modify the SurfaceParameters in just one way void modify(int i) { switch (i) { case 0: fWidth = 63; break; case 1: fHeight = 63; break; case 2: fOrigin = kBottomLeft_GrSurfaceOrigin; break; case 3: fColorType = kRGBA_F16_SkColorType; break; case 4: fColorSpace = SkColorSpace::MakeSRGBLinear(); break; case kSampleCount: fSampleCount = 4; break; case 6: fSurfaceProps = SkSurfaceProps(0x0, kRGB_H_SkPixelGeometry); break; case 7: fSurfaceProps = SkSurfaceProps(SkSurfaceProps::kUseDeviceIndependentFonts_Flag, kUnknown_SkPixelGeometry); break; } } // Create the surface with the current set of parameters sk_sp make(GrContext* context) const { // Note that Ganesh doesn't make use of the SkImageInfo's alphaType SkImageInfo ii = SkImageInfo::Make(fWidth, fHeight, fColorType, kPremul_SkAlphaType, fColorSpace); return SkSurface::MakeRenderTarget(context, SkBudgeted::kYes, ii, fSampleCount, fOrigin, &fSurfaceProps); } private: int fWidth; int fHeight; GrSurfaceOrigin fOrigin; SkColorType fColorType; sk_sp fColorSpace; int fSampleCount; SkSurfaceProps fSurfaceProps; }; // This tests SkSurfaceCharacterization/SkSurface compatibility DEF_GPUTEST_FOR_ALL_CONTEXTS(SkSurfaceCharacterization, reporter, ctxInfo) { GrContext* context = ctxInfo.grContext(); std::unique_ptr ddl; // First, create a DDL using the stock SkSurface parameters { SurfaceParameters params; sk_sp s = params.make(context); if (!s) { return; } SkSurfaceCharacterization c; SkAssertResult(s->characterize(&c)); SkDeferredDisplayListRecorder r(c); SkCanvas* canvas = r.getCanvas(); if (!canvas) { return; } canvas->drawRect(SkRect::MakeXYWH(10, 10, 10, 10), SkPaint()); ddl = r.detach(); REPORTER_ASSERT(reporter, s->draw(ddl.get())); } // Then, alter each parameter in turn and check that the DDL & surface are incompatible for (int i = 0; i < SurfaceParameters::kNumParams; ++i) { SurfaceParameters params; params.modify(i); sk_sp s = params.make(context); if (!s) { continue; } if (SurfaceParameters::kSampleCount == i) { SkSurface_Gpu* gpuSurf = static_cast(s.get()); int supportedSampleCount = context->caps()->getSampleCount( params.sampleCount(), gpuSurf->getDevice()->accessRenderTargetContext()->asRenderTargetProxy()->config()); if (0 == supportedSampleCount) { // If changing the sample count won't result in a different // surface characterization, skip this step continue; } } REPORTER_ASSERT(reporter, !s->draw(ddl.get())); } // Next test the compatibility of resource cache parameters { const SurfaceParameters params; sk_sp s = params.make(context); int maxResourceCount; size_t maxResourceBytes; context->getResourceCacheLimits(&maxResourceCount, &maxResourceBytes); context->setResourceCacheLimits(maxResourceCount/2, maxResourceBytes); REPORTER_ASSERT(reporter, !s->draw(ddl.get())); context->setResourceCacheLimits(maxResourceCount, maxResourceBytes/2); REPORTER_ASSERT(reporter, !s->draw(ddl.get())); // resource limits >= those at characterization time are accepted context->setResourceCacheLimits(2*maxResourceCount, maxResourceBytes); REPORTER_ASSERT(reporter, s->draw(ddl.get())); context->setResourceCacheLimits(maxResourceCount, 2*maxResourceBytes); REPORTER_ASSERT(reporter, s->draw(ddl.get())); context->setResourceCacheLimits(maxResourceCount, maxResourceBytes); REPORTER_ASSERT(reporter, s->draw(ddl.get())); } // Make sure non-GPU-backed surfaces fail characterization { SkImageInfo ii = SkImageInfo::MakeN32(64, 64, kOpaque_SkAlphaType); sk_sp rasterSurface = SkSurface::MakeRaster(ii); SkSurfaceCharacterization c; REPORTER_ASSERT(reporter, !rasterSurface->characterize(&c)); } } static constexpr int kSize = 8; struct TextureReleaseChecker { TextureReleaseChecker() : fReleaseCount(0) {} int fReleaseCount; static void Release(void* self) { static_cast(self)->fReleaseCount++; } }; enum class DDLStage { kMakeImage, kDrawImage, kDetach, kDrawDDL }; // This tests the ability to create and use wrapped textures in a DDL world DEF_GPUTEST_FOR_RENDERING_CONTEXTS(DDLWrapBackendTest, reporter, ctxInfo) { GrContext* context = ctxInfo.grContext(); GrGpu* gpu = context->contextPriv().getGpu(); for (auto lastStage : { DDLStage::kMakeImage, DDLStage::kDrawImage, DDLStage::kDetach, DDLStage::kDrawDDL } ) { for (auto earlyImageReset : { false , true } ) { GrBackendTexture backendTex = gpu->createTestingOnlyBackendTexture( nullptr, kSize, kSize, kRGBA_8888_GrPixelConfig, false, GrMipMapped::kNo); if (!backendTex.isValid()) { continue; } SurfaceParameters params; sk_sp s = params.make(context); if (!s) { gpu->deleteTestingOnlyBackendTexture(&backendTex); continue; } SkSurfaceCharacterization c; SkAssertResult(s->characterize(&c)); std::unique_ptr recorder( new SkDeferredDisplayListRecorder(c)); SkCanvas* canvas = recorder->getCanvas(); if (!canvas) { gpu->deleteTestingOnlyBackendTexture(&backendTex); continue; } GrContext* deferredContext = canvas->getGrContext(); if (!deferredContext) { gpu->deleteTestingOnlyBackendTexture(&backendTex); continue; } sk_sp image = SkImage::MakeFromAdoptedTexture(deferredContext, backendTex, kTopLeft_GrSurfaceOrigin, kRGBA_8888_SkColorType, kPremul_SkAlphaType, nullptr); // Adopted Textures are not supported in DDL REPORTER_ASSERT(reporter, !image); TextureReleaseChecker releaseChecker; image = SkImage::MakeFromTexture(deferredContext, backendTex, kTopLeft_GrSurfaceOrigin, kRGBA_8888_SkColorType, kPremul_SkAlphaType, nullptr, TextureReleaseChecker::Release, &releaseChecker); REPORTER_ASSERT(reporter, image); if (!image) { gpu->deleteTestingOnlyBackendTexture(&backendTex); continue; } if (DDLStage::kMakeImage == lastStage) { REPORTER_ASSERT(reporter, 0 == releaseChecker.fReleaseCount); image.reset(); REPORTER_ASSERT(reporter, 1 == releaseChecker.fReleaseCount); recorder.reset(); REPORTER_ASSERT(reporter, 1 == releaseChecker.fReleaseCount); gpu->deleteTestingOnlyBackendTexture(&backendTex); continue; } canvas->drawImage(image.get(), 0, 0); if (earlyImageReset) { REPORTER_ASSERT(reporter, 0 == releaseChecker.fReleaseCount); image.reset(); // Ref should still be held by DDL recorder since we did the draw REPORTER_ASSERT(reporter, 0 == releaseChecker.fReleaseCount); } if (DDLStage::kDrawImage == lastStage) { REPORTER_ASSERT(reporter, 0 == releaseChecker.fReleaseCount); recorder.reset(); if (earlyImageReset) { REPORTER_ASSERT(reporter, 1 == releaseChecker.fReleaseCount); } else { REPORTER_ASSERT(reporter, 0 == releaseChecker.fReleaseCount); image.reset(); REPORTER_ASSERT(reporter, 1 == releaseChecker.fReleaseCount); } gpu->deleteTestingOnlyBackendTexture(&backendTex); continue; } std::unique_ptr ddl = recorder->detach(); if (DDLStage::kDetach == lastStage) { REPORTER_ASSERT(reporter, 0 == releaseChecker.fReleaseCount); recorder.reset(); // DDL TODO: Once copies of OpLists from the recorder to DDL are implemented we can // uncomment this check. Currently the texture is getting reset when the recorder // goes away (assuming we did an earlyImageReset). // REPORTER_ASSERT(reporter, 0 == releaseChecker.fReleaseCount); ddl.reset(); if (earlyImageReset) { REPORTER_ASSERT(reporter, 1 == releaseChecker.fReleaseCount); } else { REPORTER_ASSERT(reporter, 0 == releaseChecker.fReleaseCount); image.reset(); REPORTER_ASSERT(reporter, 1 == releaseChecker.fReleaseCount); } gpu->deleteTestingOnlyBackendTexture(&backendTex); continue; } REPORTER_ASSERT(reporter, s->draw(ddl.get())); REPORTER_ASSERT(reporter, 0 == releaseChecker.fReleaseCount); recorder.reset(); // DDL TODO: Once copies of OpLists from the recorder to DDL are implemented we can // uncomment these checks. Currently the texture is getting released when the recorder // goes away (assuming we did an earlyImageReset). // REPORTER_ASSERT(reporter, 0 == releaseChecker.fReleaseCount); ddl.reset(); // REPORTER_ASSERT(reporter, 0 == releaseChecker.fReleaseCount); // Force all draws to flush and sync by calling a read pixels SkImageInfo imageInfo = SkImageInfo::Make(kSize, kSize, kRGBA_8888_SkColorType, kPremul_SkAlphaType); SkBitmap bitmap; bitmap.allocPixels(imageInfo); s->readPixels(imageInfo, bitmap.getPixels(), 0, 0, 0); if (earlyImageReset) { REPORTER_ASSERT(reporter, 1 == releaseChecker.fReleaseCount); } else { REPORTER_ASSERT(reporter, 0 == releaseChecker.fReleaseCount); image.reset(); REPORTER_ASSERT(reporter, 1 == releaseChecker.fReleaseCount); } gpu->deleteTestingOnlyBackendTexture(&backendTex); } } } #endif