[graphite] Reject creation of too-large TextureProxies

Bug: 1464023
Change-Id: I3087c81acd5947d33f000300975015813ab39340
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/723816
Reviewed-by: John Stiles <johnstiles@google.com>
Reviewed-by: Jim Van Verth <jvanverth@google.com>
Reviewed-by: Michael Ludwig <michaelludwig@google.com>
Commit-Queue: James Godfrey-Kittle <jamesgk@google.com>
diff --git a/src/gpu/graphite/DrawAtlas.cpp b/src/gpu/graphite/DrawAtlas.cpp
index f0e52f9..6750967 100644
--- a/src/gpu/graphite/DrawAtlas.cpp
+++ b/src/gpu/graphite/DrawAtlas.cpp
@@ -473,13 +473,13 @@
     SkASSERT(fNumActivePages < this->maxPages());
     SkASSERT(!fProxies[fNumActivePages]);
 
-    auto textureInfo = recorder->priv().caps()->getDefaultSampledTextureInfo(
-            fColorType,
-            /*mipmapped=*/Mipmapped::kNo,
-            Protected::kNo,
-            Renderable::kNo);
-    fProxies[fNumActivePages].reset(
-            new TextureProxy({fTextureWidth, fTextureHeight}, textureInfo, skgpu::Budgeted::kYes));
+    const Caps* caps = recorder->priv().caps();
+    auto textureInfo = caps->getDefaultSampledTextureInfo(fColorType,
+                                                          /*mipmapped=*/Mipmapped::kNo,
+                                                          Protected::kNo,
+                                                          Renderable::kNo);
+    fProxies[fNumActivePages] = TextureProxy::Make(
+            caps, {fTextureWidth, fTextureHeight}, textureInfo, skgpu::Budgeted::kYes);
     if (!fProxies[fNumActivePages]) {
         return false;
     }
diff --git a/src/gpu/graphite/ImageFactories.cpp b/src/gpu/graphite/ImageFactories.cpp
index 7f97670..24e2688 100644
--- a/src/gpu/graphite/ImageFactories.cpp
+++ b/src/gpu/graphite/ImageFactories.cpp
@@ -87,7 +87,8 @@
     }
     texture->setReleaseCallback(std::move(releaseHelper));
 
-    sk_sp<TextureProxy> proxy(new TextureProxy(std::move(texture)));
+    sk_sp<TextureProxy> proxy = TextureProxy::Wrap(std::move(texture));
+    SkASSERT(proxy);
 
     skgpu::Swizzle swizzle = caps->getReadSwizzle(ct, backendTex.info());
     TextureProxyView view(std::move(proxy), swizzle);
@@ -126,7 +127,8 @@
         return nullptr;
     }
 
-    sk_sp<TextureProxy> proxy = Image::MakePromiseImageLazyProxy(dimensions,
+    sk_sp<TextureProxy> proxy = Image::MakePromiseImageLazyProxy(caps,
+                                                                 dimensions,
                                                                  textureInfo,
                                                                  isVolatile,
                                                                  fulfillProc,
@@ -180,7 +182,8 @@
     // Make a lazy proxy for each plane
     sk_sp<TextureProxy> proxies[4];
     for (int p = 0; p < numPlanes; ++p) {
-        proxies[p] = Image_YUVA::MakePromiseImageLazyProxy(planeDimensions[p],
+        proxies[p] = Image_YUVA::MakePromiseImageLazyProxy(recorder->priv().caps(),
+                                                           planeDimensions[p],
                                                            backendTextureInfo.planeTextureInfo(p),
                                                            isVolatile,
                                                            fulfillProc,
@@ -399,7 +402,8 @@
         }
         texture->setReleaseCallback(releaseHelper);
 
-        sk_sp<TextureProxy> proxy(new TextureProxy(std::move(texture)));
+        sk_sp<TextureProxy> proxy = TextureProxy::Wrap(std::move(texture));
+        SkASSERT(proxy);
         textureProxyViews[plane] = TextureProxyView(std::move(proxy));
     }
     YUVATextureProxies yuvaProxies(recorder,
diff --git a/src/gpu/graphite/Image_Graphite.cpp b/src/gpu/graphite/Image_Graphite.cpp
index 686262d..d8a81fe 100644
--- a/src/gpu/graphite/Image_Graphite.cpp
+++ b/src/gpu/graphite/Image_Graphite.cpp
@@ -109,6 +109,7 @@
 using SkImages::GraphitePromiseTextureReleaseProc;
 
 sk_sp<TextureProxy> Image::MakePromiseImageLazyProxy(
+        const Caps* caps,
         SkISize dimensions,
         TextureInfo textureInfo,
         Volatile isVolatile,
@@ -175,7 +176,8 @@
 
     } callback(fulfillProc, std::move(releaseHelper), textureReleaseProc);
 
-    return TextureProxy::MakeLazy(dimensions,
+    return TextureProxy::MakeLazy(caps,
+                                  dimensions,
                                   textureInfo,
                                   skgpu::Budgeted::kNo,  // This is destined for a user's SkImage
                                   isVolatile,
diff --git a/src/gpu/graphite/Image_Graphite.h b/src/gpu/graphite/Image_Graphite.h
index 7303052..c38c10b 100644
--- a/src/gpu/graphite/Image_Graphite.h
+++ b/src/gpu/graphite/Image_Graphite.h
@@ -38,6 +38,7 @@
     TextureProxyView textureProxyView() const { return fTextureProxyView; }
 
     static sk_sp<TextureProxy> MakePromiseImageLazyProxy(
+            const Caps*,
             SkISize dimensions,
             TextureInfo,
             Volatile,
diff --git a/src/gpu/graphite/Image_YUVA_Graphite.cpp b/src/gpu/graphite/Image_YUVA_Graphite.cpp
index 98b5133..24491a0 100644
--- a/src/gpu/graphite/Image_YUVA_Graphite.cpp
+++ b/src/gpu/graphite/Image_YUVA_Graphite.cpp
@@ -58,6 +58,7 @@
 using SkImages::GraphitePromiseTextureReleaseProc;
 
 sk_sp<TextureProxy> Image_YUVA::MakePromiseImageLazyProxy(
+        const Caps* caps,
         SkISize dimensions,
         TextureInfo textureInfo,
         Volatile isVolatile,
@@ -128,7 +129,8 @@
 
     } callback(fulfillProc, std::move(releaseHelper), textureContext, textureReleaseProc);
 
-    return TextureProxy::MakeLazy(dimensions,
+    return TextureProxy::MakeLazy(caps,
+                                  dimensions,
                                   textureInfo,
                                   skgpu::Budgeted::kNo,  // This is destined for a user's SkImage
                                   isVolatile,
diff --git a/src/gpu/graphite/Image_YUVA_Graphite.h b/src/gpu/graphite/Image_YUVA_Graphite.h
index c6c551e..4ad685f 100644
--- a/src/gpu/graphite/Image_YUVA_Graphite.h
+++ b/src/gpu/graphite/Image_YUVA_Graphite.h
@@ -45,6 +45,7 @@
     }
 
     static sk_sp<TextureProxy> MakePromiseImageLazyProxy(
+            const Caps*,
             SkISize dimensions,
             TextureInfo,
             Volatile,
diff --git a/src/gpu/graphite/Recorder.cpp b/src/gpu/graphite/Recorder.cpp
index d71ae6f..b660c8b 100644
--- a/src/gpu/graphite/Recorder.cpp
+++ b/src/gpu/graphite/Recorder.cpp
@@ -287,7 +287,7 @@
         return false;
     }
 
-    sk_sp<TextureProxy> proxy(new TextureProxy(std::move(texture)));
+    sk_sp<TextureProxy> proxy = TextureProxy::Wrap(std::move(texture));
 
     std::vector<MipLevel> mipLevels;
     mipLevels.resize(numLevels);
diff --git a/src/gpu/graphite/Surface_Graphite.cpp b/src/gpu/graphite/Surface_Graphite.cpp
index 02c0f75..94e7a7e 100644
--- a/src/gpu/graphite/Surface_Graphite.cpp
+++ b/src/gpu/graphite/Surface_Graphite.cpp
@@ -243,7 +243,7 @@
         return nullptr;
     }
 
-    sk_sp<TextureProxy> proxy(new TextureProxy(std::move(texture)));
+    sk_sp<TextureProxy> proxy = TextureProxy::Wrap(std::move(texture));
 
     sk_sp<Device> device = Device::Make(recorder,
                                         std::move(proxy),
diff --git a/src/gpu/graphite/TextureProxy.cpp b/src/gpu/graphite/TextureProxy.cpp
index 8fb460c..3794578 100644
--- a/src/gpu/graphite/TextureProxy.cpp
+++ b/src/gpu/graphite/TextureProxy.cpp
@@ -122,32 +122,45 @@
 
 sk_sp<TextureProxy> TextureProxy::Make(const Caps* caps,
                                        SkISize dimensions,
+                                       const TextureInfo& textureInfo,
+                                       skgpu::Budgeted budgeted) {
+    if (dimensions.width() < 1 || dimensions.height() < 1 ||
+        dimensions.width() > caps->maxTextureSize() ||
+        dimensions.height() > caps->maxTextureSize() ||
+        !textureInfo.isValid()) {
+        return nullptr;
+    }
+
+    return sk_sp<TextureProxy>(new TextureProxy(dimensions, textureInfo, budgeted));
+}
+
+sk_sp<TextureProxy> TextureProxy::Make(const Caps* caps,
+                                       SkISize dimensions,
                                        SkColorType colorType,
                                        Mipmapped mipmapped,
                                        Protected isProtected,
                                        Renderable renderable,
                                        skgpu::Budgeted budgeted) {
-    if (dimensions.width() < 1 || dimensions.height() < 1) {
-        return nullptr;
-    }
-
     TextureInfo textureInfo = caps->getDefaultSampledTextureInfo(colorType,
                                                                  mipmapped,
                                                                  isProtected,
                                                                  renderable);
-    if (!textureInfo.isValid()) {
-        return nullptr;
-    }
 
-    return sk_make_sp<TextureProxy>(dimensions, textureInfo, budgeted);
+    return Make(caps, dimensions, textureInfo, budgeted);
 }
 
-sk_sp<TextureProxy> TextureProxy::MakeLazy(SkISize dimensions,
+sk_sp<TextureProxy> TextureProxy::MakeLazy(const Caps* caps,
+                                           SkISize dimensions,
                                            const TextureInfo& textureInfo,
                                            skgpu::Budgeted budgeted,
                                            Volatile isVolatile,
                                            LazyInstantiateCallback&& callback) {
     SkASSERT(textureInfo.isValid());
+    if (dimensions.width() < 1 || dimensions.height() < 1 ||
+        dimensions.width() > caps->maxTextureSize() ||
+        dimensions.height() > caps->maxTextureSize()) {
+        return nullptr;
+    }
 
     return sk_sp<TextureProxy>(new TextureProxy(dimensions, textureInfo, budgeted,
                                                 isVolatile, std::move(callback)));
@@ -157,23 +170,23 @@
                                                 skgpu::Budgeted budgeted,
                                                 Volatile isVolatile,
                                                 LazyInstantiateCallback&& callback) {
-    return MakeLazy(SkISize::Make(-1, -1), textureInfo, budgeted, isVolatile, std::move(callback));
+    SkASSERT(textureInfo.isValid());
+
+    return sk_sp<TextureProxy>(new TextureProxy(
+            SkISize::Make(-1, -1), textureInfo, budgeted, isVolatile, std::move(callback)));
 }
 
 sk_sp<TextureProxy> TextureProxy::MakeStorage(const Caps* caps,
                                               SkISize dimensions,
                                               SkColorType colorType,
                                               skgpu::Budgeted budgeted) {
-    if (dimensions.width() < 1 || dimensions.height() < 1) {
-        return nullptr;
-    }
-
     TextureInfo textureInfo = caps->getDefaultStorageTextureInfo(colorType);
-    if (!textureInfo.isValid()) {
-        return nullptr;
-    }
 
-    return sk_make_sp<TextureProxy>(dimensions, textureInfo, budgeted);
+    return Make(caps, dimensions, textureInfo, budgeted);
+}
+
+sk_sp<TextureProxy> TextureProxy::Wrap(sk_sp<Texture> texture) {
+    return sk_sp<TextureProxy>(new TextureProxy(std::move(texture)));
 }
 
 #ifdef SK_DEBUG
diff --git a/src/gpu/graphite/TextureProxy.h b/src/gpu/graphite/TextureProxy.h
index 7f81cf2..7d4422a 100644
--- a/src/gpu/graphite/TextureProxy.h
+++ b/src/gpu/graphite/TextureProxy.h
@@ -25,9 +25,6 @@
 
 class TextureProxy : public SkRefCnt {
 public:
-    TextureProxy(SkISize dimensions, const TextureInfo& info, skgpu::Budgeted budgeted);
-    TextureProxy(sk_sp<Texture>);
-
     TextureProxy() = delete;
 
     ~TextureProxy() override;
@@ -71,10 +68,15 @@
                                     Protected,
                                     Renderable,
                                     skgpu::Budgeted);
+    static sk_sp<TextureProxy> Make(const Caps*,
+                                    SkISize dimensions,
+                                    const TextureInfo&,
+                                    skgpu::Budgeted);
 
     using LazyInstantiateCallback = std::function<sk_sp<Texture> (ResourceProvider*)>;
 
-    static sk_sp<TextureProxy> MakeLazy(SkISize dimensions,
+    static sk_sp<TextureProxy> MakeLazy(const Caps*,
+                                        SkISize dimensions,
                                         const TextureInfo&,
                                         skgpu::Budgeted,
                                         Volatile,
@@ -89,12 +91,16 @@
                                            SkColorType,
                                            skgpu::Budgeted);
 
+    static sk_sp<TextureProxy> Wrap(sk_sp<Texture>);
+
 private:
+    TextureProxy(SkISize dimensions, const TextureInfo& info, skgpu::Budgeted budgeted);
     TextureProxy(SkISize dimensions,
                  const TextureInfo&,
                  skgpu::Budgeted,
                  Volatile,
                  LazyInstantiateCallback&&);
+    TextureProxy(sk_sp<Texture>);
 
 #ifdef SK_DEBUG
     void validateTexture(const Texture*);
diff --git a/src/gpu/graphite/TextureUtils.cpp b/src/gpu/graphite/TextureUtils.cpp
index 82db25c..65b19a8 100644
--- a/src/gpu/graphite/TextureUtils.cpp
+++ b/src/gpu/graphite/TextureUtils.cpp
@@ -101,7 +101,8 @@
     }
 
     // Create proxy
-    sk_sp<TextureProxy> proxy(new TextureProxy(bmpToUpload.dimensions(), textureInfo, budgeted));
+    sk_sp<TextureProxy> proxy =
+            TextureProxy::Make(caps, bmpToUpload.dimensions(), textureInfo, budgeted);
     if (!proxy) {
         return {};
     }
diff --git a/tests/graphite/TextureProxyTest.cpp b/tests/graphite/TextureProxyTest.cpp
index b2b6363..a6c7904 100644
--- a/tests/graphite/TextureProxyTest.cpp
+++ b/tests/graphite/TextureProxyTest.cpp
@@ -7,9 +7,14 @@
 
 #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"
@@ -87,7 +92,7 @@
 
     // Lazy, non-volatile TextureProxy, unsuccessful instantiation.
     textureProxy = TextureProxy::MakeLazy(
-            kValidSize, textureInfo, skgpu::Budgeted::kNo, Volatile::kNo, nullCallback);
+            caps, kValidSize, textureInfo, skgpu::Budgeted::kNo, Volatile::kNo, nullCallback);
     REPORTER_ASSERT(reporter, textureProxy->isLazy());
     REPORTER_ASSERT(reporter, !textureProxy->isFullyLazy());
     REPORTER_ASSERT(reporter, !textureProxy->isVolatile());
@@ -98,7 +103,7 @@
 
     // Lazy, non-volatile TextureProxy, successful instantiation.
     textureProxy = TextureProxy::MakeLazy(
-            kValidSize, textureInfo, skgpu::Budgeted::kNo, Volatile::kNo, callback);
+            caps, kValidSize, textureInfo, skgpu::Budgeted::kNo, Volatile::kNo, callback);
 
     instantiateSuccess = textureProxy->lazyInstantiate(resourceProvider);
     REPORTER_ASSERT(reporter, instantiateSuccess);
@@ -106,7 +111,7 @@
 
     // Lazy, volatile TextureProxy, unsuccessful instantiation.
     textureProxy = TextureProxy::MakeLazy(
-            kValidSize, textureInfo, skgpu::Budgeted::kNo, Volatile::kYes, nullCallback);
+            caps, kValidSize, textureInfo, skgpu::Budgeted::kNo, Volatile::kYes, nullCallback);
     REPORTER_ASSERT(reporter, textureProxy->isLazy());
     REPORTER_ASSERT(reporter, !textureProxy->isFullyLazy());
     REPORTER_ASSERT(reporter, textureProxy->isVolatile());
@@ -117,7 +122,7 @@
 
     // Lazy, volatile TextureProxy, successful instantiation.
     textureProxy = TextureProxy::MakeLazy(
-            kValidSize, textureInfo, skgpu::Budgeted::kNo, Volatile::kYes, callback);
+            caps, kValidSize, textureInfo, skgpu::Budgeted::kNo, Volatile::kYes, callback);
 
     instantiateSuccess = textureProxy->lazyInstantiate(resourceProvider);
     REPORTER_ASSERT(reporter, instantiateSuccess);
@@ -164,9 +169,82 @@
     REPORTER_ASSERT(reporter, instantiateSuccess);
 
     textureProxy = TextureProxy::MakeLazy(
-            kValidSize, textureInfo, skgpu::Budgeted::kNo, Volatile::kNo, nullCallback);
+            caps, kValidSize, textureInfo, skgpu::Budgeted::kNo, Volatile::kNo, nullCallback);
     instantiateSuccess = TextureProxy::InstantiateIfNotLazy(resourceProvider, textureProxy.get());
     REPORTER_ASSERT(reporter, instantiateSuccess);
 }
 
+DEF_GRAPHITE_TEST_FOR_ALL_CONTEXTS(GraphiteTextureTooLargeTest, reporter, context) {
+    std::unique_ptr<Recorder> 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<SkImage> rasterImage = SkImages::RasterFromBitmap(bitmap);
+    sk_sp<SkImage> 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) {
+    class FulfillContext {
+    public:
+        FulfillContext(BackendTexture backendTexture) : fBackendTexture(backendTexture) {}
+
+        static std::tuple<BackendTexture, void*> Fulfill(void* ctx) {
+            FulfillContext* self = reinterpret_cast<FulfillContext*>(ctx);
+            return {self->fBackendTexture, nullptr};
+        }
+
+        BackendTexture fBackendTexture;
+    };
+
+    std::unique_ptr<Recorder> 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<SkSurface> surface = SkSurfaces::RenderTarget(recorder.get(), surfaceImageInfo);
+        sk_sp<SkImage> 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> recording = recorder->snap();
+        REPORTER_ASSERT(reporter, context->insertRecording({recording.get()}));
+    }
+}
+
 }  // namespace skgpu::graphite