• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1//
2// Copyright 2019 The ANGLE Project Authors. All rights reserved.
3// Use of this source code is governed by a BSD-style license that can be
4// found in the LICENSE file.
5//
6// ProgramMtl.mm:
7//    Implements the class methods for ProgramMtl.
8//
9
10#include "libANGLE/renderer/metal/ProgramMtl.h"
11
12#include <TargetConditionals.h>
13
14#include <sstream>
15
16#include "common/WorkerThread.h"
17#include "common/debug.h"
18#include "common/system_utils.h"
19
20#include "libANGLE/Context.h"
21#include "libANGLE/ProgramLinkedResources.h"
22#include "libANGLE/renderer/metal/CompilerMtl.h"
23#include "libANGLE/renderer/metal/ContextMtl.h"
24#include "libANGLE/renderer/metal/DisplayMtl.h"
25#include "libANGLE/renderer/metal/blocklayoutMetal.h"
26#include "libANGLE/renderer/metal/mtl_msl_utils.h"
27#include "libANGLE/renderer/metal/mtl_utils.h"
28#include "libANGLE/renderer/metal/renderermtl_utils.h"
29#include "libANGLE/renderer/renderer_utils.h"
30#include "libANGLE/trace.h"
31
32namespace rx
33{
34
35namespace
36{
37inline std::map<std::string, std::string> GetDefaultSubstitutionDictionary()
38{
39    return {};
40}
41
42bool DisableFastMathForShaderCompilation(mtl::Context *context)
43{
44    return context->getDisplay()->getFeatures().intelDisableFastMath.enabled;
45}
46
47bool UsesInvariance(const mtl::TranslatedShaderInfo *translatedMslInfo)
48{
49    return translatedMslInfo->hasInvariant;
50}
51
52class Std140BlockLayoutEncoderFactory : public gl::CustomBlockLayoutEncoderFactory
53{
54  public:
55    sh::BlockLayoutEncoder *makeEncoder() override { return new sh::Std140BlockEncoder(); }
56};
57
58class CompileMslTask final : public LinkSubTask
59{
60  public:
61    CompileMslTask(ContextMtl *context,
62                   mtl::TranslatedShaderInfo *translatedMslInfo,
63                   const std::map<std::string, std::string> &substitutionMacros)
64        : mContext(context),
65          mTranslatedMslInfo(translatedMslInfo),
66          mSubstitutionMacros(substitutionMacros)
67    {}
68    ~CompileMslTask() override = default;
69
70    void operator()() override
71    {
72        mResult = CreateMslShaderLib(mContext, mInfoLog, mTranslatedMslInfo, mSubstitutionMacros);
73    }
74
75    angle::Result getResult(const gl::Context *context, gl::InfoLog &infoLog) override
76    {
77        if (!mInfoLog.empty())
78        {
79            infoLog << mInfoLog.str();
80        }
81
82        return mResult;
83    }
84
85  private:
86    // TODO: remove this, inherit from mtl::Context and ensure thread-safety.
87    // http://anglebug.com/8297
88    ContextMtl *mContext;
89    gl::InfoLog mInfoLog;
90    mtl::TranslatedShaderInfo *mTranslatedMslInfo;
91    std::map<std::string, std::string> mSubstitutionMacros;
92    angle::Result mResult = angle::Result::Continue;
93};
94}  // namespace
95
96class ProgramMtl::LinkTaskMtl final : public LinkTask
97{
98  public:
99    LinkTaskMtl(const gl::Context *context, ProgramMtl *program)
100        : mContext(context), mProgram(program)
101    {}
102    ~LinkTaskMtl() override = default;
103
104    void link(const gl::ProgramLinkedResources &resources,
105              const gl::ProgramMergedVaryings &mergedVaryings,
106              std::vector<std::shared_ptr<LinkSubTask>> *linkSubTasksOut,
107              std::vector<std::shared_ptr<LinkSubTask>> *postLinkSubTasksOut) override
108    {
109        ASSERT(linkSubTasksOut && linkSubTasksOut->empty());
110        ASSERT(postLinkSubTasksOut && postLinkSubTasksOut->empty());
111
112        mResult = mProgram->linkJobImpl(mContext, resources, linkSubTasksOut);
113        return;
114    }
115
116    angle::Result getResult(const gl::Context *context, gl::InfoLog &infoLog) override
117    {
118        return mResult;
119    }
120
121  private:
122    // TODO: remove this, inherit from mtl::Context and ensure thread-safety.
123    // http://anglebug.com/8297
124    const gl::Context *mContext;
125    ProgramMtl *mProgram;
126    angle::Result mResult = angle::Result::Continue;
127};
128
129class ProgramMtl::LoadTaskMtl final : public LinkTask
130{
131  public:
132    LoadTaskMtl(std::vector<std::shared_ptr<LinkSubTask>> &&subTasks)
133        : mSubTasks(std::move(subTasks))
134    {}
135    ~LoadTaskMtl() override = default;
136
137    void load(std::vector<std::shared_ptr<LinkSubTask>> *linkSubTasksOut,
138              std::vector<std::shared_ptr<LinkSubTask>> *postLinkSubTasksOut) override
139    {
140        ASSERT(linkSubTasksOut && linkSubTasksOut->empty());
141        ASSERT(postLinkSubTasksOut && postLinkSubTasksOut->empty());
142
143        *linkSubTasksOut = mSubTasks;
144        return;
145    }
146
147    angle::Result getResult(const gl::Context *context, gl::InfoLog &infoLog) override
148    {
149        return angle::Result::Continue;
150    }
151
152  private:
153    std::vector<std::shared_ptr<LinkSubTask>> mSubTasks;
154};
155
156// ProgramArgumentBufferEncoderMtl implementation
157void ProgramArgumentBufferEncoderMtl::reset(ContextMtl *contextMtl)
158{
159    metalArgBufferEncoder = nil;
160    bufferPool.destroy(contextMtl);
161}
162
163// ProgramShaderObjVariantMtl implementation
164void ProgramShaderObjVariantMtl::reset(ContextMtl *contextMtl)
165{
166    metalShader = nil;
167
168    uboArgBufferEncoder.reset(contextMtl);
169
170    translatedSrcInfo = nullptr;
171}
172
173// ProgramMtl implementation
174ProgramMtl::ProgramMtl(const gl::ProgramState &state) : ProgramImpl(state) {}
175
176ProgramMtl::~ProgramMtl() = default;
177
178void ProgramMtl::destroy(const gl::Context *context)
179{
180    getExecutable()->reset(mtl::GetImpl(context));
181}
182
183angle::Result ProgramMtl::load(const gl::Context *context,
184                               gl::BinaryInputStream *stream,
185                               std::shared_ptr<LinkTask> *loadTaskOut,
186                               egl::CacheGetResult *resultOut)
187{
188
189    ContextMtl *contextMtl = mtl::GetImpl(context);
190    // NOTE(hqle): No transform feedbacks for now, since we only support ES 2.0 atm
191
192    ANGLE_TRY(getExecutable()->load(contextMtl, stream));
193
194    // TODO: parallelize the above too.  http://anglebug.com/8297
195    std::vector<std::shared_ptr<LinkSubTask>> subTasks;
196
197    ANGLE_TRY(compileMslShaderLibs(context, &subTasks));
198
199    *loadTaskOut = std::shared_ptr<LinkTask>(new LoadTaskMtl(std::move(subTasks)));
200    *resultOut   = egl::CacheGetResult::Success;
201
202    return angle::Result::Continue;
203}
204
205void ProgramMtl::save(const gl::Context *context, gl::BinaryOutputStream *stream)
206{
207    getExecutable()->save(stream);
208}
209
210void ProgramMtl::setBinaryRetrievableHint(bool retrievable) {}
211
212void ProgramMtl::setSeparable(bool separable)
213{
214    UNIMPLEMENTED();
215}
216
217void ProgramMtl::prepareForLink(const gl::ShaderMap<ShaderImpl *> &shaders)
218{
219    for (gl::ShaderType shaderType : gl::AllShaderTypes())
220    {
221        mAttachedShaders[shaderType].reset();
222
223        if (shaders[shaderType] != nullptr)
224        {
225            const ShaderMtl *shaderMtl   = GetAs<ShaderMtl>(shaders[shaderType]);
226            mAttachedShaders[shaderType] = shaderMtl->getCompiledState();
227        }
228    }
229}
230
231angle::Result ProgramMtl::link(const gl::Context *context, std::shared_ptr<LinkTask> *linkTaskOut)
232{
233    *linkTaskOut = std::shared_ptr<LinkTask>(new LinkTaskMtl(context, this));
234    return angle::Result::Continue;
235}
236
237angle::Result ProgramMtl::linkJobImpl(const gl::Context *context,
238                                      const gl::ProgramLinkedResources &resources,
239                                      std::vector<std::shared_ptr<LinkSubTask>> *subTasksOut)
240{
241    ContextMtl *contextMtl              = mtl::GetImpl(context);
242    ProgramExecutableMtl *executableMtl = getExecutable();
243
244    // Link resources before calling GetShaderSource to make sure they are ready for the set/binding
245    // assignment done in that function.
246    linkResources(resources);
247
248    ANGLE_TRY(executableMtl->initDefaultUniformBlocks(contextMtl, mState.getAttachedShaders()));
249    executableMtl->linkUpdateHasFlatAttributes(mState.getAttachedShader(gl::ShaderType::Vertex));
250
251    gl::ShaderMap<std::string> shaderSources;
252    mtl::MSLGetShaderSource(mState, resources, &shaderSources);
253
254    ANGLE_TRY(mtl::MTLGetMSL(contextMtl, mState.getExecutable(), contextMtl->getCaps(),
255                             shaderSources, mAttachedShaders,
256                             &executableMtl->mMslShaderTranslateInfo));
257    executableMtl->mMslXfbOnlyVertexShaderInfo =
258        executableMtl->mMslShaderTranslateInfo[gl::ShaderType::Vertex];
259
260    return compileMslShaderLibs(context, subTasksOut);
261}
262
263angle::Result ProgramMtl::compileMslShaderLibs(
264    const gl::Context *context,
265    std::vector<std::shared_ptr<LinkSubTask>> *subTasksOut)
266{
267    ANGLE_TRACE_EVENT0("gpu.angle", "ProgramMtl::compileMslShaderLibs");
268    gl::InfoLog &infoLog = mState.getExecutable().getInfoLog();
269
270    ContextMtl *contextMtl              = mtl::GetImpl(context);
271    ProgramExecutableMtl *executableMtl = getExecutable();
272    bool asyncCompile =
273        contextMtl->getDisplay()->getFeatures().enableParallelMtlLibraryCompilation.enabled;
274    mtl::LibraryCache &libraryCache = contextMtl->getDisplay()->getLibraryCache();
275
276    for (gl::ShaderType shaderType : gl::kAllGLES2ShaderTypes)
277    {
278        mtl::TranslatedShaderInfo *translateInfo =
279            &executableMtl->mMslShaderTranslateInfo[shaderType];
280        std::map<std::string, std::string> macros = GetDefaultSubstitutionDictionary();
281        bool disableFastMath                      = DisableFastMathForShaderCompilation(contextMtl);
282        bool usesInvariance                       = UsesInvariance(translateInfo);
283
284        // Check if the shader is already in the cache and use it instead of spawning a new thread
285        translateInfo->metalLibrary = libraryCache.get(translateInfo->metalShaderSource, macros,
286                                                       disableFastMath, usesInvariance);
287
288        if (!translateInfo->metalLibrary)
289        {
290            if (asyncCompile)
291            {
292                subTasksOut->emplace_back(new CompileMslTask(contextMtl, translateInfo, macros));
293            }
294            else
295            {
296                ANGLE_TRY(CreateMslShaderLib(contextMtl, infoLog, translateInfo, macros));
297            }
298        }
299    }
300
301    return angle::Result::Continue;
302}
303
304void ProgramMtl::linkResources(const gl::ProgramLinkedResources &resources)
305{
306    Std140BlockLayoutEncoderFactory std140EncoderFactory;
307    gl::ProgramLinkedResourcesLinker linker(&std140EncoderFactory);
308
309    linker.linkResources(mState, resources);
310}
311
312GLboolean ProgramMtl::validate(const gl::Caps &caps)
313{
314    // No-op. The spec is very vague about the behavior of validation.
315    return GL_TRUE;
316}
317
318}  // namespace rx
319