Timothy Liang | e30739a | 2018-07-31 10:51:17 -0400 | [diff] [blame] | 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 | |
Timothy Liang | 49528b6 | 2018-08-02 14:18:37 -0400 | [diff] [blame] | 12 | #include "GrMtlBuffer.h" |
Timothy Liang | e30739a | 2018-07-31 10:51:17 -0400 | [diff] [blame] | 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 | |
| 24 | void 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 | }; |
Brian Salomon | ae64c19 | 2019-02-05 09:41:37 -0500 | [diff] [blame] | 32 | sk_sp<GrMtlBuffer> mtlBuffer = GrMtlBuffer::Make(fGpu, sizeof(vdata), GrGpuBufferType::kVertex, |
Brian Salomon | 12d2264 | 2019-01-29 14:38:50 -0500 | [diff] [blame] | 33 | kStatic_GrAccessPattern, vdata); |
Timothy Liang | 49528b6 | 2018-08-02 14:18:37 -0400 | [diff] [blame] | 34 | fVertexAttributeBuffer = mtlBuffer->mtlBuffer(); |
Timothy Liang | e30739a | 2018-07-31 10:51:17 -0400 | [diff] [blame] | 35 | } |
| 36 | |
| 37 | void 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 | |
| 98 | void 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 | |
| 117 | void 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 | |
| 128 | bool 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 | |
| 224 | bool GrMtlCopyManager::IsCompatible(const GrMtlCopyPipelineState* pipelineState, |
| 225 | MTLPixelFormat dstPixelFormat) { |
| 226 | return pipelineState->fPixelFormat == dstPixelFormat; |
| 227 | } |