1 /*
2 * Copyright 2017 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
10 #if SK_SUPPORT_GPU
11
12 #include "GrBackendSurface.h"
13 #include "GrGpu.h"
14 #include "SkCanvas.h"
15 #include "SkDeferredDisplayListRecorder.h"
16 #include "SkGpuDevice.h"
17 #include "SkSurface.h"
18 #include "SkSurface_Gpu.h"
19 #include "SkSurfaceCharacterization.h"
20 #include "SkSurfaceProps.h"
21 #include "Test.h"
22
23 #include "gl/GrGLDefines.h"
24 #ifdef SK_VULKAN
25 #include "vk/GrVkDefines.h"
26 #endif
27
create_backend_format(GrContext * context,SkColorType colorType)28 static GrBackendFormat create_backend_format(GrContext* context, SkColorType colorType) {
29 const GrCaps* caps = context->caps();
30
31 switch (context->contextPriv().getBackend()) {
32 case kOpenGL_GrBackend:
33 if (kRGBA_8888_SkColorType == colorType) {
34 GrGLenum format = caps->srgbSupport() ? GR_GL_SRGB8_ALPHA8 : GR_GL_RGBA8;
35 return GrBackendFormat::MakeGL(format, GR_GL_TEXTURE_2D);
36 } else if (kRGBA_F16_SkColorType == colorType) {
37 return GrBackendFormat::MakeGL(GR_GL_RGBA16F, GR_GL_TEXTURE_2D);
38 }
39 break;
40 #ifdef SK_VULKAN
41 case kVulkan_GrBackend:
42 if (kRGBA_8888_SkColorType == colorType) {
43 VkFormat format = caps->srgbSupport() ? VK_FORMAT_R8G8B8A8_SRGB
44 : VK_FORMAT_R8G8B8A8_UNORM;
45 return GrBackendFormat::MakeVK(format);
46 } else if (kRGBA_F16_SkColorType == colorType) {
47 return GrBackendFormat::MakeVK(VK_FORMAT_R16G16B16A16_SFLOAT);
48 }
49 break;
50 #endif
51 case kMock_GrBackend:
52 if (kRGBA_8888_SkColorType == colorType) {
53 GrPixelConfig config = caps->srgbSupport() ? kSRGBA_8888_GrPixelConfig
54 : kRGBA_8888_GrPixelConfig;
55 return GrBackendFormat::MakeMock(config);
56 } else if (kRGBA_F16_SkColorType == colorType) {
57 return GrBackendFormat::MakeMock(kRGBA_half_GrPixelConfig);
58 }
59 break;
60 default:
61 return GrBackendFormat(); // return an invalid format
62 }
63
64 return GrBackendFormat(); // return an invalid format
65 }
66
67
68 class SurfaceParameters {
69 public:
70 static const int kNumParams = 9;
71 static const int kSampleCount = 5;
72 static const int kMipMipCount = 8;
73
SurfaceParameters()74 SurfaceParameters()
75 : fWidth(64)
76 , fHeight(64)
77 , fOrigin(kTopLeft_GrSurfaceOrigin)
78 , fColorType(kRGBA_8888_SkColorType)
79 , fColorSpace(SkColorSpace::MakeSRGB())
80 , fSampleCount(1)
81 , fSurfaceProps(0x0, kUnknown_SkPixelGeometry)
82 , fShouldCreateMipMaps(true) {
83 }
84
sampleCount() const85 int sampleCount() const { return fSampleCount; }
86
87 // Modify the SurfaceParameters in just one way
modify(int i)88 void modify(int i) {
89 switch (i) {
90 case 0:
91 fWidth = 63;
92 break;
93 case 1:
94 fHeight = 63;
95 break;
96 case 2:
97 fOrigin = kBottomLeft_GrSurfaceOrigin;
98 break;
99 case 3:
100 fColorType = kRGBA_F16_SkColorType;
101 break;
102 case 4:
103 fColorSpace = SkColorSpace::MakeSRGBLinear();
104 break;
105 case kSampleCount:
106 fSampleCount = 4;
107 break;
108 case 6:
109 fSurfaceProps = SkSurfaceProps(0x0, kRGB_H_SkPixelGeometry);
110 break;
111 case 7:
112 fSurfaceProps = SkSurfaceProps(SkSurfaceProps::kUseDeviceIndependentFonts_Flag,
113 kUnknown_SkPixelGeometry);
114 break;
115 case 8:
116 fShouldCreateMipMaps = false;
117 break;
118 }
119 }
120
121 // Create a DDL whose characterization captures the current settings
createDDL(GrContext * context) const122 std::unique_ptr<SkDeferredDisplayList> createDDL(GrContext* context) const {
123 sk_sp<SkSurface> s = this->make(context);
124 if (!s) {
125 return nullptr;
126 }
127
128 int maxResourceCount;
129 size_t maxResourceBytes;
130 context->getResourceCacheLimits(&maxResourceCount, &maxResourceBytes);
131
132 // Note that Ganesh doesn't make use of the SkImageInfo's alphaType
133 SkImageInfo ii = SkImageInfo::Make(fWidth, fHeight, fColorType,
134 kPremul_SkAlphaType, fColorSpace);
135
136 GrBackendFormat backendFormat = create_backend_format(context, fColorType);
137
138 SkSurfaceCharacterization c = context->threadSafeProxy()->createCharacterization(
139 maxResourceBytes, ii, backendFormat, fSampleCount,
140 fOrigin, fSurfaceProps, fShouldCreateMipMaps);
141 SkAssertResult(c.isValid());
142
143 SkDeferredDisplayListRecorder r(c);
144 SkCanvas* canvas = r.getCanvas();
145 if (!canvas) {
146 return nullptr;
147 }
148
149 canvas->drawRect(SkRect::MakeXYWH(10, 10, 10, 10), SkPaint());
150 return r.detach();
151 }
152
153 // Create the surface with the current set of parameters
make(GrContext * context) const154 sk_sp<SkSurface> make(GrContext* context) const {
155 // Note that Ganesh doesn't make use of the SkImageInfo's alphaType
156 SkImageInfo ii = SkImageInfo::Make(fWidth, fHeight, fColorType,
157 kPremul_SkAlphaType, fColorSpace);
158
159 return SkSurface::MakeRenderTarget(context, SkBudgeted::kYes, ii, fSampleCount,
160 fOrigin, &fSurfaceProps, fShouldCreateMipMaps);
161 }
162
163 // Create a surface w/ the current parameters but make it non-textureable
makeNonTextureable(GrContext * context,GrBackendTexture * backend) const164 sk_sp<SkSurface> makeNonTextureable(GrContext* context, GrBackendTexture* backend) const {
165 GrGpu* gpu = context->contextPriv().getGpu();
166
167 GrPixelConfig config = SkImageInfo2GrPixelConfig(fColorType, nullptr, *context->caps());
168 SkASSERT(kUnknown_GrPixelConfig != config);
169
170 *backend = gpu->createTestingOnlyBackendTexture(nullptr, fWidth, fHeight,
171 config, true, GrMipMapped::kNo);
172
173 if (!backend->isValid() || !gpu->isTestingOnlyBackendTexture(*backend)) {
174 return nullptr;
175 }
176
177 sk_sp<SkSurface> surface = SkSurface::MakeFromBackendTextureAsRenderTarget(
178 context, *backend, fOrigin, fSampleCount, fColorType, nullptr, nullptr);
179
180 if (!surface) {
181 gpu->deleteTestingOnlyBackendTexture(backend);
182 return nullptr;
183 }
184
185 return surface;
186 }
187
cleanUpBackEnd(GrContext * context,GrBackendTexture * backend) const188 void cleanUpBackEnd(GrContext* context, GrBackendTexture* backend) const {
189 GrGpu* gpu = context->contextPriv().getGpu();
190
191 gpu->deleteTestingOnlyBackendTexture(backend);
192 }
193
194 private:
195 int fWidth;
196 int fHeight;
197 GrSurfaceOrigin fOrigin;
198 SkColorType fColorType;
199 sk_sp<SkColorSpace> fColorSpace;
200 int fSampleCount;
201 SkSurfaceProps fSurfaceProps;
202 bool fShouldCreateMipMaps;
203 };
204
205 // This tests SkSurfaceCharacterization/SkSurface compatibility
DEF_GPUTEST_FOR_ALL_CONTEXTS(DDLSurfaceCharacterizationTest,reporter,ctxInfo)206 DEF_GPUTEST_FOR_ALL_CONTEXTS(DDLSurfaceCharacterizationTest, reporter, ctxInfo) {
207 GrContext* context = ctxInfo.grContext();
208
209 // Create a bitmap that we can readback into
210 SkImageInfo imageInfo = SkImageInfo::Make(64, 64, kRGBA_8888_SkColorType,
211 kPremul_SkAlphaType);
212 SkBitmap bitmap;
213 bitmap.allocPixels(imageInfo);
214
215 std::unique_ptr<SkDeferredDisplayList> ddl;
216
217 // First, create a DDL using the stock SkSurface parameters
218 {
219 SurfaceParameters params;
220
221 ddl = params.createDDL(context);
222 SkAssertResult(ddl);
223
224 // The DDL should draw into an SkSurface created with the same parameters
225 sk_sp<SkSurface> s = params.make(context);
226 if (!s) {
227 return;
228 }
229
230 REPORTER_ASSERT(reporter, s->draw(ddl.get()));
231 s->readPixels(imageInfo, bitmap.getPixels(), bitmap.rowBytes(), 0, 0);
232 }
233
234 // Then, alter each parameter in turn and check that the DDL & surface are incompatible
235 for (int i = 0; i < SurfaceParameters::kNumParams; ++i) {
236 SurfaceParameters params;
237 params.modify(i);
238
239 sk_sp<SkSurface> s = params.make(context);
240 if (!s) {
241 continue;
242 }
243
244 if (SurfaceParameters::kSampleCount == i) {
245 SkSurface_Gpu* gpuSurf = static_cast<SkSurface_Gpu*>(s.get());
246
247 int supportedSampleCount = context->caps()->getRenderTargetSampleCount(
248 params.sampleCount(),
249 gpuSurf->getDevice()->accessRenderTargetContext()->asRenderTargetProxy()->config());
250 if (1 == supportedSampleCount) {
251 // If changing the sample count won't result in a different
252 // surface characterization, skip this step
253 continue;
254 }
255 }
256
257 if (SurfaceParameters::kMipMipCount == i && !context->caps()->mipMapSupport()) {
258 continue;
259 }
260
261 REPORTER_ASSERT(reporter, !s->draw(ddl.get()),
262 "DDLSurfaceCharacterizationTest failed on parameter: %d\n", i);
263 }
264
265 // Next test the compatibility of resource cache parameters
266 {
267 const SurfaceParameters params;
268 sk_sp<SkSurface> s = params.make(context);
269
270 int maxResourceCount;
271 size_t maxResourceBytes;
272 context->getResourceCacheLimits(&maxResourceCount, &maxResourceBytes);
273
274 context->setResourceCacheLimits(maxResourceCount, maxResourceBytes/2);
275 REPORTER_ASSERT(reporter, !s->draw(ddl.get()));
276
277 // DDL TODO: once proxies/ops can be de-instantiated we can re-enable these tests.
278 // For now, DDLs are drawn once.
279 #if 0
280 // resource limits >= those at characterization time are accepted
281 context->setResourceCacheLimits(2*maxResourceCount, maxResourceBytes);
282 REPORTER_ASSERT(reporter, s->draw(ddl.get()));
283 s->readPixels(imageInfo, bitmap.getPixels(), bitmap.rowBytes(), 0, 0);
284
285 context->setResourceCacheLimits(maxResourceCount, 2*maxResourceBytes);
286 REPORTER_ASSERT(reporter, s->draw(ddl.get()));
287 s->readPixels(imageInfo, bitmap.getPixels(), bitmap.rowBytes(), 0, 0);
288
289 context->setResourceCacheLimits(maxResourceCount, maxResourceBytes);
290 REPORTER_ASSERT(reporter, s->draw(ddl.get()));
291 s->readPixels(imageInfo, bitmap.getPixels(), bitmap.rowBytes(), 0, 0);
292 #endif
293 }
294
295 // Test that the textureability of the DDL characterization can block a DDL draw
296 {
297 GrBackendTexture backend;
298 const SurfaceParameters params;
299 sk_sp<SkSurface> s = params.makeNonTextureable(context, &backend);
300 if (s) {
301 REPORTER_ASSERT(reporter, !s->draw(ddl.get()));
302
303 s = nullptr;
304 params.cleanUpBackEnd(context, &backend);
305 }
306 }
307
308 // Make sure non-GPU-backed surfaces fail characterization
309 {
310 SkImageInfo ii = SkImageInfo::MakeN32(64, 64, kOpaque_SkAlphaType);
311
312 sk_sp<SkSurface> rasterSurface = SkSurface::MakeRaster(ii);
313 SkSurfaceCharacterization c;
314 REPORTER_ASSERT(reporter, !rasterSurface->characterize(&c));
315 }
316 }
317
318 static constexpr int kSize = 8;
319
320 struct TextureReleaseChecker {
TextureReleaseCheckerTextureReleaseChecker321 TextureReleaseChecker() : fReleaseCount(0) {}
322 int fReleaseCount;
ReleaseTextureReleaseChecker323 static void Release(void* self) {
324 static_cast<TextureReleaseChecker*>(self)->fReleaseCount++;
325 }
326 };
327
328 enum class DDLStage { kMakeImage, kDrawImage, kDetach, kDrawDDL };
329
330 // This tests the ability to create and use wrapped textures in a DDL world
DEF_GPUTEST_FOR_RENDERING_CONTEXTS(DDLWrapBackendTest,reporter,ctxInfo)331 DEF_GPUTEST_FOR_RENDERING_CONTEXTS(DDLWrapBackendTest, reporter, ctxInfo) {
332 GrContext* context = ctxInfo.grContext();
333 GrGpu* gpu = context->contextPriv().getGpu();
334 for (auto lastStage : { DDLStage::kMakeImage, DDLStage::kDrawImage,
335 DDLStage::kDetach, DDLStage::kDrawDDL } ) {
336 for (auto earlyImageReset : { false , true } ) {
337 GrBackendTexture backendTex = gpu->createTestingOnlyBackendTexture(
338 nullptr, kSize, kSize, kRGBA_8888_GrPixelConfig, false, GrMipMapped::kNo);
339 if (!backendTex.isValid()) {
340 continue;
341 }
342
343 SurfaceParameters params;
344
345 sk_sp<SkSurface> s = params.make(context);
346 if (!s) {
347 gpu->deleteTestingOnlyBackendTexture(&backendTex);
348 continue;
349 }
350
351 SkSurfaceCharacterization c;
352 SkAssertResult(s->characterize(&c));
353
354 std::unique_ptr<SkDeferredDisplayListRecorder> recorder(
355 new SkDeferredDisplayListRecorder(c));
356
357 SkCanvas* canvas = recorder->getCanvas();
358 if (!canvas) {
359 gpu->deleteTestingOnlyBackendTexture(&backendTex);
360 continue;
361 }
362
363 GrContext* deferredContext = canvas->getGrContext();
364 if (!deferredContext) {
365 gpu->deleteTestingOnlyBackendTexture(&backendTex);
366 continue;
367 }
368
369 sk_sp<SkImage> image = SkImage::MakeFromAdoptedTexture(deferredContext, backendTex,
370 kTopLeft_GrSurfaceOrigin,
371 kRGBA_8888_SkColorType,
372 kPremul_SkAlphaType, nullptr);
373 // Adopted Textures are not supported in DDL
374 REPORTER_ASSERT(reporter, !image);
375
376 TextureReleaseChecker releaseChecker;
377 image = SkImage::MakeFromTexture(deferredContext, backendTex,
378 kTopLeft_GrSurfaceOrigin,
379 kRGBA_8888_SkColorType,
380 kPremul_SkAlphaType, nullptr,
381 TextureReleaseChecker::Release, &releaseChecker);
382
383 REPORTER_ASSERT(reporter, image);
384 if (!image) {
385 gpu->deleteTestingOnlyBackendTexture(&backendTex);
386 continue;
387 }
388
389 if (DDLStage::kMakeImage == lastStage) {
390 REPORTER_ASSERT(reporter, 0 == releaseChecker.fReleaseCount);
391 image.reset();
392 REPORTER_ASSERT(reporter, 1 == releaseChecker.fReleaseCount);
393 recorder.reset();
394 REPORTER_ASSERT(reporter, 1 == releaseChecker.fReleaseCount);
395 gpu->deleteTestingOnlyBackendTexture(&backendTex);
396 continue;
397 }
398
399 canvas->drawImage(image.get(), 0, 0);
400
401 if (earlyImageReset) {
402 REPORTER_ASSERT(reporter, 0 == releaseChecker.fReleaseCount);
403 image.reset();
404 // Ref should still be held by DDL recorder since we did the draw
405 REPORTER_ASSERT(reporter, 0 == releaseChecker.fReleaseCount);
406 }
407
408 if (DDLStage::kDrawImage == lastStage) {
409 REPORTER_ASSERT(reporter, 0 == releaseChecker.fReleaseCount);
410 recorder.reset();
411 if (earlyImageReset) {
412 REPORTER_ASSERT(reporter, 1 == releaseChecker.fReleaseCount);
413 } else {
414 REPORTER_ASSERT(reporter, 0 == releaseChecker.fReleaseCount);
415 image.reset();
416 REPORTER_ASSERT(reporter, 1 == releaseChecker.fReleaseCount);
417 }
418 gpu->deleteTestingOnlyBackendTexture(&backendTex);
419 continue;
420 }
421
422 std::unique_ptr<SkDeferredDisplayList> ddl = recorder->detach();
423 if (DDLStage::kDetach == lastStage) {
424 REPORTER_ASSERT(reporter, 0 == releaseChecker.fReleaseCount);
425 recorder.reset();
426 #ifndef SK_RASTER_RECORDER_IMPLEMENTATION
427 REPORTER_ASSERT(reporter, 0 == releaseChecker.fReleaseCount);
428 #endif
429 ddl.reset();
430 if (earlyImageReset) {
431 REPORTER_ASSERT(reporter, 1 == releaseChecker.fReleaseCount);
432 } else {
433 REPORTER_ASSERT(reporter, 0 == releaseChecker.fReleaseCount);
434 image.reset();
435 REPORTER_ASSERT(reporter, 1 == releaseChecker.fReleaseCount);
436 }
437 gpu->deleteTestingOnlyBackendTexture(&backendTex);
438 continue;
439 }
440
441 REPORTER_ASSERT(reporter, s->draw(ddl.get()));
442
443 REPORTER_ASSERT(reporter, 0 == releaseChecker.fReleaseCount);
444 recorder.reset();
445 #ifndef SK_RASTER_RECORDER_IMPLEMENTATION
446 REPORTER_ASSERT(reporter, 0 == releaseChecker.fReleaseCount);
447 #endif
448 ddl.reset();
449 #ifndef SK_RASTER_RECORDER_IMPLEMENTATION
450 REPORTER_ASSERT(reporter, 0 == releaseChecker.fReleaseCount);
451 #endif
452
453 // Force all draws to flush and sync by calling a read pixels
454 SkImageInfo imageInfo = SkImageInfo::Make(kSize, kSize, kRGBA_8888_SkColorType,
455 kPremul_SkAlphaType);
456 SkBitmap bitmap;
457 bitmap.allocPixels(imageInfo);
458 s->readPixels(imageInfo, bitmap.getPixels(), bitmap.rowBytes(), 0, 0);
459
460 if (earlyImageReset) {
461 REPORTER_ASSERT(reporter, 1 == releaseChecker.fReleaseCount);
462 } else {
463 REPORTER_ASSERT(reporter, 0 == releaseChecker.fReleaseCount);
464 image.reset();
465 REPORTER_ASSERT(reporter, 1 == releaseChecker.fReleaseCount);
466 }
467
468 gpu->deleteTestingOnlyBackendTexture(&backendTex);
469 }
470 }
471 }
472
473
474 #endif
475