aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorGravatar Timothy Liang <timliang@google.com>2018-07-31 10:51:17 -0400
committerGravatar Skia Commit-Bot <skia-commit-bot@chromium.org>2018-07-31 16:25:55 +0000
commite30739a1e1d90183bc6bf7414e409e89016881f5 (patch)
tree4b84aeadee82fde5b45ec75aa1782897e2c9625f
parentc8ece3d5f877d67114dfda51a336dfdf7838095a (diff)
implemented copy surface as draw for metal gpu backend
Bug: skia: Change-Id: Ie61bd6ad9f288b8a025d44f887a0954101a7d710 Reviewed-on: https://skia-review.googlesource.com/142687 Commit-Queue: Timothy Liang <timliang@google.com> Reviewed-by: Greg Daniel <egdaniel@google.com>
-rw-r--r--gn/gpu.gni12
-rw-r--r--src/gpu/mtl/GrMtlCaps.h3
-rw-r--r--src/gpu/mtl/GrMtlCaps.mm18
-rw-r--r--src/gpu/mtl/GrMtlCopyManager.h53
-rw-r--r--src/gpu/mtl/GrMtlCopyManager.mm237
-rw-r--r--src/gpu/mtl/GrMtlCopyPipelineState.h37
-rw-r--r--src/gpu/mtl/GrMtlCopyPipelineState.mm37
-rw-r--r--src/gpu/mtl/GrMtlGpu.h16
-rw-r--r--src/gpu/mtl/GrMtlGpu.mm46
-rw-r--r--src/gpu/mtl/GrMtlResourceProvider.h33
-rw-r--r--src/gpu/mtl/GrMtlResourceProvider.mm31
-rw-r--r--src/gpu/mtl/GrMtlUtil.h17
-rw-r--r--src/gpu/mtl/GrMtlUtil.mm84
-rw-r--r--tests/CopySurfaceTest.cpp185
14 files changed, 696 insertions, 113 deletions
diff --git a/gn/gpu.gni b/gn/gpu.gni
index 51a0dc29ec..ca86484d58 100644
--- a/gn/gpu.gni
+++ b/gn/gpu.gni
@@ -626,15 +626,21 @@ skia_metal_sources = [
"$_include/gpu/mtl/GrMtlTypes.h",
"$_src/gpu/mtl/GrMtlCaps.h",
"$_src/gpu/mtl/GrMtlCaps.mm",
+ "$_src/gpu/mtl/GrMtlCopyManager.h",
+ "$_src/gpu/mtl/GrMtlCopyManager.mm",
+ "$_src/gpu/mtl/GrMtlCopyPipelineState.h",
+ "$_src/gpu/mtl/GrMtlCopyPipelineState.mm",
"$_src/gpu/mtl/GrMtlGpu.h",
"$_src/gpu/mtl/GrMtlGpu.mm",
"$_src/gpu/mtl/GrMtlGpuCommandBuffer.h",
- "$_src/gpu/mtl/GrMtlRenderTarget.mm",
"$_src/gpu/mtl/GrMtlRenderTarget.h",
- "$_src/gpu/mtl/GrMtlTexture.mm",
+ "$_src/gpu/mtl/GrMtlRenderTarget.mm",
+ "$_src/gpu/mtl/GrMtlResourceProvider.h",
+ "$_src/gpu/mtl/GrMtlResourceProvider.mm",
"$_src/gpu/mtl/GrMtlTexture.h",
- "$_src/gpu/mtl/GrMtlTextureRenderTarget.mm",
+ "$_src/gpu/mtl/GrMtlTexture.mm",
"$_src/gpu/mtl/GrMtlTextureRenderTarget.h",
+ "$_src/gpu/mtl/GrMtlTextureRenderTarget.mm",
"$_src/gpu/mtl/GrMtlTrampoline.h",
"$_src/gpu/mtl/GrMtlTrampoline.mm",
"$_src/gpu/mtl/GrMtlUtil.h",
diff --git a/src/gpu/mtl/GrMtlCaps.h b/src/gpu/mtl/GrMtlCaps.h
index 5d6c82bea8..11eb322442 100644
--- a/src/gpu/mtl/GrMtlCaps.h
+++ b/src/gpu/mtl/GrMtlCaps.h
@@ -51,6 +51,9 @@ public:
const SkIRect& srcRect, const SkIPoint& dstPoint,
bool areDstSrcSameObj) const;
+ bool canCopyAsDraw(GrPixelConfig dstConfig, bool dstIsRenderable,
+ GrPixelConfig srcConfig, bool srcIsTextureable) const;
+
bool canCopySurface(const GrSurfaceProxy* dst, const GrSurfaceProxy* src,
const SkIRect& srcRect, const SkIPoint& dstPoint) const override;
diff --git a/src/gpu/mtl/GrMtlCaps.mm b/src/gpu/mtl/GrMtlCaps.mm
index 3093352272..dc70d28e2a 100644
--- a/src/gpu/mtl/GrMtlCaps.mm
+++ b/src/gpu/mtl/GrMtlCaps.mm
@@ -129,6 +129,20 @@ bool GrMtlCaps::canCopyAsBlit(GrPixelConfig dstConfig, int dstSampleCount,
return true;
}
+bool GrMtlCaps::canCopyAsDraw(GrPixelConfig dstConfig, bool dstIsRenderable,
+ GrPixelConfig srcConfig, bool srcIsTextureable) const {
+ // TODO: Make copySurfaceAsDraw handle the swizzle
+ if (this->shaderCaps()->configOutputSwizzle(srcConfig) !=
+ this->shaderCaps()->configOutputSwizzle(dstConfig)) {
+ return false;
+ }
+
+ if (!dstIsRenderable || !srcIsTextureable) {
+ return false;
+ }
+ return true;
+}
+
bool GrMtlCaps::canCopySurface(const GrSurfaceProxy* dst, const GrSurfaceProxy* src,
const SkIRect& srcRect, const SkIPoint& dstPoint) const {
GrSurfaceOrigin dstOrigin = dst->origin();
@@ -147,7 +161,9 @@ bool GrMtlCaps::canCopySurface(const GrSurfaceProxy* dst, const GrSurfaceProxy*
return this->canCopyAsBlit(dst->config(), dstSampleCnt, dstOrigin,
src->config(), srcSampleCnt, srcOrigin,
- srcRect, dstPoint, dst == src);
+ srcRect, dstPoint, dst == src) ||
+ this->canCopyAsDraw(dst->config(), SkToBool(dst->asRenderTargetProxy()),
+ src->config(), SkToBool(src->asTextureProxy()));
}
void GrMtlCaps::initGrCaps(const id<MTLDevice> device) {
diff --git a/src/gpu/mtl/GrMtlCopyManager.h b/src/gpu/mtl/GrMtlCopyManager.h
new file mode 100644
index 0000000000..7899397e1c
--- /dev/null
+++ b/src/gpu/mtl/GrMtlCopyManager.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2018 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+*/
+
+#ifndef GrMtlCopyManager_DEFINED
+#define GrMtlCopyManager_DEFINED
+
+#include "GrTypes.h"
+
+#import <metal/metal.h>
+
+class GrMtlCopyPipelineState;
+class GrMtlGpu;
+class GrSurface;
+struct SkIPoint;
+struct SkIRect;
+
+class GrMtlCopyManager {
+public:
+ GrMtlCopyManager(GrMtlGpu* gpu) : fGpu(gpu) {}
+
+ bool copySurfaceAsDraw(GrSurface* dst, GrSurfaceOrigin dstOrigin,
+ GrSurface* src, GrSurfaceOrigin srcOrigin,
+ const SkIRect& srcRect, const SkIPoint& dstPoint,
+ bool canDiscardOutsideDstRect);
+
+ static bool IsCompatible(const GrMtlCopyPipelineState*, MTLPixelFormat dstPixelFormat);
+
+private:
+ enum BufferIndex {
+ kUniform_BufferIndex,
+ kAttribute_BufferIndex,
+ };
+
+ void createCopyProgramBuffer();
+ void createCopyProgramShaders();
+ void createCopyProgramVertexDescriptor();
+
+ void createCopyProgram();
+
+ id<MTLSamplerState> fSamplerState;
+ id<MTLBuffer> fVertexAttributeBuffer;
+ id<MTLFunction> fVertexFunction;
+ id<MTLFunction> fFragmentFunction;
+ MTLVertexDescriptor* fVertexDescriptor;
+
+ GrMtlGpu* fGpu;
+};
+
+#endif
diff --git a/src/gpu/mtl/GrMtlCopyManager.mm b/src/gpu/mtl/GrMtlCopyManager.mm
new file mode 100644
index 0000000000..529bdc1ca1
--- /dev/null
+++ b/src/gpu/mtl/GrMtlCopyManager.mm
@@ -0,0 +1,237 @@
+/*
+ * Copyright 2018 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "GrMtlCopyManager.h"
+
+#include "GrSurface.h"
+
+#include "GrMtlCopyPipelineState.h"
+#include "GrMtlGpu.h"
+#include "GrMtlResourceProvider.h"
+#include "GrMtlUtil.h"
+
+#include "SkPoint.h"
+#include "SkRect.h"
+#include "SkTraceEvent.h"
+
+#import <simd/simd.h>
+
+void GrMtlCopyManager::createCopyProgramBuffer() {
+ // Create per vertex attribute data for copy as draw
+ static const simd::float2 vdata[4] = {
+ {0, 0},
+ {0, 1},
+ {1, 0},
+ {1, 1},
+ };
+ fVertexAttributeBuffer = [fGpu->device() newBufferWithLength: sizeof(vdata)
+ options: MTLResourceStorageModePrivate];
+ id<MTLBuffer> transferBuffer =
+ [fGpu->device() newBufferWithBytes: vdata
+ length: sizeof(vdata)
+ options: MTLResourceStorageModeManaged];
+
+ id<MTLBlitCommandEncoder> blitCmdEncoder = [fGpu->commandBuffer() blitCommandEncoder];
+ [blitCmdEncoder copyFromBuffer: transferBuffer
+ sourceOffset: 0
+ toBuffer: fVertexAttributeBuffer
+ destinationOffset: 0
+ size: sizeof(vdata)];
+ [blitCmdEncoder endEncoding];
+}
+
+void GrMtlCopyManager::createCopyProgramShaders() {
+ // Create shaders required by pipeline state
+ const GrShaderCaps* shaderCaps = fGpu->caps()->shaderCaps();
+ const char* version = shaderCaps->versionDeclString();
+ SkString vertShaderText(version);
+ vertShaderText.appendf(
+ "#extension GL_ARB_separate_shader_objects : enable\n"
+ "#extension GL_ARB_shading_language_420pack : enable\n"
+ "layout(set = %d"/*kUniform_BufferIndex*/", binding = 0) uniform vertexUniformBuffer {"
+ "float4 uPosXform;"
+ "float4 uTexCoordXform;"
+ "};"
+ "layout(location = 0) in float2 inPosition;"
+ "layout(location = 1) out float2 vTexCoord;"
+
+ "// Copy Program VS\n"
+ "void main() {"
+ "vTexCoord = inPosition * uTexCoordXform.xy + uTexCoordXform.zw;"
+ "sk_Position.xy = inPosition * uPosXform.xy + uPosXform.zw;"
+ "sk_Position.zw = float2(0, 1);"
+ "}",
+ kUniform_BufferIndex
+ );
+
+ SkString fragShaderText(version);
+ fragShaderText.append(
+ "#extension GL_ARB_separate_shader_objects : enable\n"
+ "#extension GL_ARB_shading_language_420pack : enable\n"
+
+ "layout(set = 1, binding = 0) uniform sampler2D uTexture;"
+ "layout(location = 1) in float2 vTexCoord;"
+
+ "// Copy Program FS\n"
+ "void main() {"
+ "sk_FragColor = texture(uTexture, vTexCoord);"
+ "}"
+ );
+
+ SkSL::Program::Settings settings;
+ SkSL::Program::Inputs inputs;
+ id<MTLLibrary> vertexLibrary = GrCompileMtlShaderLibrary(fGpu, vertShaderText.c_str(),
+ SkSL::Program::kVertex_Kind,
+ settings, &inputs);
+ SkASSERT(inputs.isEmpty());
+ SkASSERT(vertexLibrary);
+
+ id<MTLLibrary> fragmentLibrary = GrCompileMtlShaderLibrary(fGpu, fragShaderText.c_str(),
+ SkSL::Program::kFragment_Kind,
+ settings, &inputs);
+ SkASSERT(inputs.isEmpty());
+ SkASSERT(fragmentLibrary);
+
+ id<MTLFunction> vertexFunction = [vertexLibrary newFunctionWithName: @"vertexMain"];
+ id<MTLFunction> fragmentFunction = [fragmentLibrary newFunctionWithName: @"fragmentMain"];
+ SkASSERT(vertexFunction);
+ SkASSERT(fragmentFunction);
+
+ fVertexFunction = vertexFunction;
+ fFragmentFunction = fragmentFunction;
+}
+
+void GrMtlCopyManager::createCopyProgramVertexDescriptor() {
+ // Create vertex descriptor for pipeline state
+ // Expected [[stage_in]] (vertex attribute) MSL format for copies:
+ //
+ // struct Input {
+ // float2 inPosition [[attribute(0)]];
+ // };
+ MTLVertexDescriptor* vertexDescriptor = [[MTLVertexDescriptor alloc] init];
+ vertexDescriptor.attributes[0].format = MTLVertexFormatFloat2;
+ vertexDescriptor.attributes[0].offset = 0;
+ vertexDescriptor.attributes[0].bufferIndex = kAttribute_BufferIndex;
+
+ vertexDescriptor.layouts[kAttribute_BufferIndex].stepFunction = MTLVertexStepFunctionPerVertex;
+ vertexDescriptor.layouts[kAttribute_BufferIndex].stepRate = 1;
+ vertexDescriptor.layouts[kAttribute_BufferIndex].stride = sizeof(simd::float2);
+
+ fVertexDescriptor = vertexDescriptor;
+}
+
+void GrMtlCopyManager::createCopyProgram() {
+ TRACE_EVENT0("skia", TRACE_FUNC);
+
+ MTLSamplerDescriptor* samplerDescriptor = [[MTLSamplerDescriptor alloc] init];
+ fSamplerState = [fGpu->device() newSamplerStateWithDescriptor: samplerDescriptor];
+
+ this->createCopyProgramBuffer();
+ this->createCopyProgramShaders();
+ this->createCopyProgramVertexDescriptor();
+}
+
+bool GrMtlCopyManager::copySurfaceAsDraw(GrSurface* dst, GrSurfaceOrigin dstOrigin,
+ GrSurface* src, GrSurfaceOrigin srcOrigin,
+ const SkIRect& srcRect, const SkIPoint& dstPoint,
+ bool canDiscardOutsideDstRect) {
+ SkASSERT(fGpu->mtlCaps().canCopyAsDraw(dst->config(), SkToBool(dst->asRenderTarget()),
+ src->config(), SkToBool(src->asTexture())));
+
+ id<MTLTexture> dstTex = GrGetMTLTextureFromSurface(dst, false);
+ id<MTLTexture> srcTex = GrGetMTLTextureFromSurface(src, false);
+
+ if (fSamplerState == nil) {
+ SkASSERT(fVertexAttributeBuffer == nil);
+ SkASSERT(fVertexFunction == nil);
+ SkASSERT(fFragmentFunction == nil);
+ SkASSERT(fVertexDescriptor == nil);
+
+ this->createCopyProgram();
+ }
+
+ if (!(fSamplerState && fVertexAttributeBuffer && fVertexFunction &&
+ fFragmentFunction && fVertexDescriptor)) {
+ SkASSERT(false);
+ return false;
+ }
+
+ // UPDATE UNIFORM DESCRIPTOR SET
+ int w = srcRect.width();
+ int h = srcRect.height();
+
+ // dst rect edges in NDC (-1 to 1)
+ int dw = dstTex.width;
+ int dh = dstTex.height;
+ float dx0 = 2.f * dstPoint.fX / dw - 1.f;
+ float dx1 = 2.f * (dstPoint.fX + w) / dw - 1.f;
+ float dy0 = 2.f * dstPoint.fY / dh - 1.f;
+ float dy1 = 2.f * (dstPoint.fY + h) / dh - 1.f;
+ if (kBottomLeft_GrSurfaceOrigin == dstOrigin) {
+ dy0 = -dy0;
+ dy1 = -dy1;
+ }
+
+ float sx0 = (float)srcRect.fLeft;
+ float sx1 = (float)(srcRect.fLeft + w);
+ float sy0 = (float)srcRect.fTop;
+ float sy1 = (float)(srcRect.fTop + h);
+ int sh = srcTex.height;
+ if (kBottomLeft_GrSurfaceOrigin == srcOrigin) {
+ sy0 = sh - sy0;
+ sy1 = sh - sy1;
+ }
+
+ // src rect edges in normalized texture space (0 to 1).
+ int sw = srcTex.width;
+ sx0 /= sw;
+ sx1 /= sw;
+ sy0 /= sh;
+ sy1 /= sh;
+
+ const simd::float4 vertexUniformBuffer[2] = {
+ {dx1 - dx0, dy1 - dy0, dx0, dy0}, // posXform
+ {sx1 - sx0, sy1 - sy0, sx0, sy0}, // texCoordXform
+ };
+
+ MTLRenderPassDescriptor* renderPassDesc = [[MTLRenderPassDescriptor alloc] init];
+ renderPassDesc.colorAttachments[0].texture = dstTex;
+ renderPassDesc.colorAttachments[0].slice = 0;
+ renderPassDesc.colorAttachments[0].level = 0;
+ renderPassDesc.colorAttachments[0].loadAction = canDiscardOutsideDstRect ? MTLLoadActionDontCare
+ : MTLLoadActionLoad;
+ renderPassDesc.colorAttachments[0].storeAction = MTLStoreActionStore;
+
+ id<MTLRenderCommandEncoder> renderCmdEncoder =
+ [fGpu->commandBuffer() renderCommandEncoderWithDescriptor: renderPassDesc];
+ GrMtlCopyPipelineState* copyPipelineState =
+ fGpu->resourceProvider().findOrCreateCopyPipelineState(dstTex.pixelFormat,
+ fVertexFunction,
+ fFragmentFunction,
+ fVertexDescriptor);
+ [renderCmdEncoder setRenderPipelineState: copyPipelineState->mtlCopyPipelineState()];
+ [renderCmdEncoder setVertexBuffer: fVertexAttributeBuffer
+ offset: 0
+ atIndex: kAttribute_BufferIndex];
+ [renderCmdEncoder setVertexBytes: vertexUniformBuffer
+ length: sizeof(vertexUniformBuffer)
+ atIndex: kUniform_BufferIndex];
+ [renderCmdEncoder setFragmentTexture: srcTex
+ atIndex: 0];
+ [renderCmdEncoder setFragmentSamplerState: fSamplerState
+ atIndex: 0];
+ [renderCmdEncoder drawPrimitives: MTLPrimitiveTypeTriangleStrip
+ vertexStart: 0
+ vertexCount: 4];
+ [renderCmdEncoder endEncoding];
+ return true;
+}
+
+bool GrMtlCopyManager::IsCompatible(const GrMtlCopyPipelineState* pipelineState,
+ MTLPixelFormat dstPixelFormat) {
+ return pipelineState->fPixelFormat == dstPixelFormat;
+}
diff --git a/src/gpu/mtl/GrMtlCopyPipelineState.h b/src/gpu/mtl/GrMtlCopyPipelineState.h
new file mode 100644
index 0000000000..ce4cbb2bf7
--- /dev/null
+++ b/src/gpu/mtl/GrMtlCopyPipelineState.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2018 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef GrMtlCopyPipelineState_DEFINED
+#define GrMtlCopyPipelineState_DEFINED
+
+#import <metal/metal.h>
+
+class GrMtlGpu;
+
+class GrMtlCopyPipelineState {
+public:
+ static GrMtlCopyPipelineState* CreateCopyPipelineState(GrMtlGpu* gpu,
+ MTLPixelFormat dstPixelFormat,
+ id<MTLFunction> vertexFunction,
+ id<MTLFunction> fragmentFunction,
+ MTLVertexDescriptor* vertexDescriptor);
+
+ id<MTLRenderPipelineState> mtlCopyPipelineState() { return fPipelineState; }
+
+private:
+ GrMtlCopyPipelineState(id<MTLRenderPipelineState> pipelineState,
+ MTLPixelFormat pixelFormat)
+ : fPipelineState(pipelineState)
+ , fPixelFormat(pixelFormat) {}
+
+ id<MTLRenderPipelineState> fPipelineState;
+ MTLPixelFormat fPixelFormat;
+
+ friend class GrMtlCopyManager;
+};
+
+#endif
diff --git a/src/gpu/mtl/GrMtlCopyPipelineState.mm b/src/gpu/mtl/GrMtlCopyPipelineState.mm
new file mode 100644
index 0000000000..27b52e60f2
--- /dev/null
+++ b/src/gpu/mtl/GrMtlCopyPipelineState.mm
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2018 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "GrMtlCopyPipelineState.h"
+#include "GrMtlGpu.h"
+
+GrMtlCopyPipelineState* GrMtlCopyPipelineState::CreateCopyPipelineState(
+ GrMtlGpu* gpu,
+ MTLPixelFormat dstPixelFormat,
+ id<MTLFunction> vertexFunction,
+ id<MTLFunction> fragmentFunction,
+ MTLVertexDescriptor* vertexDescriptor) {
+
+ // Create pipeline state for copy as draw
+ MTLRenderPipelineDescriptor* pipelineDescriptor = [[MTLRenderPipelineDescriptor alloc] init];
+ pipelineDescriptor.vertexFunction = vertexFunction;
+ pipelineDescriptor.fragmentFunction = fragmentFunction;
+ pipelineDescriptor.vertexDescriptor = vertexDescriptor;
+ pipelineDescriptor.colorAttachments[0].pixelFormat = dstPixelFormat;
+
+ NSError* error = nil;
+ id<MTLRenderPipelineState> pipelineState =
+ [gpu->device() newRenderPipelineStateWithDescriptor: pipelineDescriptor
+ error: &error];
+ if (error) {
+ SkDebugf("Error creating pipeline: %s\n",
+ [[error localizedDescription] cStringUsingEncoding: NSASCIIStringEncoding]);
+ return nil;
+ }
+
+ SkASSERT(pipelineState);
+ return new GrMtlCopyPipelineState(pipelineState, dstPixelFormat);
+}
diff --git a/src/gpu/mtl/GrMtlGpu.h b/src/gpu/mtl/GrMtlGpu.h
index 98132fa725..4ba07205e4 100644
--- a/src/gpu/mtl/GrMtlGpu.h
+++ b/src/gpu/mtl/GrMtlGpu.h
@@ -14,6 +14,8 @@
#include "GrTexture.h"
#include "GrMtlCaps.h"
+#include "GrMtlCopyManager.h"
+#include "GrMtlResourceProvider.h"
#import <Metal/Metal.h>
@@ -21,12 +23,16 @@ class GrMtlTexture;
class GrSemaphore;
struct GrMtlBackendContext;
+namespace SkSL {
+ class Compiler;
+}
+
class GrMtlGpu : public GrGpu {
public:
static sk_sp<GrGpu> Make(GrContext* context, const GrContextOptions& options,
id<MTLDevice> device, id<MTLCommandQueue> queue);
- ~GrMtlGpu() override {}
+ ~GrMtlGpu() override = default;
const GrMtlCaps& mtlCaps() const { return *fMtlCaps.get(); }
@@ -34,6 +40,8 @@ public:
id<MTLCommandBuffer> commandBuffer() const { return fCmdBuffer; }
+ GrMtlResourceProvider& resourceProvider() { return fResourceProvider; }
+
enum SyncQueue {
kForce_SyncQueue,
kSkip_SyncQueue
@@ -73,6 +81,8 @@ public:
GrGpuTextureCommandBuffer* createCommandBuffer(GrTexture*, GrSurfaceOrigin) override;
+ SkSL::Compiler* shaderCompiler() const { return fCompiler.get(); }
+
GrFence SK_WARN_UNUSED_RESULT insertFence() override { return 0; }
bool waitFence(GrFence, uint64_t) override { return true; }
void deleteFence(GrFence) const override {}
@@ -162,6 +172,10 @@ private:
id<MTLCommandBuffer> fCmdBuffer;
+ std::unique_ptr<SkSL::Compiler> fCompiler;
+ GrMtlCopyManager fCopyManager;
+ GrMtlResourceProvider fResourceProvider;
+
typedef GrGpu INHERITED;
};
diff --git a/src/gpu/mtl/GrMtlGpu.mm b/src/gpu/mtl/GrMtlGpu.mm
index 79eeb3ad6b..2c47c3c7aa 100644
--- a/src/gpu/mtl/GrMtlGpu.mm
+++ b/src/gpu/mtl/GrMtlGpu.mm
@@ -13,6 +13,9 @@
#include "GrMtlUtil.h"
#include "GrTexturePriv.h"
#include "SkConvertPixels.h"
+#include "SkSLCompiler.h"
+
+#import <simd/simd.h>
#if !__has_feature(objc_arc)
#error This file must be compiled with Arc. Use -fobjc-arc flag
@@ -90,7 +93,10 @@ GrMtlGpu::GrMtlGpu(GrContext* context, const GrContextOptions& options,
id<MTLDevice> device, id<MTLCommandQueue> queue, MTLFeatureSet featureSet)
: INHERITED(context)
, fDevice(device)
- , fQueue(queue) {
+ , fQueue(queue)
+ , fCompiler(new SkSL::Compiler())
+ , fCopyManager(this)
+ , fResourceProvider(this) {
fMtlCaps.reset(new GrMtlCaps(options, fDevice, featureSet));
fCaps = fMtlCaps;
@@ -527,28 +533,6 @@ void GrMtlGpu::testingOnly_flushGpuAndSync() {
}
#endif // GR_TEST_UTILS
-id<MTLTexture> get_mtl_texture_from_surface(GrSurface* surface, bool doResolve) {
- id<MTLTexture> mtlTexture = nil;
-
- GrMtlRenderTarget* renderTarget = static_cast<GrMtlRenderTarget*>(surface->asRenderTarget());
- GrMtlTexture* texture;
- if (renderTarget) {
- if (doResolve) {
- // TODO: do resolve and set mtlTexture to resolved texture. As of now, we shouldn't
- // have any multisampled render targets.
- SkASSERT(false);
- } else {
- mtlTexture = renderTarget->mtlRenderTexture();
- }
- } else {
- texture = static_cast<GrMtlTexture*>(surface->asTexture());
- if (texture) {
- mtlTexture = texture->mtlTexture();
- }
- }
- return mtlTexture;
-}
-
static int get_surface_sample_cnt(GrSurface* surf) {
if (const GrRenderTarget* rt = surf->asRenderTarget()) {
return rt->numColorSamples();
@@ -566,8 +550,8 @@ bool GrMtlGpu::copySurfaceAsBlit(GrSurface* dst, GrSurfaceOrigin dstOrigin,
src->config(), srcSampleCnt, srcOrigin,
srcRect, dstPoint, dst == src));
#endif
- id<MTLTexture> dstTex = get_mtl_texture_from_surface(dst, false);
- id<MTLTexture> srcTex = get_mtl_texture_from_surface(src, false);
+ id<MTLTexture> dstTex = GrGetMTLTextureFromSurface(dst, false);
+ id<MTLTexture> srcTex = GrGetMTLTextureFromSurface(src, false);
// Flip rect if necessary
SkIRect srcMtlRect;
@@ -625,9 +609,13 @@ bool GrMtlGpu::onCopySurface(GrSurface* dst, GrSurfaceOrigin dstOrigin,
}
bool success = false;
- if (this->mtlCaps().canCopyAsBlit(dstConfig, dstSampleCnt, dstOrigin,
- srcConfig, srcSampleCnt, srcOrigin,
- srcRect, dstPoint, dst == src)) {
+ if (this->mtlCaps().canCopyAsDraw(dst->config(), SkToBool(dst->asRenderTarget()),
+ src->config(), SkToBool(src->asTexture()))) {
+ success = fCopyManager.copySurfaceAsDraw(dst, dstOrigin, src, srcOrigin, srcRect, dstPoint,
+ canDiscardOutsideDstRect);
+ } else if (this->mtlCaps().canCopyAsBlit(dstConfig, dstSampleCnt, dstOrigin,
+ srcConfig, srcSampleCnt, srcOrigin,
+ srcRect, dstPoint, dst == src)) {
success = this->copySurfaceAsBlit(dst, dstOrigin, src, srcOrigin, srcRect, dstPoint);
}
if (success) {
@@ -668,7 +656,7 @@ bool GrMtlGpu::onReadPixels(GrSurface* surface, int left, int top, int width, in
}
bool doResolve = get_surface_sample_cnt(surface) > 1;
- id<MTLTexture> mtlTexture = get_mtl_texture_from_surface(surface, doResolve);
+ id<MTLTexture> mtlTexture = GrGetMTLTextureFromSurface(surface, doResolve);
if (!mtlTexture) {
return false;
}
diff --git a/src/gpu/mtl/GrMtlResourceProvider.h b/src/gpu/mtl/GrMtlResourceProvider.h
new file mode 100644
index 0000000000..d5b66ede66
--- /dev/null
+++ b/src/gpu/mtl/GrMtlResourceProvider.h
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2018 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+*/
+
+#ifndef GrMtlResourceProvider_DEFINED
+#define GrMtlResourceProvider_DEFINED
+
+#include "GrMtlCopyPipelineState.h"
+#include "SkTArray.h"
+
+#import <metal/metal.h>
+
+class GrMtlGpu;
+
+class GrMtlResourceProvider {
+public:
+ GrMtlResourceProvider(GrMtlGpu* gpu) : fGpu(gpu) {}
+
+ GrMtlCopyPipelineState* findOrCreateCopyPipelineState(MTLPixelFormat dstPixelFormat,
+ id<MTLFunction> vertexFunction,
+ id<MTLFunction> fragmentFunction,
+ MTLVertexDescriptor* vertexDescriptor);
+
+private:
+ SkTArray<std::unique_ptr<GrMtlCopyPipelineState>> fCopyPipelineStateCache;
+
+ GrMtlGpu* fGpu;
+};
+
+#endif
diff --git a/src/gpu/mtl/GrMtlResourceProvider.mm b/src/gpu/mtl/GrMtlResourceProvider.mm
new file mode 100644
index 0000000000..2e1e40dfe9
--- /dev/null
+++ b/src/gpu/mtl/GrMtlResourceProvider.mm
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2018 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "GrMtlResourceProvider.h"
+
+#include "GrMtlCopyManager.h"
+#include "GrMtlGpu.h"
+#include "GrMtlUtil.h"
+
+#include "SkSLCompiler.h"
+
+GrMtlCopyPipelineState* GrMtlResourceProvider::findOrCreateCopyPipelineState(
+ MTLPixelFormat dstPixelFormat,
+ id<MTLFunction> vertexFunction,
+ id<MTLFunction> fragmentFunction,
+ MTLVertexDescriptor* vertexDescriptor) {
+
+ for (const auto& copyPipelineState: fCopyPipelineStateCache) {
+ if (GrMtlCopyManager::IsCompatible(copyPipelineState.get(), dstPixelFormat)) {
+ return copyPipelineState.get();
+ }
+ }
+
+ fCopyPipelineStateCache.emplace_back(GrMtlCopyPipelineState::CreateCopyPipelineState(
+ fGpu, dstPixelFormat, vertexFunction, fragmentFunction, vertexDescriptor));
+ return fCopyPipelineStateCache.back().get();
+}
diff --git a/src/gpu/mtl/GrMtlUtil.h b/src/gpu/mtl/GrMtlUtil.h
index b3a7829531..fa43295ba1 100644
--- a/src/gpu/mtl/GrMtlUtil.h
+++ b/src/gpu/mtl/GrMtlUtil.h
@@ -11,7 +11,10 @@
#import <Metal/Metal.h>
#include "GrTypesPriv.h"
+#include "ir/SkSLProgram.h"
+class GrMtlGpu;
+class GrSurface;
/**
* Returns the Metal texture format for the given GrPixelConfig
@@ -45,4 +48,18 @@ const void* GrReleaseId(id idObject);
*/
MTLTextureDescriptor* GrGetMTLTextureDescriptor(id<MTLTexture> mtlTexture);
+/**
+ * Returns a compiled MTLLibrary created from MSL code generated by SkSLC
+ */
+id<MTLLibrary> GrCompileMtlShaderLibrary(const GrMtlGpu* gpu,
+ const char* shaderString,
+ SkSL::Program::Kind kind,
+ const SkSL::Program::Settings& settings,
+ SkSL::Program::Inputs* outInputs);
+
+/**
+ * Returns a MTLTexture corresponding to the GrSurface. Optionally can do a resolve.
+ */
+id<MTLTexture> GrGetMTLTextureFromSurface(GrSurface* surface, bool doResolve);
+
#endif
diff --git a/src/gpu/mtl/GrMtlUtil.mm b/src/gpu/mtl/GrMtlUtil.mm
index 5a58a259d4..f544421adc 100644
--- a/src/gpu/mtl/GrMtlUtil.mm
+++ b/src/gpu/mtl/GrMtlUtil.mm
@@ -8,6 +8,15 @@
#include "GrMtlUtil.h"
#include "GrTypesPriv.h"
+#include "GrSurface.h"
+#include "mtl/GrMtlGpu.h"
+#include "mtl/GrMtlTexture.h"
+#include "mtl/GrMtlRenderTarget.h"
+#include "SkSLCompiler.h"
+
+#import <Metal/Metal.h>
+
+#define PRINT_MSL 0 // print out the MSL code generated
bool GrPixelConfigToMTLFormat(GrPixelConfig config, MTLPixelFormat* format) {
MTLPixelFormat dontCare;
@@ -143,3 +152,78 @@ MTLTextureDescriptor* GrGetMTLTextureDescriptor(id<MTLTexture> mtlTexture) {
texDesc.usage = mtlTexture.usage;
return texDesc;
}
+
+#if PRINT_MSL
+void print_msl(const char* source) {
+ SkTArray<SkString> lines;
+ SkStrSplit(source, "\n", kStrict_SkStrSplitMode, &lines);
+ for (int i = 0; i < lines.count(); i++) {
+ SkString& line = lines[i];
+ line.prependf("%4i\t", i + 1);
+ SkDebugf("%s\n", line.c_str());
+ }
+}
+#endif
+
+id<MTLLibrary> GrCompileMtlShaderLibrary(const GrMtlGpu* gpu,
+ const char* shaderString,
+ SkSL::Program::Kind kind,
+ const SkSL::Program::Settings& settings,
+ SkSL::Program::Inputs* outInputs) {
+ std::unique_ptr<SkSL::Program> program =
+ gpu->shaderCompiler()->convertProgram(kind,
+ SkSL::String(shaderString),
+ settings);
+
+ if (!program) {
+ SkDebugf("SkSL error:\n%s\n", gpu->shaderCompiler()->errorText().c_str());
+ SkASSERT(false);
+ }
+
+ *outInputs = program->fInputs;
+ SkSL::String code;
+ if (!gpu->shaderCompiler()->toMetal(*program, &code)) {
+ SkDebugf("%s\n", gpu->shaderCompiler()->errorText().c_str());
+ SkASSERT(false);
+ return nil;
+ }
+ NSString* mtlCode = [[NSString alloc] initWithCString: code.c_str()
+ encoding: NSASCIIStringEncoding];
+#if PRINT_MSL
+ print_msl([mtlCode cStringUsingEncoding: NSASCIIStringEncoding]);
+#endif
+
+ MTLCompileOptions* defaultOptions = [[MTLCompileOptions alloc] init];
+ NSError* error = nil;
+ id<MTLLibrary> compiledLibrary = [gpu->device() newLibraryWithSource: mtlCode
+ options: defaultOptions
+ error: &error];
+ if (error) {
+ SkDebugf("Error compiling MSL shader: %s\n",
+ [[error localizedDescription] cStringUsingEncoding: NSASCIIStringEncoding]);
+ return nil;
+ }
+ return compiledLibrary;
+}
+
+id<MTLTexture> GrGetMTLTextureFromSurface(GrSurface* surface, bool doResolve) {
+ id<MTLTexture> mtlTexture = nil;
+
+ GrMtlRenderTarget* renderTarget = static_cast<GrMtlRenderTarget*>(surface->asRenderTarget());
+ GrMtlTexture* texture;
+ if (renderTarget) {
+ if (doResolve) {
+ // TODO: do resolve and set mtlTexture to resolved texture. As of now, we shouldn't
+ // have any multisampled render targets.
+ SkASSERT(false);
+ } else {
+ mtlTexture = renderTarget->mtlRenderTexture();
+ }
+ } else {
+ texture = static_cast<GrMtlTexture*>(surface->asTexture());
+ if (texture) {
+ mtlTexture = texture->mtlTexture();
+ }
+ }
+ return mtlTexture;
+}
diff --git a/tests/CopySurfaceTest.cpp b/tests/CopySurfaceTest.cpp
index 4921f5bbbe..d2f52498dc 100644
--- a/tests/CopySurfaceTest.cpp
+++ b/tests/CopySurfaceTest.cpp
@@ -15,6 +15,7 @@
#include "GrTextureProxy.h"
#include "GrTypes.h"
#include "ProxyUtils.h"
+#include "SkGr.h"
#include "SkImageInfo.h"
#include "SkPoint.h"
#include "SkRect.h"
@@ -58,7 +59,10 @@ DEF_GPUTEST_FOR_RENDERING_CONTEXTS(CopySurface, reporter, ctxInfo) {
{-1 , -1 },
};
- const SkImageInfo ii = SkImageInfo::Make(kW, kH, kRGBA_8888_SkColorType, kPremul_SkAlphaType);
+ static const SkImageInfo kImageInfos[] {
+ SkImageInfo::Make(kW, kH, kRGBA_8888_SkColorType, kPremul_SkAlphaType),
+ SkImageInfo::Make(kW, kH, kBGRA_8888_SkColorType, kPremul_SkAlphaType)
+ };
SkAutoTMalloc<uint32_t> read(kW * kH);
@@ -68,88 +72,111 @@ DEF_GPUTEST_FOR_RENDERING_CONTEXTS(CopySurface, reporter, ctxInfo) {
for (auto dRT : {true, false}) {
for (auto srcRect : kSrcRects) {
for (auto dstPoint : kDstPoints) {
- auto src = sk_gpu_test::MakeTextureProxyFromData(
- context, sRT, kW, kH, ii.colorType(), sOrigin, srcPixels.get(),
- kRowBytes);
- auto dst = sk_gpu_test::MakeTextureProxyFromData(
- context, dRT, kW, kH, ii.colorType(), dOrigin, dstPixels.get(),
- kRowBytes);
- if (!src || !dst) {
- ERRORF(reporter,
- "Could not create surfaces for copy surface test.");
- continue;
- }
+ for (auto ii: kImageInfos) {
+ auto src = sk_gpu_test::MakeTextureProxyFromData(
+ context, sRT, kW, kH, ii.colorType(), sOrigin,
+ srcPixels.get(), kRowBytes);
+ auto dst = sk_gpu_test::MakeTextureProxyFromData(
+ context, dRT, kW, kH, ii.colorType(), dOrigin,
+ dstPixels.get(), kRowBytes);
+
+ // Should always work if the color type is RGBA, but may not work
+ // for BGRA
+ if (ii.colorType() == kBGRA_8888_SkColorType) {
+ if (!src || !dst) {
+ ERRORF(reporter,
+ "Could not create surfaces for copy surface test.");
+ continue;
+ }
+ } else {
+ GrPixelConfig config =
+ SkColorType2GrPixelConfig(kBGRA_8888_SkColorType);
+ if (!context->contextPriv().caps()->isConfigTexturable(
+ config)) {
+ continue;
+ }
+ if (!src || !dst) {
+ ERRORF(reporter,
+ "Could not create surfaces for copy surface test.");
+ continue;
+ }
+ }
- sk_sp<GrSurfaceContext> dstContext =
- context->contextPriv().makeWrappedSurfaceContext(std::move(dst));
-
- bool result = dstContext->copy(src.get(), srcRect, dstPoint);
-
- bool expectedResult = true;
- SkIPoint dstOffset = { dstPoint.fX - srcRect.fLeft,
- dstPoint.fY - srcRect.fTop };
- SkIRect copiedDstRect = SkIRect::MakeXYWH(dstPoint.fX,
- dstPoint.fY,
- srcRect.width(),
- srcRect.height());
-
- SkIRect copiedSrcRect;
- if (!copiedSrcRect.intersect(srcRect, SkIRect::MakeWH(kW, kH))) {
- expectedResult = false;
- } else {
- // If the src rect was clipped, apply same clipping to each side of
- // copied dst rect.
- copiedDstRect.fLeft += copiedSrcRect.fLeft - srcRect.fLeft;
- copiedDstRect.fTop += copiedSrcRect.fTop - srcRect.fTop;
- copiedDstRect.fRight -= copiedSrcRect.fRight - srcRect.fRight;
- copiedDstRect.fBottom -= copiedSrcRect.fBottom - srcRect.fBottom;
- }
- if (copiedDstRect.isEmpty() ||
- !copiedDstRect.intersect(SkIRect::MakeWH(kW, kH))) {
- expectedResult = false;
- }
- // To make the copied src rect correct we would apply any dst clipping
- // back to the src rect, but we don't use it again so don't bother.
- if (expectedResult != result) {
- ERRORF(reporter, "Expected return value %d from copySurface, got "
- "%d.", expectedResult, result);
- continue;
- }
+ sk_sp<GrSurfaceContext> dstContext =
+ context->contextPriv().makeWrappedSurfaceContext(
+ std::move(dst));
+
+ bool result = dstContext->copy(src.get(), srcRect, dstPoint);
+
+ bool expectedResult = true;
+ SkIPoint dstOffset = { dstPoint.fX - srcRect.fLeft,
+ dstPoint.fY - srcRect.fTop };
+ SkIRect copiedDstRect = SkIRect::MakeXYWH(dstPoint.fX,
+ dstPoint.fY,
+ srcRect.width(),
+ srcRect.height());
+
+ SkIRect copiedSrcRect;
+ if (!copiedSrcRect.intersect(srcRect, SkIRect::MakeWH(kW, kH))) {
+ expectedResult = false;
+ } else {
+ // If the src rect was clipped, apply same clipping to each side
+ // of copied dst rect.
+ copiedDstRect.fLeft += copiedSrcRect.fLeft - srcRect.fLeft;
+ copiedDstRect.fTop += copiedSrcRect.fTop - srcRect.fTop;
+ copiedDstRect.fRight -= copiedSrcRect.fRight - srcRect.fRight;
+ copiedDstRect.fBottom -= copiedSrcRect.fBottom -
+ srcRect.fBottom;
+ }
+ if (copiedDstRect.isEmpty() ||
+ !copiedDstRect.intersect(SkIRect::MakeWH(kW, kH))) {
+ expectedResult = false;
+ }
+ // To make the copied src rect correct we would apply any dst
+ // clipping back to the src rect, but we don't use it again so
+ // don't bother.
+ if (expectedResult != result) {
+ ERRORF(reporter, "Expected return value %d from copySurface, "
+ "got %d.", expectedResult, result);
+ continue;
+ }
- if (!expectedResult || !result) {
- continue;
- }
+ if (!expectedResult || !result) {
+ continue;
+ }
- sk_memset32(read.get(), 0, kW * kH);
- if (!dstContext->readPixels(ii, read.get(), kRowBytes, 0, 0)) {
- ERRORF(reporter, "Error calling readPixels");
- continue;
- }
+ sk_memset32(read.get(), 0, kW * kH);
+ if (!dstContext->readPixels(ii, read.get(), kRowBytes, 0, 0)) {
+ ERRORF(reporter, "Error calling readPixels");
+ continue;
+ }
- bool abort = false;
- // Validate that pixels inside copiedDstRect received the correct value
- // from src and that those outside were not modified.
- for (int y = 0; y < kH && !abort; ++y) {
- for (int x = 0; x < kW; ++x) {
- uint32_t r = read.get()[y * kW + x];
- if (copiedDstRect.contains(x, y)) {
- int sx = x - dstOffset.fX;
- int sy = y - dstOffset.fY;
- uint32_t s = srcPixels.get()[sy * kW + sx];
- if (s != r) {
- ERRORF(reporter, "Expected dst %d,%d to contain "
- "0x%08x copied from src location %d,%d. Got "
- "0x%08x", x, y, s, sx, sy, r);
- abort = true;
- break;
- }
- } else {
- uint32_t d = dstPixels.get()[y * kW + x];
- if (d != r) {
- ERRORF(reporter, "Expected dst %d,%d to be unmodified ("
- "0x%08x). Got 0x%08x", x, y, d, r);
- abort = true;
- break;
+ bool abort = false;
+ // Validate that pixels inside copiedDstRect received the correct
+ // value from src and that those outside were not modified.
+ for (int y = 0; y < kH && !abort; ++y) {
+ for (int x = 0; x < kW; ++x) {
+ uint32_t r = read.get()[y * kW + x];
+ if (copiedDstRect.contains(x, y)) {
+ int sx = x - dstOffset.fX;
+ int sy = y - dstOffset.fY;
+ uint32_t s = srcPixels.get()[sy * kW + sx];
+ if (s != r) {
+ ERRORF(reporter, "Expected dst %d,%d to contain "
+ "0x%08x copied from src location %d,%d. Got "
+ "0x%08x", x, y, s, sx, sy, r);
+ abort = true;
+ break;
+ }
+ } else {
+ uint32_t d = dstPixels.get()[y * kW + x];
+ if (d != r) {
+ ERRORF(reporter, "Expected dst %d,%d to be "
+ "unmodified (0x%08x). Got 0x%08x",
+ x, y, d, r);
+ abort = true;
+ break;
+ }
}
}
}