Add SkCapabilities object

This describes the capabilities of a particular Skia rendering context
(GPU context, or the CPU backend). At the moment, it only contains the
supported SkSL version (with a new enum added to specify the current
value as "100" and a new ES3 value as "300".

SkCapabilities can not be retrieved from an SkCanvas - the client must
have a concrete way of knowing what their destination device that will
do the actual rendering is (GrCaps or SkSurface).

This CL doesn't make use of the SkCapabilities yet, that's coming in
follow-up CLs that alter the SkSL compiler and SkRuntimeEffect API.

Bug: skia:11209
Change-Id: I4e9fd21ff7ffd79f1926c5c2eb34e10b3af4bc9b
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/537876
Reviewed-by: Michael Ludwig <michaelludwig@google.com>
Reviewed-by: Brian Salomon <bsalomon@google.com>
Commit-Queue: Brian Osman <brianosman@google.com>
Reviewed-by: John Stiles <johnstiles@google.com>
diff --git a/gm/runtimeintrinsics.cpp b/gm/runtimeintrinsics.cpp
index 5eb96b6..180c1ed 100644
--- a/gm/runtimeintrinsics.cpp
+++ b/gm/runtimeintrinsics.cpp
@@ -206,8 +206,8 @@
                            ctx, canvas, errorMsg,
                            columns_to_width(3),
                            rows_to_height(2)) {
-    if (!ctx->priv().caps()->shaderCaps()->supportsSkSLES3()) {
-        *errorMsg = "SkSL ES3 is not supported.";
+    if (ctx->priv().caps()->shaderCaps()->supportedSkSLVerion() < SkSL::Version::k300) {
+        *errorMsg = "SkSL 300 is not supported.";
         return skiagm::DrawResult::kSkip;
     }
 
@@ -317,8 +317,8 @@
                            ctx, canvas, errorMsg,
                            columns_to_width(6),
                            rows_to_height(5)) {
-    if (!ctx->priv().caps()->shaderCaps()->supportsSkSLES3()) {
-        *errorMsg = "SkSL ES3 is not supported.";
+    if (ctx->priv().caps()->shaderCaps()->supportedSkSLVerion() < SkSL::Version::k300) {
+        *errorMsg = "SkSL 300 is not supported.";
         return skiagm::DrawResult::kSkip;
     }
 
diff --git a/gn/core.gni b/gn/core.gni
index 762b4f3..009de96 100644
--- a/gn/core.gni
+++ b/gn/core.gni
@@ -14,6 +14,7 @@
   "$_include/core/SkBlender.h",
   "$_include/core/SkBlendMode.h",
   "$_include/core/SkBlurTypes.h",
+  "$_include/core/SkCapabilities.h",
   "$_include/core/SkCanvas.h",
   "$_include/core/SkCanvasVirtualEnforcer.h",
   "$_include/core/SkClipOp.h",
@@ -148,6 +149,7 @@
   "$_src/core/SkCanvas.cpp",
   "$_src/core/SkCanvasPriv.cpp",
   "$_src/core/SkCanvasPriv.h",
+  "$_src/core/SkCapabilities.cpp",
   "$_src/core/SkChromeRemoteGlyphCache.cpp",
   "$_src/core/SkClipStack.cpp",
   "$_src/core/SkClipStack.h",
diff --git a/gn/sksl.gni b/gn/sksl.gni
index b9b1c36..adce342 100644
--- a/gn/sksl.gni
+++ b/gn/sksl.gni
@@ -36,6 +36,7 @@
   "$_include/sksl/SkSLErrorReporter.h",
   "$_include/sksl/SkSLOperator.h",
   "$_include/sksl/SkSLPosition.h",
+  "$_include/sksl/SkSLVersion.h",
   "$_src/sksl/SkSLAnalysis.cpp",
   "$_src/sksl/SkSLAnalysis.h",
   "$_src/sksl/SkSLBuiltinMap.cpp",
diff --git a/include/core/BUILD.bazel b/include/core/BUILD.bazel
index aea2a87..f845e8a 100644
--- a/include/core/BUILD.bazel
+++ b/include/core/BUILD.bazel
@@ -970,3 +970,13 @@
         ":SkTypes_hdr",
     ],
 )
+
+generated_cc_atom(
+    name = "SkCapabilities_hdr",
+    hdrs = ["SkCapabilities.h"],
+    visibility = ["//:__subpackages__"],
+    deps = [
+        ":SkRefCnt_hdr",
+        "//include/sksl:SkSLVersion_hdr",
+    ],
+)
diff --git a/include/core/SkCapabilities.h b/include/core/SkCapabilities.h
new file mode 100644
index 0000000..9f8b340
--- /dev/null
+++ b/include/core/SkCapabilities.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2022 Google LLC
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef SkCapabilities_DEFINED
+#define SkCapabilities_DEFINED
+
+#include "include/core/SkRefCnt.h"
+#include "include/sksl/SkSLVersion.h"
+
+namespace skgpu::graphite { class Caps; }
+
+class SK_API SkCapabilities : public SkNVRefCnt<SkCapabilities> {
+public:
+    static sk_sp<SkCapabilities> RasterBackend();
+
+    SkSL::Version skslVersion() const { return fSkSLVersion; }
+
+private:
+    SkCapabilities() = default;
+
+    SkSL::Version fSkSLVersion = SkSL::Version::k100;
+
+    friend class GrCaps;
+    friend class skgpu::graphite::Caps;
+};
+
+#endif
diff --git a/include/core/SkSurface.h b/include/core/SkSurface.h
index 1e9b594..65c0567 100644
--- a/include/core/SkSurface.h
+++ b/include/core/SkSurface.h
@@ -26,6 +26,7 @@
 #endif
 
 class SkCanvas;
+class SkCapabilities;
 class SkDeferredDisplayList;
 class SkPaint;
 class SkSurfaceCharacterization;
@@ -600,6 +601,12 @@
     */
     SkCanvas* getCanvas();
 
+    /** Returns SkCapabilities that describes the capabilities of the SkSurface's device.
+
+        @return  SkCapabilities of SkSurface's device.
+    */
+    sk_sp<SkCapabilities> capabilities();
+
     /** Returns a compatible SkSurface, or nullptr. Returned SkSurface contains
         the same raster, GPU, or null properties as the original. Returned SkSurface
         does not share the same pixels.
diff --git a/include/gpu/GrRecordingContext.h b/include/gpu/GrRecordingContext.h
index 8b8b4fe..8370a47 100644
--- a/include/gpu/GrRecordingContext.h
+++ b/include/gpu/GrRecordingContext.h
@@ -30,6 +30,7 @@
 class GrTextBlobRedrawCoordinator;
 class GrThreadSafeCache;
 class SkArenaAlloc;
+class SkCapabilities;
 class SkJSONWriter;
 
 namespace sktext::gpu {
@@ -97,6 +98,8 @@
         return INHERITED::maxSurfaceSampleCountForColorType(colorType);
     }
 
+    SK_API sk_sp<SkCapabilities> skCapabilities() const;
+
     // Provides access to functions that aren't part of the public API.
     GrRecordingContextPriv priv();
     const GrRecordingContextPriv priv() const;  // NOLINT(readability-const-return-type)
diff --git a/include/sksl/BUILD.bazel b/include/sksl/BUILD.bazel
index 2983718..02ac8d6 100644
--- a/include/sksl/BUILD.bazel
+++ b/include/sksl/BUILD.bazel
@@ -191,3 +191,9 @@
     hdrs = ["SkSLOperator.h"],
     visibility = ["//:__subpackages__"],
 )
+
+generated_cc_atom(
+    name = "SkSLVersion_hdr",
+    hdrs = ["SkSLVersion.h"],
+    visibility = ["//:__subpackages__"],
+)
diff --git a/include/sksl/SkSLVersion.h b/include/sksl/SkSLVersion.h
new file mode 100644
index 0000000..ad059d5
--- /dev/null
+++ b/include/sksl/SkSLVersion.h
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2022 Google LLC
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef SkSLVersion_DEFINED
+#define SkSLVersion_DEFINED
+
+namespace SkSL {
+
+enum class Version {
+    /**
+     * Desktop GLSL 1.10, GLSL ES 1.00, WebGL 1.0
+     */
+    k100,
+
+    /**
+     * Desktop GLSL 3.30, GLSL ES 3.00, WebGL 2.0
+     */
+    k300,
+};
+
+}  // namespace SkSL
+
+#endif
diff --git a/src/core/BUILD.bazel b/src/core/BUILD.bazel
index 676e23b..462aa77 100644
--- a/src/core/BUILD.bazel
+++ b/src/core/BUILD.bazel
@@ -36,6 +36,7 @@
         ":SkCachedData_src",
         ":SkCanvasPriv_src",
         ":SkCanvas_src",
+        ":SkCapabilities_src",
         ":SkChromeRemoteGlyphCache_src",
         ":SkClipStackDevice_src",
         ":SkClipStack_src",
@@ -5771,3 +5772,10 @@
     visibility = ["//:__subpackages__"],
     deps = ["//include/core:SkTypes_hdr"],
 )
+
+generated_cc_atom(
+    name = "SkCapabilities_src",
+    srcs = ["SkCapabilities.cpp"],
+    visibility = ["//:__subpackages__"],
+    deps = ["//include/core:SkCapabilities_hdr"],
+)
diff --git a/src/core/SkCapabilities.cpp b/src/core/SkCapabilities.cpp
new file mode 100644
index 0000000..4a329fd
--- /dev/null
+++ b/src/core/SkCapabilities.cpp
@@ -0,0 +1,18 @@
+/*
+ * 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 "include/core/SkCapabilities.h"
+
+sk_sp<SkCapabilities> SkCapabilities::RasterBackend() {
+    static SkCapabilities* sCaps = [](){
+        SkCapabilities* caps = new SkCapabilities;
+        caps->fSkSLVersion = SkSL::Version::k100;
+        return caps;
+    }();
+
+    return sk_ref_sp(sCaps);
+}
diff --git a/src/gpu/ganesh/BUILD.bazel b/src/gpu/ganesh/BUILD.bazel
index 55a361c..a6fdd04 100644
--- a/src/gpu/ganesh/BUILD.bazel
+++ b/src/gpu/ganesh/BUILD.bazel
@@ -741,6 +741,7 @@
         ":GrSamplerState_hdr",
         ":GrShaderCaps_hdr",
         ":GrSurfaceProxy_hdr",
+        "//include/core:SkCapabilities_hdr",
         "//include/core:SkImageInfo_hdr",
         "//include/core:SkRefCnt_hdr",
         "//include/core:SkString_hdr",
@@ -764,6 +765,7 @@
         ":GrSurface_hdr",
         ":GrTestUtils_hdr",
         ":GrWindowRectangles_hdr",
+        "//include/core:SkCapabilities_hdr",
         "//include/gpu:GrBackendSurface_hdr",
         "//include/gpu:GrContextOptions_hdr",
         "//include/private/gpu/ganesh:GrTypesPriv_hdr",
@@ -2205,6 +2207,7 @@
         ":GrRecordingContextPriv_hdr",
         ":SkGr_hdr",
         ":SurfaceContext_hdr",
+        "//include/core:SkCapabilities_hdr",
         "//include/gpu:GrContextThreadSafeProxy_hdr",
         "//include/gpu:GrRecordingContext_hdr",
         "//src/core:SkArenaAlloc_hdr",
diff --git a/src/gpu/ganesh/GrCaps.cpp b/src/gpu/ganesh/GrCaps.cpp
index 9ffb75f..e27050f 100644
--- a/src/gpu/ganesh/GrCaps.cpp
+++ b/src/gpu/ganesh/GrCaps.cpp
@@ -7,6 +7,7 @@
 
 #include "src/gpu/ganesh/GrCaps.h"
 
+#include "include/core/SkCapabilities.h"
 #include "include/gpu/GrBackendSurface.h"
 #include "include/gpu/GrContextOptions.h"
 #include "include/private/gpu/ganesh/GrTypesPriv.h"
@@ -103,6 +104,9 @@
     // Our render targets are always created with textures as the color attachment, hence this min:
     fMaxRenderTargetSize = std::min(fMaxRenderTargetSize, fMaxTextureSize);
     fMaxPreferredRenderTargetSize = std::min(fMaxPreferredRenderTargetSize, fMaxRenderTargetSize);
+
+    fSkCaps.reset(new SkCapabilities);
+    fSkCaps->fSkSLVersion = this->shaderCaps()->supportedSkSLVerion();
 }
 
 void GrCaps::applyOptionsOverrides(const GrContextOptions& options) {
@@ -480,3 +484,8 @@
     } while (ct != GrColorType::kUnknown);
     return {GrColorType::kUnknown, {}};
 }
+
+sk_sp<SkCapabilities> GrCaps::asSkCapabilities() const {
+    SkASSERTF(fSkCaps, "Calling asSkCapabilities before it's initialized");
+    return fSkCaps;
+}
diff --git a/src/gpu/ganesh/GrCaps.h b/src/gpu/ganesh/GrCaps.h
index 5ad1749..796cd10 100644
--- a/src/gpu/ganesh/GrCaps.h
+++ b/src/gpu/ganesh/GrCaps.h
@@ -8,6 +8,7 @@
 #ifndef GrCaps_DEFINED
 #define GrCaps_DEFINED
 
+#include "include/core/SkCapabilities.h"
 #include "include/core/SkImageInfo.h"
 #include "include/core/SkRefCnt.h"
 #include "include/core/SkString.h"
@@ -41,6 +42,7 @@
 public:
     GrCaps(const GrContextOptions&);
 
+    sk_sp<SkCapabilities> asSkCapabilities() const;
     void dumpJSON(SkJSONWriter*) const;
 
     const GrShaderCaps* shaderCaps() const { return fShaderCaps.get(); }
@@ -525,6 +527,7 @@
     virtual bool onSupportsDynamicMSAA(const GrRenderTargetProxy*) const { return false; }
 
     std::unique_ptr<GrShaderCaps> fShaderCaps;
+    sk_sp<SkCapabilities> fSkCaps;
 
     bool fNPOTTextureTileSupport                     : 1;
     bool fMipmapSupport                              : 1;
diff --git a/src/gpu/ganesh/GrRecordingContext.cpp b/src/gpu/ganesh/GrRecordingContext.cpp
index 1949802..29c8220 100644
--- a/src/gpu/ganesh/GrRecordingContext.cpp
+++ b/src/gpu/ganesh/GrRecordingContext.cpp
@@ -7,6 +7,7 @@
 
 #include "include/gpu/GrRecordingContext.h"
 
+#include "include/core/SkCapabilities.h"
 #include "include/gpu/GrContextThreadSafeProxy.h"
 #include "src/core/SkArenaAlloc.h"
 #include "src/gpu/ganesh/GrAuditTrail.h"
@@ -159,6 +160,10 @@
 
 ////////////////////////////////////////////////////////////////////////////////
 
+sk_sp<SkCapabilities> GrRecordingContext::skCapabilities() const {
+    return this->caps()->asSkCapabilities();
+}
+
 int GrRecordingContext::maxTextureSize() const { return this->caps()->maxTextureSize(); }
 
 int GrRecordingContext::maxRenderTargetSize() const { return this->caps()->maxRenderTargetSize(); }
diff --git a/src/gpu/ganesh/GrShaderCaps.h b/src/gpu/ganesh/GrShaderCaps.h
index a2347c6..0e0ca57 100644
--- a/src/gpu/ganesh/GrShaderCaps.h
+++ b/src/gpu/ganesh/GrShaderCaps.h
@@ -54,14 +54,6 @@
     // reduce the number of unique shaders generated.
     bool reducedShaderMode() const { return fReducedShaderMode; }
 
-    /**
-     * SkSL ES3 requires support for derivatives, nonsquare matrices and bitwise integer operations.
-     */
-    bool supportsSkSLES3() const {
-        return fShaderDerivativeSupport && fNonsquareMatrixSupport && fIntegerSupport &&
-               fGLSLGeneration >= SkSL::GLSLGeneration::k330;
-    }
-
     // SkSL only.
     bool colorSpaceMathNeedsFloat() const { return fColorSpaceMathNeedsFloat; }
 
diff --git a/src/gpu/graphite/BUILD.bazel b/src/gpu/graphite/BUILD.bazel
index 1576f90..7cca8e6 100644
--- a/src/gpu/graphite/BUILD.bazel
+++ b/src/gpu/graphite/BUILD.bazel
@@ -44,6 +44,7 @@
     visibility = ["//:__subpackages__"],
     deps = [
         ":ResourceTypes_hdr",
+        "//include/core:SkCapabilities_hdr",
         "//include/core:SkImageInfo_hdr",
         "//include/core:SkRefCnt_hdr",
         "//src/core:SkEnumBitMask_hdr",
@@ -58,6 +59,7 @@
     visibility = ["//:__subpackages__"],
     deps = [
         ":Caps_hdr",
+        "//include/core:SkCapabilities_hdr",
         "//include/gpu/graphite:TextureInfo_hdr",
         "//src/sksl:SkSLUtil_hdr",
     ],
@@ -673,9 +675,12 @@
     srcs = ["Surface_Graphite.cpp"],
     visibility = ["//:__subpackages__"],
     deps = [
+        ":Caps_hdr",
         ":Device_hdr",
         ":Image_Graphite_hdr",
+        ":RecorderPriv_hdr",
         ":Surface_Graphite_hdr",
+        "//include/core:SkCapabilities_hdr",
         "//include/gpu/graphite:Recorder_hdr",
         "//include/gpu/graphite:SkStuff_hdr",
     ],
diff --git a/src/gpu/graphite/Caps.cpp b/src/gpu/graphite/Caps.cpp
index b128d26..e92ca8b 100644
--- a/src/gpu/graphite/Caps.cpp
+++ b/src/gpu/graphite/Caps.cpp
@@ -7,6 +7,7 @@
 
 #include "src/gpu/graphite/Caps.h"
 
+#include "include/core/SkCapabilities.h"
 #include "include/gpu/graphite/TextureInfo.h"
 #include "src/sksl/SkSLUtil.h"
 
@@ -52,4 +53,12 @@
     return colorTypeInfo->fWriteSwizzle;
 }
 
+sk_sp<SkCapabilities> Caps::asSkCapabilities() const {
+    if (!fSkCaps) {
+        fSkCaps.reset(new SkCapabilities);
+        fSkCaps->fSkSLVersion = this->shaderCaps()->supportedSkSLVerion();
+    }
+    return fSkCaps;
+}
+
 } // namespace skgpu::graphite
diff --git a/src/gpu/graphite/Caps.h b/src/gpu/graphite/Caps.h
index 376d28f..5cd00a4 100644
--- a/src/gpu/graphite/Caps.h
+++ b/src/gpu/graphite/Caps.h
@@ -8,6 +8,7 @@
 #ifndef skgpu_graphite_Caps_DEFINED
 #define skgpu_graphite_Caps_DEFINED
 
+#include "include/core/SkCapabilities.h"
 #include "include/core/SkImageInfo.h"
 #include "include/core/SkRefCnt.h"
 #include "src/core/SkEnumBitMask.h"
@@ -30,6 +31,7 @@
 public:
     ~Caps() override;
 
+    sk_sp<SkCapabilities> asSkCapabilities() const;
     const SkSL::ShaderCaps* shaderCaps() const { return fShaderCaps.get(); }
 
     virtual TextureInfo getDefaultSampledTextureInfo(SkColorType,
@@ -103,6 +105,7 @@
     size_t fRequiredUniformBufferAlignment = 0;
 
     std::unique_ptr<SkSL::ShaderCaps> fShaderCaps;
+    mutable sk_sp<SkCapabilities> fSkCaps;
 
     bool fClampToBorderSupport = true;
 
diff --git a/src/gpu/graphite/Surface_Graphite.cpp b/src/gpu/graphite/Surface_Graphite.cpp
index d790340..29e4811 100644
--- a/src/gpu/graphite/Surface_Graphite.cpp
+++ b/src/gpu/graphite/Surface_Graphite.cpp
@@ -7,10 +7,13 @@
 
 #include "src/gpu/graphite/Surface_Graphite.h"
 
+#include "include/core/SkCapabilities.h"
 #include "include/gpu/graphite/Recorder.h"
 #include "include/gpu/graphite/SkStuff.h"
+#include "src/gpu/graphite/Caps.h"
 #include "src/gpu/graphite/Device.h"
 #include "src/gpu/graphite/Image_Graphite.h"
+#include "src/gpu/graphite/RecorderPriv.h"
 
 namespace skgpu::graphite {
 
@@ -56,4 +59,8 @@
     return fDevice->readPixels(context, recorder, dst, srcX, srcY);
 }
 
+sk_sp<SkCapabilities> Surface::onCapabilities() {
+    return fDevice->recorder()->priv().caps()->asSkCapabilities();
+}
+
 } // namespace skgpu::graphite
diff --git a/src/gpu/graphite/Surface_Graphite.h b/src/gpu/graphite/Surface_Graphite.h
index b1f7b9b..ccc87b4 100644
--- a/src/gpu/graphite/Surface_Graphite.h
+++ b/src/gpu/graphite/Surface_Graphite.h
@@ -28,6 +28,7 @@
     void onWritePixels(const SkPixmap&, int x, int y) override;
     bool onCopyOnWrite(ContentChangeMode) override;
     bool onReadPixels(Context*, Recorder*, const SkPixmap& dst, int srcX, int srcY);
+    sk_sp<SkCapabilities> onCapabilities() override;
 
 private:
     sk_sp<Device> fDevice;
diff --git a/src/image/BUILD.bazel b/src/image/BUILD.bazel
index 6c8eccc..102e2f7 100644
--- a/src/image/BUILD.bazel
+++ b/src/image/BUILD.bazel
@@ -382,6 +382,7 @@
         ":SkSurface_Base_hdr",
         ":SkSurface_Gpu_hdr",
         "//include/core:SkCanvas_hdr",
+        "//include/core:SkCapabilities_hdr",
         "//include/core:SkDeferredDisplayList_hdr",
         "//include/core:SkSurfaceCharacterization_hdr",
         "//include/gpu:GrBackendSurface_hdr",
@@ -408,6 +409,7 @@
     deps = [
         ":SkSurface_Base_hdr",
         "//include/core:SkCanvas_hdr",
+        "//include/core:SkCapabilities_hdr",
         "//include/core:SkMallocPixelRef_hdr",
         "//include/private:SkImageInfoPriv_hdr",
         "//src/core:SkDevice_hdr",
@@ -424,6 +426,7 @@
         ":SkRescaleAndReadPixels_hdr",
         ":SkSurface_Base_hdr",
         "//include/core:SkCanvas_hdr",
+        "//include/core:SkCapabilities_hdr",
         "//include/gpu:GrBackendSurface_hdr",
         "//include/utils:SkNoDrawCanvas_hdr",
         "//src/core:SkAutoPixmapStorage_hdr",
diff --git a/src/image/SkSurface.cpp b/src/image/SkSurface.cpp
index 622e22c..6d37b8a 100644
--- a/src/image/SkSurface.cpp
+++ b/src/image/SkSurface.cpp
@@ -8,6 +8,7 @@
 #include <atomic>
 #include <cmath>
 #include "include/core/SkCanvas.h"
+#include "include/core/SkCapabilities.h"
 #include "src/core/SkAutoPixmapStorage.h"
 #include "src/core/SkImagePriv.h"
 #include "src/core/SkPaintPriv.h"
@@ -205,6 +206,10 @@
     return asSB(this)->getCachedCanvas();
 }
 
+sk_sp<SkCapabilities> SkSurface::capabilities() {
+    return asSB(this)->onCapabilities();
+}
+
 sk_sp<SkImage> SkSurface::makeImageSnapshot() {
     return asSB(this)->refCachedImage();
 }
@@ -410,6 +415,10 @@
     void onWritePixels(const SkPixmap&, int x, int y) override {}
     void onDraw(SkCanvas*, SkScalar, SkScalar, const SkSamplingOptions&, const SkPaint*) override {}
     bool onCopyOnWrite(ContentChangeMode) override { return true; }
+    sk_sp<SkCapabilities> onCapabilities() override {
+        // Not really, but we have to return *something*
+        return SkCapabilities::RasterBackend();
+    }
 };
 
 sk_sp<SkSurface> SkSurface::MakeNull(int width, int height) {
diff --git a/src/image/SkSurface_Base.h b/src/image/SkSurface_Base.h
index f018d947..a6d5e6f 100644
--- a/src/image/SkSurface_Base.h
+++ b/src/image/SkSurface_Base.h
@@ -137,6 +137,8 @@
         return false;
     }
 
+    virtual sk_sp<SkCapabilities> onCapabilities() = 0;
+
     inline SkCanvas* getCachedCanvas();
     inline sk_sp<SkImage> refCachedImage();
 
diff --git a/src/image/SkSurface_Gpu.cpp b/src/image/SkSurface_Gpu.cpp
index 6fd6c8b..4c50ac2 100644
--- a/src/image/SkSurface_Gpu.cpp
+++ b/src/image/SkSurface_Gpu.cpp
@@ -8,6 +8,7 @@
 #include "src/image/SkSurface_Gpu.h"
 
 #include "include/core/SkCanvas.h"
+#include "include/core/SkCapabilities.h"
 #include "include/core/SkDeferredDisplayList.h"
 #include "include/core/SkSurfaceCharacterization.h"
 #include "include/gpu/GrBackendSurface.h"
@@ -391,6 +392,10 @@
     return true;
 }
 
+sk_sp<SkCapabilities> SkSurface_Gpu::onCapabilities() {
+    return fDevice->recordingContext()->skCapabilities();
+}
+
 ///////////////////////////////////////////////////////////////////////////////
 
 sk_sp<SkSurface> SkSurface::MakeRenderTarget(GrRecordingContext* rContext,
diff --git a/src/image/SkSurface_Gpu.h b/src/image/SkSurface_Gpu.h
index 39e5ed7..ea9d519 100644
--- a/src/image/SkSurface_Gpu.h
+++ b/src/image/SkSurface_Gpu.h
@@ -57,6 +57,7 @@
                 const SkPaint* paint) override;
     bool onDraw(sk_sp<const SkDeferredDisplayList>, SkIPoint offset) override;
 
+    sk_sp<SkCapabilities> onCapabilities() override;
     skgpu::BaseDevice* getDevice();
 
 private:
diff --git a/src/image/SkSurface_Raster.cpp b/src/image/SkSurface_Raster.cpp
index 4376d9c..f02fd80 100644
--- a/src/image/SkSurface_Raster.cpp
+++ b/src/image/SkSurface_Raster.cpp
@@ -6,6 +6,7 @@
  */
 
 #include "include/core/SkCanvas.h"
+#include "include/core/SkCapabilities.h"
 #include "include/core/SkMallocPixelRef.h"
 #include "include/private/SkImageInfoPriv.h"
 #include "src/core/SkDevice.h"
@@ -26,6 +27,7 @@
     void onDraw(SkCanvas*, SkScalar, SkScalar, const SkSamplingOptions&, const SkPaint*) override;
     bool onCopyOnWrite(ContentChangeMode) override;
     void onRestoreBackingMutability() override;
+    sk_sp<SkCapabilities> onCapabilities() override;
 
 private:
     SkBitmap    fBitmap;
@@ -153,6 +155,10 @@
     return true;
 }
 
+sk_sp<SkCapabilities> SkSurface_Raster::onCapabilities() {
+    return SkCapabilities::RasterBackend();
+}
+
 ///////////////////////////////////////////////////////////////////////////////
 
 sk_sp<SkSurface> SkSurface::MakeRasterDirectReleaseProc(const SkImageInfo& info, void* pixels,
diff --git a/src/sksl/BUILD.bazel b/src/sksl/BUILD.bazel
index 98b7e61..932f893 100644
--- a/src/sksl/BUILD.bazel
+++ b/src/sksl/BUILD.bazel
@@ -875,6 +875,7 @@
     deps = [
         ":SkSLGLSL_hdr",
         "//include/core:SkTypes_hdr",
+        "//include/sksl:SkSLVersion_hdr",
         "//src/core:SkSLTypeShared_hdr",
     ],
 )
diff --git a/src/sksl/SkSLUtil.h b/src/sksl/SkSLUtil.h
index f9095f2..3c5e2f2 100644
--- a/src/sksl/SkSLUtil.h
+++ b/src/sksl/SkSLUtil.h
@@ -8,6 +8,7 @@
 #ifndef SKSL_UTIL
 #define SKSL_UTIL
 
+#include "include/sksl/SkSLVersion.h"
 #include "src/core/SkSLTypeShared.h"
 #include "src/sksl/SkSLGLSL.h"
 
@@ -180,6 +181,17 @@
         return fSecondExternalTextureExtensionString;
     }
 
+    /**
+     * SkSL 300 requires support for derivatives, nonsquare matrices and bitwise integer operations.
+     */
+    SkSL::Version supportedSkSLVerion() const {
+        if (fShaderDerivativeSupport && fNonsquareMatrixSupport && fIntegerSupport &&
+            fGLSLGeneration >= SkSL::GLSLGeneration::k330) {
+            return SkSL::Version::k300;
+        }
+        return SkSL::Version::k100;
+    }
+
     SkSL::GLSLGeneration generation() const { return fGLSLGeneration; }
 
     SkSL::GLSLGeneration fGLSLGeneration = SkSL::GLSLGeneration::k330;
diff --git a/tests/BUILD.bazel b/tests/BUILD.bazel
index 85f7807..f550f5c 100644
--- a/tests/BUILD.bazel
+++ b/tests/BUILD.bazel
@@ -5965,6 +5965,7 @@
         "//include/private:SkSLProgramElement_hdr",
         "//include/private:SkSLProgramKind_hdr",
         "//include/sksl:DSLCore_hdr",
+        "//include/sksl:SkSLVersion_hdr",
         "//src/core:SkRuntimeEffectPriv_hdr",
         "//src/gpu/ganesh:GrCaps_hdr",
         "//src/gpu/ganesh:GrDirectContextPriv_hdr",
diff --git a/tests/SkSLTest.cpp b/tests/SkSLTest.cpp
index 913d842..9c34e95 100644
--- a/tests/SkSLTest.cpp
+++ b/tests/SkSLTest.cpp
@@ -24,6 +24,7 @@
 #include "include/private/SkSLProgramElement.h"
 #include "include/private/SkSLProgramKind.h"
 #include "include/sksl/DSLCore.h"
+#include "include/sksl/SkSLVersion.h"
 #include "src/core/SkRuntimeEffectPriv.h"
 #include "src/gpu/ganesh/GrCaps.h"
 #include "src/gpu/ganesh/GrDirectContextPriv.h"
@@ -232,8 +233,9 @@
 static void test_gpu(skiatest::Reporter* r, GrDirectContext* ctx, const char* testFile, int flags) {
     // If this is an ES3-only test on a GPU which doesn't support SkSL ES3, return immediately.
     bool shouldRunGPU = (flags & SkSLTestFlags::GPU);
-    bool shouldRunGPU_ES3 = (flags & SkSLTestFlags::GPU_ES3) &&
-                            ctx->priv().caps()->shaderCaps()->supportsSkSLES3();
+    bool shouldRunGPU_ES3 =
+            (flags & SkSLTestFlags::GPU_ES3) &&
+            (ctx->priv().caps()->shaderCaps()->supportedSkSLVerion() >= SkSL::Version::k300);
     if (!shouldRunGPU && !shouldRunGPU_ES3) {
         return;
     }