From e30739a1e1d90183bc6bf7414e409e89016881f5 Mon Sep 17 00:00:00 2001 From: Timothy Liang Date: Tue, 31 Jul 2018 10:51:17 -0400 Subject: 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 Reviewed-by: Greg Daniel --- gn/gpu.gni | 12 +- src/gpu/mtl/GrMtlCaps.h | 3 + src/gpu/mtl/GrMtlCaps.mm | 18 ++- src/gpu/mtl/GrMtlCopyManager.h | 53 ++++++++ src/gpu/mtl/GrMtlCopyManager.mm | 237 ++++++++++++++++++++++++++++++++++ src/gpu/mtl/GrMtlCopyPipelineState.h | 37 ++++++ src/gpu/mtl/GrMtlCopyPipelineState.mm | 37 ++++++ src/gpu/mtl/GrMtlGpu.h | 16 ++- src/gpu/mtl/GrMtlGpu.mm | 46 +++---- src/gpu/mtl/GrMtlResourceProvider.h | 33 +++++ src/gpu/mtl/GrMtlResourceProvider.mm | 31 +++++ src/gpu/mtl/GrMtlUtil.h | 17 +++ src/gpu/mtl/GrMtlUtil.mm | 84 ++++++++++++ tests/CopySurfaceTest.cpp | 185 ++++++++++++++------------ 14 files changed, 696 insertions(+), 113 deletions(-) create mode 100644 src/gpu/mtl/GrMtlCopyManager.h create mode 100644 src/gpu/mtl/GrMtlCopyManager.mm create mode 100644 src/gpu/mtl/GrMtlCopyPipelineState.h create mode 100644 src/gpu/mtl/GrMtlCopyPipelineState.mm create mode 100644 src/gpu/mtl/GrMtlResourceProvider.h create mode 100644 src/gpu/mtl/GrMtlResourceProvider.mm 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 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 + +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 fSamplerState; + id fVertexAttributeBuffer; + id fVertexFunction; + id 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 + +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 transferBuffer = + [fGpu->device() newBufferWithBytes: vdata + length: sizeof(vdata) + options: MTLResourceStorageModeManaged]; + + id 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 vertexLibrary = GrCompileMtlShaderLibrary(fGpu, vertShaderText.c_str(), + SkSL::Program::kVertex_Kind, + settings, &inputs); + SkASSERT(inputs.isEmpty()); + SkASSERT(vertexLibrary); + + id fragmentLibrary = GrCompileMtlShaderLibrary(fGpu, fragShaderText.c_str(), + SkSL::Program::kFragment_Kind, + settings, &inputs); + SkASSERT(inputs.isEmpty()); + SkASSERT(fragmentLibrary); + + id vertexFunction = [vertexLibrary newFunctionWithName: @"vertexMain"]; + id 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 dstTex = GrGetMTLTextureFromSurface(dst, false); + id 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 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 + +class GrMtlGpu; + +class GrMtlCopyPipelineState { +public: + static GrMtlCopyPipelineState* CreateCopyPipelineState(GrMtlGpu* gpu, + MTLPixelFormat dstPixelFormat, + id vertexFunction, + id fragmentFunction, + MTLVertexDescriptor* vertexDescriptor); + + id mtlCopyPipelineState() { return fPipelineState; } + +private: + GrMtlCopyPipelineState(id pipelineState, + MTLPixelFormat pixelFormat) + : fPipelineState(pipelineState) + , fPixelFormat(pixelFormat) {} + + id 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 vertexFunction, + id 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 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 @@ -21,12 +23,16 @@ class GrMtlTexture; class GrSemaphore; struct GrMtlBackendContext; +namespace SkSL { + class Compiler; +} + class GrMtlGpu : public GrGpu { public: static sk_sp Make(GrContext* context, const GrContextOptions& options, id device, id queue); - ~GrMtlGpu() override {} + ~GrMtlGpu() override = default; const GrMtlCaps& mtlCaps() const { return *fMtlCaps.get(); } @@ -34,6 +40,8 @@ public: id 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 fCmdBuffer; + std::unique_ptr 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 #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 device, id 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 get_mtl_texture_from_surface(GrSurface* surface, bool doResolve) { - id mtlTexture = nil; - - GrMtlRenderTarget* renderTarget = static_cast(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(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 dstTex = get_mtl_texture_from_surface(dst, false); - id srcTex = get_mtl_texture_from_surface(src, false); + id dstTex = GrGetMTLTextureFromSurface(dst, false); + id 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 = get_mtl_texture_from_surface(surface, doResolve); + id 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 + +class GrMtlGpu; + +class GrMtlResourceProvider { +public: + GrMtlResourceProvider(GrMtlGpu* gpu) : fGpu(gpu) {} + + GrMtlCopyPipelineState* findOrCreateCopyPipelineState(MTLPixelFormat dstPixelFormat, + id vertexFunction, + id fragmentFunction, + MTLVertexDescriptor* vertexDescriptor); + +private: + SkTArray> 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 vertexFunction, + id 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 #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); +/** + * Returns a compiled MTLLibrary created from MSL code generated by SkSLC + */ +id 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 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 + +#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) { texDesc.usage = mtlTexture.usage; return texDesc; } + +#if PRINT_MSL +void print_msl(const char* source) { + SkTArray 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 GrCompileMtlShaderLibrary(const GrMtlGpu* gpu, + const char* shaderString, + SkSL::Program::Kind kind, + const SkSL::Program::Settings& settings, + SkSL::Program::Inputs* outInputs) { + std::unique_ptr 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 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 GrGetMTLTextureFromSurface(GrSurface* surface, bool doResolve) { + id mtlTexture = nil; + + GrMtlRenderTarget* renderTarget = static_cast(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(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 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 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 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; + } } } } -- cgit v1.2.3