Add compressed backend textures to Metal (take 2)

Bug: skia:9680
Change-Id: I88035added2fd1721ef98048bd3344ab90e3da5c
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/266199
Reviewed-by: Greg Daniel <egdaniel@google.com>
Commit-Queue: Robert Phillips <robertphillips@google.com>
This commit is contained in:
Robert Phillips 2020-01-30 08:38:40 -05:00 committed by Skia Commit-Bot
parent 7ad393e58a
commit 314524ea80
3 changed files with 141 additions and 53 deletions

View File

@ -9,6 +9,7 @@
#include "include/core/SkRect.h"
#include "include/gpu/GrBackendSurface.h"
#include "src/core/SkCompressedDataUtils.h"
#include "src/gpu/GrProcessor.h"
#include "src/gpu/GrProgramDesc.h"
#include "src/gpu/GrProgramInfo.h"
@ -873,6 +874,13 @@ bool GrMtlCaps::onSurfaceSupportsWritePixels(const GrSurface* surface) const {
bool GrMtlCaps::onAreColorTypeAndFormatCompatible(GrColorType ct,
const GrBackendFormat& format) const {
MTLPixelFormat mtlFormat = GrBackendFormatAsMTLPixelFormat(format);
SkImage::CompressionType compression = GrMtlFormatToCompressionType(mtlFormat);
if (compression != SkImage::CompressionType::kNone) {
return ct == (SkCompressionTypeIsOpaque(compression) ? GrColorType::kRGB_888x
: GrColorType::kRGBA_8888);
}
const auto& info = this->getFormatInfo(mtlFormat);
for (int i = 0; i < info.fColorTypeInfoCount; ++i) {
if (info.fColorTypeInfos[i].fColorType == ct) {
@ -992,6 +1000,17 @@ GrCaps::SupportedRead GrMtlCaps::onSupportedReadPixelsColorType(
GrColorType dstColorType) const {
MTLPixelFormat mtlFormat = GrBackendFormatAsMTLPixelFormat(srcBackendFormat);
SkImage::CompressionType compression = GrMtlFormatToCompressionType(mtlFormat);
if (compression != SkImage::CompressionType::kNone) {
#ifdef SK_BUILD_FOR_IOS
// Reading back to kRGB_888x doesn't work on Metal/iOS (skbug.com/9839)
return { GrColorType::kUnknown, 0 };
#else
return { SkCompressionTypeIsOpaque(compression) ? GrColorType::kRGB_888x
: GrColorType::kRGBA_8888, 0 };
#endif
}
// Metal requires the destination offset for copyFromTexture to be a multiple of the textures
// pixels size.
size_t offsetAlignment = GrColorTypeBytesPerPixel(srcColorType);

View File

@ -7,6 +7,7 @@
#include "src/gpu/mtl/GrMtlGpu.h"
#include "include/private/GrTypesPriv.h"
#include "src/core/SkCompressedDataUtils.h"
#include "src/core/SkConvertPixels.h"
#include "src/core/SkMipMap.h"
@ -594,7 +595,7 @@ sk_sp<GrTexture> GrMtlGpu::onCreateCompressedTexture(SkISize dimensions,
// TODO: can this all be done in one go?
[blitCmdEncoder copyFromBuffer: transferBuffer
sourceOffset: individualMipOffsets[currentMipLevel]
sourceOffset: bufferOffset + individualMipOffsets[currentMipLevel]
sourceBytesPerRow: levelRowBytes
sourceBytesPerImage: levelSize
sourceSize: MTLSizeMake(levelDimensions.width(),
@ -644,7 +645,8 @@ static inline void init_surface_desc(GrSurfaceDesc* surfaceDesc, id<MTLTexture>
sk_sp<GrTexture> GrMtlGpu::onWrapBackendTexture(const GrBackendTexture& backendTex,
GrColorType grColorType,
GrWrapOwnership,
GrWrapCacheable cacheable, GrIOType ioType) {
GrWrapCacheable cacheable,
GrIOType ioType) {
id<MTLTexture> mtlTexture = get_texture_from_backend(backendTex);
if (!mtlTexture) {
return nullptr;
@ -657,9 +659,17 @@ sk_sp<GrTexture> GrMtlGpu::onWrapBackendTexture(const GrBackendTexture& backendT
}
sk_sp<GrTexture> GrMtlGpu::onWrapCompressedBackendTexture(const GrBackendTexture& backendTex,
GrWrapOwnership ownership,
GrWrapOwnership,
GrWrapCacheable cacheable) {
return nullptr;
id<MTLTexture> mtlTexture = get_texture_from_backend(backendTex);
if (!mtlTexture) {
return nullptr;
}
GrSurfaceDesc surfDesc;
init_surface_desc(&surfDesc, mtlTexture, GrRenderable::kNo);
return GrMtlTexture::MakeWrappedTexture(this, surfDesc, mtlTexture, cacheable, kRead_GrIOType);
}
sk_sp<GrTexture> GrMtlGpu::onWrapRenderableBackendTexture(const GrBackendTexture& backendTex,
@ -788,7 +798,7 @@ void copy_src_data(char* dst, size_t bytesPerPixel, const SkTArray<size_t>& indi
}
}
bool GrMtlGpu::createMtlTextureForBackendSurface(MTLPixelFormat format,
bool GrMtlGpu::createMtlTextureForBackendSurface(MTLPixelFormat mtlFormat,
SkISize dimensions,
GrTexturable texturable,
GrRenderable renderable,
@ -800,15 +810,10 @@ bool GrMtlGpu::createMtlTextureForBackendSurface(MTLPixelFormat format,
SkASSERT(!data && mipMapped == GrMipMapped::kNo);
}
#ifdef SK_BUILD_FOR_IOS
// Compressed formats go through onCreateCompressedBackendTexture
SkASSERT(!GrMtlFormatIsCompressed(format));
#endif
if (texturable == GrTexturable::kYes && !fMtlCaps->isFormatTexturable(format)) {
if (texturable == GrTexturable::kYes && !fMtlCaps->isFormatTexturable(mtlFormat)) {
return false;
}
if (renderable == GrRenderable::kYes && !fMtlCaps->isFormatRenderable(format, 1)) {
if (renderable == GrRenderable::kYes && !fMtlCaps->isFormatRenderable(mtlFormat, 1)) {
return false;
}
@ -817,7 +822,7 @@ bool GrMtlGpu::createMtlTextureForBackendSurface(MTLPixelFormat format,
}
MTLTextureDescriptor* desc =
[MTLTextureDescriptor texture2DDescriptorWithPixelFormat: format
[MTLTextureDescriptor texture2DDescriptorWithPixelFormat: mtlFormat
width: dimensions.width()
height: dimensions.height()
mipmapped: mipMapped == GrMipMapped::kYes];
@ -835,8 +840,6 @@ bool GrMtlGpu::createMtlTextureForBackendSurface(MTLPixelFormat format,
}
// Create the transfer buffer
size_t bytesPerPixel = fMtlCaps->bytesPerPixel(format);
NSUInteger options = 0; // TODO: consider other options here
if (@available(macOS 10.11, iOS 9.0, *)) {
#ifdef SK_BUILD_FOR_MAC
@ -851,69 +854,121 @@ bool GrMtlGpu::createMtlTextureForBackendSurface(MTLPixelFormat format,
numMipLevels = SkMipMap::ComputeLevelCount(dimensions.width(), dimensions.height()) + 1;
}
SkImage::CompressionType compression = GrMtlFormatToCompressionType(mtlFormat);
// Create a transfer buffer and fill with data.
SkSTArray<16, size_t> individualMipOffsets;
id<MTLBuffer> transferBuffer;
size_t transferBufferSize;
SkASSERT(data->type() != BackendTextureData::Type::kCompressed);
if (data->type() == BackendTextureData::Type::kPixmaps) {
transferBufferSize = GrComputeTightCombinedBufferSize(bytesPerPixel, dimensions,
&individualMipOffsets, numMipLevels);
if (data->type() == BackendTextureData::Type::kCompressed ||
data->type() == BackendTextureData::Type::kPixmaps) {
if (compression == SkImage::CompressionType::kNone) {
size_t bytesPerPixel = fMtlCaps->bytesPerPixel(mtlFormat);
transferBufferSize = GrComputeTightCombinedBufferSize(bytesPerPixel, dimensions,
&individualMipOffsets,
numMipLevels);
} else {
transferBufferSize = SkCompressedDataSize(compression, dimensions,
&individualMipOffsets,
mipMapped == GrMipMapped::kYes);
}
SkASSERT(individualMipOffsets.count() == numMipLevels);
transferBuffer = [fDevice newBufferWithLength: transferBufferSize
options: options];
if (nil == transferBuffer) {
return false;
}
char* buffer = (char*)transferBuffer.contents;
copy_src_data(buffer, bytesPerPixel, individualMipOffsets, data->pixmaps(), numMipLevels,
transferBufferSize);
if (data->type() == BackendTextureData::Type::kPixmaps) {
size_t bytesPerPixel = fMtlCaps->bytesPerPixel(mtlFormat);
copy_src_data(buffer, bytesPerPixel, individualMipOffsets, data->pixmaps(),
numMipLevels, transferBufferSize);
} else {
SkASSERT(data->type() == BackendTextureData::Type::kCompressed);
memcpy(buffer, data->compressedData(), data->compressedSize());
}
} else {
SkASSERT(data->type() == BackendTextureData::Type::kColor);
auto colorType = mtl_format_to_backend_tex_clear_colortype(format);
if (colorType == GrColorType::kUnknown) {
return false;
if (compression == SkImage::CompressionType::kNone) {
auto colorType = mtl_format_to_backend_tex_clear_colortype(mtlFormat);
if (colorType == GrColorType::kUnknown) {
return false;
}
GrImageInfo ii(colorType, kUnpremul_SkAlphaType, nullptr, dimensions);
auto rb = ii.minRowBytes();
transferBufferSize = rb*dimensions.height();
transferBuffer = [fDevice newBufferWithLength: transferBufferSize
options: options];
if (nil == transferBuffer) {
return false;
}
if (!GrClearImage(ii, transferBuffer.contents, rb, data->color())) {
return false;
}
// Reuse the same buffer for all levels. Should be ok since we made the row bytes tight.
individualMipOffsets.push_back_n(numMipLevels, (size_t)0);
} else {
transferBufferSize = SkCompressedDataSize(compression, dimensions,
&individualMipOffsets,
mipMapped == GrMipMapped::kYes);
SkASSERT(individualMipOffsets.count() == numMipLevels);
transferBuffer = [fDevice newBufferWithLength: transferBufferSize
options: options];
if (nil == transferBuffer) {
return false;
}
char* buffer = (char*)transferBuffer.contents;
GrFillInCompressedData(compression, dimensions, mipMapped, buffer, data->color());
}
GrImageInfo ii(colorType, kUnpremul_SkAlphaType, nullptr, dimensions);
auto rb = ii.minRowBytes();
transferBufferSize = rb*dimensions.height();
transferBuffer = [fDevice newBufferWithLength: transferBufferSize
options: options];
if (nil == transferBuffer) {
return false;
}
if (!GrClearImage(ii, transferBuffer.contents, rb, data->color())) {
return false;
}
// Reuse the same buffer for all levels. Should be ok since we made the row bytes tight.
individualMipOffsets.push_back_n(numMipLevels, (size_t)0);
}
// Transfer buffer contents to texture
int currentWidth = dimensions.width();
int currentHeight = dimensions.height();
MTLOrigin origin = MTLOriginMake(0, 0, 0);
id<MTLCommandBuffer> cmdBuffer = [fQueue commandBuffer];
id<MTLBlitCommandEncoder> blitCmdEncoder = [cmdBuffer blitCommandEncoder];
SkISize levelDimensions(dimensions);
for (int currentMipLevel = 0; currentMipLevel < numMipLevels; currentMipLevel++) {
size_t trimRowBytes = currentWidth * bytesPerPixel;
size_t levelSize = trimRowBytes*currentHeight;
size_t levelRowBytes;
size_t levelSize;
if (compression == SkImage::CompressionType::kNone) {
size_t bytesPerPixel = fMtlCaps->bytesPerPixel(mtlFormat);
levelRowBytes = levelDimensions.width() * bytesPerPixel;
levelSize = levelRowBytes * levelDimensions.height();
} else {
levelRowBytes = GrCompressedRowBytes(compression, levelDimensions.width());
levelSize = SkCompressedDataSize(compression, levelDimensions, nullptr,
false);
}
// TODO: can this all be done in one go?
[blitCmdEncoder copyFromBuffer: transferBuffer
sourceOffset: individualMipOffsets[currentMipLevel]
sourceBytesPerRow: trimRowBytes
sourceBytesPerRow: levelRowBytes
sourceBytesPerImage: levelSize
sourceSize: MTLSizeMake(currentWidth, currentHeight, 1)
sourceSize: MTLSizeMake(levelDimensions.width(),
levelDimensions.height(), 1)
toTexture: testTexture
destinationSlice: 0
destinationLevel: currentMipLevel
destinationOrigin: origin];
currentWidth = SkTMax(1, currentWidth/2);
currentHeight = SkTMax(1, currentHeight/2);
levelDimensions = { SkTMax(1, levelDimensions.width() / 2),
SkTMax(1, levelDimensions.height() / 2) };
}
#ifdef SK_BUILD_FOR_MAC
[transferBuffer didModifyRange: NSMakeRange(0, transferBufferSize)];
@ -925,7 +980,6 @@ bool GrMtlGpu::createMtlTextureForBackendSurface(MTLPixelFormat format,
transferBuffer = nil;
info->fTexture.reset(GrRetainPtrFromId(testTexture));
return true;
}
@ -935,8 +989,8 @@ GrBackendTexture GrMtlGpu::onCreateBackendTexture(SkISize dimensions,
GrMipMapped mipMapped,
GrProtected isProtected,
const BackendTextureData* data) {
// GrGpu::createBackendTexture should've ensured these conditions
const MTLPixelFormat mtlFormat = GrBackendFormatAsMTLPixelFormat(format);
GrMtlTextureInfo info;
if (!this->createMtlTextureForBackendSurface(mtlFormat, dimensions, GrTexturable::kYes,
renderable, mipMapped, &info, data)) {
@ -948,11 +1002,20 @@ GrBackendTexture GrMtlGpu::onCreateBackendTexture(SkISize dimensions,
}
GrBackendTexture GrMtlGpu::onCreateCompressedBackendTexture(SkISize dimensions,
const GrBackendFormat&,
GrMipMapped,
GrProtected,
const BackendTextureData*) {
return {};
const GrBackendFormat& format,
GrMipMapped mipMapped,
GrProtected isProtected,
const BackendTextureData* data) {
const MTLPixelFormat mtlFormat = GrBackendFormatAsMTLPixelFormat(format);
GrMtlTextureInfo info;
if (!this->createMtlTextureForBackendSurface(mtlFormat, dimensions, GrTexturable::kYes,
GrRenderable::kNo, mipMapped, &info, data)) {
return {};
}
GrBackendTexture backendTex(dimensions.width(), dimensions.height(), mipMapped, info);
return backendTex;
}
void GrMtlGpu::deleteBackendTexture(const GrBackendTexture& tex) {

View File

@ -110,6 +110,13 @@ static void check_readback(GrContext* context, sk_sp<SkImage> img,
SkImage::CompressionType compressionType,
const SkColor4f& expectedColor,
skiatest::Reporter* reporter, const char* label) {
#ifdef SK_BUILD_FOR_IOS
// reading back ETC2 is broken on Metal/iOS (skbug.com/9839)
if (context->backend() == GrBackendApi::kMetal) {
return;
}
#endif
SkAutoPixmapStorage actual;
SkImageInfo readBackII = SkImageInfo::Make(img->width(), img->height(),
@ -137,7 +144,6 @@ static void test_compressed_color_init(GrContext* context,
GrMipMapped mipMapped) {
GrBackendTexture backendTex = create(context, color, mipMapped);
if (!backendTex.isValid()) {
// errors here should be reported by the test_wrapping test
return;
}