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:
parent
7ad393e58a
commit
314524ea80
@ -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);
|
||||
|
@ -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) {
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user