blob: d033f890d88016d0ae53eacf4ea8622779c6b335 [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 "GrContextPriv.h"
#include "GrGpu.h"
#include "GrTextureProxyPriv.h"
#include "SkCanvas.h"
#include "SkColorSpacePriv.h"
#include "SkDeferredDisplayListRecorder.h"
#include "SkGpuDevice.h"
#include "SkImage_Gpu.h"
#include "SkSurface.h"
#include "SkSurfaceCharacterization.h"
#include "SkSurfaceProps.h"
#include "SkSurface_Gpu.h"
#include "Test.h"
#include "gl/GrGLCaps.h"
#include "gl/GrGLDefines.h"
#ifdef SK_VULKAN
#include "vk/GrVkDefines.h"
#endif
// Try to create a backend format from the provided colorType and config. Return an invalid
// backend format if the combination is infeasible.
static GrBackendFormat create_backend_format(GrContext* context,
SkColorType ct, SkColorSpace* cs,
GrPixelConfig config) {
const GrCaps* caps = context->contextPriv().caps();
// TODO: what should be done if we have a colorspace that doesn't have a gammaCloseToSRGB?
switch (context->contextPriv().getBackend()) {
case kOpenGL_GrBackend: {
const GrGLCaps* glCaps = static_cast<const GrGLCaps*>(caps);
GrGLStandard standard = glCaps->standard();
switch (ct) {
case kUnknown_SkColorType:
return GrBackendFormat();
case kAlpha_8_SkColorType:
if (kAlpha_8_as_Alpha_GrPixelConfig == config) {
return GrBackendFormat::MakeGL(GR_GL_ALPHA8, GR_GL_TEXTURE_2D);
} else if (kAlpha_8_GrPixelConfig == config ||
kAlpha_8_as_Red_GrPixelConfig == config) {
return GrBackendFormat::MakeGL(GR_GL_R8, GR_GL_TEXTURE_2D);
}
break;
case kRGB_565_SkColorType:
if (kRGB_565_GrPixelConfig == config) {
return GrBackendFormat::MakeGL(GR_GL_RGB565, GR_GL_TEXTURE_2D);
}
break;
case kARGB_4444_SkColorType:
if (kRGBA_4444_GrPixelConfig == config) {
return GrBackendFormat::MakeGL(GR_GL_RGBA4, GR_GL_TEXTURE_2D);
}
break;
case kRGBA_8888_SkColorType:
if (kRGBA_8888_GrPixelConfig == config) {
if (!cs || (cs->gammaCloseToSRGB() && !caps->srgbSupport())) {
return GrBackendFormat::MakeGL(GR_GL_RGBA8, GR_GL_TEXTURE_2D);
}
} else if (kSRGBA_8888_GrPixelConfig == config) {
if (caps->srgbSupport() && cs && cs->gammaCloseToSRGB()) {
return GrBackendFormat::MakeGL(GR_GL_SRGB8_ALPHA8, GR_GL_TEXTURE_2D);
}
}
break;
case kRGB_888x_SkColorType:
if (kRGB_888_GrPixelConfig == config) {
return GrBackendFormat::MakeGL(GR_GL_RGB8, GR_GL_TEXTURE_2D);
}
break;
case kBGRA_8888_SkColorType:
if (kBGRA_8888_GrPixelConfig == config) {
if (kGL_GrGLStandard == standard) {
return GrBackendFormat::MakeGL(GR_GL_RGBA8, GR_GL_TEXTURE_2D);
} else if (kGLES_GrGLStandard == standard) {
return GrBackendFormat::MakeGL(GR_GL_BGRA8, GR_GL_TEXTURE_2D);
}
} else if (kSBGRA_8888_GrPixelConfig == config) {
if (caps->srgbSupport() && cs && cs->gammaCloseToSRGB()) {
return GrBackendFormat::MakeGL(GR_GL_SRGB8_ALPHA8, GR_GL_TEXTURE_2D);
}
}
break;
case kRGBA_1010102_SkColorType:
if (kRGBA_1010102_GrPixelConfig == config) {
return GrBackendFormat::MakeGL(GR_GL_RGB10_A2, GR_GL_TEXTURE_2D);
}
break;
case kRGB_101010x_SkColorType:
return GrBackendFormat();
case kGray_8_SkColorType:
if (kGray_8_as_Lum_GrPixelConfig == config) {
return GrBackendFormat::MakeGL(GR_GL_LUMINANCE8, GR_GL_TEXTURE_2D);
} else if (kGray_8_GrPixelConfig == config ||
kGray_8_as_Red_GrPixelConfig == config) {
return GrBackendFormat::MakeGL(GR_GL_R8, GR_GL_TEXTURE_2D);
}
break;
case kRGBA_F16_SkColorType:
if (kRGBA_half_GrPixelConfig == config) {
return GrBackendFormat::MakeGL(GR_GL_RGBA16F, GR_GL_TEXTURE_2D);
}
break;
}
}
break;
#ifdef SK_VULKAN
case kVulkan_GrBackend:
switch (ct) {
case kUnknown_SkColorType:
return GrBackendFormat();
case kAlpha_8_SkColorType:
// TODO: what about kAlpha_8_GrPixelConfig and kAlpha_8_as_Alpha_GrPixelConfig
if (kAlpha_8_as_Red_GrPixelConfig == config) {
return GrBackendFormat::MakeVk(VK_FORMAT_R8_UNORM);
}
break;
case kRGB_565_SkColorType:
if (kRGB_565_GrPixelConfig == config) {
return GrBackendFormat::MakeVk(VK_FORMAT_R5G6B5_UNORM_PACK16);
}
break;
case kARGB_4444_SkColorType:
if (kRGBA_4444_GrPixelConfig == config) {
return GrBackendFormat::MakeVk(VK_FORMAT_B4G4R4A4_UNORM_PACK16);
}
break;
case kRGBA_8888_SkColorType:
if (kRGBA_8888_GrPixelConfig == config) {
if (!cs || (cs->gammaCloseToSRGB() && !caps->srgbSupport())) {
return GrBackendFormat::MakeVk(VK_FORMAT_R8G8B8A8_UNORM);
}
} else if (kSRGBA_8888_GrPixelConfig == config) {
if (caps->srgbSupport() && cs && cs->gammaCloseToSRGB()) {
return GrBackendFormat::MakeVk(VK_FORMAT_R8G8B8A8_SRGB);
}
}
break;
case kRGB_888x_SkColorType:
if (kRGB_888_GrPixelConfig == config) {
return GrBackendFormat::MakeVk(VK_FORMAT_R8G8B8_UNORM);
}
break;
case kBGRA_8888_SkColorType:
if (kBGRA_8888_GrPixelConfig == config) {
return GrBackendFormat::MakeVk(VK_FORMAT_B8G8R8A8_UNORM);
} else if (kSBGRA_8888_GrPixelConfig == config) {
if (caps->srgbSupport() && cs && cs->gammaCloseToSRGB()) {
return GrBackendFormat::MakeVk(VK_FORMAT_B8G8R8A8_SRGB);
}
}
break;
case kRGBA_1010102_SkColorType:
if (kRGBA_1010102_GrPixelConfig == config) {
return GrBackendFormat::MakeVk(VK_FORMAT_A2B10G10R10_UNORM_PACK32);
}
break;
case kRGB_101010x_SkColorType:
return GrBackendFormat();
case kGray_8_SkColorType:
// TODO: what about kAlpha_8_GrPixelConfig and kGray_8_as_Lum_GrPixelConfig?
if (kGray_8_as_Red_GrPixelConfig == config) {
return GrBackendFormat::MakeVk(VK_FORMAT_R8_UNORM);
}
break;
case kRGBA_F16_SkColorType:
if (kRGBA_half_GrPixelConfig == config) {
return GrBackendFormat::MakeVk(VK_FORMAT_R16G16B16A16_SFLOAT);
}
break;
}
break;
#endif
case kMock_GrBackend:
switch (ct) {
case kUnknown_SkColorType:
return GrBackendFormat();
case kAlpha_8_SkColorType:
if (kAlpha_8_GrPixelConfig == config ||
kAlpha_8_as_Alpha_GrPixelConfig == config ||
kAlpha_8_as_Red_GrPixelConfig == config) {
return GrBackendFormat::MakeMock(config);
}
break;
case kRGB_565_SkColorType:
if (kRGB_565_GrPixelConfig == config) {
return GrBackendFormat::MakeMock(config);
}
break;
case kARGB_4444_SkColorType:
if (kRGBA_4444_GrPixelConfig == config) {
return GrBackendFormat::MakeMock(config);
}
break;
case kRGBA_8888_SkColorType:
if (kRGBA_8888_GrPixelConfig == config) {
if (!cs || (cs->gammaCloseToSRGB() && !caps->srgbSupport())) {
return GrBackendFormat::MakeMock(config);
}
} else if (kSRGBA_8888_GrPixelConfig == config) {
if (caps->srgbSupport() && cs && cs->gammaCloseToSRGB()) {
return GrBackendFormat::MakeMock(config);
}
}
break;
case kRGB_888x_SkColorType:
if (kRGB_888_GrPixelConfig == config) {
return GrBackendFormat::MakeMock(config);
}
break;
case kBGRA_8888_SkColorType:
if (kBGRA_8888_GrPixelConfig == config) {
return GrBackendFormat::MakeMock(config);
} else if (kSBGRA_8888_GrPixelConfig == config) {
if (caps->srgbSupport() && cs && cs->gammaCloseToSRGB()) {
return GrBackendFormat::MakeMock(config);
}
}
break;
case kRGBA_1010102_SkColorType:
if (kRGBA_1010102_GrPixelConfig == config) {
return GrBackendFormat::MakeMock(config);
}
break;
case kRGB_101010x_SkColorType:
return GrBackendFormat();
case kGray_8_SkColorType:
if (kGray_8_GrPixelConfig == config ||
kGray_8_as_Lum_GrPixelConfig == config ||
kGray_8_as_Red_GrPixelConfig == config) {
return GrBackendFormat::MakeMock(config);
}
break;
case kRGBA_F16_SkColorType:
if (kRGBA_half_GrPixelConfig == config) {
return GrBackendFormat::MakeMock(config);
}
break;
}
break;
default:
return GrBackendFormat(); // return an invalid format
}
return GrBackendFormat(); // return an invalid format
}
class SurfaceParameters {
public:
static const int kNumParams = 9;
static const int kSampleCount = 5;
static const int kMipMipCount = 8;
SurfaceParameters(const GrCaps* caps)
: fWidth(64)
, fHeight(64)
, fOrigin(kTopLeft_GrSurfaceOrigin)
, fColorType(kRGBA_8888_SkColorType)
, fConfig(caps->srgbSupport() ? kSRGBA_8888_GrPixelConfig : kRGBA_8888_GrPixelConfig)
, fColorSpace(SkColorSpace::MakeSRGB())
, fSampleCount(1)
, fSurfaceProps(0x0, kUnknown_SkPixelGeometry)
, fShouldCreateMipMaps(true) {
}
int sampleCount() const { return fSampleCount; }
void setColorType(SkColorType ct) { fColorType = ct; }
void setColorSpace(sk_sp<SkColorSpace> cs) { fColorSpace = std::move(cs); }
void setConfig(GrPixelConfig config) { fConfig = config; }
// 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:
// The color type and config need to be changed together.
// The original SRGB color space no longer makes sense for F16
fColorType = kRGBA_F16_SkColorType;
fConfig = kRGBA_half_GrPixelConfig;
fColorSpace = SkColorSpace::MakeSRGBLinear();
break;
case 4:
// This just needs to be a colorSpace different from that returned by MakeSRGB()
// but still be considered SRGB. In this case we just change the gamut.
fColorSpace = SkColorSpace::MakeRGB(SkColorSpace::kSRGB_RenderTargetGamma,
SkColorSpace::kAdobeRGB_Gamut);
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;
case 8:
fShouldCreateMipMaps = false;
break;
}
}
SkSurfaceCharacterization createCharacterization(GrContext* context) const {
int maxResourceCount;
size_t maxResourceBytes;
context->getResourceCacheLimits(&maxResourceCount, &maxResourceBytes);
// Note that Ganesh doesn't make use of the SkImageInfo's alphaType
SkImageInfo ii = SkImageInfo::Make(fWidth, fHeight, fColorType,
kPremul_SkAlphaType, fColorSpace);
GrBackendFormat backendFormat = create_backend_format(context, fColorType,
fColorSpace.get(), fConfig);
if (!backendFormat.isValid()) {
return SkSurfaceCharacterization();
}
SkSurfaceCharacterization c = context->threadSafeProxy()->createCharacterization(
maxResourceBytes, ii, backendFormat, fSampleCount,
fOrigin, fSurfaceProps, fShouldCreateMipMaps);
return c;
}
// Create a DDL whose characterization captures the current settings
std::unique_ptr<SkDeferredDisplayList> createDDL(GrContext* context) const {
SkSurfaceCharacterization c = this->createCharacterization(context);
SkAssertResult(c.isValid());
SkDeferredDisplayListRecorder r(c);
SkCanvas* canvas = r.getCanvas();
if (!canvas) {
return nullptr;
}
canvas->drawRect(SkRect::MakeXYWH(10, 10, 10, 10), SkPaint());
return r.detach();
}
// Create the surface with the current set of parameters
sk_sp<SkSurface> make(GrContext* context, GrBackendTexture* backend,
bool nonTextureable) const {
GrGpu* gpu = context->contextPriv().getGpu();
GrMipMapped mipmapped = nonTextureable
? GrMipMapped::kNo
: GrMipMapped(fShouldCreateMipMaps);
*backend = gpu->createTestingOnlyBackendTexture(nullptr, fWidth, fHeight,
fConfig, true, mipmapped);
if (!backend->isValid() || !gpu->isTestingOnlyBackendTexture(*backend)) {
return nullptr;
}
sk_sp<SkSurface> surface;
if (nonTextureable) {
// Create a surface w/ the current parameters but make it non-textureable
surface = SkSurface::MakeFromBackendTextureAsRenderTarget(
context, *backend, fOrigin, fSampleCount, fColorType,
fColorSpace, &fSurfaceProps);
} else {
surface = SkSurface::MakeFromBackendTexture(
context, *backend, fOrigin, fSampleCount, fColorType,
fColorSpace, &fSurfaceProps);
}
if (!surface) {
gpu->deleteTestingOnlyBackendTexture(*backend);
return nullptr;
}
return surface;
}
void cleanUpBackEnd(GrContext* context, const GrBackendTexture& backend) const {
GrGpu* gpu = context->contextPriv().getGpu();
gpu->deleteTestingOnlyBackendTexture(backend);
}
private:
int fWidth;
int fHeight;
GrSurfaceOrigin fOrigin;
SkColorType fColorType;
GrPixelConfig fConfig;
sk_sp<SkColorSpace> fColorSpace;
int fSampleCount;
SkSurfaceProps fSurfaceProps;
bool fShouldCreateMipMaps;
};
// Test out operator== && operator!=
DEF_GPUTEST_FOR_RENDERING_CONTEXTS(DDLOperatorEqTest, reporter, ctxInfo) {
GrContext* context = ctxInfo.grContext();
for (int i = 0; i < SurfaceParameters::kNumParams; ++i) {
SurfaceParameters params1(context->contextPriv().caps());
params1.modify(i);
SkSurfaceCharacterization char1 = params1.createCharacterization(context);
if (!char1.isValid()) {
continue; // can happen on some platforms (ChromeOS)
}
for (int j = 0; j < SurfaceParameters::kNumParams; ++j) {
SurfaceParameters params2(context->contextPriv().caps());
params2.modify(j);
SkSurfaceCharacterization char2 = params2.createCharacterization(context);
if (!char2.isValid()) {
continue; // can happen on some platforms (ChromeOS)
}
if (i == j) {
REPORTER_ASSERT(reporter, char1 == char2);
} else {
REPORTER_ASSERT(reporter, char1 != char2);
}
}
}
{
SurfaceParameters params(context->contextPriv().caps());
SkSurfaceCharacterization valid = params.createCharacterization(context);
SkASSERT(valid.isValid());
SkSurfaceCharacterization inval1, inval2;
SkASSERT(!inval1.isValid() && !inval2.isValid());
REPORTER_ASSERT(reporter, inval1 != inval2);
REPORTER_ASSERT(reporter, valid != inval1);
REPORTER_ASSERT(reporter, inval1 != valid);
}
}
////////////////////////////////////////////////////////////////////////////////
// This tests SkSurfaceCharacterization/SkSurface compatibility
DEF_GPUTEST_FOR_RENDERING_CONTEXTS(DDLSurfaceCharacterizationTest, reporter, ctxInfo) {
GrContext* context = ctxInfo.grContext();
GrGpu* gpu = context->contextPriv().getGpu();
// 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(context->contextPriv().caps());
ddl = params.createDDL(context);
SkAssertResult(ddl);
// The DDL should draw into an SkSurface created with the same parameters
GrBackendTexture backend;
sk_sp<SkSurface> s = params.make(context, &backend, false);
if (!s) {
return;
}
REPORTER_ASSERT(reporter, s->draw(ddl.get()));
s->readPixels(imageInfo, bitmap.getPixels(), bitmap.rowBytes(), 0, 0);
context->flush();
gpu->testingOnly_flushGpuAndSync();
s = nullptr;
params.cleanUpBackEnd(context, backend);
}
// Then, alter each parameter in turn and check that the DDL & surface are incompatible
for (int i = 0; i < SurfaceParameters::kNumParams; ++i) {
SurfaceParameters params(context->contextPriv().caps());
params.modify(i);
GrBackendTexture backend;
sk_sp<SkSurface> s = params.make(context, &backend, false);
if (!s) {
continue;
}
if (SurfaceParameters::kSampleCount == i) {
SkSurface_Gpu* gpuSurf = static_cast<SkSurface_Gpu*>(s.get());
int supportedSampleCount = context->contextPriv().caps()->getRenderTargetSampleCount(
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
s = nullptr;
params.cleanUpBackEnd(context, backend);
continue;
}
}
if (SurfaceParameters::kMipMipCount == i &&
!context->contextPriv().caps()->mipMapSupport()) {
// If changing the mipmap setting won't result in a different surface characterization,
// skip this step
s = nullptr;
params.cleanUpBackEnd(context, backend);
continue;
}
REPORTER_ASSERT(reporter, !s->draw(ddl.get()),
"DDLSurfaceCharacterizationTest failed on parameter: %d\n", i);
context->flush();
gpu->testingOnly_flushGpuAndSync();
s = nullptr;
params.cleanUpBackEnd(context, backend);
}
// Next test the compatibility of resource cache parameters
{
const SurfaceParameters params(context->contextPriv().caps());
GrBackendTexture backend;
sk_sp<SkSurface> s = params.make(context, &backend, false);
int maxResourceCount;
size_t maxResourceBytes;
context->getResourceCacheLimits(&maxResourceCount, &maxResourceBytes);
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
context->flush();
gpu->testingOnly_flushGpuAndSync();
s = nullptr;
params.cleanUpBackEnd(context, backend);
}
// Test that the textureability of the DDL characterization can block a DDL draw
{
GrBackendTexture backend;
const SurfaceParameters params(context->contextPriv().caps());
sk_sp<SkSurface> s = params.make(context, &backend, true);
if (s) {
REPORTER_ASSERT(reporter, !s->draw(ddl.get()));
context->flush();
gpu->testingOnly_flushGpuAndSync();
s = nullptr;
params.cleanUpBackEnd(context, backend);
}
}
// 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));
}
// Exercise the createResized method
{
SurfaceParameters params(context->contextPriv().caps());
GrBackendTexture backend;
sk_sp<SkSurface> s = params.make(context, &backend, false);
if (!s) {
return;
}
SkSurfaceCharacterization char0;
SkAssertResult(s->characterize(&char0));
// Too small
SkSurfaceCharacterization char1 = char0.createResized(-1, -1);
REPORTER_ASSERT(reporter, !char1.isValid());
// Too large
SkSurfaceCharacterization char2 = char0.createResized(1000000, 32);
REPORTER_ASSERT(reporter, !char2.isValid());
// Just right
SkSurfaceCharacterization char3 = char0.createResized(32, 32);
REPORTER_ASSERT(reporter, char3.isValid());
REPORTER_ASSERT(reporter, 32 == char3.width());
REPORTER_ASSERT(reporter, 32 == char3.height());
s = nullptr;
params.cleanUpBackEnd(context, backend);
}
}
////////////////////////////////////////////////////////////////////////////////
// This tests the SkSurface::MakeRenderTarget variant that takes an SkSurfaceCharacterization.
// In particular, the SkSurface and the SkSurfaceCharacterization should always be compatible.
DEF_GPUTEST_FOR_RENDERING_CONTEXTS(DDLMakeRenderTargetTest, reporter, ctxInfo) {
GrContext* context = ctxInfo.grContext();
for (int i = 0; i < SurfaceParameters::kNumParams; ++i) {
SurfaceParameters params(context->contextPriv().caps());
params.modify(i);
SkSurfaceCharacterization c = params.createCharacterization(context);
GrBackendTexture backend;
if (!c.isValid()) {
sk_sp<SkSurface> tmp = params.make(context, &backend, false);
// If we couldn't characterize the surface we shouldn't be able to create it either
REPORTER_ASSERT(reporter, !tmp);
if (tmp) {
tmp = nullptr;
params.cleanUpBackEnd(context, backend);
}
continue;
}
sk_sp<SkSurface> s = params.make(context, &backend, false);
if (!s) {
REPORTER_ASSERT(reporter, !c.isValid());
continue;
}
REPORTER_ASSERT(reporter, c.isValid());
s = SkSurface::MakeRenderTarget(context, c, SkBudgeted::kYes);
REPORTER_ASSERT(reporter, s);
SkSurface_Gpu* g = static_cast<SkSurface_Gpu*>(s.get());
REPORTER_ASSERT(reporter, g->isCompatible(c));
s = nullptr;
params.cleanUpBackEnd(context, backend);
}
}
////////////////////////////////////////////////////////////////////////////////
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();
GrBackendTexture backendTex = gpu->createTestingOnlyBackendTexture(
nullptr, kSize, kSize, kRGBA_8888_GrPixelConfig, false, GrMipMapped::kNo);
if (!backendTex.isValid()) {
return;
}
SurfaceParameters params(context->contextPriv().caps());
GrBackendTexture backend;
sk_sp<SkSurface> s = params.make(context, &backend, false);
if (!s) {
gpu->deleteTestingOnlyBackendTexture(backendTex);
return;
}
SkSurfaceCharacterization c;
SkAssertResult(s->characterize(&c));
std::unique_ptr<SkDeferredDisplayListRecorder> recorder(new SkDeferredDisplayListRecorder(c));
SkCanvas* canvas = recorder->getCanvas();
if (!canvas) {
s = nullptr;
params.cleanUpBackEnd(context, backend);
gpu->deleteTestingOnlyBackendTexture(backendTex);
return;
}
GrContext* deferredContext = canvas->getGrContext();
if (!deferredContext) {
s = nullptr;
params.cleanUpBackEnd(context, backend);
gpu->deleteTestingOnlyBackendTexture(backendTex);
return;
}
// Wrapped Backend Textures are not supported in DDL
sk_sp<SkImage> image =
SkImage::MakeFromAdoptedTexture(deferredContext, backendTex, kTopLeft_GrSurfaceOrigin,
kRGBA_8888_SkColorType, kPremul_SkAlphaType, nullptr);
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);
gpu->deleteTestingOnlyBackendTexture(backendTex);
s = nullptr;
params.cleanUpBackEnd(context, backend);
}
static void dummy_fulfill_proc(void*, GrBackendTexture*) { SkASSERT(0); }
static void dummy_release_proc(void*) { SkASSERT(0); }
static void dummy_done_proc(void*) { }
////////////////////////////////////////////////////////////////////////////////
// Test out the behavior of an invalid DDLRecorder
DEF_GPUTEST_FOR_RENDERING_CONTEXTS(DDLInvalidRecorder, reporter, ctxInfo) {
GrContext* context = ctxInfo.grContext();
{
SkImageInfo ii = SkImageInfo::MakeN32Premul(32, 32);
sk_sp<SkSurface> s = SkSurface::MakeRenderTarget(context, SkBudgeted::kNo, ii);
SkSurfaceCharacterization characterization;
SkAssertResult(s->characterize(&characterization));
// never calling getCanvas means the backing surface is never allocated
SkDeferredDisplayListRecorder recorder(characterization);
}
{
SkSurfaceCharacterization invalid;
SkDeferredDisplayListRecorder recorder(invalid);
const SkSurfaceCharacterization c = recorder.characterization();
REPORTER_ASSERT(reporter, !c.isValid());
REPORTER_ASSERT(reporter, !recorder.getCanvas());
REPORTER_ASSERT(reporter, !recorder.detach());
GrBackendFormat format = create_backend_format(context, kRGBA_8888_SkColorType,
nullptr, kRGBA_8888_GrPixelConfig);
sk_sp<SkImage> image = recorder.makePromiseTexture(format, 32, 32, GrMipMapped::kNo,
kTopLeft_GrSurfaceOrigin,
kRGBA_8888_SkColorType,
kPremul_SkAlphaType, nullptr,
dummy_fulfill_proc,
dummy_release_proc,
dummy_done_proc,
nullptr);
REPORTER_ASSERT(reporter, !image);
}
}
////////////////////////////////////////////////////////////////////////////////
// Ensure that flushing while DDL recording doesn't cause a crash
DEF_GPUTEST_FOR_RENDERING_CONTEXTS(DDLFlushWhileRecording, reporter, ctxInfo) {
GrContext* context = ctxInfo.grContext();
SkImageInfo ii = SkImageInfo::MakeN32Premul(32, 32);
sk_sp<SkSurface> s = SkSurface::MakeRenderTarget(context, SkBudgeted::kNo, ii);
SkSurfaceCharacterization characterization;
SkAssertResult(s->characterize(&characterization));
SkDeferredDisplayListRecorder recorder(characterization);
SkCanvas* canvas = recorder.getCanvas();
canvas->flush();
canvas->getGrContext()->flush();
}
////////////////////////////////////////////////////////////////////////////////
// Check that the texture-specific flags (i.e., for external & rectangle textures) work
// for promise images. As such, this is a GL-only test.
DEF_GPUTEST_FOR_GL_RENDERING_CONTEXTS(DDLTextureFlagsTest, reporter, ctxInfo) {
GrContext* context = ctxInfo.grContext();
SkImageInfo ii = SkImageInfo::MakeN32Premul(32, 32);
sk_sp<SkSurface> s = SkSurface::MakeRenderTarget(context, SkBudgeted::kNo, ii);
SkSurfaceCharacterization characterization;
SkAssertResult(s->characterize(&characterization));
SkDeferredDisplayListRecorder recorder(characterization);
for (GrGLenum target : { GR_GL_TEXTURE_EXTERNAL, GR_GL_TEXTURE_RECTANGLE, GR_GL_TEXTURE_2D } ) {
GrBackendFormat format = GrBackendFormat::MakeGL(GR_GL_RGBA8, target);
sk_sp<SkImage> image = recorder.makePromiseTexture(format, 32, 32, GrMipMapped::kYes,
kTopLeft_GrSurfaceOrigin,
kRGBA_8888_SkColorType,
kPremul_SkAlphaType, nullptr,
dummy_fulfill_proc,
dummy_release_proc,
dummy_done_proc,
nullptr);
REPORTER_ASSERT(reporter, image);
GrTextureProxy* backingProxy = ((SkImage_Gpu*) image.get())->peekProxy();
if (GR_GL_TEXTURE_2D == target) {
REPORTER_ASSERT(reporter, !backingProxy->texPriv().doesNotSupportMipMaps());
REPORTER_ASSERT(reporter, !backingProxy->texPriv().isClampOnly());
} else {
REPORTER_ASSERT(reporter, backingProxy->texPriv().doesNotSupportMipMaps());
REPORTER_ASSERT(reporter, backingProxy->texPriv().isClampOnly());
}
}
}
////////////////////////////////////////////////////////////////////////////////
// Exhaustively test colorType and pixelConfig compatibility.
DEF_GPUTEST_FOR_GL_RENDERING_CONTEXTS(DDLCompatibilityTest, reporter, ctxInfo) {
GrContext* context = ctxInfo.grContext();
for (int ct = 0; ct <= kLastEnum_SkColorType; ++ct) {
SkColorType colorType = static_cast<SkColorType>(ct);
for (int config = 0; config < kGrPixelConfigCnt; ++config) {
GrPixelConfig pixelConfig = static_cast<GrPixelConfig>(config);
SurfaceParameters params(context->contextPriv().caps());
params.setColorType(colorType);
params.setConfig(pixelConfig);
params.setColorSpace(nullptr);
if (kSRGBA_8888_GrPixelConfig == pixelConfig ||
kSBGRA_8888_GrPixelConfig == pixelConfig) {
params.setColorSpace(SkColorSpace::MakeSRGB());
}
SkSurfaceCharacterization c = params.createCharacterization(context);
GrBackendTexture backend;
if (!c.isValid()) {
// TODO: this would be cool to enable but there is, currently, too much crossover
// allowed internally (e.g., kAlpha_8_SkColorType/kGray_8_as_Red_GrPixelConfig
// is permitted on GL).
#if 0
sk_sp<SkSurface> tmp = params.make(context, &backend, false);
// If we couldn't characterize the surface we shouldn't be able to create it either
REPORTER_ASSERT(reporter, !tmp);
if (tmp) {
tmp = nullptr;
params.cleanUpBackEnd(context, backend);
}
#endif
continue;
}
sk_sp<SkSurface> s = params.make(context, &backend, false);
REPORTER_ASSERT(reporter, s);
if (!s) {
s = nullptr;
params.cleanUpBackEnd(context, backend);
continue;
}
SkSurface_Gpu* gpuSurface = static_cast<SkSurface_Gpu*>(s.get());
REPORTER_ASSERT(reporter, gpuSurface->isCompatible(c));
s = nullptr;
params.cleanUpBackEnd(context, backend);
s = SkSurface::MakeRenderTarget(context, c, SkBudgeted::kYes);
REPORTER_ASSERT(reporter, s);
if (!s) {
continue;
}
gpuSurface = static_cast<SkSurface_Gpu*>(s.get());
REPORTER_ASSERT(reporter, gpuSurface->isCompatible(c));
}
}
}
#endif