blob: f5ecf62aeb924c565b27c04fe3159f56bfffa0ad [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 "include/core/SkAlphaType.h"
#include "include/core/SkBitmap.h"
#include "include/core/SkCanvas.h"
#include "include/core/SkColor.h"
#include "include/core/SkColorSpace.h"
#include "include/core/SkColorType.h"
#include "include/core/SkImageInfo.h"
#include "include/core/SkPaint.h"
#include "include/core/SkRect.h"
#include "include/core/SkRefCnt.h"
#include "include/core/SkSize.h"
#include "include/core/SkSurface.h"
#include "include/core/SkSurfaceProps.h"
#include "include/core/SkTypes.h"
#include "include/gpu/GpuTypes.h"
#include "include/gpu/GrBackendSurface.h"
#include "include/gpu/GrContextThreadSafeProxy.h"
#include "include/gpu/GrDirectContext.h"
#include "include/gpu/GrRecordingContext.h"
#include "include/gpu/GrTypes.h"
#include "include/gpu/ganesh/SkImageGanesh.h"
#include "include/gpu/ganesh/SkSurfaceGanesh.h"
#include "include/private/chromium/GrDeferredDisplayList.h"
#include "include/private/chromium/GrDeferredDisplayListRecorder.h"
#include "include/private/chromium/GrPromiseImageTexture.h"
#include "include/private/chromium/GrSurfaceCharacterization.h"
#include "include/private/chromium/SkImageChromium.h"
#include "src/gpu/ganesh/GrCaps.h"
#include "src/gpu/ganesh/GrDeferredDisplayListPriv.h"
#include "src/gpu/ganesh/GrDirectContextPriv.h"
#include "src/gpu/ganesh/GrRecordingContextPriv.h"
#include "src/gpu/ganesh/GrTextureProxy.h"
#include "tests/CtsEnforcement.h"
#include "tests/Test.h"
#include "tools/gpu/BackendSurfaceFactory.h"
#include "tools/gpu/ManagedBackendTexture.h"
#include "tools/gpu/ProxyUtils.h"
#include <cstddef>
#include <initializer_list>
#include <memory>
#include <utility>
class SkImage;
struct GrContextOptions;
#ifdef SK_GL
#include "include/gpu/ganesh/gl/GrGLBackendSurface.h"
#include "include/gpu/gl/GrGLTypes.h"
#include "src/gpu/ganesh/gl/GrGLDefines.h"
#endif
#ifdef SK_VULKAN
#include "include/gpu/ganesh/vk/GrVkBackendSurface.h"
#include "include/gpu/vk/GrVkTypes.h"
#include "include/private/chromium/GrVkSecondaryCBDrawContext.h"
#include "src/gpu/ganesh/vk/GrVkCaps.h"
#include "tools/gpu/vk/VkTestHelper.h"
#include <vulkan/vulkan_core.h>
#endif
static bool is_compatible(const GrSurfaceCharacterization& gsc, const GrBackendTexture& backendTex) {
if (!gsc.isValid() || !backendTex.isValid()) {
return false;
}
if (gsc.backendFormat() != backendTex.getBackendFormat()) {
return false;
}
if (gsc.usesGLFBO0()) {
// It is a backend texture so can't be wrapping FBO0
return false;
}
if (gsc.vulkanSecondaryCBCompatible()) {
return false;
}
if (gsc.vkRTSupportsInputAttachment()) {
if (backendTex.backend() != GrBackendApi::kVulkan) {
return false;
}
#ifdef SK_VULKAN
GrVkImageInfo vkInfo;
if (!GrBackendTextures::GetVkImageInfo(backendTex, &vkInfo)) {
return false;
}
if (!SkToBool(vkInfo.fImageUsageFlags & VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT)) {
return false;
}
#endif // SK_VULKAN
}
if (gsc.isMipMapped() && !backendTex.hasMipmaps()) {
// backend texture is allowed to have mipmaps even if the characterization doesn't require
// them.
return false;
}
if (gsc.width() != backendTex.width() || gsc.height() != backendTex.height()) {
return false;
}
if (gsc.isProtected() != skgpu::Protected(backendTex.isProtected())) {
return false;
}
return true;
}
class SurfaceParameters {
public:
static const int kNumParams = 13;
static const int kFBO0Count = 9;
static const int kVkSCBCount = 12;
SurfaceParameters(GrRecordingContext* rContext)
: fBackend(rContext->backend())
, fCanBeProtected(false)
, fWidth(64)
, fHeight(64)
, fOrigin(kTopLeft_GrSurfaceOrigin)
, fColorType(kRGBA_8888_SkColorType)
, fColorSpace(SkColorSpace::MakeSRGB())
, fSampleCount(1)
, fSurfaceProps(0x0, kUnknown_SkPixelGeometry)
, fShouldCreateMipMaps(skgpu::Mipmapped::kYes)
, fUsesGLFBO0(false)
, fIsTextureable(true)
, fIsProtected(skgpu::Protected::kNo)
, fVkRTSupportsInputAttachment(false)
, fForVulkanSecondaryCommandBuffer(false) {
#ifdef SK_VULKAN
if (rContext->backend() == GrBackendApi::kVulkan) {
auto vkCaps = static_cast<const GrVkCaps*>(rContext->priv().caps());
fCanBeProtected = vkCaps->supportsProtectedContent();
if (fCanBeProtected) {
fIsProtected = skgpu::Protected::kYes;
}
}
#endif
if (!rContext->priv().caps()->mipmapSupport()) {
fShouldCreateMipMaps = skgpu::Mipmapped::kNo;
}
}
int sampleCount() const { return fSampleCount; }
void setColorType(SkColorType ct) { fColorType = ct; }
SkColorType colorType() const { return fColorType; }
void setColorSpace(sk_sp<SkColorSpace> cs) { fColorSpace = std::move(cs); }
void disableTextureability() {
fIsTextureable = false;
fShouldCreateMipMaps = skgpu::Mipmapped::kNo;
}
void setShouldCreateMipMaps(skgpu::Mipmapped shouldCreateMipMaps) {
fShouldCreateMipMaps = shouldCreateMipMaps;
}
void setVkRTInputAttachmentSupport(bool inputSupport) {
fVkRTSupportsInputAttachment = inputSupport;
}
void setForVulkanSecondaryCommandBuffer(bool forVkSCB) {
fForVulkanSecondaryCommandBuffer = forVkSCB;
}
// Modify the SurfaceParameters in just one way. Returns false if the requested modification had
// no effect.
bool modify(int i) {
bool changed = false;
auto set = [&changed](auto& var, auto value) {
if (var != value) {
changed = true;
}
var = value;
};
switch (i) {
case 0:
set(fWidth, 63);
break;
case 1:
set(fHeight, 63);
break;
case 2:
set(fOrigin, kBottomLeft_GrSurfaceOrigin);
break;
case 3:
set(fColorType, kRGBA_F16_SkColorType);
break;
case 4:
// This just needs to be a colorSpace different from that returned by MakeSRGB().
// In this case we just change the gamut.
set(fColorSpace, SkColorSpace::MakeRGB(SkNamedTransferFn::kSRGB,
SkNamedGamut::kAdobeRGB));
break;
case 5:
set(fSampleCount, 4);
break;
case 6:
set(fSurfaceProps, SkSurfaceProps(0x0, kRGB_H_SkPixelGeometry));
break;
case 7:
set(fSurfaceProps, SkSurfaceProps(SkSurfaceProps::kUseDeviceIndependentFonts_Flag,
kUnknown_SkPixelGeometry));
break;
case 8:
set(fShouldCreateMipMaps, skgpu::Mipmapped::kNo);
break;
case 9:
if (GrBackendApi::kOpenGL == fBackend) {
set(fUsesGLFBO0, true);
set(fShouldCreateMipMaps,
skgpu::Mipmapped::kNo); // needs to changed in tandem w/ textureability
set(fIsTextureable, false);
}
break;
case 10:
set(fShouldCreateMipMaps,
skgpu::Mipmapped::kNo); // needs to changed in tandem w/ textureability
set(fIsTextureable, false);
break;
case 11:
if (fCanBeProtected) {
set(fIsProtected, skgpu::Protected(!static_cast<bool>(fIsProtected)));
}
break;
case 12:
if (GrBackendApi::kVulkan == fBackend) {
set(fForVulkanSecondaryCommandBuffer, true);
set(fUsesGLFBO0, false);
set(fShouldCreateMipMaps,
skgpu::Mipmapped::kNo); // needs to changed in tandem w/ textureability
set(fIsTextureable, false);
set(fVkRTSupportsInputAttachment, false);
}
break;
}
return changed;
}
GrSurfaceCharacterization createCharacterization(GrDirectContext* dContext) const {
size_t maxResourceBytes = dContext->getResourceCacheLimit();
if (!dContext->colorTypeSupportedAsSurface(fColorType)) {
return GrSurfaceCharacterization();
}
// Note that Ganesh doesn't make use of the SkImageInfo's alphaType
SkImageInfo ii = SkImageInfo::Make(fWidth, fHeight, fColorType,
kPremul_SkAlphaType, fColorSpace);
GrBackendFormat backendFormat = dContext->defaultBackendFormat(fColorType,
GrRenderable::kYes);
if (!backendFormat.isValid()) {
return GrSurfaceCharacterization();
}
GrSurfaceCharacterization c = dContext->threadSafeProxy()->createCharacterization(
maxResourceBytes, ii, backendFormat, fSampleCount,
fOrigin, fSurfaceProps, fShouldCreateMipMaps,
fUsesGLFBO0, fIsTextureable, fIsProtected,
fVkRTSupportsInputAttachment,
fForVulkanSecondaryCommandBuffer);
return c;
}
// Create a DDL whose characterization captures the current settings
sk_sp<GrDeferredDisplayList> createDDL(GrDirectContext* dContext) const {
GrSurfaceCharacterization c = this->createCharacterization(dContext);
SkAssertResult(c.isValid());
GrDeferredDisplayListRecorder 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(GrDirectContext* dContext) const {
const GrSurfaceCharacterization c = this->createCharacterization(dContext);
#ifdef SK_GL
if (fUsesGLFBO0) {
if (GrBackendApi::kOpenGL != dContext->backend()) {
return nullptr;
}
GrGLFramebufferInfo fboInfo;
fboInfo.fFBOID = 0;
fboInfo.fFormat = GR_GL_RGBA8;
fboInfo.fProtected = skgpu::Protected::kNo;
static constexpr int kStencilBits = 8;
GrBackendRenderTarget backendRT =
GrBackendRenderTargets::MakeGL(fWidth, fHeight, 1, kStencilBits, fboInfo);
if (!backendRT.isValid()) {
return nullptr;
}
sk_sp<SkSurface> result = SkSurfaces::WrapBackendRenderTarget(
dContext, backendRT, fOrigin, fColorType, fColorSpace, &fSurfaceProps);
SkASSERT(result->isCompatible(c));
return result;
}
#endif
// We can't make SkSurfaces for vulkan secondary command buffers.
if (fForVulkanSecondaryCommandBuffer) {
return nullptr;
}
sk_sp<SkSurface> surface;
if (fIsTextureable) {
surface = sk_gpu_test::MakeBackendTextureSurface(dContext,
{fWidth, fHeight},
fOrigin,
fSampleCount,
fColorType,
fColorSpace,
fShouldCreateMipMaps,
fIsProtected,
&fSurfaceProps);
} else {
// Create a surface w/ the current parameters but make it non-textureable
SkASSERT(fShouldCreateMipMaps == skgpu::Mipmapped::kNo);
surface = sk_gpu_test::MakeBackendRenderTargetSurface(dContext,
{fWidth, fHeight},
fOrigin,
fSampleCount,
fColorType,
fColorSpace,
fIsProtected,
&fSurfaceProps);
}
if (!surface) {
SkASSERT(!c.isValid());
return nullptr;
}
GrBackendTexture texture = SkSurfaces::GetBackendTexture(
surface.get(), SkSurfaces::BackendHandleAccess::kFlushRead);
if (texture.isValid()) {
SkASSERT(is_compatible(c, texture));
}
SkASSERT(c.isValid());
SkASSERT(surface->isCompatible(c));
return surface;
}
#ifdef SK_VULKAN
sk_sp<GrVkSecondaryCBDrawContext> makeVkSCB(GrDirectContext* dContext) {
const GrSurfaceCharacterization c = this->createCharacterization(dContext);
SkImageInfo imageInfo = SkImageInfo::Make({fWidth, fHeight},
{fColorType, kPremul_SkAlphaType, fColorSpace});
GrVkDrawableInfo vkInfo;
// putting in a bunch of placeholder values here
vkInfo.fSecondaryCommandBuffer = (VkCommandBuffer)1;
vkInfo.fColorAttachmentIndex = 0;
vkInfo.fCompatibleRenderPass = (VkRenderPass)1;
vkInfo.fFormat = VK_FORMAT_R8G8B8A8_UNORM;
vkInfo.fDrawBounds = nullptr;
return GrVkSecondaryCBDrawContext::Make(dContext, imageInfo, vkInfo, &fSurfaceProps);
}
#endif
private:
GrBackendApi fBackend;
bool fCanBeProtected;
int fWidth;
int fHeight;
GrSurfaceOrigin fOrigin;
SkColorType fColorType;
sk_sp<SkColorSpace> fColorSpace;
int fSampleCount;
SkSurfaceProps fSurfaceProps;
skgpu::Mipmapped fShouldCreateMipMaps;
bool fUsesGLFBO0;
bool fIsTextureable;
skgpu::Protected fIsProtected;
bool fVkRTSupportsInputAttachment;
bool fForVulkanSecondaryCommandBuffer;
};
// Test out operator== && operator!=
DEF_GANESH_TEST_FOR_RENDERING_CONTEXTS(DDLOperatorEqTest,
reporter,
ctxInfo,
CtsEnforcement::kNever) {
auto context = ctxInfo.directContext();
for (int i = -1; i < SurfaceParameters::kNumParams; ++i) {
SurfaceParameters params1(context);
bool didModify1 = i >= 0 && params1.modify(i);
GrSurfaceCharacterization char1 = params1.createCharacterization(context);
if (!char1.isValid()) {
continue; // can happen on some platforms (ChromeOS)
}
for (int j = -1; j < SurfaceParameters::kNumParams; ++j) {
SurfaceParameters params2(context);
bool didModify2 = j >= 0 && params2.modify(j);
GrSurfaceCharacterization char2 = params2.createCharacterization(context);
if (!char2.isValid()) {
continue; // can happen on some platforms (ChromeOS)
}
if (i == j || (!didModify1 && !didModify2)) {
REPORTER_ASSERT(reporter, char1 == char2);
} else {
REPORTER_ASSERT(reporter, char1 != char2);
}
}
}
{
SurfaceParameters params(context);
GrSurfaceCharacterization valid = params.createCharacterization(context);
SkASSERT(valid.isValid());
GrSurfaceCharacterization inval1, inval2;
SkASSERT(!inval1.isValid() && !inval2.isValid());
REPORTER_ASSERT(reporter, inval1 != inval2);
REPORTER_ASSERT(reporter, valid != inval1);
REPORTER_ASSERT(reporter, inval1 != valid);
}
}
////////////////////////////////////////////////////////////////////////////////
// This tests GrSurfaceCharacterization/SkSurface compatibility
void DDLSurfaceCharacterizationTestImpl(GrDirectContext* dContext, skiatest::Reporter* reporter) {
// Create a bitmap that we can readback into
SkImageInfo imageInfo = SkImageInfo::Make(64, 64, kRGBA_8888_SkColorType,
kPremul_SkAlphaType);
SkBitmap bitmap;
bitmap.allocPixels(imageInfo);
sk_sp<GrDeferredDisplayList> ddl;
// First, create a DDL using the stock SkSurface parameters
{
SurfaceParameters params(dContext);
if (dContext->backend() == GrBackendApi::kVulkan) {
params.setVkRTInputAttachmentSupport(true);
}
ddl = params.createDDL(dContext);
SkAssertResult(ddl);
// The DDL should draw into an SkSurface created with the same parameters
sk_sp<SkSurface> s = params.make(dContext);
if (!s) {
return;
}
REPORTER_ASSERT(reporter, skgpu::ganesh::DrawDDL(s, ddl));
s->readPixels(imageInfo, bitmap.getPixels(), bitmap.rowBytes(), 0, 0);
dContext->flush();
}
// Then, alter each parameter in turn and check that the DDL & surface are incompatible
for (int i = 0; i < SurfaceParameters::kNumParams; ++i) {
SurfaceParameters params(dContext);
if (!params.modify(i)) {
continue;
}
sk_sp<SkSurface> s = params.make(dContext);
if (!s) {
continue;
}
REPORTER_ASSERT(reporter, !skgpu::ganesh::DrawDDL(s, ddl),
"DDLSurfaceCharacterizationTest failed on parameter: %d\n", i);
dContext->flush();
}
// Next test the compatibility of resource cache parameters
{
const SurfaceParameters params(dContext);
sk_sp<SkSurface> s = params.make(dContext);
size_t maxResourceBytes = dContext->getResourceCacheLimit();
dContext->setResourceCacheLimit(maxResourceBytes/2);
REPORTER_ASSERT(reporter, !skgpu::ganesh::DrawDDL(s, ddl));
// 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, skgpu::ganesh::DrawDDL(s, ddl));
s->readPixels(imageInfo, bitmap.getPixels(), bitmap.rowBytes(), 0, 0);
context->setResourceCacheLimits(maxResourceCount, 2*maxResourceBytes);
REPORTER_ASSERT(reporter, skgpu::ganesh::DrawDDL(s, ddl));
s->readPixels(imageInfo, bitmap.getPixels(), bitmap.rowBytes(), 0, 0);
context->setResourceCacheLimits(maxResourceCount, maxResourceBytes);
REPORTER_ASSERT(reporter, skgpu::ganesh::DrawDDL(s, ddl));
s->readPixels(imageInfo, bitmap.getPixels(), bitmap.rowBytes(), 0, 0);
#endif
dContext->flush();
}
// Test that the textureability of the DDL characterization can block a DDL draw
{
SurfaceParameters params(dContext);
params.disableTextureability();
sk_sp<SkSurface> s = params.make(dContext);
if (s) {
// bc the DDL was made w/ textureability
REPORTER_ASSERT(reporter, !skgpu::ganesh::DrawDDL(s, ddl));
dContext->flush();
}
}
// Make sure non-GPU-backed surfaces fail characterization
{
SkImageInfo ii = SkImageInfo::MakeN32(64, 64, kOpaque_SkAlphaType);
sk_sp<SkSurface> rasterSurface = SkSurfaces::Raster(ii);
GrSurfaceCharacterization c;
REPORTER_ASSERT(reporter, !rasterSurface->characterize(&c));
}
// Exercise the createResized method
{
SurfaceParameters params(dContext);
sk_sp<SkSurface> s = params.make(dContext);
if (!s) {
return;
}
GrSurfaceCharacterization char0;
SkAssertResult(s->characterize(&char0));
// Too small
GrSurfaceCharacterization char1 = char0.createResized(-1, -1);
REPORTER_ASSERT(reporter, !char1.isValid());
// Too large
GrSurfaceCharacterization char2 = char0.createResized(1000000, 32);
REPORTER_ASSERT(reporter, !char2.isValid());
// Just right
GrSurfaceCharacterization char3 = char0.createResized(32, 32);
REPORTER_ASSERT(reporter, char3.isValid());
REPORTER_ASSERT(reporter, 32 == char3.width());
REPORTER_ASSERT(reporter, 32 == char3.height());
}
// Exercise the createColorSpace method
{
SurfaceParameters params(dContext);
sk_sp<SkSurface> s = params.make(dContext);
if (!s) {
return;
}
GrSurfaceCharacterization char0;
SkAssertResult(s->characterize(&char0));
// The default params create an sRGB color space
REPORTER_ASSERT(reporter, char0.colorSpace()->isSRGB());
REPORTER_ASSERT(reporter, !char0.colorSpace()->gammaIsLinear());
{
sk_sp<SkColorSpace> newCS = SkColorSpace::MakeSRGBLinear();
GrSurfaceCharacterization char1 = char0.createColorSpace(std::move(newCS));
REPORTER_ASSERT(reporter, char1.isValid());
REPORTER_ASSERT(reporter, !char1.colorSpace()->isSRGB());
REPORTER_ASSERT(reporter, char1.colorSpace()->gammaIsLinear());
}
{
GrSurfaceCharacterization char2 = char0.createColorSpace(nullptr);
REPORTER_ASSERT(reporter, char2.isValid());
REPORTER_ASSERT(reporter, !char2.colorSpace());
}
{
sk_sp<SkColorSpace> newCS = SkColorSpace::MakeSRGBLinear();
GrSurfaceCharacterization invalid;
REPORTER_ASSERT(reporter, !invalid.isValid());
GrSurfaceCharacterization stillInvalid = invalid.createColorSpace(std::move(newCS));
REPORTER_ASSERT(reporter, !stillInvalid.isValid());
}
}
// Exercise the createBackendFormat method
{
SurfaceParameters params(dContext);
sk_sp<SkSurface> s = params.make(dContext);
if (!s) {
return;
}
GrSurfaceCharacterization char0;
SkAssertResult(s->characterize(&char0));
// The default params create a renderable RGBA8 surface
auto originalBackendFormat = dContext->defaultBackendFormat(kRGBA_8888_SkColorType,
GrRenderable::kYes);
REPORTER_ASSERT(reporter, originalBackendFormat.isValid());
REPORTER_ASSERT(reporter, char0.backendFormat() == originalBackendFormat);
auto newBackendFormat = dContext->defaultBackendFormat(kRGB_565_SkColorType,
GrRenderable::kYes);
if (newBackendFormat.isValid()) {
GrSurfaceCharacterization char1 = char0.createBackendFormat(kRGB_565_SkColorType,
newBackendFormat);
REPORTER_ASSERT(reporter, char1.isValid());
REPORTER_ASSERT(reporter, char1.backendFormat() == newBackendFormat);
GrSurfaceCharacterization invalid;
REPORTER_ASSERT(reporter, !invalid.isValid());
auto stillInvalid = invalid.createBackendFormat(kRGB_565_SkColorType,
newBackendFormat);
REPORTER_ASSERT(reporter, !stillInvalid.isValid());
}
}
// Exercise the createFBO0 method
if (dContext->backend() == GrBackendApi::kOpenGL) {
SurfaceParameters params(dContext);
// If the original characterization is textureable then we will fail trying to make an
// FBO0 characterization
params.disableTextureability();
sk_sp<SkSurface> s = params.make(dContext);
if (!s) {
return;
}
GrSurfaceCharacterization char0;
SkAssertResult(s->characterize(&char0));
// The default params create a non-FBO0 surface
REPORTER_ASSERT(reporter, !char0.usesGLFBO0());
{
GrSurfaceCharacterization char1 = char0.createFBO0(true);
REPORTER_ASSERT(reporter, char1.isValid());
REPORTER_ASSERT(reporter, char1.usesGLFBO0());
}
{
GrSurfaceCharacterization invalid;
REPORTER_ASSERT(reporter, !invalid.isValid());
GrSurfaceCharacterization stillInvalid = invalid.createFBO0(true);
REPORTER_ASSERT(reporter, !stillInvalid.isValid());
}
}
}
#ifdef SK_GL
// Test out the surface compatibility checks regarding FBO0-ness. This test constructs
// two parallel arrays of characterizations and surfaces in the order:
// FBO0 w/ MSAA, FBO0 w/o MSAA, not-FBO0 w/ MSAA, not-FBO0 w/o MSAA
// and then tries all sixteen combinations to check the expected compatibility.
// Note: this is a GL-only test
DEF_GANESH_TEST_FOR_GL_CONTEXT(CharacterizationFBO0nessTest,
reporter,
ctxInfo,
CtsEnforcement::kApiLevel_T) {
auto context = ctxInfo.directContext();
const GrCaps* caps = context->priv().caps();
sk_sp<GrContextThreadSafeProxy> proxy = context->threadSafeProxy();
const size_t resourceCacheLimit = context->getResourceCacheLimit();
GrBackendFormat format = GrBackendFormats::MakeGL(GR_GL_RGBA8, GR_GL_TEXTURE_2D);
int availableSamples = caps->getRenderTargetSampleCount(4, format);
if (availableSamples <= 1) {
// This context doesn't support MSAA for RGBA8
return;
}
SkImageInfo ii = SkImageInfo::Make({ 128, 128 }, kRGBA_8888_SkColorType, kPremul_SkAlphaType);
static constexpr int kStencilBits = 8;
static constexpr bool kNotTextureable = false;
const SkSurfaceProps surfaceProps(0x0, kRGB_H_SkPixelGeometry);
// Rows are characterizations and columns are surfaces
static const bool kExpectedCompatibility[4][4] = {
// FBO0 & MSAA, FBO0 & not-MSAA, not-FBO0 & MSAA, not-FBO0 & not-MSAA
/* FBO0 & MSAA */ { true, false, false, false },
/* FBO0 & not-MSAA */ { false, true, false, true },
/* not-FBO0 & MSAA */ { false, false, true, false },
/* not-FBO0 & not- */ { false, false, false, true }
};
GrSurfaceCharacterization characterizations[4];
sk_sp<SkSurface> surfaces[4];
int index = 0;
for (bool isFBO0 : { true, false }) {
for (int numSamples : { availableSamples, 1 }) {
characterizations[index] = proxy->createCharacterization(resourceCacheLimit,
ii,
format,
numSamples,
kTopLeft_GrSurfaceOrigin,
surfaceProps,
skgpu::Mipmapped::kNo,
isFBO0,
kNotTextureable);
SkASSERT(characterizations[index].sampleCount() == numSamples);
SkASSERT(characterizations[index].usesGLFBO0() == isFBO0);
GrGLFramebufferInfo fboInfo{ isFBO0 ? 0 : (GrGLuint) 1, GR_GL_RGBA8 };
GrBackendRenderTarget backendRT =
GrBackendRenderTargets::MakeGL(128, 128, numSamples, kStencilBits, fboInfo);
SkAssertResult(backendRT.isValid());
surfaces[index] = SkSurfaces::WrapBackendRenderTarget(context,
backendRT,
kTopLeft_GrSurfaceOrigin,
kRGBA_8888_SkColorType,
nullptr,
&surfaceProps);
++index;
}
}
for (int c = 0; c < 4; ++c) {
for (int s = 0; s < 4; ++s) {
REPORTER_ASSERT(reporter,
kExpectedCompatibility[c][s] ==
surfaces[s]->isCompatible(characterizations[c]));
}
}
}
#endif
#ifdef SK_VULKAN
DEF_GANESH_TEST_FOR_VULKAN_CONTEXT(CharacterizationVkSCBnessTest,
reporter,
ctxInfo,
CtsEnforcement::kApiLevel_T) {
auto dContext = ctxInfo.directContext();
SurfaceParameters params(dContext);
params.modify(SurfaceParameters::kVkSCBCount);
GrSurfaceCharacterization characterization = params.createCharacterization(dContext);
REPORTER_ASSERT(reporter, characterization.isValid());
sk_sp<GrDeferredDisplayList> ddl = params.createDDL(dContext);
REPORTER_ASSERT(reporter, ddl.get());
sk_sp<GrVkSecondaryCBDrawContext> scbDrawContext = params.makeVkSCB(dContext);
REPORTER_ASSERT(reporter, scbDrawContext->isCompatible(characterization));
scbDrawContext->releaseResources();
}
#endif
DEF_GANESH_TEST_FOR_RENDERING_CONTEXTS(DDLSurfaceCharacterizationTest,
reporter,
ctxInfo,
CtsEnforcement::kNever) {
auto context = ctxInfo.directContext();
DDLSurfaceCharacterizationTestImpl(context, reporter);
}
#if defined(SK_VULKAN)
DEF_GANESH_TEST(VkProtectedContext_DDLSurfaceCharacterizationTest,
reporter,
ctxInfo,
CtsEnforcement::kNever) {
std::unique_ptr<VkTestHelper> helper = VkTestHelper::Make(skiatest::TestType::kGanesh,
/* isProtected= */ true);
if (!helper) {
return;
}
REPORTER_ASSERT(reporter, helper->isValid());
DDLSurfaceCharacterizationTestImpl(helper->directContext(), reporter);
}
#endif
// Test that a DDL created w/o textureability can be replayed into both a textureable and
// non-textureable destination. Note that DDLSurfaceCharacterizationTest tests that a
// textureable DDL cannot be played into a non-textureable destination but can be replayed
// into a textureable destination.
DEF_GANESH_TEST_FOR_RENDERING_CONTEXTS(DDLNonTextureabilityTest,
reporter,
ctxInfo,
CtsEnforcement::kNever) {
auto context = ctxInfo.directContext();
// Create a bitmap that we can readback into
SkImageInfo imageInfo = SkImageInfo::Make(64, 64, kRGBA_8888_SkColorType,
kPremul_SkAlphaType);
SkBitmap bitmap;
bitmap.allocPixels(imageInfo);
for (bool textureability : { true, false }) {
sk_sp<GrDeferredDisplayList> ddl;
// First, create a DDL w/o textureability (and thus no mipmaps). TODO: once we have
// reusable DDLs, move this outside of the loop.
{
SurfaceParameters params(context);
params.disableTextureability();
if (context->backend() == GrBackendApi::kVulkan) {
params.setVkRTInputAttachmentSupport(true);
}
ddl = params.createDDL(context);
SkAssertResult(ddl);
}
// Then verify it can draw into either flavor of destination
SurfaceParameters params(context);
if (!textureability) {
params.disableTextureability();
}
if (context->backend() == GrBackendApi::kVulkan) {
params.setVkRTInputAttachmentSupport(true);
}
sk_sp<SkSurface> s = params.make(context);
if (!s) {
continue;
}
REPORTER_ASSERT(reporter, skgpu::ganesh::DrawDDL(s, ddl));
s->readPixels(imageInfo, bitmap.getPixels(), bitmap.rowBytes(), 0, 0);
context->flush();
}
}
static void test_make_render_target(skiatest::Reporter* reporter,
GrDirectContext* dContext,
const SurfaceParameters& params) {
{
const GrSurfaceCharacterization c = params.createCharacterization(dContext);
if (!c.isValid()) {
sk_sp<SkSurface> tmp = params.make(dContext);
// If we couldn't characterize the surface we shouldn't be able to create it either
REPORTER_ASSERT(reporter, !tmp);
return;
}
}
const GrSurfaceCharacterization c = params.createCharacterization(dContext);
{
sk_sp<SkSurface> s = params.make(dContext);
REPORTER_ASSERT(reporter, s);
if (!s) {
REPORTER_ASSERT(reporter, !c.isValid());
return;
}
REPORTER_ASSERT(reporter, c.isValid());
GrBackendTexture backend =
SkSurfaces::GetBackendTexture(s.get(), SkSurfaces::BackendHandleAccess::kFlushRead);
if (backend.isValid()) {
REPORTER_ASSERT(reporter, is_compatible(c, backend));
}
REPORTER_ASSERT(reporter, s->isCompatible(c));
// Note that we're leaving 'backend' live here
}
// Make an SkSurface from scratch
{
sk_sp<SkSurface> s = SkSurfaces::RenderTarget(dContext, c, skgpu::Budgeted::kYes);
REPORTER_ASSERT(reporter, s);
REPORTER_ASSERT(reporter, s->isCompatible(c));
}
}
////////////////////////////////////////////////////////////////////////////////
// This tests the SkSurfaces::RenderTarget variants that take a GrSurfaceCharacterization.
// In particular, the SkSurface, backendTexture and GrSurfaceCharacterization
// should always be compatible.
void DDLMakeRenderTargetTestImpl(GrDirectContext* dContext, skiatest::Reporter* reporter) {
for (int i = -1; i < SurfaceParameters::kNumParams; ++i) {
if (i == SurfaceParameters::kFBO0Count || i == SurfaceParameters::kVkSCBCount) {
// MakeRenderTarget doesn't support FBO0 or vulkan secondary command buffers
continue;
}
SurfaceParameters params(dContext);
if (i >= 0 && !params.modify(i)) {
continue;
}
test_make_render_target(reporter, dContext, params);
}
}
DEF_GANESH_TEST_FOR_RENDERING_CONTEXTS(DDLMakeRenderTargetTest,
reporter,
ctxInfo,
CtsEnforcement::kNever) {
auto context = ctxInfo.directContext();
DDLMakeRenderTargetTestImpl(context, reporter);
}
#if defined(SK_VULKAN)
DEF_GANESH_TEST(VkProtectedContext_DDLMakeRenderTargetTest,
reporter,
ctxInfo,
CtsEnforcement::kNever) {
std::unique_ptr<VkTestHelper> helper = VkTestHelper::Make(skiatest::TestType::kGanesh,
/* isProtected= */ true);
if (!helper) {
return;
}
REPORTER_ASSERT(reporter, helper->isValid());
DDLMakeRenderTargetTestImpl(helper->directContext(), reporter);
}
#endif
////////////////////////////////////////////////////////////////////////////////
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_GANESH_TEST_FOR_RENDERING_CONTEXTS(DDLWrapBackendTest,
reporter,
ctxInfo,
CtsEnforcement::kNever) {
auto dContext = ctxInfo.directContext();
auto mbet = sk_gpu_test::ManagedBackendTexture::MakeWithoutData(dContext,
kSize,
kSize,
kRGBA_8888_SkColorType,
skgpu::Mipmapped::kNo,
GrRenderable::kNo,
skgpu::Protected::kNo);
if (!mbet) {
return;
}
SurfaceParameters params(dContext);
sk_sp<SkSurface> s = params.make(dContext);
if (!s) {
return;
}
GrSurfaceCharacterization c;
SkAssertResult(s->characterize(&c));
GrDeferredDisplayListRecorder recorder(c);
SkCanvas* canvas = recorder.getCanvas();
SkASSERT(canvas);
auto rContext = canvas->recordingContext();
if (!rContext) {
return;
}
// Wrapped Backend Textures are not supported in DDL
TextureReleaseChecker releaseChecker;
sk_sp<SkImage> image = SkImages::BorrowTextureFrom(
rContext,
mbet->texture(),
kTopLeft_GrSurfaceOrigin,
kRGBA_8888_SkColorType,
kPremul_SkAlphaType,
nullptr,
sk_gpu_test::ManagedBackendTexture::ReleaseProc,
mbet->releaseContext(TextureReleaseChecker::Release, &releaseChecker));
REPORTER_ASSERT(reporter, !image);
}
////////////////////////////////////////////////////////////////////////////////
// Test out the behavior of an invalid DDLRecorder
DEF_GANESH_TEST_FOR_RENDERING_CONTEXTS(DDLInvalidRecorder,
reporter,
ctxInfo,
CtsEnforcement::kNever) {
auto dContext = ctxInfo.directContext();
{
SkImageInfo ii = SkImageInfo::MakeN32Premul(32, 32);
sk_sp<SkSurface> s = SkSurfaces::RenderTarget(dContext, skgpu::Budgeted::kNo, ii);
GrSurfaceCharacterization characterization;
SkAssertResult(s->characterize(&characterization));
// never calling getCanvas means the backing surface is never allocated
GrDeferredDisplayListRecorder recorder(characterization);
}
{
GrSurfaceCharacterization invalid;
GrDeferredDisplayListRecorder recorder(invalid);
const GrSurfaceCharacterization c = recorder.characterization();
REPORTER_ASSERT(reporter, !c.isValid());
REPORTER_ASSERT(reporter, !recorder.getCanvas());
REPORTER_ASSERT(reporter, !recorder.detach());
}
}
DEF_GANESH_TEST_FOR_RENDERING_CONTEXTS(DDLCreateCharacterizationFailures,
reporter,
ctxInfo,
CtsEnforcement::kNever) {
using namespace skgpu;
auto dContext = ctxInfo.directContext();
size_t maxResourceBytes = dContext->getResourceCacheLimit();
auto proxy = dContext->threadSafeProxy().get();
Protected isProtected = Protected(dContext->priv().caps()->supportsProtectedContent());
auto check_create_fails = [proxy, reporter, maxResourceBytes](
const GrBackendFormat& backendFormat,
int width,
int height,
SkColorType ct,
bool willUseGLFBO0,
bool isTextureable,
Protected prot,
bool vkRTSupportsInputAttachment,
bool forVulkanSecondaryCommandBuffer) {
const SkSurfaceProps surfaceProps(0x0, kRGB_H_SkPixelGeometry);
SkImageInfo ii = SkImageInfo::Make(width, height, ct,
kPremul_SkAlphaType, nullptr);
GrSurfaceCharacterization c =
proxy->createCharacterization(maxResourceBytes,
ii,
backendFormat,
1,
kBottomLeft_GrSurfaceOrigin,
surfaceProps,
Mipmapped::kNo,
willUseGLFBO0,
isTextureable,
prot,
vkRTSupportsInputAttachment,
forVulkanSecondaryCommandBuffer);
REPORTER_ASSERT(reporter, !c.isValid());
};
GrBackendFormat goodBackendFormat = dContext->defaultBackendFormat(kRGBA_8888_SkColorType,
GrRenderable::kYes);
SkASSERT(goodBackendFormat.isValid());
GrBackendFormat badBackendFormat;
SkASSERT(!badBackendFormat.isValid());
SkColorType kGoodCT = kRGBA_8888_SkColorType;
SkColorType kBadCT = kUnknown_SkColorType;
static const bool kIsTextureable = true;
static const bool kIsNotTextureable = false;
static const bool kGoodUseFBO0 = false;
static const bool kBadUseFBO0 = true;
static const bool kGoodVkInputAttachment = false;
static const bool kBadVkInputAttachment = true;
static const bool kGoodForVkSCB = false;
static const bool kBadForVkSCB = true;
int goodWidth = 64;
int goodHeight = 64;
int badWidths[] = { 0, 1048576 };
int badHeights[] = { 0, 1048576 };
// In each of the check_create_fails calls there is one bad parameter that should cause the
// creation of the characterization to fail.
check_create_fails(goodBackendFormat, goodWidth, badHeights[0], kGoodCT, kGoodUseFBO0,
kIsTextureable, isProtected, kGoodVkInputAttachment, kGoodForVkSCB);
check_create_fails(goodBackendFormat, goodWidth, badHeights[1], kGoodCT, kGoodUseFBO0,
kIsTextureable, isProtected, kGoodVkInputAttachment, kGoodForVkSCB);
check_create_fails(goodBackendFormat, badWidths[0], goodHeight, kGoodCT, kGoodUseFBO0,
kIsTextureable, isProtected, kGoodVkInputAttachment, kGoodForVkSCB);
check_create_fails(goodBackendFormat, badWidths[1], goodHeight, kGoodCT, kGoodUseFBO0,
kIsTextureable, isProtected, kGoodVkInputAttachment, kGoodForVkSCB);
check_create_fails(badBackendFormat, goodWidth, goodHeight, kGoodCT, kGoodUseFBO0,
kIsTextureable, isProtected, kGoodVkInputAttachment, kGoodForVkSCB);
check_create_fails(goodBackendFormat, goodWidth, goodHeight, kBadCT, kGoodUseFBO0,
kIsTextureable, isProtected, kGoodVkInputAttachment, kGoodForVkSCB);
// This fails because we always try to make a characterization that is textureable and we can't
// have UseFBO0 be true and textureable.
check_create_fails(goodBackendFormat, goodWidth, goodHeight, kGoodCT, kBadUseFBO0,
kIsTextureable, isProtected, kGoodVkInputAttachment, kGoodForVkSCB);
if (dContext->backend() == GrBackendApi::kVulkan) {
// The following fails because forVulkanSecondaryCommandBuffer is true and
// isTextureable is true. This is not a legal combination.
check_create_fails(goodBackendFormat, goodWidth, goodHeight, kGoodCT, kGoodUseFBO0,
kIsTextureable, isProtected, kGoodVkInputAttachment, kBadForVkSCB);
// The following fails because forVulkanSecondaryCommandBuffer is true and
// vkRTSupportsInputAttachment is true. This is not a legal combination.
check_create_fails(goodBackendFormat, goodWidth, goodHeight, kGoodCT, kGoodUseFBO0,
kIsNotTextureable, isProtected, kBadVkInputAttachment,
kBadForVkSCB);
// The following fails because forVulkanSecondaryCommandBuffer is true and
// willUseGLFBO0 is true. This is not a legal combination.
check_create_fails(goodBackendFormat, goodWidth, goodHeight, kGoodCT, kBadUseFBO0,
kIsNotTextureable, isProtected, kGoodVkInputAttachment,
kBadForVkSCB);
} else {
// The following set vulkan only flags on non vulkan backends.
check_create_fails(goodBackendFormat, goodWidth, goodHeight, kGoodCT, kGoodUseFBO0,
kIsTextureable, isProtected, kBadVkInputAttachment, kGoodForVkSCB);
check_create_fails(goodBackendFormat, goodWidth, goodHeight, kGoodCT, kGoodUseFBO0,
kIsNotTextureable, isProtected, kGoodVkInputAttachment,
kBadForVkSCB);
}
}
////////////////////////////////////////////////////////////////////////////////
// Test that flushing a DDL via SkSurface::flush works
struct FulfillInfo {
sk_sp<GrPromiseImageTexture> fTex;
bool fFulfilled = false;
bool fReleased = false;
};
static sk_sp<GrPromiseImageTexture> tracking_fulfill_proc(void* context) {
FulfillInfo* info = (FulfillInfo*) context;
info->fFulfilled = true;
return info->fTex;
}
static void tracking_release_proc(void* context) {
FulfillInfo* info = (FulfillInfo*) context;
info->fReleased = true;
}
DEF_GANESH_TEST_FOR_RENDERING_CONTEXTS(DDLSkSurfaceFlush,
reporter,
ctxInfo,
CtsEnforcement::kNever) {
using namespace skgpu;
auto context = ctxInfo.directContext();
Protected isProtected = Protected(context->priv().caps()->supportsProtectedContent());
SkImageInfo ii = SkImageInfo::Make(32, 32, kRGBA_8888_SkColorType, kPremul_SkAlphaType);
sk_sp<SkSurface> s = SkSurfaces::RenderTarget(context, Budgeted::kNo, ii);
GrSurfaceCharacterization characterization;
SkAssertResult(s->characterize(&characterization));
auto mbet = sk_gpu_test::ManagedBackendTexture::MakeFromInfo(context, ii,
Mipmapped::kNo,
Renderable::kNo,
isProtected);
if (!mbet) {
ERRORF(reporter, "Could not make texture.");
return;
}
FulfillInfo fulfillInfo;
fulfillInfo.fTex = GrPromiseImageTexture::Make(mbet->texture());
sk_sp<GrDeferredDisplayList> ddl;
{
GrDeferredDisplayListRecorder recorder(characterization);
GrBackendFormat format = context->defaultBackendFormat(kRGBA_8888_SkColorType,
GrRenderable::kNo);
SkASSERT(format.isValid());
SkCanvas* canvas = recorder.getCanvas();
sk_sp<SkImage> promiseImage =
SkImages::PromiseTextureFrom(canvas->recordingContext()->threadSafeProxy(),
format,
SkISize::Make(32, 32),
skgpu::Mipmapped::kNo,
kTopLeft_GrSurfaceOrigin,
kRGBA_8888_SkColorType,
kPremul_SkAlphaType,
nullptr,
tracking_fulfill_proc,
tracking_release_proc,
&fulfillInfo);
canvas->clear(SK_ColorRED);
canvas->drawImage(promiseImage, 0, 0);
ddl = recorder.detach();
}
context->flushAndSubmit();
REPORTER_ASSERT(reporter, skgpu::ganesh::DrawDDL(s, ddl));
GrFlushInfo flushInfo;
context->flush(s.get(), SkSurfaces::BackendSurfaceAccess::kPresent, flushInfo);
context->submit(GrSyncCpu::kNo);
REPORTER_ASSERT(reporter, fulfillInfo.fFulfilled);
// In order to receive the done callback with the low-level APIs we need to re-flush
context->flush(s.get());
context->submit(GrSyncCpu::kYes);
REPORTER_ASSERT(reporter, fulfillInfo.fReleased);
REPORTER_ASSERT(reporter, fulfillInfo.fTex->unique());
fulfillInfo.fTex.reset();
}
////////////////////////////////////////////////////////////////////////////////
// Ensure that reusing a single DDLRecorder to create multiple DDLs works cleanly
DEF_GANESH_TEST_FOR_RENDERING_CONTEXTS(DDLMultipleDDLs, reporter, ctxInfo, CtsEnforcement::kNever) {
auto context = ctxInfo.directContext();
SkImageInfo ii = SkImageInfo::MakeN32Premul(32, 32);
sk_sp<SkSurface> s = SkSurfaces::RenderTarget(context, skgpu::Budgeted::kNo, ii);
SkBitmap bitmap;
bitmap.allocPixels(ii);
GrSurfaceCharacterization characterization;
SkAssertResult(s->characterize(&characterization));
GrDeferredDisplayListRecorder recorder(characterization);
SkCanvas* canvas1 = recorder.getCanvas();
canvas1->clear(SK_ColorRED);
canvas1->save();
canvas1->clipRect(SkRect::MakeXYWH(8, 8, 16, 16));
sk_sp<GrDeferredDisplayList> ddl1 = recorder.detach();
SkCanvas* canvas2 = recorder.getCanvas();
SkPaint p;
p.setColor(SK_ColorGREEN);
canvas2->drawRect(SkRect::MakeWH(32, 32), p);
sk_sp<GrDeferredDisplayList> ddl2 = recorder.detach();
REPORTER_ASSERT(reporter, ddl1->priv().lazyProxyData());
REPORTER_ASSERT(reporter, ddl2->priv().lazyProxyData());
// The lazy proxy data being different ensures that the SkSurface, SkCanvas and backing-
// lazy proxy are all different between the two DDLs
REPORTER_ASSERT(reporter, ddl1->priv().lazyProxyData() != ddl2->priv().lazyProxyData());
skgpu::ganesh::DrawDDL(s, ddl1);
skgpu::ganesh::DrawDDL(s, ddl2);
// Make sure the clipRect from DDL1 didn't percolate into DDL2
s->readPixels(ii, bitmap.getPixels(), bitmap.rowBytes(), 0, 0);
for (int y = 0; y < 32; ++y) {
for (int x = 0; x < 32; ++x) {
REPORTER_ASSERT(reporter, bitmap.getColor(x, y) == SK_ColorGREEN);
if (bitmap.getColor(x, y) != SK_ColorGREEN) {
return; // we only really need to report the error once
}
}
}
}
#ifdef SK_GL
static sk_sp<GrPromiseImageTexture> noop_fulfill_proc(void*) {
SkASSERT(0);
return nullptr;
}
////////////////////////////////////////////////////////////////////////////////
// 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_GANESH_TEST_FOR_GL_CONTEXT(DDLTextureFlagsTest, reporter, ctxInfo, CtsEnforcement::kNever) {
auto context = ctxInfo.directContext();
SkImageInfo ii = SkImageInfo::MakeN32Premul(32, 32);
sk_sp<SkSurface> s = SkSurfaces::RenderTarget(context, skgpu::Budgeted::kNo, ii);
GrSurfaceCharacterization characterization;
SkAssertResult(s->characterize(&characterization));
GrDeferredDisplayListRecorder recorder(characterization);
for (GrGLenum target : { GR_GL_TEXTURE_EXTERNAL, GR_GL_TEXTURE_RECTANGLE, GR_GL_TEXTURE_2D } ) {
for (auto mipmapped : {skgpu::Mipmapped::kNo, skgpu::Mipmapped::kYes}) {
GrBackendFormat format = GrBackendFormats::MakeGL(GR_GL_RGBA8, target);
sk_sp<SkImage> image = SkImages::PromiseTextureFrom(
recorder.getCanvas()->recordingContext()->threadSafeProxy(),
format,
SkISize::Make(32, 32),
mipmapped,
kTopLeft_GrSurfaceOrigin,
kRGBA_8888_SkColorType,
kPremul_SkAlphaType,
/*color space*/ nullptr,
noop_fulfill_proc,
/*release proc*/ nullptr,
/*context*/ nullptr);
if (GR_GL_TEXTURE_2D != target && mipmapped == skgpu::Mipmapped::kYes) {
REPORTER_ASSERT(reporter, !image);
continue;
}
if (!context->priv().caps()->isFormatTexturable(format, format.textureType())) {
REPORTER_ASSERT(reporter, !image);
continue;
}
REPORTER_ASSERT(reporter, image);
GrTextureProxy* backingProxy = sk_gpu_test::GetTextureImageProxy(image.get(), context);
REPORTER_ASSERT(reporter, backingProxy->mipmapped() == mipmapped);
if (GR_GL_TEXTURE_2D == target) {
REPORTER_ASSERT(reporter, !backingProxy->hasRestrictedSampling());
} else {
REPORTER_ASSERT(reporter, backingProxy->hasRestrictedSampling());
}
}
}
}
#endif // SK_GL
////////////////////////////////////////////////////////////////////////////////
// Test colorType and pixelConfig compatibility.
DEF_GANESH_TEST_FOR_GL_CONTEXT(DDLCompatibilityTest, reporter, ctxInfo, CtsEnforcement::kNever) {
auto context = ctxInfo.directContext();
for (int ct = 0; ct <= kLastEnum_SkColorType; ++ct) {
SkColorType colorType = static_cast<SkColorType>(ct);
SurfaceParameters params(context);
params.setColorType(colorType);
params.setColorSpace(nullptr);
test_make_render_target(reporter, context, params);
}
}