* 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/SkBlendMode.h"
#include "include/core/SkCanvas.h"
#include "include/core/SkColor.h"
#include "include/core/SkColorSpace.h"
#include "include/core/SkColorType.h"
#include "include/core/SkImage.h"
#include "include/core/SkImageInfo.h"
#include "include/core/SkMatrix.h"
#include "include/core/SkRect.h"
#include "include/core/SkRefCnt.h"
#include "include/core/SkSamplingOptions.h"
#include "include/core/SkSurface.h"
#include "include/core/SkSurfaceProps.h"
#include "include/core/SkTypes.h"
#include "include/gpu/GpuTypes.h"
#include "include/gpu/ganesh/GrBackendSurface.h"
#include "include/gpu/ganesh/GrContextOptions.h"
#include "include/gpu/ganesh/GrDirectContext.h"
#include "include/gpu/ganesh/GrRecordingContext.h"
#include "include/gpu/ganesh/GrTypes.h"
#include "include/gpu/ganesh/SkImageGanesh.h"
#include "include/gpu/ganesh/SkSurfaceGanesh.h"
#include "include/gpu/ganesh/mock/GrMockTypes.h"
#include "include/private/SkColorData.h"
#include "include/private/gpu/ganesh/GrTextureGenerator.h"
#include "include/private/gpu/ganesh/GrTypesPriv.h"
#include "src/gpu/SkBackingFit.h"
#include "src/gpu/Swizzle.h"
#include "src/gpu/ganesh/Device.h"
#include "src/gpu/ganesh/GrBackendTextureImageGenerator.h"
#include "src/gpu/ganesh/GrCaps.h"
#include "src/gpu/ganesh/GrColorSpaceXform.h"
#include "src/gpu/ganesh/GrDirectContextPriv.h"
#include "src/gpu/ganesh/GrDrawingManager.h"
#include "src/gpu/ganesh/GrProxyProvider.h"
#include "src/gpu/ganesh/GrRecordingContextPriv.h"
#include "src/gpu/ganesh/GrSamplerState.h"
#include "src/gpu/ganesh/GrSemaphore.h"
#include "src/gpu/ganesh/GrSurfaceProxy.h"
#include "src/gpu/ganesh/GrSurfaceProxyPriv.h"
#include "src/gpu/ganesh/GrSurfaceProxyView.h"
#include "src/gpu/ganesh/GrTexture.h"
#include "src/gpu/ganesh/GrTextureProxy.h"
#include "src/gpu/ganesh/SkGr.h"
#include "src/gpu/ganesh/SurfaceDrawContext.h"
#include "src/gpu/ganesh/ops/OpsTask.h"
#include "src/gpu/ganesh/surface/SkSurface_Ganesh.h"
#include "tests/CtsEnforcement.h"
#include "tests/Test.h"
#include "tools/gpu/BackendSurfaceFactory.h"
#include "tools/gpu/BackendTextureImageFactory.h"
#include "tools/gpu/ManagedBackendTexture.h"
#include "tools/gpu/ProxyUtils.h"
#include <initializer_list>
#include <memory>
#include <utility>
class GrRenderTask;
#if defined(SK_DIRECT3D)
#include "include/gpu/ganesh/d3d/GrD3DTypes.h"
#endif
#if defined(SK_METAL)
#include "include/gpu/ganesh/mtl/GrMtlBackendSurface.h"
#endif
#if defined(SK_GL)
#include "include/gpu/ganesh/gl/GrGLBackendSurface.h"
#include "include/gpu/ganesh/gl/GrGLTypes.h"
#endif
#if defined(SK_VULKAN)
#include "include/gpu/ganesh/vk/GrVkBackendSurface.h"
#include "include/gpu/ganesh/vk/GrVkTypes.h"
#endif
static constexpr int kSize = 8;
DEF_GANESH_TEST_FOR_RENDERING_CONTEXTS(GrWrappedMipMappedTest,
reporter,
ctxInfo,
CtsEnforcement::kApiLevel_T) {
using namespace skgpu;
auto dContext = ctxInfo.directContext();
if (!dContext->priv().caps()->mipmapSupport()) {
return;
}
Protected isProtected = Protected(dContext->priv().caps()->supportsProtectedContent());
for (auto mipmapped : { Mipmapped::kNo, Mipmapped::kYes }) {
for (auto renderable : {GrRenderable::kNo, GrRenderable::kYes}) {
auto mbet = sk_gpu_test::ManagedBackendTexture::MakeWithData(dContext,
kSize,
kSize,
kRGBA_8888_SkColorType,
SkColors::kTransparent,
mipmapped,
renderable,
isProtected);
if (!mbet) {
ERRORF(reporter, "Could not make texture.");
return;
}
sk_sp<GrTextureProxy> proxy;
sk_sp<SkImage> image;
if (renderable == GrRenderable::kYes) {
sk_sp<SkSurface> surface = SkSurfaces::WrapBackendTexture(
dContext,
mbet->texture(),
kTopLeft_GrSurfaceOrigin,
0,
kRGBA_8888_SkColorType,
nullptr,
nullptr,
sk_gpu_test::ManagedBackendTexture::ReleaseProc,
mbet->releaseContext());
auto device = ((SkSurface_Ganesh*)surface.get())->getDevice();
proxy = device->readSurfaceView().asTextureProxyRef();
} else {
image = SkImages::BorrowTextureFrom(dContext,
mbet->texture(),
kTopLeft_GrSurfaceOrigin,
kRGBA_8888_SkColorType,
kPremul_SkAlphaType,
nullptr,
sk_gpu_test::ManagedBackendTexture::ReleaseProc,
mbet->releaseContext());
REPORTER_ASSERT(reporter, (mipmapped == Mipmapped::kYes) == image->hasMipmaps());
proxy = sk_ref_sp(sk_gpu_test::GetTextureImageProxy(image.get(), dContext));
}
REPORTER_ASSERT(reporter, proxy);
if (!proxy) {
continue;
}
REPORTER_ASSERT(reporter, proxy->isInstantiated());
GrTexture* texture = proxy->peekTexture();
REPORTER_ASSERT(reporter, texture);
if (!texture) {
continue;
}
if (mipmapped == Mipmapped::kYes) {
REPORTER_ASSERT(reporter, Mipmapped::kYes == texture->mipmapped());
if (GrRenderable::kYes == renderable) {
REPORTER_ASSERT(reporter, texture->mipmapsAreDirty());
} else {
REPORTER_ASSERT(reporter, !texture->mipmapsAreDirty());
}
} else {
REPORTER_ASSERT(reporter, Mipmapped::kNo == texture->mipmapped());
}
}
}
}
DEF_GANESH_TEST_FOR_RENDERING_CONTEXTS(GrBackendTextureImageMipMappedTest,
reporter,
ctxInfo,
CtsEnforcement::kApiLevel_T) {
using namespace skgpu;
auto dContext = ctxInfo.directContext();
if (!dContext->priv().caps()->mipmapSupport()) {
return;
}
Protected isProtected = Protected(dContext->priv().caps()->supportsProtectedContent());
for (auto betMipmapped : { Mipmapped::kNo, Mipmapped::kYes }) {
for (auto requestMipmapped : { Mipmapped::kNo, Mipmapped::kYes }) {
auto ii =
SkImageInfo::Make({kSize, kSize}, kRGBA_8888_SkColorType, kPremul_SkAlphaType);
sk_sp<SkImage> image = sk_gpu_test::MakeBackendTextureImage(
dContext, ii, SkColors::kTransparent, betMipmapped,
Renderable::kNo,
GrSurfaceOrigin::kTopLeft_GrSurfaceOrigin,
isProtected);
REPORTER_ASSERT(reporter, (betMipmapped == Mipmapped::kYes) == image->hasMipmaps());
GrTextureProxy* proxy = sk_gpu_test::GetTextureImageProxy(image.get(), dContext);
REPORTER_ASSERT(reporter, proxy);
if (!proxy) {
return;
}
REPORTER_ASSERT(reporter, proxy->isInstantiated());
sk_sp<GrTexture> texture = sk_ref_sp(proxy->peekTexture());
REPORTER_ASSERT(reporter, texture);
if (!texture) {
return;
}
std::unique_ptr<GrTextureGenerator> textureGen = GrBackendTextureImageGenerator::Make(
texture, kTopLeft_GrSurfaceOrigin, nullptr, kRGBA_8888_SkColorType,
kPremul_SkAlphaType, nullptr);
REPORTER_ASSERT(reporter, textureGen);
if (!textureGen) {
return;
}
SkImageInfo imageInfo = SkImageInfo::Make(kSize, kSize, kRGBA_8888_SkColorType,
kPremul_SkAlphaType);
GrSurfaceProxyView genView = textureGen->generateTexture(
dContext, imageInfo, requestMipmapped, GrImageTexGenPolicy::kDraw);
GrSurfaceProxy* genProxy = genView.proxy();
REPORTER_ASSERT(reporter, genProxy);
if (!genProxy) {
return;
}
if (genProxy->isLazy()) {
genProxy->priv().doLazyInstantiation(dContext->priv().resourceProvider());
} else if (!genProxy->isInstantiated()) {
genProxy->instantiate(dContext->priv().resourceProvider());
}
REPORTER_ASSERT(reporter, genProxy->isInstantiated());
if (!genProxy->isInstantiated()) {
return;
}
GrTexture* genTexture = genProxy->peekTexture();
REPORTER_ASSERT(reporter, genTexture);
if (!genTexture) {
return;
}
GrBackendTexture backendTex = texture->getBackendTexture();
GrBackendTexture genBackendTex = genTexture->getBackendTexture();
if (GrBackendApi::kOpenGL == genBackendTex.backend()) {
#ifdef SK_GL
GrGLTextureInfo genTexInfo;
GrGLTextureInfo origTexInfo;
if (GrBackendTextures::GetGLTextureInfo(genBackendTex, &genTexInfo) &&
GrBackendTextures::GetGLTextureInfo(backendTex, &origTexInfo)) {
if (requestMipmapped == Mipmapped::kYes && betMipmapped == Mipmapped::kNo) {
REPORTER_ASSERT(reporter, origTexInfo.fID != genTexInfo.fID);
} else {
REPORTER_ASSERT(reporter, origTexInfo.fID == genTexInfo.fID);
}
} else {
ERRORF(reporter, "Failed to get GrGLTextureInfo");
}
#endif
#ifdef SK_VULKAN
} else if (GrBackendApi::kVulkan == genBackendTex.backend()) {
GrVkImageInfo genImageInfo;
GrVkImageInfo origImageInfo;
if (GrBackendTextures::GetVkImageInfo(genBackendTex, &genImageInfo) &&
GrBackendTextures::GetVkImageInfo(backendTex, &origImageInfo)) {
if (requestMipmapped == Mipmapped::kYes && betMipmapped == Mipmapped::kNo) {
REPORTER_ASSERT(reporter, origImageInfo.fImage != genImageInfo.fImage);
} else {
REPORTER_ASSERT(reporter, origImageInfo.fImage == genImageInfo.fImage);
}
} else {
ERRORF(reporter, "Failed to get GrVkImageInfo");
}
#endif
#ifdef SK_METAL
} else if (GrBackendApi::kMetal == genBackendTex.backend()) {
GrMtlTextureInfo genImageInfo;
GrMtlTextureInfo origImageInfo;
if (GrBackendTextures::GetMtlTextureInfo(genBackendTex, &genImageInfo) &&
GrBackendTextures::GetMtlTextureInfo(backendTex, &origImageInfo)) {
if (requestMipmapped == Mipmapped::kYes && betMipmapped == Mipmapped::kNo) {
REPORTER_ASSERT(reporter, origImageInfo.fTexture != genImageInfo.fTexture);
} else {
REPORTER_ASSERT(reporter, origImageInfo.fTexture == genImageInfo.fTexture);
}
} else {
ERRORF(reporter, "Failed to get GrMtlTextureInfo");
}
#endif
#ifdef SK_DIRECT3D
} else if (GrBackendApi::kDirect3D == genBackendTex.backend()) {
GrD3DTextureResourceInfo genImageInfo;
GrD3DTextureResourceInfo origImageInfo;
if (genBackendTex.getD3DTextureResourceInfo(&genImageInfo) &&
backendTex.getD3DTextureResourceInfo(&origImageInfo)) {
if (requestMipmapped == Mipmapped::kYes && betMipmapped == Mipmapped::kNo) {
REPORTER_ASSERT(reporter,
origImageInfo.fResource != genImageInfo.fResource);
} else {
REPORTER_ASSERT(reporter,
origImageInfo.fResource == genImageInfo.fResource);
}
} else {
ERRORF(reporter, "Failed to get GrMtlTextureInfo");
}
#endif
} else {
REPORTER_ASSERT(reporter, false);
}
}
}
}
DEF_GANESH_TEST_FOR_RENDERING_CONTEXTS(GrImageSnapshotMipMappedTest,
reporter,
ctxInfo,
CtsEnforcement::kApiLevel_T) {
auto dContext = ctxInfo.directContext();
if (!dContext->priv().caps()->mipmapSupport()) {
return;
}
GrProtected isProtected = GrProtected(dContext->priv().caps()->supportsProtectedContent());
auto resourceProvider = dContext->priv().resourceProvider();
for (auto willUseMips : {false, true}) {
for (auto isWrapped : {false, true}) {
skgpu::Mipmapped mipmapped =
willUseMips ? skgpu::Mipmapped::kYes : skgpu::Mipmapped::kNo;
sk_sp<SkSurface> surface;
SkImageInfo info =
SkImageInfo::Make(kSize, kSize, kRGBA_8888_SkColorType, kPremul_SkAlphaType);
if (isWrapped) {
surface = sk_gpu_test::MakeBackendTextureSurface(dContext,
info,
kTopLeft_GrSurfaceOrigin,
1,
mipmapped,
isProtected);
} else {
surface = SkSurfaces::RenderTarget(dContext,
skgpu::Budgeted::kYes,
info,
1,
kTopLeft_GrSurfaceOrigin,
nullptr,
willUseMips);
}
REPORTER_ASSERT(reporter, surface);
auto device = ((SkSurface_Ganesh*)surface.get())->getDevice();
GrTextureProxy* texProxy = device->readSurfaceView().asTextureProxy();
REPORTER_ASSERT(reporter, mipmapped == texProxy->mipmapped());
texProxy->instantiate(resourceProvider);
GrTexture* texture = texProxy->peekTexture();
REPORTER_ASSERT(reporter, mipmapped == texture->mipmapped());
sk_sp<SkImage> image = surface->makeImageSnapshot();
REPORTER_ASSERT(reporter, willUseMips == image->hasMipmaps());
REPORTER_ASSERT(reporter, image);
texProxy = sk_gpu_test::GetTextureImageProxy(image.get(), dContext);
REPORTER_ASSERT(reporter, mipmapped == texProxy->mipmapped());
texProxy->instantiate(resourceProvider);
texture = texProxy->peekTexture();
REPORTER_ASSERT(reporter, mipmapped == texture->mipmapped());
}
}
}
DEF_GANESH_TEST_FOR_RENDERING_CONTEXTS(Gr1x1TextureMipMappedTest,
reporter,
ctxInfo,
CtsEnforcement::kApiLevel_T) {
auto dContext = ctxInfo.directContext();
if (!dContext->priv().caps()->mipmapSupport()) {
return;
}
SkImageInfo info = SkImageInfo::MakeN32(16, 16, kPremul_SkAlphaType);
sk_sp<SkSurface> surface = SkSurfaces::RenderTarget(dContext, skgpu::Budgeted::kNo, info);
SkBitmap bmp;
bmp.allocN32Pixels(1, 1);
SkPMColor* pixel = reinterpret_cast<SkPMColor*>(bmp.getPixels());
*pixel = 0;
sk_sp<SkImage> bmpImage = bmp.asImage();
surface->getCanvas()->scale(0.5f, 0.5f);
surface->getCanvas()->drawImage(bmpImage, 0, 0);
dContext->flushAndSubmit(surface.get(), GrSyncCpu::kNo);
surface->getCanvas()->drawImage(bmpImage, 0, 0, SkSamplingOptions({1.0f/3, 1.0f/3}));
dContext->flushAndSubmit(surface.get(), GrSyncCpu::kNo);
}
static std::unique_ptr<skgpu::ganesh::SurfaceDrawContext> draw_mipmap_into_new_render_target(
GrRecordingContext* rContext,
GrColorType colorType,
SkAlphaType alphaType,
GrSurfaceProxyView mipmapView,
GrSamplerState::MipmapMode mm) {
auto proxyProvider = rContext->priv().proxyProvider();
sk_sp<GrSurfaceProxy> renderTarget =
proxyProvider->createProxy(mipmapView.proxy()->backendFormat(),
{1, 1},
GrRenderable::kYes,
1,
skgpu::Mipmapped::kNo,
SkBackingFit::kApprox,
skgpu::Budgeted::kYes,
GrProtected::kNo,
"DrawMipMapViewTest");
auto sdc = skgpu::ganesh::SurfaceDrawContext::Make(rContext,
colorType,
std::move(renderTarget),
nullptr,
kTopLeft_GrSurfaceOrigin,
SkSurfaceProps());
sdc->drawTexture(nullptr,
std::move(mipmapView),
alphaType,
GrSamplerState::Filter::kLinear,
mm,
SkBlendMode::kSrcOver,
{1, 1, 1, 1},
SkRect::MakeWH(4, 4),
SkRect::MakeWH(1, 1),
GrQuadAAFlags::kAll,
SkCanvas::kFast_SrcRectConstraint,
SkMatrix::I(),
nullptr);
return sdc;
}
DEF_GANESH_TEST(GrManyDependentsMipMappedTest,
reporter,
,
CtsEnforcement::kApiLevel_T) {
using Enable = GrContextOptions::Enable;
using MipmapMode = GrSamplerState::MipmapMode;
for (auto enableSortingAndReduction : {Enable::kYes, Enable::kNo}) {
GrMockOptions mockOptions;
mockOptions.fMipmapSupport = true;
GrContextOptions ctxOptions;
ctxOptions.fReduceOpsTaskSplitting = enableSortingAndReduction;
sk_sp<GrDirectContext> dContext = GrDirectContext::MakeMock(&mockOptions, ctxOptions);
GrDrawingManager* drawingManager = dContext->priv().drawingManager();
if (!dContext) {
ERRORF(reporter, "could not create mock dContext with fReduceOpsTaskSplitting %s.",
(Enable::kYes == enableSortingAndReduction) ? "enabled" : "disabled");
continue;
}
SkASSERT(dContext->priv().caps()->mipmapSupport());
GrBackendFormat format = dContext->defaultBackendFormat(
kRGBA_8888_SkColorType, GrRenderable::kYes);
GrColorType colorType = GrColorType::kRGBA_8888;
SkAlphaType alphaType = kPremul_SkAlphaType;
GrProxyProvider* proxyProvider = dContext->priv().proxyProvider();
sk_sp<GrTextureProxy> mipmapProxy =
proxyProvider->createProxy(format,
{4, 4},
GrRenderable::kYes,
1,
skgpu::Mipmapped::kYes,
SkBackingFit::kExact,
skgpu::Budgeted::kYes,
GrProtected::kNo,
"ManyDependentsMipMappedTest");
mipmapProxy->markMipmapsClean();
auto mipmapSDC = skgpu::ganesh::SurfaceDrawContext::Make(dContext.get(),
colorType,
mipmapProxy,
nullptr,
kTopLeft_GrSurfaceOrigin,
SkSurfaceProps());
mipmapSDC->clear(SkPMColor4f{.1f, .2f, .3f, .4f});
REPORTER_ASSERT(reporter, drawingManager->getLastRenderTask(mipmapProxy.get()));
REPORTER_ASSERT(reporter,
mipmapSDC->testingOnly_PeekLastOpsTask() ==
drawingManager->getLastRenderTask(mipmapProxy.get()));
REPORTER_ASSERT(reporter, !mipmapProxy->mipmapsAreDirty());
skgpu::Swizzle swizzle = dContext->priv().caps()->getReadSwizzle(format, colorType);
GrSurfaceProxyView mipmapView(mipmapProxy, kTopLeft_GrSurfaceOrigin, swizzle);
auto sdc1 = draw_mipmap_into_new_render_target(dContext.get(), colorType, alphaType,
mipmapView, MipmapMode::kLinear);
auto sdc1Task = sk_ref_sp(sdc1->testingOnly_PeekLastOpsTask());
GrRenderTask* initialMipmapRegenTask = drawingManager->getLastRenderTask(mipmapProxy.get());
REPORTER_ASSERT(reporter, initialMipmapRegenTask);
REPORTER_ASSERT(reporter,
initialMipmapRegenTask != mipmapSDC->testingOnly_PeekLastOpsTask());
REPORTER_ASSERT(reporter, !mipmapProxy->mipmapsAreDirty());
auto sdc2 = draw_mipmap_into_new_render_target(dContext.get(), colorType, alphaType,
mipmapView, MipmapMode::kLinear);
auto sdc2Task = sk_ref_sp(sdc2->testingOnly_PeekLastOpsTask());
REPORTER_ASSERT(reporter,
drawingManager->getLastRenderTask(mipmapProxy.get()) == initialMipmapRegenTask);
SkASSERT(!mipmapProxy->mipmapsAreDirty());
dContext->flushAndSubmit();
REPORTER_ASSERT(reporter, sdc1Task->dependsOn(initialMipmapRegenTask));
REPORTER_ASSERT(reporter, sdc2Task->dependsOn(initialMipmapRegenTask));
mipmapSDC->clear(SkPMColor4f{.1f, .2f, .3f, .4f});
auto mipmapRTCTask = sk_ref_sp(mipmapSDC->testingOnly_PeekLastOpsTask());
REPORTER_ASSERT(reporter, mipmapRTCTask);
REPORTER_ASSERT(reporter,
mipmapRTCTask.get() == drawingManager->getLastRenderTask(mipmapProxy.get()));
REPORTER_ASSERT(reporter, !mipmapProxy->mipmapsAreDirty());
sdc1 = draw_mipmap_into_new_render_target(dContext.get(), colorType, alphaType,
mipmapView, MipmapMode::kNone);
REPORTER_ASSERT(reporter, mipmapProxy->mipmapsAreDirty());
REPORTER_ASSERT(reporter,
mipmapRTCTask.get() == drawingManager->getLastRenderTask(mipmapProxy.get()));
sdc2 = draw_mipmap_into_new_render_target(dContext.get(), colorType, alphaType,
std::move(mipmapView), MipmapMode::kLinear);
sdc2Task = sk_ref_sp(sdc2->testingOnly_PeekLastOpsTask());
auto mipRegenTask2 = drawingManager->getLastRenderTask(mipmapProxy.get());
REPORTER_ASSERT(reporter, mipRegenTask2);
REPORTER_ASSERT(reporter, mipmapRTCTask.get() != mipRegenTask2);
SkASSERT(!mipmapProxy->mipmapsAreDirty());
dContext->flushAndSubmit();
REPORTER_ASSERT(reporter, sdc2Task->dependsOn(mipRegenTask2));
}
}