• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1/*
2 * Copyright 2021 Google LLC
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 "src/gpu/graphite/mtl/MtlResourceProvider.h"
9
10#include "include/gpu/ShaderErrorHandler.h"
11#include "include/gpu/graphite/BackendTexture.h"
12#include "src/sksl/SkSLProgramKind.h"
13
14#include "src/core/SkSLTypeShared.h"
15#include "src/gpu/Blend.h"
16#include "src/gpu/PipelineUtils.h"
17#include "src/gpu/Swizzle.h"
18#include "src/gpu/graphite/ComputePipelineDesc.h"
19#include "src/gpu/graphite/ContextUtils.h"
20#include "src/gpu/graphite/GlobalCache.h"
21#include "src/gpu/graphite/GraphicsPipelineDesc.h"
22#include "src/gpu/graphite/RenderPassDesc.h"
23#include "src/gpu/graphite/Renderer.h"
24#include "src/gpu/graphite/RendererProvider.h"
25#include "src/gpu/graphite/compute/ComputeStep.h"
26#include "src/gpu/graphite/mtl/MtlBuffer.h"
27#include "src/gpu/graphite/mtl/MtlCommandBuffer.h"
28#include "src/gpu/graphite/mtl/MtlComputePipeline.h"
29#include "src/gpu/graphite/mtl/MtlGraphicsPipeline.h"
30#include "src/gpu/graphite/mtl/MtlGraphiteUtilsPriv.h"
31#include "src/gpu/graphite/mtl/MtlSampler.h"
32#include "src/gpu/graphite/mtl/MtlSharedContext.h"
33#include "src/gpu/graphite/mtl/MtlTexture.h"
34#include "src/gpu/mtl/MtlUtilsPriv.h"
35#include "src/sksl/SkSLCompiler.h"
36#include "src/sksl/SkSLProgramSettings.h"
37#include "src/sksl/ir/SkSLProgram.h"
38
39#import <Metal/Metal.h>
40
41namespace skgpu::graphite {
42
43MtlResourceProvider::MtlResourceProvider(SharedContext* sharedContext,
44                                         SingleOwner* singleOwner,
45                                         uint32_t recorderID,
46                                         size_t resourceBudget)
47        : ResourceProvider(sharedContext, singleOwner, recorderID, resourceBudget) {}
48
49const MtlSharedContext* MtlResourceProvider::mtlSharedContext() {
50    return static_cast<const MtlSharedContext*>(fSharedContext);
51}
52
53sk_sp<MtlGraphicsPipeline> MtlResourceProvider::findOrCreateLoadMSAAPipeline(
54        const RenderPassDesc& renderPassDesc) {
55    uint64_t renderPassKey =
56            this->mtlSharedContext()->mtlCaps().getRenderPassDescKey(renderPassDesc);
57    sk_sp<MtlGraphicsPipeline> pipeline = fLoadMSAAPipelines[renderPassKey];
58    if (!pipeline) {
59        static const char* kLoadMSAAShaderText = R"(
60                #include <metal_stdlib>
61                #include <simd/simd.h>
62                using namespace metal;
63
64                typedef struct {
65                    float4 position [[position]];
66                } VertexOutput;
67
68                vertex VertexOutput vertexMain(uint vertexID [[vertex_id]]) {
69                    VertexOutput out;
70                    float2 position = float2(float(vertexID >> 1), float(vertexID & 1));
71                    out.position = float4(2.0 * position - 1.0, 0.0, 1.0);
72                    return out;
73                }
74
75                fragment float4 fragmentMain(VertexOutput in [[stage_in]],
76                                             texture2d<half> colorMap [[texture(0)]]) {
77                    uint2 coords = uint2(in.position.x, in.position.y);
78                    half4 colorSample   = colorMap.read(coords);
79                    return float4(colorSample);
80                }
81        )";
82
83        auto mtlLibrary = MtlCompileShaderLibrary(this->mtlSharedContext(),
84                                                  "LoadMSAAFromResolve",
85                                                  kLoadMSAAShaderText,
86                                                  fSharedContext->caps()->shaderErrorHandler());
87        BlendInfo noBlend{}; // default is equivalent to kSrc blending
88        sk_cfp<id<MTLDepthStencilState>> ignoreDS =
89                this->findOrCreateCompatibleDepthStencilState({});
90
91        std::string pipelineLabel = "LoadMSAAFromResolve + ";
92        pipelineLabel += renderPassDesc.toString().c_str();
93        pipeline = MtlGraphicsPipeline::Make(this->mtlSharedContext(),
94                                             pipelineLabel,
95                                             {mtlLibrary.get(), "vertexMain"},
96                                             /*vertexAttrs=*/{},
97                                             /*instanceAttrs=*/{},
98                                             {mtlLibrary.get(), "fragmentMain"},
99                                             std::move(ignoreDS),
100                                             /*stencilRefValue=*/0,
101                                             noBlend,
102                                             renderPassDesc,
103                                             /*pipelineInfo=*/nullptr);
104        if (pipeline) {
105            fLoadMSAAPipelines.set(renderPassKey, pipeline);
106        }
107    }
108
109    return pipeline;
110}
111
112sk_sp<GraphicsPipeline> MtlResourceProvider::createGraphicsPipeline(
113        const RuntimeEffectDictionary* runtimeDict,
114        const GraphicsPipelineDesc& pipelineDesc,
115        const RenderPassDesc& renderPassDesc) {
116    std::string vsMSL, fsMSL;
117    SkSL::Program::Interface vsInterface, fsInterface;
118    SkSL::ProgramSettings settings;
119
120    settings.fForceNoRTFlip = true;
121
122    SkSL::Compiler skslCompiler;
123    ShaderErrorHandler* errorHandler = fSharedContext->caps()->shaderErrorHandler();
124
125    const RenderStep* step =
126            fSharedContext->rendererProvider()->lookup(pipelineDesc.renderStepID());
127    const bool useStorageBuffers = fSharedContext->caps()->storageBufferPreferred();
128
129    UniquePaintParamsID paintID = pipelineDesc.paintParamsID();
130    FragSkSLInfo fsSkSLInfo = BuildFragmentSkSL(fSharedContext->caps(),
131                                                fSharedContext->shaderCodeDictionary(),
132                                                runtimeDict,
133                                                step,
134                                                paintID,
135                                                useStorageBuffers,
136                                                renderPassDesc.fWriteSwizzle);
137    std::string& fsSkSL = fsSkSLInfo.fSkSL;
138    const BlendInfo& blendInfo = fsSkSLInfo.fBlendInfo;
139    const bool localCoordsNeeded = fsSkSLInfo.fRequiresLocalCoords;
140    if (!SkSLToMSL(fSharedContext->caps()->shaderCaps(),
141                   fsSkSL,
142                   SkSL::ProgramKind::kGraphiteFragment,
143                   settings,
144                   &fsMSL,
145                   &fsInterface,
146                   errorHandler)) {
147        return nullptr;
148    }
149
150    VertSkSLInfo vsSkSLInfo = BuildVertexSkSL(fSharedContext->caps()->resourceBindingRequirements(),
151                                              step,
152                                              useStorageBuffers,
153                                              localCoordsNeeded);
154    const std::string& vsSkSL = vsSkSLInfo.fSkSL;
155    if (!SkSLToMSL(fSharedContext->caps()->shaderCaps(),
156                   vsSkSL,
157                   SkSL::ProgramKind::kGraphiteVertex,
158                   settings,
159                   &vsMSL,
160                   &vsInterface,
161                   errorHandler)) {
162        return nullptr;
163    }
164
165    auto vsLibrary = MtlCompileShaderLibrary(
166            this->mtlSharedContext(), vsSkSLInfo.fLabel, vsMSL, errorHandler);
167    auto fsLibrary = MtlCompileShaderLibrary(
168            this->mtlSharedContext(), fsSkSLInfo.fLabel, fsMSL, errorHandler);
169
170    sk_cfp<id<MTLDepthStencilState>> dss =
171            this->findOrCreateCompatibleDepthStencilState(step->depthStencilSettings());
172
173#if defined(GRAPHITE_TEST_UTILS)
174    GraphicsPipeline::PipelineInfo pipelineInfo = {pipelineDesc.renderStepID(),
175                                                   pipelineDesc.paintParamsID(),
176                                                   std::move(vsSkSL),
177                                                   std::move(fsSkSL),
178                                                   std::move(vsMSL),
179                                                   std::move(fsMSL) };
180    GraphicsPipeline::PipelineInfo* pipelineInfoPtr = &pipelineInfo;
181#else
182    GraphicsPipeline::PipelineInfo* pipelineInfoPtr = nullptr;
183#endif
184    std::string pipelineLabel =
185            GetPipelineLabel(fSharedContext->shaderCodeDictionary(), renderPassDesc, step, paintID);
186    return MtlGraphicsPipeline::Make(this->mtlSharedContext(),
187                                     pipelineLabel,
188                                     {vsLibrary.get(), "vertexMain"},
189                                     step->vertexAttributes(),
190                                     step->instanceAttributes(),
191                                     {fsLibrary.get(), "fragmentMain"},
192                                     std::move(dss),
193                                     step->depthStencilSettings().fStencilReferenceValue,
194                                     blendInfo,
195                                     renderPassDesc,
196                                     pipelineInfoPtr);
197}
198
199sk_sp<ComputePipeline> MtlResourceProvider::createComputePipeline(
200        const ComputePipelineDesc& pipelineDesc) {
201    sk_cfp<id<MTLLibrary>> library;
202    std::string entryPointName;
203    ShaderErrorHandler* errorHandler = fSharedContext->caps()->shaderErrorHandler();
204    if (pipelineDesc.computeStep()->supportsNativeShader()) {
205        auto nativeShader = pipelineDesc.computeStep()->nativeShaderSource(
206                ComputeStep::NativeShaderFormat::kMSL);
207        library = MtlCompileShaderLibrary(this->mtlSharedContext(),
208                                          pipelineDesc.computeStep()->name(),
209                                          nativeShader.fSource,
210                                          errorHandler);
211        if (library == nil) {
212            return nullptr;
213        }
214        entryPointName = std::move(nativeShader.fEntryPoint);
215    } else {
216        std::string msl;
217        SkSL::Program::Interface interface;
218        SkSL::ProgramSettings settings;
219
220        SkSL::Compiler skslCompiler;
221        std::string sksl = BuildComputeSkSL(fSharedContext->caps(), pipelineDesc.computeStep());
222        if (!SkSLToMSL(fSharedContext->caps()->shaderCaps(),
223                       sksl,
224                       SkSL::ProgramKind::kCompute,
225                       settings,
226                       &msl,
227                       &interface,
228                       errorHandler)) {
229            return nullptr;
230        }
231        library = MtlCompileShaderLibrary(this->mtlSharedContext(),
232                                          pipelineDesc.computeStep()->name(),
233                                          msl,
234                                          errorHandler);
235        entryPointName = "computeMain";
236    }
237    return MtlComputePipeline::Make(this->mtlSharedContext(),
238                                    pipelineDesc.computeStep()->name(),
239                                    {library.get(), std::move(entryPointName)});
240}
241
242sk_sp<Texture> MtlResourceProvider::createTexture(SkISize dimensions,
243                                                  const TextureInfo& info,
244                                                  skgpu::Budgeted budgeted) {
245    return MtlTexture::Make(this->mtlSharedContext(), dimensions, info, budgeted);
246}
247
248sk_sp<Texture> MtlResourceProvider::onCreateWrappedTexture(const BackendTexture& texture) {
249    CFTypeRef mtlHandleTexture = texture.getMtlTexture();
250    if (!mtlHandleTexture) {
251        return nullptr;
252    }
253    sk_cfp<id<MTLTexture>> mtlTexture = sk_ret_cfp((id<MTLTexture>)mtlHandleTexture);
254    return MtlTexture::MakeWrapped(this->mtlSharedContext(), texture.dimensions(), texture.info(),
255                                   std::move(mtlTexture));
256}
257
258sk_sp<Buffer> MtlResourceProvider::createBuffer(size_t size,
259                                                BufferType type,
260                                                AccessPattern accessPattern) {
261    return MtlBuffer::Make(this->mtlSharedContext(), size, type, accessPattern);
262}
263
264sk_sp<Sampler> MtlResourceProvider::createSampler(const SamplerDesc& samplerDesc) {
265    return MtlSampler::Make(this->mtlSharedContext(),
266                            samplerDesc.samplingOptions(),
267                            samplerDesc.tileModeX(),
268                            samplerDesc.tileModeY());
269}
270
271namespace {
272MTLCompareFunction compare_op_to_mtl(CompareOp op) {
273    switch (op) {
274        case CompareOp::kAlways:
275            return MTLCompareFunctionAlways;
276        case CompareOp::kNever:
277            return MTLCompareFunctionNever;
278        case CompareOp::kGreater:
279            return MTLCompareFunctionGreater;
280        case CompareOp::kGEqual:
281            return MTLCompareFunctionGreaterEqual;
282        case CompareOp::kLess:
283            return MTLCompareFunctionLess;
284        case CompareOp::kLEqual:
285            return MTLCompareFunctionLessEqual;
286        case CompareOp::kEqual:
287            return MTLCompareFunctionEqual;
288        case CompareOp::kNotEqual:
289            return MTLCompareFunctionNotEqual;
290    }
291}
292
293MTLStencilOperation stencil_op_to_mtl(StencilOp op) {
294    switch (op) {
295        case StencilOp::kKeep:
296            return MTLStencilOperationKeep;
297        case StencilOp::kZero:
298            return MTLStencilOperationZero;
299        case StencilOp::kReplace:
300            return MTLStencilOperationReplace;
301        case StencilOp::kInvert:
302            return MTLStencilOperationInvert;
303        case StencilOp::kIncWrap:
304            return MTLStencilOperationIncrementWrap;
305        case StencilOp::kDecWrap:
306            return MTLStencilOperationDecrementWrap;
307        case StencilOp::kIncClamp:
308            return MTLStencilOperationIncrementClamp;
309        case StencilOp::kDecClamp:
310            return MTLStencilOperationDecrementClamp;
311    }
312}
313
314MTLStencilDescriptor* stencil_face_to_mtl(DepthStencilSettings::Face face) {
315    MTLStencilDescriptor* result = [[MTLStencilDescriptor alloc] init];
316    result.stencilCompareFunction = compare_op_to_mtl(face.fCompareOp);
317    result.readMask = face.fReadMask;
318    result.writeMask = face.fWriteMask;
319    result.depthStencilPassOperation = stencil_op_to_mtl(face.fDepthStencilPassOp);
320    result.stencilFailureOperation = stencil_op_to_mtl(face.fStencilFailOp);
321    return result;
322}
323}  // anonymous namespace
324
325sk_cfp<id<MTLDepthStencilState>> MtlResourceProvider::findOrCreateCompatibleDepthStencilState(
326            const DepthStencilSettings& depthStencilSettings) {
327    sk_cfp<id<MTLDepthStencilState>>* depthStencilState;
328    depthStencilState = fDepthStencilStates.find(depthStencilSettings);
329    if (!depthStencilState) {
330        MTLDepthStencilDescriptor* desc = [[MTLDepthStencilDescriptor alloc] init];
331        SkASSERT(depthStencilSettings.fDepthTestEnabled ||
332                 depthStencilSettings.fDepthCompareOp == CompareOp::kAlways);
333        desc.depthCompareFunction = compare_op_to_mtl(depthStencilSettings.fDepthCompareOp);
334        if (depthStencilSettings.fDepthTestEnabled) {
335            desc.depthWriteEnabled = depthStencilSettings.fDepthWriteEnabled;
336        }
337        if (depthStencilSettings.fStencilTestEnabled) {
338            desc.frontFaceStencil = stencil_face_to_mtl(depthStencilSettings.fFrontStencil);
339            desc.backFaceStencil = stencil_face_to_mtl(depthStencilSettings.fBackStencil);
340        }
341
342        sk_cfp<id<MTLDepthStencilState>> dss(
343                [this->mtlSharedContext()->device() newDepthStencilStateWithDescriptor: desc]);
344        depthStencilState = fDepthStencilStates.set(depthStencilSettings, std::move(dss));
345    }
346
347    SkASSERT(depthStencilState);
348    return *depthStencilState;
349}
350
351BackendTexture MtlResourceProvider::onCreateBackendTexture(SkISize dimensions,
352                                                           const TextureInfo& info) {
353    sk_cfp<id<MTLTexture>> texture = MtlTexture::MakeMtlTexture(this->mtlSharedContext(),
354                                                                dimensions,
355                                                                info);
356    if (!texture) {
357        return {};
358    }
359    return BackendTexture(dimensions, (CFTypeRef)texture.release());
360}
361
362void MtlResourceProvider::onDeleteBackendTexture(const BackendTexture& texture) {
363    SkASSERT(texture.backend() == BackendApi::kMetal);
364    CFTypeRef texHandle = texture.getMtlTexture();
365    SkCFSafeRelease(texHandle);
366}
367
368} // namespace skgpu::graphite
369