1/* 2 * Copyright 2018 Google Inc. 3 * 4 * Use of this source code is governed by a BSD-style license that can be 5 * found in the LICENSE file. 6 */ 7 8#include "GrMtlCopyManager.h" 9 10#include "GrSurface.h" 11 12#include "GrMtlBuffer.h" 13#include "GrMtlCopyPipelineState.h" 14#include "GrMtlGpu.h" 15#include "GrMtlResourceProvider.h" 16#include "GrMtlUtil.h" 17 18#include "SkPoint.h" 19#include "SkRect.h" 20#include "SkTraceEvent.h" 21 22#import <simd/simd.h> 23 24void GrMtlCopyManager::createCopyProgramBuffer() { 25 // Create per vertex attribute data for copy as draw 26 static const simd::float2 vdata[4] = { 27 {0, 0}, 28 {0, 1}, 29 {1, 0}, 30 {1, 1}, 31 }; 32 sk_sp<GrMtlBuffer> mtlBuffer = GrMtlBuffer::Make(fGpu, sizeof(vdata), GrGpuBufferType::kVertex, 33 kStatic_GrAccessPattern, vdata); 34 fVertexAttributeBuffer = mtlBuffer->mtlBuffer(); 35} 36 37void GrMtlCopyManager::createCopyProgramShaders() { 38 // Create shaders required by pipeline state 39 const GrShaderCaps* shaderCaps = fGpu->caps()->shaderCaps(); 40 const char* version = shaderCaps->versionDeclString(); 41 SkString vertShaderText(version); 42 vertShaderText.appendf( 43 "#extension GL_ARB_separate_shader_objects : enable\n" 44 "#extension GL_ARB_shading_language_420pack : enable\n" 45 "layout(set = %d"/*kUniform_BufferIndex*/", binding = 0) uniform vertexUniformBuffer {" 46 "float4 uPosXform;" 47 "float4 uTexCoordXform;" 48 "};" 49 "layout(location = 0) in float2 inPosition;" 50 "layout(location = 1) out float2 vTexCoord;" 51 52 "// Copy Program VS\n" 53 "void main() {" 54 "vTexCoord = inPosition * uTexCoordXform.xy + uTexCoordXform.zw;" 55 "sk_Position.xy = inPosition * uPosXform.xy + uPosXform.zw;" 56 "sk_Position.zw = float2(0, 1);" 57 "}", 58 kUniform_BufferIndex 59 ); 60 61 SkString fragShaderText(version); 62 fragShaderText.append( 63 "#extension GL_ARB_separate_shader_objects : enable\n" 64 "#extension GL_ARB_shading_language_420pack : enable\n" 65 66 "layout(set = 1, binding = 0) uniform sampler2D uTexture;" 67 "layout(location = 1) in float2 vTexCoord;" 68 69 "// Copy Program FS\n" 70 "void main() {" 71 "sk_FragColor = texture(uTexture, vTexCoord);" 72 "}" 73 ); 74 75 SkSL::Program::Settings settings; 76 SkSL::Program::Inputs inputs; 77 id<MTLLibrary> vertexLibrary = GrCompileMtlShaderLibrary(fGpu, vertShaderText.c_str(), 78 SkSL::Program::kVertex_Kind, 79 settings, &inputs); 80 SkASSERT(inputs.isEmpty()); 81 SkASSERT(vertexLibrary); 82 83 id<MTLLibrary> fragmentLibrary = GrCompileMtlShaderLibrary(fGpu, fragShaderText.c_str(), 84 SkSL::Program::kFragment_Kind, 85 settings, &inputs); 86 SkASSERT(inputs.isEmpty()); 87 SkASSERT(fragmentLibrary); 88 89 id<MTLFunction> vertexFunction = [vertexLibrary newFunctionWithName: @"vertexMain"]; 90 id<MTLFunction> fragmentFunction = [fragmentLibrary newFunctionWithName: @"fragmentMain"]; 91 SkASSERT(vertexFunction); 92 SkASSERT(fragmentFunction); 93 94 fVertexFunction = vertexFunction; 95 fFragmentFunction = fragmentFunction; 96} 97 98void GrMtlCopyManager::createCopyProgramVertexDescriptor() { 99 // Create vertex descriptor for pipeline state 100 // Expected [[stage_in]] (vertex attribute) MSL format for copies: 101 // 102 // struct Input { 103 // float2 inPosition [[attribute(0)]]; 104 // }; 105 MTLVertexDescriptor* vertexDescriptor = [[MTLVertexDescriptor alloc] init]; 106 vertexDescriptor.attributes[0].format = MTLVertexFormatFloat2; 107 vertexDescriptor.attributes[0].offset = 0; 108 vertexDescriptor.attributes[0].bufferIndex = kAttribute_BufferIndex; 109 110 vertexDescriptor.layouts[kAttribute_BufferIndex].stepFunction = MTLVertexStepFunctionPerVertex; 111 vertexDescriptor.layouts[kAttribute_BufferIndex].stepRate = 1; 112 vertexDescriptor.layouts[kAttribute_BufferIndex].stride = sizeof(simd::float2); 113 114 fVertexDescriptor = vertexDescriptor; 115} 116 117void GrMtlCopyManager::createCopyProgram() { 118 TRACE_EVENT0("skia", TRACE_FUNC); 119 120 MTLSamplerDescriptor* samplerDescriptor = [[MTLSamplerDescriptor alloc] init]; 121 fSamplerState = [fGpu->device() newSamplerStateWithDescriptor: samplerDescriptor]; 122 123 this->createCopyProgramBuffer(); 124 this->createCopyProgramShaders(); 125 this->createCopyProgramVertexDescriptor(); 126} 127 128bool GrMtlCopyManager::copySurfaceAsDraw(GrSurface* dst, GrSurfaceOrigin dstOrigin, 129 GrSurface* src, GrSurfaceOrigin srcOrigin, 130 const SkIRect& srcRect, const SkIPoint& dstPoint, 131 bool canDiscardOutsideDstRect) { 132 SkASSERT(fGpu->mtlCaps().canCopyAsDraw(dst->config(), SkToBool(dst->asRenderTarget()), 133 src->config(), SkToBool(src->asTexture()))); 134 135 id<MTLTexture> dstTex = GrGetMTLTextureFromSurface(dst, false); 136 id<MTLTexture> srcTex = GrGetMTLTextureFromSurface(src, false); 137 138 if (fSamplerState == nil) { 139 SkASSERT(fVertexAttributeBuffer == nil); 140 SkASSERT(fVertexFunction == nil); 141 SkASSERT(fFragmentFunction == nil); 142 SkASSERT(fVertexDescriptor == nil); 143 144 this->createCopyProgram(); 145 } 146 147 if (!(fSamplerState && fVertexAttributeBuffer && fVertexFunction && 148 fFragmentFunction && fVertexDescriptor)) { 149 SkASSERT(false); 150 return false; 151 } 152 153 // UPDATE UNIFORM DESCRIPTOR SET 154 int w = srcRect.width(); 155 int h = srcRect.height(); 156 157 // dst rect edges in NDC (-1 to 1) 158 int dw = dstTex.width; 159 int dh = dstTex.height; 160 float dx0 = 2.f * dstPoint.fX / dw - 1.f; 161 float dx1 = 2.f * (dstPoint.fX + w) / dw - 1.f; 162 float dy0 = 2.f * dstPoint.fY / dh - 1.f; 163 float dy1 = 2.f * (dstPoint.fY + h) / dh - 1.f; 164 if (kBottomLeft_GrSurfaceOrigin == dstOrigin) { 165 dy0 = -dy0; 166 dy1 = -dy1; 167 } 168 169 float sx0 = (float)srcRect.fLeft; 170 float sx1 = (float)(srcRect.fLeft + w); 171 float sy0 = (float)srcRect.fTop; 172 float sy1 = (float)(srcRect.fTop + h); 173 int sh = srcTex.height; 174 if (kBottomLeft_GrSurfaceOrigin == srcOrigin) { 175 sy0 = sh - sy0; 176 sy1 = sh - sy1; 177 } 178 179 // src rect edges in normalized texture space (0 to 1). 180 int sw = srcTex.width; 181 sx0 /= sw; 182 sx1 /= sw; 183 sy0 /= sh; 184 sy1 /= sh; 185 186 const simd::float4 vertexUniformBuffer[2] = { 187 {dx1 - dx0, dy1 - dy0, dx0, dy0}, // posXform 188 {sx1 - sx0, sy1 - sy0, sx0, sy0}, // texCoordXform 189 }; 190 191 MTLRenderPassDescriptor* renderPassDesc = [[MTLRenderPassDescriptor alloc] init]; 192 renderPassDesc.colorAttachments[0].texture = dstTex; 193 renderPassDesc.colorAttachments[0].slice = 0; 194 renderPassDesc.colorAttachments[0].level = 0; 195 renderPassDesc.colorAttachments[0].loadAction = canDiscardOutsideDstRect ? MTLLoadActionDontCare 196 : MTLLoadActionLoad; 197 renderPassDesc.colorAttachments[0].storeAction = MTLStoreActionStore; 198 199 id<MTLRenderCommandEncoder> renderCmdEncoder = 200 [fGpu->commandBuffer() renderCommandEncoderWithDescriptor: renderPassDesc]; 201 GrMtlCopyPipelineState* copyPipelineState = 202 fGpu->resourceProvider().findOrCreateCopyPipelineState(dstTex.pixelFormat, 203 fVertexFunction, 204 fFragmentFunction, 205 fVertexDescriptor); 206 [renderCmdEncoder setRenderPipelineState: copyPipelineState->mtlCopyPipelineState()]; 207 [renderCmdEncoder setVertexBuffer: fVertexAttributeBuffer 208 offset: 0 209 atIndex: kAttribute_BufferIndex]; 210 [renderCmdEncoder setVertexBytes: vertexUniformBuffer 211 length: sizeof(vertexUniformBuffer) 212 atIndex: kUniform_BufferIndex]; 213 [renderCmdEncoder setFragmentTexture: srcTex 214 atIndex: 0]; 215 [renderCmdEncoder setFragmentSamplerState: fSamplerState 216 atIndex: 0]; 217 [renderCmdEncoder drawPrimitives: MTLPrimitiveTypeTriangleStrip 218 vertexStart: 0 219 vertexCount: 4]; 220 [renderCmdEncoder endEncoding]; 221 return true; 222} 223 224bool GrMtlCopyManager::IsCompatible(const GrMtlCopyPipelineState* pipelineState, 225 MTLPixelFormat dstPixelFormat) { 226 return pipelineState->fPixelFormat == dstPixelFormat; 227} 228