[graphite] Add Context create/deleteBackendTexture calls.
Bug: skia:12633
Change-Id: Ida78c4145423376dc0267096a1d758b74144fd0c
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/477139
Reviewed-by: Jim Van Verth <jvanverth@google.com>
Commit-Queue: Greg Daniel <egdaniel@google.com>
diff --git a/BUILD.gn b/BUILD.gn
index f378cad..aa0df7f 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -2038,6 +2038,9 @@
}
if (skia_enable_graphite) {
sources += graphite_tests_sources
+ if (skia_use_metal) {
+ sources += graphite_metal_tests_sources
+ }
}
if (!skia_enable_skgpu_v1) {
sources -= skgpu_v1_tests_sources
diff --git a/experimental/graphite/include/BackendTexture.h b/experimental/graphite/include/BackendTexture.h
index 9d70806..192d8e3 100644
--- a/experimental/graphite/include/BackendTexture.h
+++ b/experimental/graphite/include/BackendTexture.h
@@ -20,10 +20,22 @@
class BackendTexture {
public:
+ BackendTexture() {}
#ifdef SK_METAL
- BackendTexture(SkISize dimensions, sk_cfp<mtl::Handle> mtlTexture);
+ // The BackendTexture will not call retain or release on the passed in mtl::Handle. Thus the
+ // client must keep the mtl::Handle valid until they are no longer using the BackendTexture.
+ BackendTexture(SkISize dimensions, mtl::Handle mtlTexture);
#endif
+ BackendTexture(const BackendTexture&);
+
+ ~BackendTexture();
+
+ BackendTexture& operator=(const BackendTexture&);
+
+ bool operator==(const BackendTexture&) const;
+ bool operator!=(const BackendTexture& that) const { return !(*this == that); }
+
bool isValid() const { return fInfo.isValid(); }
BackendApi backend() const { return fInfo.backend(); }
@@ -32,7 +44,7 @@
const TextureInfo& info() const { return fInfo; }
#ifdef SK_METAL
- sk_cfp<mtl::Handle> getMtlTexture() const;
+ mtl::Handle getMtlTexture() const;
#endif
private:
@@ -41,7 +53,7 @@
union {
#ifdef SK_METAL
- sk_cfp<mtl::Handle> fMtlTexture;
+ mtl::Handle fMtlTexture;
#endif
};
};
diff --git a/experimental/graphite/include/Context.h b/experimental/graphite/include/Context.h
index cc9ec30..33c88a9 100644
--- a/experimental/graphite/include/Context.h
+++ b/experimental/graphite/include/Context.h
@@ -18,10 +18,12 @@
namespace skgpu {
+class BackendTexture;
class ContextPriv;
class Gpu;
class Recorder;
class Recording;
+class TextureInfo;
namespace mtl { struct BackendContext; }
struct ShaderCombo {
@@ -57,6 +59,8 @@
static sk_sp<Context> MakeMetal(const skgpu::mtl::BackendContext&);
#endif
+ BackendApi backend() const { return fBackend; }
+
sk_sp<Recorder> createRecorder();
void insertRecording(std::unique_ptr<Recording>);
@@ -64,18 +68,40 @@
void preCompile(const PaintCombo&);
+ /**
+ * Creates a new backend gpu texture matching the dimensinos and TextureInfo. If an invalid
+ * TextureInfo or a TextureInfo Skia can't support is passed in, this will return an invalid
+ * BackendTexture. Thus the client should check isValid on the returned BackendTexture to know
+ * if it succeeded or not.
+ *
+ * If this does return a valid BackendTexture, the caller is required to use
+ * Context::deleteBackendTexture to delete that texture.
+ */
+ BackendTexture createBackendTexture(SkISize dimensions, const TextureInfo&);
+
+ /**
+ * Called to delete the passed in BackendTexture. This should only be called if the
+ * BackendTexture was created by calling Context::createBackendTexture. If the BackendTexture is
+ * not valid or does not match the BackendApi of the Context then nothing happens.
+ *
+ * Otherwise this will delete/release the backend object that is wrapped in the BackendTexture.
+ * The BackendTexture will be reset to an invalid state and should not be used again.
+ */
+ void deleteBackendTexture(BackendTexture&);
+
// Provides access to functions that aren't part of the public API.
ContextPriv priv();
const ContextPriv priv() const; // NOLINT(readability-const-return-type)
protected:
- Context(sk_sp<Gpu>);
+ Context(sk_sp<Gpu>, BackendApi);
private:
friend class ContextPriv;
std::vector<std::unique_ptr<Recording>> fRecordings;
sk_sp<Gpu> fGpu;
+ BackendApi fBackend;
};
} // namespace skgpu
diff --git a/experimental/graphite/include/TextureInfo.h b/experimental/graphite/include/TextureInfo.h
index eb5cf3c..850e1d7 100644
--- a/experimental/graphite/include/TextureInfo.h
+++ b/experimental/graphite/include/TextureInfo.h
@@ -39,9 +39,10 @@
~TextureInfo() {}
TextureInfo(const TextureInfo&) = default;
- TextureInfo& operator=(const TextureInfo&) = delete;
+ TextureInfo& operator=(const TextureInfo&);
bool operator==(const TextureInfo&) const;
+ bool operator!=(const TextureInfo& that) const { return !(*this == that); }
bool isValid() const { return fValid; }
BackendApi backend() const { return fBackend; }
diff --git a/experimental/graphite/src/BackendTexture.cpp b/experimental/graphite/src/BackendTexture.cpp
index 4556c0a..9dd2cc5 100644
--- a/experimental/graphite/src/BackendTexture.cpp
+++ b/experimental/graphite/src/BackendTexture.cpp
@@ -9,13 +9,65 @@
namespace skgpu {
-#ifdef SK_METAL
-BackendTexture::BackendTexture(SkISize dimensions, sk_cfp<mtl::Handle> mtlTexture)
- : fDimensions(dimensions)
- , fInfo(mtl::TextureInfo(mtlTexture.get()))
- , fMtlTexture(std::move(mtlTexture)) {}
+BackendTexture::~BackendTexture() {}
-sk_cfp<mtl::Handle> BackendTexture::getMtlTexture() const {
+BackendTexture::BackendTexture(const BackendTexture& that) {
+ *this = that;
+}
+
+BackendTexture& BackendTexture::operator=(const BackendTexture& that) {
+ bool valid = this->isValid();
+ if (!that.isValid()) {
+ fInfo = {};
+ return *this;
+ } else if (valid && this->backend() != that.backend()) {
+ valid = false;
+ }
+ fDimensions = that.fDimensions;
+ fInfo = that.fInfo;
+
+ switch (that.backend()) {
+#ifdef SK_METAL
+ case BackendApi::kMetal:
+ fMtlTexture = that.fMtlTexture;
+ break;
+#endif
+ default:
+ SK_ABORT("Unsupport Backend");
+ }
+ return *this;
+}
+
+bool BackendTexture::operator==(const BackendTexture& that) const {
+ if (!this->isValid() || !that.isValid()) {
+ return false;
+ }
+
+ if (fDimensions != that.fDimensions || fInfo != that.fInfo) {
+ return false;
+ }
+
+ switch (that.backend()) {
+#ifdef SK_METAL
+ case BackendApi::kMetal:
+ if (fMtlTexture != that.fMtlTexture) {
+ return false;
+ }
+ break;
+#endif
+ default:
+ SK_ABORT("Unsupport Backend");
+ }
+ return true;
+}
+
+#ifdef SK_METAL
+BackendTexture::BackendTexture(SkISize dimensions, mtl::Handle mtlTexture)
+ : fDimensions(dimensions)
+ , fInfo(mtl::TextureInfo(mtlTexture))
+ , fMtlTexture(mtlTexture) {}
+
+mtl::Handle BackendTexture::getMtlTexture() const {
if (this->isValid() && this->backend() == BackendApi::kMetal) {
return fMtlTexture;
}
diff --git a/experimental/graphite/src/Caps.h b/experimental/graphite/src/Caps.h
index b5029c2..73dad79 100644
--- a/experimental/graphite/src/Caps.h
+++ b/experimental/graphite/src/Caps.h
@@ -44,9 +44,13 @@
virtual bool isTexturable(const TextureInfo&) const = 0;
virtual bool isRenderable(const TextureInfo&) const = 0;
+ int maxTextureSize() const { return fMaxTextureSize; }
+
protected:
Caps();
+ int fMaxTextureSize = 0;
+
std::unique_ptr<SkSL::ShaderCaps> fShaderCaps;
private:
diff --git a/experimental/graphite/src/Context.cpp b/experimental/graphite/src/Context.cpp
index e42690e..294f63d 100644
--- a/experimental/graphite/src/Context.cpp
+++ b/experimental/graphite/src/Context.cpp
@@ -7,6 +7,8 @@
#include "experimental/graphite/include/Context.h"
+#include "experimental/graphite/include/BackendTexture.h"
+#include "experimental/graphite/include/TextureInfo.h"
#include "experimental/graphite/src/Caps.h"
#include "experimental/graphite/src/CommandBuffer.h"
#include "experimental/graphite/src/ContextUtils.h"
@@ -22,7 +24,7 @@
namespace skgpu {
-Context::Context(sk_sp<Gpu> gpu) : fGpu(std::move(gpu)) {}
+Context::Context(sk_sp<Gpu> gpu, BackendApi backend) : fGpu(std::move(gpu)), fBackend(backend) {}
Context::~Context() {}
#ifdef SK_METAL
@@ -32,7 +34,7 @@
return nullptr;
}
- return sk_sp<Context>(new Context(std::move(gpu)));
+ return sk_sp<Context>(new Context(std::move(gpu), BackendApi::kMetal));
}
#endif
@@ -79,4 +81,18 @@
}
}
+BackendTexture Context::createBackendTexture(SkISize dimensions, const TextureInfo& info) {
+ if (!info.isValid() || info.backend() != this->backend()) {
+ return {};
+ }
+ return fGpu->createBackendTexture(dimensions, info);
+}
+
+void Context::deleteBackendTexture(BackendTexture& texture) {
+ if (!texture.isValid() || texture.backend() != this->backend()) {
+ return;
+ }
+ fGpu->deleteBackendTexture(texture);
+}
+
} // namespace skgpu
diff --git a/experimental/graphite/src/Gpu.cpp b/experimental/graphite/src/Gpu.cpp
index 17b5706..9e2803f 100644
--- a/experimental/graphite/src/Gpu.cpp
+++ b/experimental/graphite/src/Gpu.cpp
@@ -7,6 +7,8 @@
#include "experimental/graphite/src/Gpu.h"
+#include "experimental/graphite/include/BackendTexture.h"
+#include "experimental/graphite/include/TextureInfo.h"
#include "experimental/graphite/src/Caps.h"
#include "experimental/graphite/src/CommandBuffer.h"
#include "experimental/graphite/src/GpuWorkSubmission.h"
@@ -83,4 +85,20 @@
SkASSERT(sync == SyncToCpu::kNo || fOutstandingSubmissions.empty());
}
+BackendTexture Gpu::createBackendTexture(SkISize dimensions, const TextureInfo& info) {
+ if (dimensions.isEmpty() || dimensions.width() > this->caps()->maxTextureSize() ||
+ dimensions.height() > this->caps()->maxTextureSize()) {
+ return {};
+ }
+
+ return this->onCreateBackendTexture(dimensions, info);
+}
+
+void Gpu::deleteBackendTexture(BackendTexture& texture) {
+ this->onDeleteBackendTexture(texture);
+ // Invalidate the texture;
+ texture = BackendTexture();
+}
+
+
} // namespace skgpu
diff --git a/experimental/graphite/src/Gpu.h b/experimental/graphite/src/Gpu.h
index 1cbdcd4..9750bb3 100644
--- a/experimental/graphite/src/Gpu.h
+++ b/experimental/graphite/src/Gpu.h
@@ -9,6 +9,7 @@
#define skgpu_Gpu_DEFINED
#include "include/core/SkRefCnt.h"
+#include "include/core/SkSize.h"
#include "include/private/SkDeque.h"
#include "experimental/graphite/include/GraphiteTypes.h"
@@ -19,10 +20,12 @@
namespace skgpu {
+class BackendTexture;
class Caps;
-class ResourceProvider;
class CommandBuffer;
class GpuWorkSubmission;
+class ResourceProvider;
+class TextureInfo;
class Gpu : public SkRefCnt {
public:
@@ -41,6 +44,9 @@
bool submit(sk_sp<CommandBuffer>);
void checkForFinishedWork(SyncToCpu);
+ BackendTexture createBackendTexture(SkISize dimensions, const TextureInfo&);
+ void deleteBackendTexture(BackendTexture&);
+
#if GRAPHITE_TEST_UTILS
virtual void testingOnly_startCapture() {}
virtual void testingOnly_endCapture() {}
@@ -60,6 +66,9 @@
private:
virtual bool onSubmit(sk_sp<CommandBuffer>) = 0;
+ virtual BackendTexture onCreateBackendTexture(SkISize dimensions, const TextureInfo&) = 0;
+ virtual void onDeleteBackendTexture(BackendTexture&) = 0;
+
sk_sp<const Caps> fCaps;
// Compiler used for compiling SkSL into backend shader code. We only want to create the
// compiler once, as there is significant overhead to the first compile.
diff --git a/experimental/graphite/src/TextureInfo.cpp b/experimental/graphite/src/TextureInfo.cpp
index 1c631c6..5db2eab 100644
--- a/experimental/graphite/src/TextureInfo.cpp
+++ b/experimental/graphite/src/TextureInfo.cpp
@@ -9,6 +9,30 @@
namespace skgpu {
+TextureInfo& TextureInfo::operator=(const TextureInfo& that) {
+ if (!that.isValid()) {
+ fValid = false;
+ return *this;
+ }
+ fBackend = that.fBackend;
+ fSampleCount = that.fSampleCount;
+ fLevelCount = that.fLevelCount;
+ fProtected = that.fProtected;
+
+ switch (that.backend()) {
+#ifdef SK_METAL
+ case BackendApi::kMetal:
+ fMtlSpec = that.fMtlSpec;
+ break;
+#endif
+ default:
+ SK_ABORT("Unsupport Backend");
+ }
+
+ fValid = true;
+ return *this;
+}
+
bool TextureInfo::operator==(const TextureInfo& that) const {
if (!this->isValid() || !that.isValid()) {
return false;
diff --git a/experimental/graphite/src/mtl/MtlCaps.mm b/experimental/graphite/src/mtl/MtlCaps.mm
index 6620ba9..1a7b604 100644
--- a/experimental/graphite/src/mtl/MtlCaps.mm
+++ b/experimental/graphite/src/mtl/MtlCaps.mm
@@ -212,7 +212,11 @@
}
void Caps::initCaps(const id<MTLDevice> device) {
- // TODO
+ if (this->isMac() || fFamilyGroup >= 3) {
+ fMaxTextureSize = 16384;
+ } else {
+ fMaxTextureSize = 8192;
+ }
}
void Caps::initShaderCaps() {
diff --git a/experimental/graphite/src/mtl/MtlGpu.h b/experimental/graphite/src/mtl/MtlGpu.h
index b63efdb..19cd403 100644
--- a/experimental/graphite/src/mtl/MtlGpu.h
+++ b/experimental/graphite/src/mtl/MtlGpu.h
@@ -34,6 +34,9 @@
bool onSubmit(sk_sp<skgpu::CommandBuffer>) override;
+ BackendTexture onCreateBackendTexture(SkISize dimensions, const skgpu::TextureInfo&) override;
+ void onDeleteBackendTexture(BackendTexture&) override;
+
#if GRAPHITE_TEST_UTILS
void testingOnly_startCapture() override;
void testingOnly_endCapture() override;
diff --git a/experimental/graphite/src/mtl/MtlGpu.mm b/experimental/graphite/src/mtl/MtlGpu.mm
index 336d6ad..bf708ae 100644
--- a/experimental/graphite/src/mtl/MtlGpu.mm
+++ b/experimental/graphite/src/mtl/MtlGpu.mm
@@ -7,9 +7,12 @@
#include "experimental/graphite/src/mtl/MtlGpu.h"
+#include "experimental/graphite/include/BackendTexture.h"
+#include "experimental/graphite/include/TextureInfo.h"
#include "experimental/graphite/src/Caps.h"
#include "experimental/graphite/src/mtl/MtlCommandBuffer.h"
#include "experimental/graphite/src/mtl/MtlResourceProvider.h"
+#include "experimental/graphite/src/mtl/MtlTexture.h"
namespace skgpu::mtl {
@@ -76,6 +79,20 @@
return true;
}
+BackendTexture Gpu::onCreateBackendTexture(SkISize dimensions, const skgpu::TextureInfo& info) {
+ sk_cfp<id<MTLTexture>> texture = Texture::MakeMtlTexture(this, dimensions, info);
+ if (!texture) {
+ return {};
+ }
+ return BackendTexture(dimensions, (Handle)texture.release());
+}
+
+void Gpu::onDeleteBackendTexture(BackendTexture& texture) {
+ SkASSERT(texture.backend() == BackendApi::kMetal);
+ Handle texHandle = texture.getMtlTexture();
+ SkCFSafeRelease(texHandle);
+}
+
#if GRAPHITE_TEST_UTILS
void Gpu::testingOnly_startCapture() {
if (@available(macOS 10.13, iOS 11.0, *)) {
diff --git a/experimental/graphite/src/mtl/MtlResourceProvider.mm b/experimental/graphite/src/mtl/MtlResourceProvider.mm
index cd177a2..1501bd5 100644
--- a/experimental/graphite/src/mtl/MtlResourceProvider.mm
+++ b/experimental/graphite/src/mtl/MtlResourceProvider.mm
@@ -42,11 +42,11 @@
}
sk_sp<skgpu::Texture> ResourceProvider::createWrappedTexture(const BackendTexture& texture) {
- sk_cfp<mtl::Handle> mtlHandleTexture = texture.getMtlTexture();
+ mtl::Handle mtlHandleTexture = texture.getMtlTexture();
if (!mtlHandleTexture) {
return nullptr;
}
- sk_cfp<id<MTLTexture>> mtlTexture((id<MTLTexture>)mtlHandleTexture.release());
+ sk_cfp<id<MTLTexture>> mtlTexture = sk_ret_cfp((id<MTLTexture>)mtlHandleTexture);
return Texture::MakeWrapped(texture.dimensions(), texture.info(), std::move(mtlTexture));
}
diff --git a/experimental/graphite/src/mtl/MtlTexture.h b/experimental/graphite/src/mtl/MtlTexture.h
index a0a7ceb..6488039 100644
--- a/experimental/graphite/src/mtl/MtlTexture.h
+++ b/experimental/graphite/src/mtl/MtlTexture.h
@@ -18,6 +18,10 @@
class Texture : public skgpu::Texture {
public:
+ static sk_cfp<id<MTLTexture>> MakeMtlTexture(const Gpu*,
+ SkISize dimensions,
+ const skgpu::TextureInfo&);
+
static sk_sp<Texture> Make(const Gpu* gpu,
SkISize dimensions,
const skgpu::TextureInfo&);
diff --git a/experimental/graphite/src/mtl/MtlTexture.mm b/experimental/graphite/src/mtl/MtlTexture.mm
index 5789909..457f9e1 100644
--- a/experimental/graphite/src/mtl/MtlTexture.mm
+++ b/experimental/graphite/src/mtl/MtlTexture.mm
@@ -9,29 +9,32 @@
#include "experimental/graphite/include/mtl/MtlTypes.h"
#include "experimental/graphite/include/private/MtlTypesPriv.h"
+#include "experimental/graphite/src/mtl/MtlCaps.h"
#include "experimental/graphite/src/mtl/MtlGpu.h"
#include "experimental/graphite/src/mtl/MtlUtils.h"
namespace skgpu::mtl {
-Texture::Texture(SkISize dimensions,
- const skgpu::TextureInfo& info,
- sk_cfp<id<MTLTexture>> texture,
- Ownership ownership)
- : skgpu::Texture(dimensions, info, ownership)
- , fTexture(std::move(texture)) {}
-
-sk_sp<Texture> Texture::Make(const Gpu* gpu,
- SkISize dimensions,
- const skgpu::TextureInfo& info) {
- // TODO: get this from Caps
- if (dimensions.width() > 16384 || dimensions.height() > 16384) {
+sk_cfp<id<MTLTexture>> Texture::MakeMtlTexture(const Gpu* gpu,
+ SkISize dimensions,
+ const skgpu::TextureInfo& info) {
+ const skgpu::Caps* caps = gpu->caps();
+ if (dimensions.width() > caps->maxTextureSize() ||
+ dimensions.height() > caps->maxTextureSize()) {
return nullptr;
}
const TextureSpec& mtlSpec = info.mtlTextureSpec();
SkASSERT(!mtlSpec.fFramebufferOnly);
+ if (mtlSpec.fUsage & MTLTextureUsageShaderRead && !caps->isTexturable(info)) {
+ return nullptr;
+ }
+
+ if (mtlSpec.fUsage & MTLTextureUsageRenderTarget && !caps->isRenderable(info)) {
+ return nullptr;
+ }
+
sk_cfp<MTLTextureDescriptor*> desc([[MTLTextureDescriptor alloc] init]);
(*desc).textureType = (info.numSamples() > 1) ? MTLTextureType2DMultisample : MTLTextureType2D;
(*desc).pixelFormat = (MTLPixelFormat)mtlSpec.fFormat;
@@ -70,6 +73,23 @@
}
#endif
+ return texture;
+}
+
+Texture::Texture(SkISize dimensions,
+ const skgpu::TextureInfo& info,
+ sk_cfp<id<MTLTexture>> texture,
+ Ownership ownership)
+ : skgpu::Texture(dimensions, info, ownership)
+ , fTexture(std::move(texture)) {}
+
+sk_sp<Texture> Texture::Make(const Gpu* gpu,
+ SkISize dimensions,
+ const skgpu::TextureInfo& info) {
+ sk_cfp<id<MTLTexture>> texture = MakeMtlTexture(gpu, dimensions, info);
+ if (!texture) {
+ return nullptr;
+ }
return sk_sp<Texture>(new Texture(dimensions, info, std::move(texture), Ownership::kOwned));
}
diff --git a/gn/tests.gni b/gn/tests.gni
index c51d9da..1e7d0ff 100644
--- a/gn/tests.gni
+++ b/gn/tests.gni
@@ -330,6 +330,7 @@
]
graphite_tests_sources = [
+ "$_tests/graphite/BackendTextureTest.cpp",
"$_tests/graphite/CommandBufferTest.cpp",
"$_tests/graphite/IntersectionTreeTest.cpp",
"$_tests/graphite/MaskTest.cpp",
@@ -340,6 +341,8 @@
"$_tests/graphite/UniformTest.cpp",
]
+graphite_metal_tests_sources = [ "$_tests/graphite/MtlBackendTextureTest.mm" ]
+
pathops_tests_sources = [
"$_tests/PathOpsAngleIdeas.cpp",
"$_tests/PathOpsAngleTest.cpp",
diff --git a/tests/graphite/BackendTextureTest.cpp b/tests/graphite/BackendTextureTest.cpp
new file mode 100644
index 0000000..6aea980
--- /dev/null
+++ b/tests/graphite/BackendTextureTest.cpp
@@ -0,0 +1,63 @@
+/*
+ * Copyright 2021 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 "experimental/graphite/include/BackendTexture.h"
+#include "experimental/graphite/include/Context.h"
+#include "experimental/graphite/src/Caps.h"
+#include "experimental/graphite/src/ContextPriv.h"
+#include "experimental/graphite/src/Gpu.h"
+#include "experimental/graphite/src/ResourceTypes.h"
+
+using namespace skgpu;
+
+namespace {
+ const SkISize kSize = {16, 16};
+}
+
+DEF_GRAPHITE_TEST_FOR_CONTEXTS(BackendTextureTest, reporter, context) {
+ auto caps = context->priv().gpu()->caps();
+
+ TextureInfo info = caps->getDefaultSampledTextureInfo(kRGBA_8888_SkColorType,
+ /*levelCount=*/1,
+ Protected::kNo,
+ Renderable::kNo);
+ REPORTER_ASSERT(reporter, info.isValid());
+
+ auto texture1 = context->createBackendTexture(kSize, info);
+ REPORTER_ASSERT(reporter, texture1.isValid());
+
+ // We make a copy to do the remaining tests so we still have texture1 to safely delete the
+ // backend object.
+ auto texture1Copy = texture1;
+ REPORTER_ASSERT(reporter, texture1Copy.isValid());
+ REPORTER_ASSERT(reporter, texture1 == texture1Copy);
+
+ auto texture2 = context->createBackendTexture(kSize, info);
+ REPORTER_ASSERT(reporter, texture2.isValid());
+
+ REPORTER_ASSERT(reporter, texture1Copy != texture2);
+
+ // Test state after assignment
+ texture1Copy = texture2;
+ REPORTER_ASSERT(reporter, texture1Copy.isValid());
+ REPORTER_ASSERT(reporter, texture1Copy == texture2);
+
+ BackendTexture invalidTexture;
+ REPORTER_ASSERT(reporter, !invalidTexture.isValid());
+
+ texture1Copy = invalidTexture;
+ REPORTER_ASSERT(reporter, !texture1Copy.isValid());
+
+ texture1Copy = texture1;
+ REPORTER_ASSERT(reporter, texture1Copy.isValid());
+ REPORTER_ASSERT(reporter, texture1 == texture1Copy);
+
+ context->deleteBackendTexture(texture1);
+ context->deleteBackendTexture(texture2);
+}
diff --git a/tests/graphite/MtlBackendTextureTest.mm b/tests/graphite/MtlBackendTextureTest.mm
new file mode 100644
index 0000000..aa8c6ba
--- /dev/null
+++ b/tests/graphite/MtlBackendTextureTest.mm
@@ -0,0 +1,57 @@
+/*
+ * Copyright 2021 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 "experimental/graphite/include/BackendTexture.h"
+#include "experimental/graphite/include/Context.h"
+#include "experimental/graphite/include/mtl/MtlTypes.h"
+
+#import <Metal/Metal.h>
+
+using namespace skgpu;
+
+namespace {
+ const SkISize kSize = {16, 16};
+}
+
+DEF_GRAPHITE_TEST_FOR_CONTEXTS(MtlBackendTextureTest, reporter, context) {
+ mtl::TextureInfo textureInfo;
+ textureInfo.fSampleCount = 1;
+ textureInfo.fLevelCount = 1;
+ textureInfo.fFormat = MTLPixelFormatRGBA8Unorm;
+ textureInfo.fStorageMode = MTLStorageModePrivate;
+ textureInfo.fUsage = MTLTextureUsageShaderRead;
+
+ // TODO: For now we are just testing the basic case of RGBA single sample because that is what
+ // we've added to the backend. However, once we expand the backend support to handle all the
+ // formats this test should iterate over a large set of combinations. See the Ganesh
+ // MtlBackendAllocationTest for example of doing this.
+
+ auto beTexture = context->createBackendTexture(kSize, textureInfo);
+ REPORTER_ASSERT(reporter, beTexture.isValid());
+ context->deleteBackendTexture(beTexture);
+
+ // It should also pass if we set the usage to be a render target
+ textureInfo.fUsage |= MTLTextureUsageRenderTarget;
+ beTexture = context->createBackendTexture(kSize, textureInfo);
+ REPORTER_ASSERT(reporter, beTexture.isValid());
+ context->deleteBackendTexture(beTexture);
+
+ // It should fail with a format that isn't rgba8
+ textureInfo.fFormat = MTLPixelFormatR8Unorm;
+ beTexture = context->createBackendTexture(kSize, textureInfo);
+ REPORTER_ASSERT(reporter, !beTexture.isValid());
+ context->deleteBackendTexture(beTexture);
+
+ // It should fail with a sample count greater than 1
+ textureInfo.fFormat = MTLPixelFormatRGBA8Unorm;
+ textureInfo.fSampleCount = 4;
+ beTexture = context->createBackendTexture(kSize, textureInfo);
+ REPORTER_ASSERT(reporter, !beTexture.isValid());
+ context->deleteBackendTexture(beTexture);
+}