From c25802db30fa371d3984eeef6b04a605e7b7f51f Mon Sep 17 00:00:00 2001 From: Jim Van Verth Date: Mon, 16 Sep 2019 13:57:37 -0400 Subject: [PATCH] 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 Reviewed-by: Brian Salomon --- src/gpu/gl/GrGLTexture.cpp | 2 +- src/gpu/mtl/GrMtlCaps.mm | 3 +- src/gpu/mtl/GrMtlGpu.h | 4 +- src/gpu/mtl/GrMtlGpu.mm | 86 +++++++++++++++++++++++++++++++++++++ src/gpu/mtl/GrMtlTexture.mm | 3 ++ src/gpu/mtl/GrMtlUtil.h | 10 +++++ src/gpu/mtl/GrMtlUtil.mm | 24 +++++++++++ src/gpu/vk/GrVkTexture.cpp | 2 +- 8 files changed, 127 insertions(+), 7 deletions(-) diff --git a/src/gpu/gl/GrGLTexture.cpp b/src/gpu/gl/GrGLTexture.cpp index 5473980d6a..a5a1a3708b 100644 --- a/src/gpu/gl/GrGLTexture.cpp +++ b/src/gpu/gl/GrGLTexture.cpp @@ -50,7 +50,7 @@ GrGLTexture::GrGLTexture(GrGLGpu* gpu, SkBudgeted budgeted, const Desc& desc, , fParameters(sk_make_sp()) { 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 14cb2dbec4..fbadae3431 100644 --- a/src/gpu/mtl/GrMtlCaps.mm +++ b/src/gpu/mtl/GrMtlCaps.mm @@ -745,8 +745,7 @@ void GrMtlCaps::initFormatTable() { #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 bc5996c508..eff8d3514d 100644 --- a/src/gpu/mtl/GrMtlGpu.h +++ b/src/gpu/mtl/GrMtlGpu.h @@ -143,9 +143,7 @@ private: uint32_t levelClearMask) override; sk_sp onCreateCompressedTexture(int width, int height, const GrBackendFormat&, SkImage::CompressionType, SkBudgeted, - const void* data) override { - return nullptr; - } + const void* data) override; sk_sp onWrapBackendTexture(const GrBackendTexture&, GrColorType, GrWrapOwnership, GrWrapCacheable, GrIOType) override; diff --git a/src/gpu/mtl/GrMtlGpu.mm b/src/gpu/mtl/GrMtlGpu.mm index a0a4c21589..6d61529dd9 100644 --- a/src/gpu/mtl/GrMtlGpu.mm +++ b/src/gpu/mtl/GrMtlGpu.mm @@ -455,6 +455,92 @@ sk_sp GrMtlGpu::onCreateTexture(const GrSurfaceDesc& desc, return tex; } +sk_sp 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 = 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 transferBuffer = this->resourceProvider().getDynamicBuffer(dataSize, + &bufferOffset); + if (!transferBuffer) { + return nullptr; + } + char* buffer = (char*) transferBuffer.contents + bufferOffset; + + MTLOrigin origin = MTLOriginMake(0, 0, 0); + + id 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 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 a55b066598..1e94396b92 100644 --- a/src/gpu/mtl/GrMtlTexture.mm +++ b/src/gpu/mtl/GrMtlTexture.mm @@ -26,6 +26,9 @@ GrMtlTexture::GrMtlTexture(GrMtlGpu* gpu, , 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 35325f08ad..e09bd5e644 100644 --- a/src/gpu/mtl/GrMtlUtil.h +++ b/src/gpu/mtl/GrMtlUtil.h @@ -116,4 +116,14 @@ static inline MTLPixelFormat GrBackendFormatAsMTLPixelFormat(const GrBackendForm return static_cast(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 62d62909db..fbe12aeb22 100644 --- a/src/gpu/mtl/GrMtlUtil.mm +++ b/src/gpu/mtl/GrMtlUtil.mm @@ -330,6 +330,30 @@ size_t GrMtlBytesPerFormat(MTLPixelFormat format) { 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 967ecc6e08..5d09f66fca 100644 --- a/src/gpu/vk/GrVkTexture.cpp +++ b/src/gpu/vk/GrVkTexture.cpp @@ -32,7 +32,7 @@ GrVkTexture::GrVkTexture(GrVkGpu* gpu, , fTextureView(view) { SkASSERT((GrMipMapsStatus::kNotAllocated == mipMapsStatus) == (1 == info.fLevelCount)); this->registerWithCache(budgeted); - if (GrPixelConfigIsCompressed(desc.fConfig)) { + if (GrVkFormatIsCompressed(info.fFormat)) { this->setReadOnly(); } }