From ff19c8f5e7156a016b16180552e12119c823e6a9 Mon Sep 17 00:00:00 2001 From: Timothy Liang Date: Wed, 11 Jul 2018 13:27:21 -0400 Subject: implemented uploading intial texture data for metal gpu backend Bug: skia: Change-Id: I76c820beb9b29b230a2f7d82fe8b6b141eee43d7 Reviewed-on: https://skia-review.googlesource.com/139163 Commit-Queue: Timothy Liang Reviewed-by: Greg Daniel --- src/gpu/mtl/GrMtlGpu.h | 5 +++ src/gpu/mtl/GrMtlGpu.mm | 109 +++++++++++++++++++++++++++++++++++++++++------ src/gpu/mtl/GrMtlUtil.h | 6 +++ src/gpu/mtl/GrMtlUtil.mm | 13 ++++++ 4 files changed, 120 insertions(+), 13 deletions(-) (limited to 'src/gpu/mtl') diff --git a/src/gpu/mtl/GrMtlGpu.h b/src/gpu/mtl/GrMtlGpu.h index f8254031d1..be922e5fdb 100644 --- a/src/gpu/mtl/GrMtlGpu.h +++ b/src/gpu/mtl/GrMtlGpu.h @@ -17,6 +17,7 @@ #import +class GrMtlTexture; class GrSemaphore; struct GrMtlBackendContext; @@ -121,6 +122,10 @@ private: // command buffer to finish before creating a new buffer and returning. void submitCommandBuffer(SyncQueue sync); + // Function that uploads data onto textures with private storage mode (GPU access only). + bool uploadToTexture(GrMtlTexture* tex, int left, int top, int width, int height, + GrColorType dataColorType, const GrMipLevel texels[], int mipLevels); + GrStencilAttachment* createStencilAttachmentForRenderTarget(const GrRenderTarget*, int width, int height) override { diff --git a/src/gpu/mtl/GrMtlGpu.mm b/src/gpu/mtl/GrMtlGpu.mm index 8c5f9ddf67..41ed23e3a5 100644 --- a/src/gpu/mtl/GrMtlGpu.mm +++ b/src/gpu/mtl/GrMtlGpu.mm @@ -10,6 +10,7 @@ #include "GrMtlTexture.h" #include "GrMtlTextureRenderTarget.h" #include "GrMtlUtil.h" +#include "GrTexturePriv.h" #include "SkConvertPixels.h" #if !__has_feature(objc_arc) @@ -120,6 +121,82 @@ void GrMtlGpu::submitCommandBuffer(SyncQueue sync) { fCmdBuffer = [fQueue commandBuffer]; } +static bool check_max_blit_width(int widthInPixels) { + if (widthInPixels > 32767) { + SkASSERT(false); // surfaces should not be this wide anyway + return false; + } + return true; +} + +bool GrMtlGpu::uploadToTexture(GrMtlTexture* tex, int left, int top, int width, int height, + GrColorType dataColorType, const GrMipLevel texels[], + int mipLevelCount) { + SkASSERT(this->caps()->isConfigTexturable(tex->config())); + if (!check_max_blit_width(width)) { + return false; + } + if (width == 0 || height == 0) { + return false; + } + if (GrPixelConfigToColorType(tex->config()) != dataColorType) { + return false; + } + + id mtlTexture = tex->mtlTexture(); + SkASSERT(mtlTexture); + // Either upload only the first miplevel or all miplevels + SkASSERT(1 == mipLevelCount || mipLevelCount == (int)mtlTexture.mipmapLevelCount); + + MTLTextureDescriptor* transferDesc = GrGetMTLTextureDescriptor(mtlTexture); + transferDesc.mipmapLevelCount = mipLevelCount; + transferDesc.cpuCacheMode = MTLCPUCacheModeWriteCombined; + transferDesc.storageMode = MTLStorageModeManaged; + // TODO: implement some way of reusing transfer textures + id transferTexture = [fDevice newTextureWithDescriptor:transferDesc]; + SkASSERT(transferTexture); + + int currentWidth = width; + int currentHeight = height; + size_t bpp = GrColorTypeBytesPerPixel(dataColorType); + MTLOrigin origin = MTLOriginMake(left, top, 0); + + SkASSERT(mtlTexture.pixelFormat == transferTexture.pixelFormat); + SkASSERT(mtlTexture.sampleCount == transferTexture.sampleCount); + + id blitCmdEncoder = [fCmdBuffer blitCommandEncoder]; + for (int currentMipLevel = 0; currentMipLevel < mipLevelCount; currentMipLevel++) { + size_t rowBytes = texels[currentMipLevel].fRowBytes ? texels[currentMipLevel].fRowBytes + : bpp * currentWidth; + SkASSERT(texels[currentMipLevel].fPixels); + if (rowBytes < bpp * currentWidth || rowBytes % bpp) { + return false; + } + [transferTexture replaceRegion: MTLRegionMake2D(left, top, width, height) + mipmapLevel: currentMipLevel + withBytes: texels[currentMipLevel].fPixels + bytesPerRow: rowBytes]; + + [blitCmdEncoder copyFromTexture: transferTexture + sourceSlice: 0 + sourceLevel: currentMipLevel + sourceOrigin: origin + sourceSize: MTLSizeMake(width, height, 1) + toTexture: mtlTexture + destinationSlice: 0 + destinationLevel: currentMipLevel + destinationOrigin: origin]; + currentWidth = SkTMax(1, currentWidth/2); + currentHeight = SkTMax(1, currentHeight/2); + } + [blitCmdEncoder endEncoding]; + + if (mipLevelCount < (int) tex->mtlTexture().mipmapLevelCount) { + tex->texturePriv().markMipMapsDirty(); + } + return true; +} + sk_sp GrMtlGpu::onCreateTexture(const GrSurfaceDesc& desc, SkBudgeted budgeted, const GrMipLevel texels[], int mipLevelCount) { int mipLevels = !mipLevelCount ? 1 : mipLevelCount; @@ -150,19 +227,22 @@ sk_sp GrMtlGpu::onCreateTexture(const GrSurfaceDesc& desc, SkBudgeted // 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 | renderTarget ? MTLTextureUsageRenderTarget : 0; + texDesc.usage = MTLTextureUsageShaderRead; + texDesc.usage |= renderTarget ? MTLTextureUsageRenderTarget : 0; GrMipMapsStatus mipMapsStatus = GrMipMapsStatus::kNotAllocated; if (mipLevels > 1) { - mipMapsStatus = GrMipMapsStatus::kValid; - for (int i = 0; i < mipLevels; ++i) { - if (!texels[i].fPixels) { - mipMapsStatus = GrMipMapsStatus::kDirty; - break; + mipMapsStatus = texels[0].fPixels ? GrMipMapsStatus::kValid : GrMipMapsStatus::kDirty; +#ifdef SK_DEBUG + for (int i = 1; i < mipLevels; ++i) { + if (mipMapsStatus == GrMipMapsStatus::kValid) { + SkASSERT(texels[i].fPixels); + } else { + SkASSERT(!texels[i].fPixels); } } +#endif } - sk_sp tex; if (renderTarget) { tex = GrMtlTextureRenderTarget::CreateNewTextureRenderTarget(this, budgeted, @@ -175,14 +255,19 @@ sk_sp GrMtlGpu::onCreateTexture(const GrSurfaceDesc& desc, SkBudgeted return nullptr; } - if (mipLevelCount) { - // Perform initial data upload here + auto colorType = GrPixelConfigToColorType(desc.fConfig); + if (mipLevelCount && texels[0].fPixels) { + if (!this->uploadToTexture(tex.get(), 0, 0, desc.fWidth, desc.fHeight, colorType, texels, + mipLevelCount)) { + tex->unref(); + return nullptr; + } } if (desc.fFlags & kPerformInitialClear_GrSurfaceFlag) { // Do initial clear of the texture } - return tex; + return std::move(tex); } static id get_texture_from_backend(const GrBackendTexture& backendTex, @@ -281,10 +366,8 @@ sk_sp GrMtlGpu::onWrapBackendTextureAsRenderTarget( bool GrMtlGpu::onReadPixels(GrSurface* surface, int left, int top, int width, int height, GrColorType dstColorType, void* buffer, size_t rowBytes) { - static const int MAX_BLIT_WIDTH = 32767; // in pixels SkASSERT(surface); - if (width > MAX_BLIT_WIDTH) { - SkASSERT(false); // A texture/RT shouldn't be this wide anyway. + if (!check_max_blit_width(width)) { return false; } if (GrPixelConfigToColorType(surface->config()) != dstColorType) { diff --git a/src/gpu/mtl/GrMtlUtil.h b/src/gpu/mtl/GrMtlUtil.h index 932ebec2b5..a184bfa421 100644 --- a/src/gpu/mtl/GrMtlUtil.h +++ b/src/gpu/mtl/GrMtlUtil.h @@ -34,4 +34,10 @@ id GrGetMTLTexture(const void* mtlTexture, GrWrapOwnership); */ const void* GrGetPtrFromId(id idObject); +/** + * Returns a MTLTextureDescriptor which describes the MTLTexture. Useful when creating a duplicate + * MTLTexture without the same storage allocation. + */ +MTLTextureDescriptor* GrGetMTLTextureDescriptor(id mtlTexture); + #endif diff --git a/src/gpu/mtl/GrMtlUtil.mm b/src/gpu/mtl/GrMtlUtil.mm index c9766597e8..b658920191 100644 --- a/src/gpu/mtl/GrMtlUtil.mm +++ b/src/gpu/mtl/GrMtlUtil.mm @@ -126,3 +126,16 @@ const void* GrGetPtrFromId(id idObject) { return (__bridge const void*)idObject; } +MTLTextureDescriptor* GrGetMTLTextureDescriptor(id mtlTexture) { + MTLTextureDescriptor* texDesc = [[MTLTextureDescriptor alloc] init]; + texDesc.textureType = mtlTexture.textureType; + texDesc.pixelFormat = mtlTexture.pixelFormat; + texDesc.width = mtlTexture.width; + texDesc.height = mtlTexture.height; + texDesc.depth = mtlTexture.depth; + texDesc.mipmapLevelCount = mtlTexture.mipmapLevelCount; + texDesc.arrayLength = mtlTexture.arrayLength; + texDesc.sampleCount = mtlTexture.sampleCount; + texDesc.usage = mtlTexture.usage; + return texDesc; +} -- cgit v1.2.3