blob: 9938d498ce1fd4a36d7bc1722ea3f07539c8c917 [file] [log] [blame]
/*
* 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(1)
, 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<SkSurface> 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<SkColorSpace> fColorSpace;
int fSampleCount;
SkSurfaceProps fSurfaceProps;
};
// This tests SkSurfaceCharacterization/SkSurface compatibility
DEF_GPUTEST_FOR_ALL_CONTEXTS(SkSurfaceCharacterization, reporter, ctxInfo) {
GrContext* context = ctxInfo.grContext();
// Create a bitmap that we can readback into
SkImageInfo imageInfo = SkImageInfo::Make(64, 64, kRGBA_8888_SkColorType,
kPremul_SkAlphaType);
SkBitmap bitmap;
bitmap.allocPixels(imageInfo);
std::unique_ptr<SkDeferredDisplayList> ddl;
// First, create a DDL using the stock SkSurface parameters
{
SurfaceParameters params;
sk_sp<SkSurface> 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()));
s->readPixels(imageInfo, bitmap.getPixels(), bitmap.rowBytes(), 0, 0);
}
// 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<SkSurface> s = params.make(context);
if (!s) {
continue;
}
if (SurfaceParameters::kSampleCount == i) {
SkSurface_Gpu* gpuSurf = static_cast<SkSurface_Gpu*>(s.get());
int supportedSampleCount = context->caps()->getSampleCount(
params.sampleCount(),
gpuSurf->getDevice()->accessRenderTargetContext()->asRenderTargetProxy()->config());
if (1 == 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<SkSurface> 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()));
// DDL TODO: once proxies/ops can be de-instantiated we can re-enable these tests.
// For now, DDLs are drawn once.
#if 0
// resource limits >= those at characterization time are accepted
context->setResourceCacheLimits(2*maxResourceCount, maxResourceBytes);
REPORTER_ASSERT(reporter, s->draw(ddl.get()));
s->readPixels(imageInfo, bitmap.getPixels(), bitmap.rowBytes(), 0, 0);
context->setResourceCacheLimits(maxResourceCount, 2*maxResourceBytes);
REPORTER_ASSERT(reporter, s->draw(ddl.get()));
s->readPixels(imageInfo, bitmap.getPixels(), bitmap.rowBytes(), 0, 0);
context->setResourceCacheLimits(maxResourceCount, maxResourceBytes);
REPORTER_ASSERT(reporter, s->draw(ddl.get()));
s->readPixels(imageInfo, bitmap.getPixels(), bitmap.rowBytes(), 0, 0);
#endif
}
// Make sure non-GPU-backed surfaces fail characterization
{
SkImageInfo ii = SkImageInfo::MakeN32(64, 64, kOpaque_SkAlphaType);
sk_sp<SkSurface> 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<TextureReleaseChecker*>(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<SkSurface> s = params.make(context);
if (!s) {
gpu->deleteTestingOnlyBackendTexture(&backendTex);
continue;
}
SkSurfaceCharacterization c;
SkAssertResult(s->characterize(&c));
std::unique_ptr<SkDeferredDisplayListRecorder> 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<SkImage> 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<SkDeferredDisplayList> ddl = recorder->detach();
if (DDLStage::kDetach == lastStage) {
REPORTER_ASSERT(reporter, 0 == releaseChecker.fReleaseCount);
recorder.reset();
#ifndef SK_RASTER_RECORDER_IMPLEMENTATION
REPORTER_ASSERT(reporter, 0 == releaseChecker.fReleaseCount);
#endif
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();
#ifndef SK_RASTER_RECORDER_IMPLEMENTATION
REPORTER_ASSERT(reporter, 0 == releaseChecker.fReleaseCount);
#endif
ddl.reset();
#ifndef SK_RASTER_RECORDER_IMPLEMENTATION
REPORTER_ASSERT(reporter, 0 == releaseChecker.fReleaseCount);
#endif
// 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(), bitmap.rowBytes(), 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