Add ETC2 support to Metal backend.
Fills out onCreateCompressedTexture and sets iOS caps to support ETC2.
Skia supports no compressed texture formats on MacOS as yet.
Bug: skia:8243
Change-Id: I2ce20f601c035a8822e658c88b815fdd8587aa98
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/240692
Commit-Queue: Jim Van Verth <jvanverth@google.com>
Reviewed-by: Brian Salomon <bsalomon@google.com>
diff --git a/src/gpu/gl/GrGLTexture.cpp b/src/gpu/gl/GrGLTexture.cpp
index 5473980..a5a1a37 100644
--- a/src/gpu/gl/GrGLTexture.cpp
+++ b/src/gpu/gl/GrGLTexture.cpp
@@ -50,7 +50,7 @@
, fParameters(sk_make_sp<GrGLTextureParameters>()) {
this->init(desc);
this->registerWithCache(budgeted);
- if (GrPixelConfigIsCompressed(desc.fConfig)) {
+ if (GrGLFormatIsCompressed(desc.fFormat)) {
this->setReadOnly();
}
}
diff --git a/src/gpu/mtl/GrMtlCaps.mm b/src/gpu/mtl/GrMtlCaps.mm
index 14cb2db..fbadae3 100644
--- a/src/gpu/mtl/GrMtlCaps.mm
+++ b/src/gpu/mtl/GrMtlCaps.mm
@@ -745,8 +745,7 @@
#ifdef SK_BUILD_FOR_IOS
// ETC2_RGB8
info = &fFormatTable[GetFormatIndex(MTLPixelFormatETC2_RGB8)];
- // GrMtlGpu::onCreateCompressedTexture() not implemented.
- info->fFlags = 0;
+ info->fFlags = FormatInfo::kTexturable_Flag;
// NO supported colorTypes
#endif
diff --git a/src/gpu/mtl/GrMtlGpu.h b/src/gpu/mtl/GrMtlGpu.h
index bc5996c..eff8d35 100644
--- a/src/gpu/mtl/GrMtlGpu.h
+++ b/src/gpu/mtl/GrMtlGpu.h
@@ -143,9 +143,7 @@
uint32_t levelClearMask) override;
sk_sp<GrTexture> onCreateCompressedTexture(int width, int height, const GrBackendFormat&,
SkImage::CompressionType, SkBudgeted,
- const void* data) override {
- return nullptr;
- }
+ const void* data) override;
sk_sp<GrTexture> onWrapBackendTexture(const GrBackendTexture&, GrColorType,
GrWrapOwnership, GrWrapCacheable, GrIOType) override;
diff --git a/src/gpu/mtl/GrMtlGpu.mm b/src/gpu/mtl/GrMtlGpu.mm
index a0a4c21..6d61529 100644
--- a/src/gpu/mtl/GrMtlGpu.mm
+++ b/src/gpu/mtl/GrMtlGpu.mm
@@ -455,6 +455,92 @@
return tex;
}
+sk_sp<GrTexture> GrMtlGpu::onCreateCompressedTexture(int width, int height,
+ const GrBackendFormat& format,
+ SkImage::CompressionType compressionType,
+ SkBudgeted budgeted, const void* data) {
+ SkASSERT(this->caps()->isFormatTexturable(format));
+ SkASSERT(data);
+
+ if (!check_max_blit_width(width)) {
+ return nullptr;
+ }
+
+ MTLPixelFormat mtlPixelFormat = GrBackendFormatAsMTLPixelFormat(format);
+
+ // This TexDesc refers to the texture that will be read by the client. Thus even if msaa is
+ // requested, this TexDesc describes the resolved texture. Therefore we always have samples
+ // set to 1.
+ // Compressed textures with MIP levels or multiple samples are not supported as of now.
+ MTLTextureDescriptor* texDesc = [[MTLTextureDescriptor alloc] init];
+ texDesc.textureType = MTLTextureType2D;
+ texDesc.pixelFormat = mtlPixelFormat;
+ texDesc.width = width;
+ texDesc.height = height;
+ texDesc.depth = 1;
+ texDesc.mipmapLevelCount = 1;
+ texDesc.sampleCount = 1;
+ texDesc.arrayLength = 1;
+ // Make all textures have private gpu only access. We can use transfer buffers or textures
+ // to copy to them.
+ texDesc.storageMode = MTLStorageModePrivate;
+ texDesc.usage = MTLTextureUsageShaderRead;
+
+ GrSurfaceDesc desc;
+ desc.fConfig = GrCompressionTypePixelConfig(compressionType);
+ desc.fWidth = width;
+ desc.fHeight = height;
+ auto tex = GrMtlTexture::MakeNewTexture(this, budgeted, desc, texDesc,
+ GrMipMapsStatus::kNotAllocated);
+ if (!tex) {
+ return nullptr;
+ }
+
+ // Upload to texture
+ id<MTLTexture> mtlTexture = tex->mtlTexture();
+ SkASSERT(mtlTexture);
+
+ SkImage::CompressionType textureCompressionType;
+ if (!GrMtlFormatToCompressionType(mtlTexture.pixelFormat, &textureCompressionType) ||
+ textureCompressionType != compressionType) {
+ return nullptr;
+ }
+
+ size_t dataSize = GrCompressedDataSize(compressionType, width, height);
+ SkASSERT(dataSize);
+
+ size_t bufferOffset;
+ id<MTLBuffer> transferBuffer = this->resourceProvider().getDynamicBuffer(dataSize,
+ &bufferOffset);
+ if (!transferBuffer) {
+ return nullptr;
+ }
+ char* buffer = (char*) transferBuffer.contents + bufferOffset;
+
+ MTLOrigin origin = MTLOriginMake(0, 0, 0);
+
+ id<MTLBlitCommandEncoder> blitCmdEncoder = this->commandBuffer()->getBlitCommandEncoder();
+ // sourceBytesPerRow: must be at least 32
+ const size_t rowBytes = SkTMax(dataSize/height, (size_t)32);
+
+ // copy data into the buffer, skipping any trailing bytes
+ memcpy(buffer, data, dataSize);
+ [blitCmdEncoder copyFromBuffer: transferBuffer
+ sourceOffset: bufferOffset
+ sourceBytesPerRow: rowBytes
+ sourceBytesPerImage: dataSize
+ sourceSize: MTLSizeMake(width, height, 1)
+ toTexture: mtlTexture
+ destinationSlice: 0
+ destinationLevel: 0
+ destinationOrigin: origin];
+#ifdef SK_BUILD_FOR_MAC
+ [transferBuffer didModifyRange: NSMakeRange(bufferOffset, dataSize)];
+#endif
+
+ return tex;
+}
+
static id<MTLTexture> get_texture_from_backend(const GrBackendTexture& backendTex) {
GrMtlTextureInfo textureInfo;
if (!backendTex.getMtlTextureInfo(&textureInfo)) {
diff --git a/src/gpu/mtl/GrMtlTexture.mm b/src/gpu/mtl/GrMtlTexture.mm
index a55b066..1e94396 100644
--- a/src/gpu/mtl/GrMtlTexture.mm
+++ b/src/gpu/mtl/GrMtlTexture.mm
@@ -26,6 +26,9 @@
, fTexture(texture) {
SkASSERT((GrMipMapsStatus::kNotAllocated == mipMapsStatus) == (1 == texture.mipmapLevelCount));
this->registerWithCache(budgeted);
+ if (GrMtlFormatIsCompressed(texture.pixelFormat)) {
+ this->setReadOnly();
+ }
}
GrMtlTexture::GrMtlTexture(GrMtlGpu* gpu,
diff --git a/src/gpu/mtl/GrMtlUtil.h b/src/gpu/mtl/GrMtlUtil.h
index 35325f0..e09bd5e 100644
--- a/src/gpu/mtl/GrMtlUtil.h
+++ b/src/gpu/mtl/GrMtlUtil.h
@@ -116,4 +116,14 @@
return static_cast<MTLPixelFormat>(format.asMtlFormat());
}
+/**
+ * Returns true if the format is compressed.
+ */
+bool GrMtlFormatIsCompressed(MTLPixelFormat mtlFormat);
+
+/**
+ * Maps a MTLPixelFormat into the CompressionType enum if applicable.
+ */
+bool GrMtlFormatToCompressionType(MTLPixelFormat mtlFormat,
+ SkImage::CompressionType* compressionType);
#endif
diff --git a/src/gpu/mtl/GrMtlUtil.mm b/src/gpu/mtl/GrMtlUtil.mm
index 62d6290..fbe12ae 100644
--- a/src/gpu/mtl/GrMtlUtil.mm
+++ b/src/gpu/mtl/GrMtlUtil.mm
@@ -330,6 +330,30 @@
SK_ABORT("Invalid Mtl format");
}
+bool GrMtlFormatIsCompressed(MTLPixelFormat mtlFormat) {
+ switch (mtlFormat) {
+#ifdef SK_BUILD_FOR_IOS
+ case MTLPixelFormatETC2_RGB8:
+ return true;
+#endif
+ default:
+ return false;
+ }
+}
+
+bool GrMtlFormatToCompressionType(MTLPixelFormat mtlFormat,
+ SkImage::CompressionType* compressionType) {
+ switch (mtlFormat) {
+#ifdef SK_BUILD_FOR_IOS
+ case MTLPixelFormatETC2_RGB8:
+ *compressionType = SkImage::kETC1_CompressionType;
+ return true;
+#endif
+ default:
+ return false;
+ }
+}
+
#if GR_TEST_UTILS
const char* GrMtlFormatToStr(GrMTLPixelFormat mtlFormat) {
switch (mtlFormat) {
diff --git a/src/gpu/vk/GrVkTexture.cpp b/src/gpu/vk/GrVkTexture.cpp
index 967ecc6..5d09f66 100644
--- a/src/gpu/vk/GrVkTexture.cpp
+++ b/src/gpu/vk/GrVkTexture.cpp
@@ -32,7 +32,7 @@
, fTextureView(view) {
SkASSERT((GrMipMapsStatus::kNotAllocated == mipMapsStatus) == (1 == info.fLevelCount));
this->registerWithCache(budgeted);
- if (GrPixelConfigIsCompressed(desc.fConfig)) {
+ if (GrVkFormatIsCompressed(info.fFormat)) {
this->setReadOnly();
}
}