aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorGravatar Timothy Liang <timliang@google.com>2018-07-11 13:27:21 -0400
committerGravatar Skia Commit-Bot <skia-commit-bot@chromium.org>2018-07-11 18:05:28 +0000
commitff19c8f5e7156a016b16180552e12119c823e6a9 (patch)
treec206b1153ebb9aec2d63aa2e52a8ca72c1c19268
parentf3dc33e4c6f44782cff5dc314da3398c2652fd56 (diff)
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 <timliang@google.com> Reviewed-by: Greg Daniel <egdaniel@google.com>
-rw-r--r--src/gpu/mtl/GrMtlGpu.h5
-rw-r--r--src/gpu/mtl/GrMtlGpu.mm109
-rw-r--r--src/gpu/mtl/GrMtlUtil.h6
-rw-r--r--src/gpu/mtl/GrMtlUtil.mm13
4 files changed, 120 insertions, 13 deletions
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 <Metal/Metal.h>
+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> 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<MTLTexture> 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<MTLBlitCommandEncoder> 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<GrTexture> GrMtlGpu::onCreateTexture(const GrSurfaceDesc& desc, SkBudgeted budgeted,
const GrMipLevel texels[], int mipLevelCount) {
int mipLevels = !mipLevelCount ? 1 : mipLevelCount;
@@ -150,19 +227,22 @@ sk_sp<GrTexture> 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<GrMtlTexture> tex;
if (renderTarget) {
tex = GrMtlTextureRenderTarget::CreateNewTextureRenderTarget(this, budgeted,
@@ -175,14 +255,19 @@ sk_sp<GrTexture> 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<MTLTexture> get_texture_from_backend(const GrBackendTexture& backendTex,
@@ -281,10 +366,8 @@ sk_sp<GrRenderTarget> 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<MTLTexture> 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> 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> 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;
+}