1cb93a386Sopenharmony_ci/*
2cb93a386Sopenharmony_ci * Copyright 2018 Google Inc.
3cb93a386Sopenharmony_ci *
4cb93a386Sopenharmony_ci * Use of this source code is governed by a BSD-style license that can be
5cb93a386Sopenharmony_ci * found in the LICENSE file.
6cb93a386Sopenharmony_ci */
7cb93a386Sopenharmony_ci
8cb93a386Sopenharmony_ci#include "src/gpu/mtl/GrMtlResourceProvider.h"
9cb93a386Sopenharmony_ci
10cb93a386Sopenharmony_ci#include "include/gpu/GrContextOptions.h"
11cb93a386Sopenharmony_ci#include "include/gpu/GrDirectContext.h"
12cb93a386Sopenharmony_ci#include "src/core/SkTraceEvent.h"
13cb93a386Sopenharmony_ci#include "src/gpu/GrDirectContextPriv.h"
14cb93a386Sopenharmony_ci#include "src/gpu/GrProgramDesc.h"
15cb93a386Sopenharmony_ci#include "src/gpu/mtl/GrMtlCommandBuffer.h"
16cb93a386Sopenharmony_ci#include "src/gpu/mtl/GrMtlGpu.h"
17cb93a386Sopenharmony_ci#include "src/gpu/mtl/GrMtlPipelineState.h"
18cb93a386Sopenharmony_ci#include "src/gpu/mtl/GrMtlUtil.h"
19cb93a386Sopenharmony_ci
20cb93a386Sopenharmony_ci#include "src/sksl/SkSLCompiler.h"
21cb93a386Sopenharmony_ci
22cb93a386Sopenharmony_ci#if !__has_feature(objc_arc)
23cb93a386Sopenharmony_ci#error This file must be compiled with Arc. Use -fobjc-arc flag
24cb93a386Sopenharmony_ci#endif
25cb93a386Sopenharmony_ci
26cb93a386Sopenharmony_ciGR_NORETAIN_BEGIN
27cb93a386Sopenharmony_ci
28cb93a386Sopenharmony_ciGrMtlResourceProvider::GrMtlResourceProvider(GrMtlGpu* gpu)
29cb93a386Sopenharmony_ci    : fGpu(gpu) {
30cb93a386Sopenharmony_ci    fPipelineStateCache.reset(new PipelineStateCache(gpu));
31cb93a386Sopenharmony_ci}
32cb93a386Sopenharmony_ci
33cb93a386Sopenharmony_ciGrMtlPipelineState* GrMtlResourceProvider::findOrCreateCompatiblePipelineState(
34cb93a386Sopenharmony_ci        const GrProgramDesc& programDesc,
35cb93a386Sopenharmony_ci        const GrProgramInfo& programInfo,
36cb93a386Sopenharmony_ci        GrThreadSafePipelineBuilder::Stats::ProgramCacheResult* stat) {
37cb93a386Sopenharmony_ci    return fPipelineStateCache->refPipelineState(programDesc, programInfo, stat);
38cb93a386Sopenharmony_ci}
39cb93a386Sopenharmony_ci
40cb93a386Sopenharmony_cibool GrMtlResourceProvider::precompileShader(const SkData& key, const SkData& data) {
41cb93a386Sopenharmony_ci    return fPipelineStateCache->precompileShader(key, data);
42cb93a386Sopenharmony_ci}
43cb93a386Sopenharmony_ci
44cb93a386Sopenharmony_ci////////////////////////////////////////////////////////////////////////////////////////////////
45cb93a386Sopenharmony_ci
46cb93a386Sopenharmony_ciGrMtlDepthStencil* GrMtlResourceProvider::findOrCreateCompatibleDepthStencilState(
47cb93a386Sopenharmony_ci        const GrStencilSettings& stencil, GrSurfaceOrigin origin) {
48cb93a386Sopenharmony_ci    GrMtlDepthStencil* depthStencilState;
49cb93a386Sopenharmony_ci    GrMtlDepthStencil::Key key = GrMtlDepthStencil::GenerateKey(stencil, origin);
50cb93a386Sopenharmony_ci    depthStencilState = fDepthStencilStates.find(key);
51cb93a386Sopenharmony_ci    if (!depthStencilState) {
52cb93a386Sopenharmony_ci        depthStencilState = GrMtlDepthStencil::Create(fGpu, stencil, origin);
53cb93a386Sopenharmony_ci        fDepthStencilStates.add(depthStencilState);
54cb93a386Sopenharmony_ci    }
55cb93a386Sopenharmony_ci    SkASSERT(depthStencilState);
56cb93a386Sopenharmony_ci    return depthStencilState;
57cb93a386Sopenharmony_ci}
58cb93a386Sopenharmony_ci
59cb93a386Sopenharmony_ciGrMtlSampler* GrMtlResourceProvider::findOrCreateCompatibleSampler(GrSamplerState params) {
60cb93a386Sopenharmony_ci    GrMtlSampler* sampler;
61cb93a386Sopenharmony_ci    sampler = fSamplers.find(GrMtlSampler::GenerateKey(params));
62cb93a386Sopenharmony_ci    if (!sampler) {
63cb93a386Sopenharmony_ci        sampler = GrMtlSampler::Create(fGpu, params);
64cb93a386Sopenharmony_ci        fSamplers.add(sampler);
65cb93a386Sopenharmony_ci    }
66cb93a386Sopenharmony_ci    SkASSERT(sampler);
67cb93a386Sopenharmony_ci    return sampler;
68cb93a386Sopenharmony_ci}
69cb93a386Sopenharmony_ci
70cb93a386Sopenharmony_ciconst GrMtlRenderPipeline* GrMtlResourceProvider::findOrCreateMSAALoadPipeline(
71cb93a386Sopenharmony_ci        MTLPixelFormat colorFormat, int sampleCount, MTLPixelFormat stencilFormat) {
72cb93a386Sopenharmony_ci    if (!fMSAALoadLibrary) {
73cb93a386Sopenharmony_ci        TRACE_EVENT0("skia", TRACE_FUNC);
74cb93a386Sopenharmony_ci
75cb93a386Sopenharmony_ci        SkSL::String shaderText;
76cb93a386Sopenharmony_ci        shaderText.append(
77cb93a386Sopenharmony_ci                "#include <metal_stdlib>\n"
78cb93a386Sopenharmony_ci                "#include <simd/simd.h>\n"
79cb93a386Sopenharmony_ci                "using namespace metal;\n"
80cb93a386Sopenharmony_ci                "\n"
81cb93a386Sopenharmony_ci                "typedef struct {\n"
82cb93a386Sopenharmony_ci                "    float4 position [[position]];\n"
83cb93a386Sopenharmony_ci                "} VertexOutput;\n"
84cb93a386Sopenharmony_ci                "\n"
85cb93a386Sopenharmony_ci                "typedef struct {\n"
86cb93a386Sopenharmony_ci                "    float4 uPosXform;\n"
87cb93a386Sopenharmony_ci                "    uint2 uTextureSize;\n"
88cb93a386Sopenharmony_ci                "} VertexUniforms;\n"
89cb93a386Sopenharmony_ci                "\n"
90cb93a386Sopenharmony_ci                "vertex VertexOutput vertexMain(constant VertexUniforms& uniforms [[buffer(0)]],\n"
91cb93a386Sopenharmony_ci                "                               uint vertexID [[vertex_id]]) {\n"
92cb93a386Sopenharmony_ci                "    VertexOutput out;\n"
93cb93a386Sopenharmony_ci                "    float2 position = float2(float(vertexID >> 1), float(vertexID & 1));\n"
94cb93a386Sopenharmony_ci                "    out.position.xy = position * uniforms.uPosXform.xy + uniforms.uPosXform.zw;\n"
95cb93a386Sopenharmony_ci                "    out.position.zw = float2(0.0, 1.0);\n"
96cb93a386Sopenharmony_ci                "    return out;\n"
97cb93a386Sopenharmony_ci                "}\n"
98cb93a386Sopenharmony_ci                "\n"
99cb93a386Sopenharmony_ci                "fragment float4 fragmentMain(VertexOutput in [[stage_in]],\n"
100cb93a386Sopenharmony_ci                "                             texture2d<half> colorMap [[texture(0)]]) {\n"
101cb93a386Sopenharmony_ci                "    uint2 coords = uint2(in.position.x, in.position.y);"
102cb93a386Sopenharmony_ci                "    half4 colorSample   = colorMap.read(coords);\n"
103cb93a386Sopenharmony_ci                "    return float4(colorSample);\n"
104cb93a386Sopenharmony_ci                "}"
105cb93a386Sopenharmony_ci        );
106cb93a386Sopenharmony_ci
107cb93a386Sopenharmony_ci        auto errorHandler = fGpu->getContext()->priv().getShaderErrorHandler();
108cb93a386Sopenharmony_ci        fMSAALoadLibrary = GrCompileMtlShaderLibrary(fGpu, shaderText, errorHandler);
109cb93a386Sopenharmony_ci        if (!fMSAALoadLibrary) {
110cb93a386Sopenharmony_ci            return nullptr;
111cb93a386Sopenharmony_ci        }
112cb93a386Sopenharmony_ci    }
113cb93a386Sopenharmony_ci
114cb93a386Sopenharmony_ci    for (int i = 0; i < fMSAALoadPipelines.count(); ++i) {
115cb93a386Sopenharmony_ci        if (fMSAALoadPipelines[i].fColorFormat == colorFormat &&
116cb93a386Sopenharmony_ci            fMSAALoadPipelines[i].fSampleCount == sampleCount &&
117cb93a386Sopenharmony_ci            fMSAALoadPipelines[i].fStencilFormat == stencilFormat) {
118cb93a386Sopenharmony_ci            return fMSAALoadPipelines[i].fPipeline.get();
119cb93a386Sopenharmony_ci        }
120cb93a386Sopenharmony_ci    }
121cb93a386Sopenharmony_ci
122cb93a386Sopenharmony_ci    auto pipelineDescriptor = [[MTLRenderPipelineDescriptor alloc] init];
123cb93a386Sopenharmony_ci
124cb93a386Sopenharmony_ci    pipelineDescriptor.label = @"loadMSAAFromResolve";
125cb93a386Sopenharmony_ci
126cb93a386Sopenharmony_ci    pipelineDescriptor.vertexFunction =
127cb93a386Sopenharmony_ci            [fMSAALoadLibrary newFunctionWithName: @"vertexMain"];
128cb93a386Sopenharmony_ci    pipelineDescriptor.fragmentFunction =
129cb93a386Sopenharmony_ci            [fMSAALoadLibrary newFunctionWithName: @"fragmentMain"];
130cb93a386Sopenharmony_ci
131cb93a386Sopenharmony_ci    auto mtlColorAttachment = [[MTLRenderPipelineColorAttachmentDescriptor alloc] init];
132cb93a386Sopenharmony_ci
133cb93a386Sopenharmony_ci    mtlColorAttachment.pixelFormat = colorFormat;
134cb93a386Sopenharmony_ci    mtlColorAttachment.blendingEnabled = FALSE;
135cb93a386Sopenharmony_ci    mtlColorAttachment.writeMask = MTLColorWriteMaskAll;
136cb93a386Sopenharmony_ci
137cb93a386Sopenharmony_ci    pipelineDescriptor.colorAttachments[0] = mtlColorAttachment;
138cb93a386Sopenharmony_ci    pipelineDescriptor.sampleCount = sampleCount;
139cb93a386Sopenharmony_ci
140cb93a386Sopenharmony_ci    pipelineDescriptor.stencilAttachmentPixelFormat = stencilFormat;
141cb93a386Sopenharmony_ci
142cb93a386Sopenharmony_ci    NSError* error;
143cb93a386Sopenharmony_ci    auto pso =
144cb93a386Sopenharmony_ci            [fGpu->device() newRenderPipelineStateWithDescriptor: pipelineDescriptor
145cb93a386Sopenharmony_ci                                                          error: &error];
146cb93a386Sopenharmony_ci    if (!pso) {
147cb93a386Sopenharmony_ci        SkDebugf("Error creating pipeline: %s\n",
148cb93a386Sopenharmony_ci                 [[error localizedDescription] cStringUsingEncoding: NSASCIIStringEncoding]);
149cb93a386Sopenharmony_ci    }
150cb93a386Sopenharmony_ci
151cb93a386Sopenharmony_ci    auto renderPipeline = GrMtlRenderPipeline::Make(pso);
152cb93a386Sopenharmony_ci
153cb93a386Sopenharmony_ci    fMSAALoadPipelines.push_back({renderPipeline, colorFormat, sampleCount, stencilFormat});
154cb93a386Sopenharmony_ci    return fMSAALoadPipelines[fMSAALoadPipelines.count()-1].fPipeline.get();
155cb93a386Sopenharmony_ci}
156cb93a386Sopenharmony_ci
157cb93a386Sopenharmony_civoid GrMtlResourceProvider::destroyResources() {
158cb93a386Sopenharmony_ci    fMSAALoadLibrary = nil;
159cb93a386Sopenharmony_ci    fMSAALoadPipelines.reset();
160cb93a386Sopenharmony_ci
161cb93a386Sopenharmony_ci    fSamplers.foreach([&](GrMtlSampler* sampler) { sampler->unref(); });
162cb93a386Sopenharmony_ci    fSamplers.reset();
163cb93a386Sopenharmony_ci
164cb93a386Sopenharmony_ci    fDepthStencilStates.foreach([&](GrMtlDepthStencil* stencil) { stencil->unref(); });
165cb93a386Sopenharmony_ci    fDepthStencilStates.reset();
166cb93a386Sopenharmony_ci
167cb93a386Sopenharmony_ci    fPipelineStateCache->release();
168cb93a386Sopenharmony_ci}
169cb93a386Sopenharmony_ci
170cb93a386Sopenharmony_ci////////////////////////////////////////////////////////////////////////////////////////////////
171cb93a386Sopenharmony_ci
172cb93a386Sopenharmony_cistruct GrMtlResourceProvider::PipelineStateCache::Entry {
173cb93a386Sopenharmony_ci    Entry(GrMtlPipelineState* pipelineState)
174cb93a386Sopenharmony_ci            : fPipelineState(pipelineState) {}
175cb93a386Sopenharmony_ci    Entry(const GrMtlPrecompiledLibraries& precompiledLibraries)
176cb93a386Sopenharmony_ci            : fPipelineState(nullptr)
177cb93a386Sopenharmony_ci            , fPrecompiledLibraries(precompiledLibraries) {}
178cb93a386Sopenharmony_ci
179cb93a386Sopenharmony_ci    std::unique_ptr<GrMtlPipelineState> fPipelineState;
180cb93a386Sopenharmony_ci
181cb93a386Sopenharmony_ci    // TODO: change to one library once we can build that
182cb93a386Sopenharmony_ci    GrMtlPrecompiledLibraries fPrecompiledLibraries;
183cb93a386Sopenharmony_ci};
184cb93a386Sopenharmony_ci
185cb93a386Sopenharmony_ciGrMtlResourceProvider::PipelineStateCache::PipelineStateCache(GrMtlGpu* gpu)
186cb93a386Sopenharmony_ci    : fMap(gpu->getContext()->priv().options().fRuntimeProgramCacheSize)
187cb93a386Sopenharmony_ci    , fGpu(gpu) {}
188cb93a386Sopenharmony_ci
189cb93a386Sopenharmony_ciGrMtlResourceProvider::PipelineStateCache::~PipelineStateCache() {
190cb93a386Sopenharmony_ci    SkASSERT(0 == fMap.count());
191cb93a386Sopenharmony_ci}
192cb93a386Sopenharmony_ci
193cb93a386Sopenharmony_civoid GrMtlResourceProvider::PipelineStateCache::release() {
194cb93a386Sopenharmony_ci    fMap.reset();
195cb93a386Sopenharmony_ci}
196cb93a386Sopenharmony_ci
197cb93a386Sopenharmony_ciGrMtlPipelineState* GrMtlResourceProvider::PipelineStateCache::refPipelineState(
198cb93a386Sopenharmony_ci        const GrProgramDesc& desc,
199cb93a386Sopenharmony_ci        const GrProgramInfo& programInfo,
200cb93a386Sopenharmony_ci        Stats::ProgramCacheResult* statPtr) {
201cb93a386Sopenharmony_ci
202cb93a386Sopenharmony_ci    if (!statPtr) {
203cb93a386Sopenharmony_ci        // If stat is NULL we are using inline compilation rather than through DDL,
204cb93a386Sopenharmony_ci        // so we need to track those stats as well.
205cb93a386Sopenharmony_ci        GrThreadSafePipelineBuilder::Stats::ProgramCacheResult stat;
206cb93a386Sopenharmony_ci        auto tmp = this->onRefPipelineState(desc, programInfo, &stat);
207cb93a386Sopenharmony_ci        if (!tmp) {
208cb93a386Sopenharmony_ci            fStats.incNumInlineCompilationFailures();
209cb93a386Sopenharmony_ci        } else {
210cb93a386Sopenharmony_ci            fStats.incNumInlineProgramCacheResult(stat);
211cb93a386Sopenharmony_ci        }
212cb93a386Sopenharmony_ci        return tmp;
213cb93a386Sopenharmony_ci    } else {
214cb93a386Sopenharmony_ci        return this->onRefPipelineState(desc, programInfo, statPtr);
215cb93a386Sopenharmony_ci    }
216cb93a386Sopenharmony_ci}
217cb93a386Sopenharmony_ci
218cb93a386Sopenharmony_ciGrMtlPipelineState* GrMtlResourceProvider::PipelineStateCache::onRefPipelineState(
219cb93a386Sopenharmony_ci        const GrProgramDesc& desc,
220cb93a386Sopenharmony_ci        const GrProgramInfo& programInfo,
221cb93a386Sopenharmony_ci        Stats::ProgramCacheResult* stat) {
222cb93a386Sopenharmony_ci    *stat = Stats::ProgramCacheResult::kHit;
223cb93a386Sopenharmony_ci    std::unique_ptr<Entry>* entry = fMap.find(desc);
224cb93a386Sopenharmony_ci    if (entry && !(*entry)->fPipelineState) {
225cb93a386Sopenharmony_ci        // We've pre-compiled the MSL shaders but don't yet have the pipelineState
226cb93a386Sopenharmony_ci        const GrMtlPrecompiledLibraries* precompiledLibs = &((*entry)->fPrecompiledLibraries);
227cb93a386Sopenharmony_ci        SkASSERT(precompiledLibs->fVertexLibrary);
228cb93a386Sopenharmony_ci        SkASSERT(precompiledLibs->fFragmentLibrary);
229cb93a386Sopenharmony_ci        (*entry)->fPipelineState.reset(
230cb93a386Sopenharmony_ci                GrMtlPipelineStateBuilder::CreatePipelineState(fGpu, desc, programInfo,
231cb93a386Sopenharmony_ci                                                               precompiledLibs));
232cb93a386Sopenharmony_ci        if (!(*entry)->fPipelineState) {
233cb93a386Sopenharmony_ci            // Should we purge the precompiled shaders from the cache at this point?
234cb93a386Sopenharmony_ci            SkDEBUGFAIL("Couldn't create pipelineState from precompiled shaders");
235cb93a386Sopenharmony_ci            fStats.incNumCompilationFailures();
236cb93a386Sopenharmony_ci            return nullptr;
237cb93a386Sopenharmony_ci        }
238cb93a386Sopenharmony_ci        // release the libraries
239cb93a386Sopenharmony_ci        (*entry)->fPrecompiledLibraries.fVertexLibrary = nil;
240cb93a386Sopenharmony_ci        (*entry)->fPrecompiledLibraries.fFragmentLibrary = nil;
241cb93a386Sopenharmony_ci
242cb93a386Sopenharmony_ci        fStats.incNumPartialCompilationSuccesses();
243cb93a386Sopenharmony_ci        *stat = Stats::ProgramCacheResult::kPartial;
244cb93a386Sopenharmony_ci    } else if (!entry) {
245cb93a386Sopenharmony_ci        GrMtlPipelineState* pipelineState(
246cb93a386Sopenharmony_ci                GrMtlPipelineStateBuilder::CreatePipelineState(fGpu, desc, programInfo));
247cb93a386Sopenharmony_ci        if (!pipelineState) {
248cb93a386Sopenharmony_ci            fStats.incNumCompilationFailures();
249cb93a386Sopenharmony_ci           return nullptr;
250cb93a386Sopenharmony_ci        }
251cb93a386Sopenharmony_ci        fStats.incNumCompilationSuccesses();
252cb93a386Sopenharmony_ci        entry = fMap.insert(desc, std::unique_ptr<Entry>(new Entry(pipelineState)));
253cb93a386Sopenharmony_ci        *stat = Stats::ProgramCacheResult::kMiss;
254cb93a386Sopenharmony_ci        return (*entry)->fPipelineState.get();
255cb93a386Sopenharmony_ci    }
256cb93a386Sopenharmony_ci    return (*entry)->fPipelineState.get();
257cb93a386Sopenharmony_ci}
258cb93a386Sopenharmony_ci
259cb93a386Sopenharmony_cibool GrMtlResourceProvider::PipelineStateCache::precompileShader(const SkData& key,
260cb93a386Sopenharmony_ci                                                                 const SkData& data) {
261cb93a386Sopenharmony_ci    GrProgramDesc desc;
262cb93a386Sopenharmony_ci    if (!GrProgramDesc::BuildFromData(&desc, key.data(), key.size())) {
263cb93a386Sopenharmony_ci        return false;
264cb93a386Sopenharmony_ci    }
265cb93a386Sopenharmony_ci
266cb93a386Sopenharmony_ci    std::unique_ptr<Entry>* entry = fMap.find(desc);
267cb93a386Sopenharmony_ci    if (entry) {
268cb93a386Sopenharmony_ci        // We've already seen/compiled this shader
269cb93a386Sopenharmony_ci        return true;
270cb93a386Sopenharmony_ci    }
271cb93a386Sopenharmony_ci
272cb93a386Sopenharmony_ci    GrMtlPrecompiledLibraries precompiledLibraries;
273cb93a386Sopenharmony_ci    if (!GrMtlPipelineStateBuilder::PrecompileShaders(fGpu, data, &precompiledLibraries)) {
274cb93a386Sopenharmony_ci        return false;
275cb93a386Sopenharmony_ci    }
276cb93a386Sopenharmony_ci
277cb93a386Sopenharmony_ci    fMap.insert(desc, std::make_unique<Entry>(precompiledLibraries));
278cb93a386Sopenharmony_ci    return true;
279cb93a386Sopenharmony_ci
280cb93a386Sopenharmony_ci}
281cb93a386Sopenharmony_ci
282cb93a386Sopenharmony_ciGR_NORETAIN_END
283