/* * Copyright 2022 Google LLC * * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ #include "tests/Test.h" #include "include/core/SkBitmap.h" #include "include/core/SkCanvas.h" #include "include/core/SkColorSpace.h" #include "include/gpu/graphite/BackendTexture.h" #include "include/gpu/graphite/Context.h" #include "include/gpu/graphite/Image.h" #include "include/gpu/graphite/Recorder.h" #include "include/gpu/graphite/Surface.h" #include "src/gpu/graphite/Caps.h" #include "src/gpu/graphite/ContextPriv.h" #include "src/gpu/graphite/RecorderPriv.h" #include "src/gpu/graphite/ResourceProvider.h" #include "src/gpu/graphite/Texture.h" #include "src/gpu/graphite/TextureProxy.h" namespace skgpu::graphite { DEF_GRAPHITE_TEST_FOR_ALL_CONTEXTS(GraphiteTextureProxyTest, reporter, context, CtsEnforcement::kApiLevel_V) { const Caps* caps = context->priv().caps(); constexpr SkISize kValidSize = SkISize::Make(1, 1); constexpr SkISize kInvalidSize = SkISize::MakeEmpty(); constexpr SkColorType kValidColorType = kRGBA_8888_SkColorType; constexpr SkColorType kInvalidColorType = kUnknown_SkColorType; Protected isProtected = Protected(caps->protectedSupport()); std::unique_ptr recorder = context->makeRecorder(); ResourceProvider* resourceProvider = recorder->priv().resourceProvider(); const TextureInfo textureInfo = caps->getDefaultSampledTextureInfo( kValidColorType, Mipmapped::kNo, isProtected, Renderable::kNo); BackendTexture backendTexture = recorder->createBackendTexture(kValidSize, textureInfo); sk_sp texture = resourceProvider->createWrappedTexture(backendTexture, "TextureProxyTestWrappedTex"); auto makeProxy = [&](SkISize dimensions, SkColorType colorType, Mipmapped mipmapped, Protected isProtected, Renderable renderable, Budgeted budgeted) { auto textureInfo = caps->getDefaultSampledTextureInfo(colorType, mipmapped, isProtected, renderable); return TextureProxy::Make(caps, recorder->priv().resourceProvider(), dimensions, textureInfo, "TextureProxyTestTexture", budgeted); }; auto nullCallback = [](ResourceProvider*) -> sk_sp { return nullptr; }; auto callback = [texture](ResourceProvider*) -> sk_sp { return texture; }; // Assign to assignableTexture before instantiating with this callback. sk_sp assignableTexture; auto assignableCallback = [&assignableTexture](ResourceProvider*) -> sk_sp { return assignableTexture; }; // Invalid parameters. sk_sp textureProxy; textureProxy = makeProxy(kInvalidSize, kValidColorType, Mipmapped::kNo, isProtected, Renderable::kNo, skgpu::Budgeted::kNo); REPORTER_ASSERT(reporter, textureProxy == nullptr); textureProxy = makeProxy(kValidSize, kInvalidColorType, Mipmapped::kNo, isProtected, Renderable::kNo, skgpu::Budgeted::kNo); REPORTER_ASSERT(reporter, textureProxy == nullptr); // Non-budgeted, non-lazy TextureProxy is instantiated on return textureProxy = makeProxy(kValidSize, kValidColorType, Mipmapped::kNo, isProtected, Renderable::kNo, skgpu::Budgeted::kNo); REPORTER_ASSERT(reporter, !textureProxy->isLazy()); REPORTER_ASSERT(reporter, !textureProxy->isFullyLazy()); REPORTER_ASSERT(reporter, !textureProxy->isVolatile()); REPORTER_ASSERT(reporter, textureProxy->isInstantiated()); REPORTER_ASSERT(reporter, textureProxy->dimensions() == kValidSize); // Budgeted, non-lazy TextureProxy, successful instantiation later on textureProxy = makeProxy(kValidSize, kValidColorType, Mipmapped::kNo, isProtected, Renderable::kNo, skgpu::Budgeted::kYes); REPORTER_ASSERT(reporter, !textureProxy->isLazy()); REPORTER_ASSERT(reporter, !textureProxy->isFullyLazy()); REPORTER_ASSERT(reporter, !textureProxy->isVolatile()); REPORTER_ASSERT(reporter, !textureProxy->isInstantiated()); REPORTER_ASSERT(reporter, textureProxy->dimensions() == kValidSize); bool instantiateSuccess = textureProxy->instantiate(resourceProvider); REPORTER_ASSERT(reporter, instantiateSuccess); REPORTER_ASSERT(reporter, textureProxy->isInstantiated()); REPORTER_ASSERT(reporter, textureProxy->dimensions() == kValidSize); const Texture* createdTexture = textureProxy->texture(); instantiateSuccess = textureProxy->instantiate(resourceProvider); REPORTER_ASSERT(reporter, instantiateSuccess); REPORTER_ASSERT(reporter, textureProxy->texture() == createdTexture); // Lazy, non-volatile TextureProxy, unsuccessful instantiation. textureProxy = TextureProxy::MakeLazy( caps, kValidSize, textureInfo, skgpu::Budgeted::kNo, Volatile::kNo, nullCallback); REPORTER_ASSERT(reporter, textureProxy->isLazy()); REPORTER_ASSERT(reporter, !textureProxy->isFullyLazy()); REPORTER_ASSERT(reporter, !textureProxy->isVolatile()); instantiateSuccess = textureProxy->lazyInstantiate(resourceProvider); REPORTER_ASSERT(reporter, !instantiateSuccess); REPORTER_ASSERT(reporter, !textureProxy->isInstantiated()); // Lazy, non-volatile TextureProxy, successful instantiation. textureProxy = TextureProxy::MakeLazy( caps, kValidSize, textureInfo, skgpu::Budgeted::kNo, Volatile::kNo, callback); instantiateSuccess = textureProxy->lazyInstantiate(resourceProvider); REPORTER_ASSERT(reporter, instantiateSuccess); REPORTER_ASSERT(reporter, textureProxy->texture() == texture.get()); // Lazy, volatile TextureProxy, unsuccessful instantiation. textureProxy = TextureProxy::MakeLazy( caps, kValidSize, textureInfo, skgpu::Budgeted::kNo, Volatile::kYes, nullCallback); REPORTER_ASSERT(reporter, textureProxy->isLazy()); REPORTER_ASSERT(reporter, !textureProxy->isFullyLazy()); REPORTER_ASSERT(reporter, textureProxy->isVolatile()); instantiateSuccess = textureProxy->lazyInstantiate(resourceProvider); REPORTER_ASSERT(reporter, !instantiateSuccess); REPORTER_ASSERT(reporter, !textureProxy->isInstantiated()); // Lazy, volatile TextureProxy, successful instantiation. textureProxy = TextureProxy::MakeLazy( caps, kValidSize, textureInfo, skgpu::Budgeted::kNo, Volatile::kYes, callback); instantiateSuccess = textureProxy->lazyInstantiate(resourceProvider); REPORTER_ASSERT(reporter, instantiateSuccess); REPORTER_ASSERT(reporter, textureProxy->texture() == texture.get()); textureProxy->deinstantiate(); REPORTER_ASSERT(reporter, !textureProxy->isInstantiated()); // Fully-lazy TextureProxy. textureProxy = TextureProxy::MakeFullyLazy( textureInfo, skgpu::Budgeted::kNo, Volatile::kYes, assignableCallback); REPORTER_ASSERT(reporter, textureProxy->isLazy()); REPORTER_ASSERT(reporter, textureProxy->isFullyLazy()); REPORTER_ASSERT(reporter, textureProxy->isVolatile()); assignableTexture = texture; instantiateSuccess = textureProxy->lazyInstantiate(resourceProvider); REPORTER_ASSERT(reporter, instantiateSuccess); REPORTER_ASSERT(reporter, textureProxy->isInstantiated()); REPORTER_ASSERT(reporter, textureProxy->isFullyLazy()); REPORTER_ASSERT(reporter, textureProxy->dimensions() == kValidSize); textureProxy->deinstantiate(); REPORTER_ASSERT(reporter, !textureProxy->isInstantiated()); REPORTER_ASSERT(reporter, textureProxy->isFullyLazy()); constexpr SkISize kLargerSize = SkISize::Make(2, 2); BackendTexture largerBackendTexture = recorder->createBackendTexture(kLargerSize, textureInfo); assignableTexture = resourceProvider->createWrappedTexture(largerBackendTexture, "TextureProxyTestWrappedTex"); instantiateSuccess = textureProxy->lazyInstantiate(resourceProvider); REPORTER_ASSERT(reporter, instantiateSuccess); REPORTER_ASSERT(reporter, textureProxy->dimensions() == kLargerSize); // InstantiateIfNotLazy tests. textureProxy = makeProxy(kValidSize, kValidColorType, Mipmapped::kNo, isProtected, Renderable::kNo, skgpu::Budgeted::kYes); instantiateSuccess = TextureProxy::InstantiateIfNotLazy(resourceProvider, textureProxy.get()); REPORTER_ASSERT(reporter, instantiateSuccess); textureProxy = TextureProxy::MakeLazy( caps, kValidSize, textureInfo, skgpu::Budgeted::kNo, Volatile::kNo, nullCallback); instantiateSuccess = TextureProxy::InstantiateIfNotLazy(resourceProvider, textureProxy.get()); REPORTER_ASSERT(reporter, instantiateSuccess); // Clean up the backend textures. recorder->deleteBackendTexture(backendTexture); recorder->deleteBackendTexture(largerBackendTexture); } DEF_GRAPHITE_TEST_FOR_ALL_CONTEXTS(GraphiteTextureTooLargeTest, reporter, context, CtsEnforcement::kApiLevel_V) { std::unique_ptr recorder = context->makeRecorder(); const Caps* caps = context->priv().caps(); // Try to create a texture that is too large for the backend. SkBitmap bitmap; SkISize dimensions = SkISize::Make(caps->maxTextureSize() + 1, 1); bitmap.allocPixels(SkImageInfo::Make( dimensions, SkColorType::kRGBA_8888_SkColorType, SkAlphaType::kPremul_SkAlphaType)); sk_sp rasterImage = SkImages::RasterFromBitmap(bitmap); sk_sp graphiteImage = SkImages::TextureFromImage(recorder.get(), rasterImage.get(), /*requiredProps=*/{}); // Image creation should have failed. REPORTER_ASSERT(reporter, !graphiteImage); // Snapping should still succeed, no texture upload should actually be attempted. REPORTER_ASSERT(reporter, recorder->snap()); } DEF_GRAPHITE_TEST_FOR_ALL_CONTEXTS(GraphiteLazyTextureInvalidDimensions, reporter, context, CtsEnforcement::kApiLevel_V) { class FulfillContext { public: FulfillContext(BackendTexture backendTexture) : fBackendTexture(backendTexture) {} static std::tuple Fulfill(void* ctx) { FulfillContext* self = reinterpret_cast(ctx); return {self->fBackendTexture, nullptr}; } BackendTexture fBackendTexture; }; std::unique_ptr recorder = context->makeRecorder(); const Caps* caps = context->priv().caps(); // Try to create textures with invalid dimensions. SkISize largeDimensions = SkISize::Make(caps->maxTextureSize() + 1, 1); SkISize negativeDimensions = SkISize::Make(-1, -1); for (const SkISize& dimensions : {largeDimensions, negativeDimensions}) { SkImageInfo imageInfo = SkImageInfo::Make( dimensions, SkColorType::kRGBA_8888_SkColorType, SkAlphaType::kPremul_SkAlphaType); TextureInfo textureInfo = caps->getDefaultSampledTextureInfo( imageInfo.colorInfo().colorType(), Mipmapped::kNo, Protected::kNo, Renderable::kNo); // The created BackendTexture should be invalid, so an invalid texture would be used to // fulfill the promise image created later, if we were to attempt to draw it. BackendTexture backendTexture = recorder->createBackendTexture(imageInfo.dimensions(), textureInfo); FulfillContext fulfillContext(backendTexture); REPORTER_ASSERT(reporter, !backendTexture.isValid()); // Drawing should still succeed, as no image draw should actually be attempted with this // texture. SkImageInfo surfaceImageInfo = SkImageInfo::Make( 1, 1, SkColorType::kRGBA_8888_SkColorType, SkAlphaType::kPremul_SkAlphaType); sk_sp surface = SkSurfaces::RenderTarget(recorder.get(), surfaceImageInfo); sk_sp promiseImage = SkImages::PromiseTextureFrom(recorder.get(), imageInfo.dimensions(), textureInfo, imageInfo.colorInfo(), Volatile::kNo, FulfillContext::Fulfill, nullptr, nullptr, &fulfillContext); surface->getCanvas()->drawImage(promiseImage, 0.0f, 0.0f); std::unique_ptr recording = recorder->snap(); REPORTER_ASSERT(reporter, context->insertRecording({recording.get()})); // Clean up backend texture context->deleteBackendTexture(fulfillContext.fBackendTexture); } } } // namespace skgpu::graphite