aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/gpu/mtl/GrMtlCopyManager.mm
diff options
context:
space:
mode:
Diffstat (limited to 'src/gpu/mtl/GrMtlCopyManager.mm')
-rw-r--r--src/gpu/mtl/GrMtlCopyManager.mm237
1 files changed, 237 insertions, 0 deletions
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;
+}