• 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
42class Std140BlockLayoutEncoderFactory : public gl::CustomBlockLayoutEncoderFactory
43{
44  public:
45    sh::BlockLayoutEncoder *makeEncoder() override { return new sh::Std140BlockEncoder(); }
46};
47
48class CompileMslTask final : public LinkSubTask, public mtl::Context
49{
50  public:
51    CompileMslTask(DisplayMtl *displayMtl,
52                   mtl::TranslatedShaderInfo *translatedMslInfo,
53                   const std::map<std::string, std::string> &substitutionMacros)
54        : mtl::Context(displayMtl),
55          mTranslatedMslInfo(translatedMslInfo),
56          mSubstitutionMacros(substitutionMacros)
57    {}
58    ~CompileMslTask() override = default;
59
60    void operator()() override
61    {
62        mResult = CreateMslShaderLib(this, mInfoLog, mTranslatedMslInfo, mSubstitutionMacros);
63    }
64
65    angle::Result getResult(const gl::Context *context, gl::InfoLog &infoLog) override
66    {
67        if (!mInfoLog.empty())
68        {
69            infoLog << mInfoLog.str();
70        }
71        if (mErrorCode != GL_NO_ERROR)
72        {
73            mtl::GetImpl(context)->handleError(mErrorCode, mErrorMessage.c_str(), mErrorFile,
74                                               mErrorFunction, mErrorLine);
75            return angle::Result::Stop;
76        }
77        return mResult;
78    }
79
80    // override mtl::ErrorHandler
81    void handleError(GLenum glErrorCode,
82                     const char *message,
83                     const char *file,
84                     const char *function,
85                     unsigned int line) override
86    {
87        mErrorCode     = glErrorCode;
88        mErrorMessage  = message;
89        mErrorFile     = file;
90        mErrorFunction = function;
91        mErrorLine     = line;
92    }
93
94  private:
95    mtl::TranslatedShaderInfo *mTranslatedMslInfo;
96    std::map<std::string, std::string> mSubstitutionMacros;
97    angle::Result mResult = angle::Result::Continue;
98    gl::InfoLog mInfoLog;
99    GLenum mErrorCode = GL_NO_ERROR;
100    std::string mErrorMessage;
101    const char *mErrorFile     = nullptr;
102    const char *mErrorFunction = nullptr;
103    unsigned int mErrorLine    = 0;
104};
105
106}  // namespace
107
108class ProgramMtl::LinkTaskMtl final : public LinkTask, public mtl::Context
109{
110  public:
111    LinkTaskMtl(DisplayMtl *displayMtl, ProgramMtl *program)
112        : mtl::Context(displayMtl), mProgram(program)
113    {}
114    ~LinkTaskMtl() override = default;
115
116    void link(const gl::ProgramLinkedResources &resources,
117              const gl::ProgramMergedVaryings &mergedVaryings,
118              std::vector<std::shared_ptr<LinkSubTask>> *linkSubTasksOut,
119              std::vector<std::shared_ptr<LinkSubTask>> *postLinkSubTasksOut) override
120    {
121        ASSERT(linkSubTasksOut && linkSubTasksOut->empty());
122        ASSERT(postLinkSubTasksOut && postLinkSubTasksOut->empty());
123
124        mResult = mProgram->linkJobImpl(this, resources, linkSubTasksOut);
125        return;
126    }
127
128    angle::Result getResult(const gl::Context *context, gl::InfoLog &infoLog) override
129    {
130        if (mErrorCode != GL_NO_ERROR)
131        {
132            mtl::GetImpl(context)->handleError(mErrorCode, mErrorMessage.c_str(), mErrorFile,
133                                               mErrorFunction, mErrorLine);
134            return angle::Result::Stop;
135        }
136
137        return mResult;
138    }
139
140    // override mtl::ErrorHandler
141    void handleError(GLenum glErrorCode,
142                     const char *message,
143                     const char *file,
144                     const char *function,
145                     unsigned int line) override
146    {
147        mErrorCode     = glErrorCode;
148        mErrorMessage  = message;
149        mErrorFile     = file;
150        mErrorFunction = function;
151        mErrorLine     = line;
152    }
153
154  private:
155    ProgramMtl *mProgram;
156    angle::Result mResult = angle::Result::Continue;
157
158    GLenum mErrorCode          = GL_NO_ERROR;
159    std::string mErrorMessage;
160    const char *mErrorFile     = nullptr;
161    const char *mErrorFunction = nullptr;
162    unsigned int mErrorLine    = 0;
163};
164
165class ProgramMtl::LoadTaskMtl final : public LinkTask
166{
167  public:
168    LoadTaskMtl(std::vector<std::shared_ptr<LinkSubTask>> &&subTasks)
169        : mSubTasks(std::move(subTasks))
170    {}
171    ~LoadTaskMtl() override = default;
172
173    void load(std::vector<std::shared_ptr<LinkSubTask>> *linkSubTasksOut,
174              std::vector<std::shared_ptr<LinkSubTask>> *postLinkSubTasksOut) override
175    {
176        ASSERT(linkSubTasksOut && linkSubTasksOut->empty());
177        ASSERT(postLinkSubTasksOut && postLinkSubTasksOut->empty());
178
179        *linkSubTasksOut = mSubTasks;
180        return;
181    }
182
183    angle::Result getResult(const gl::Context *context, gl::InfoLog &infoLog) override
184    {
185        return angle::Result::Continue;
186    }
187
188  private:
189    std::vector<std::shared_ptr<LinkSubTask>> mSubTasks;
190};
191
192// ProgramArgumentBufferEncoderMtl implementation
193void ProgramArgumentBufferEncoderMtl::reset(ContextMtl *contextMtl)
194{
195    metalArgBufferEncoder = nil;
196    bufferPool.destroy(contextMtl);
197}
198
199// ProgramShaderObjVariantMtl implementation
200void ProgramShaderObjVariantMtl::reset(ContextMtl *contextMtl)
201{
202    metalShader = nil;
203
204    uboArgBufferEncoder.reset(contextMtl);
205
206    translatedSrcInfo = nullptr;
207}
208
209// ProgramMtl implementation
210ProgramMtl::ProgramMtl(const gl::ProgramState &state) : ProgramImpl(state) {}
211
212ProgramMtl::~ProgramMtl() = default;
213
214void ProgramMtl::destroy(const gl::Context *context)
215{
216    getExecutable()->reset(mtl::GetImpl(context));
217}
218
219angle::Result ProgramMtl::load(const gl::Context *context,
220                               gl::BinaryInputStream *stream,
221                               std::shared_ptr<LinkTask> *loadTaskOut,
222                               egl::CacheGetResult *resultOut)
223{
224
225    ContextMtl *contextMtl = mtl::GetImpl(context);
226    // NOTE(hqle): No transform feedbacks for now, since we only support ES 2.0 atm
227
228    ANGLE_TRY(getExecutable()->load(contextMtl, stream));
229
230    // TODO: parallelize the above too.  http://anglebug.com/41488637
231    std::vector<std::shared_ptr<LinkSubTask>> subTasks;
232
233    ANGLE_TRY(compileMslShaderLibs(contextMtl, &subTasks));
234
235    *loadTaskOut = std::shared_ptr<LinkTask>(new LoadTaskMtl(std::move(subTasks)));
236    *resultOut   = egl::CacheGetResult::Success;
237
238    return angle::Result::Continue;
239}
240
241void ProgramMtl::save(const gl::Context *context, gl::BinaryOutputStream *stream)
242{
243    getExecutable()->save(stream);
244}
245
246void ProgramMtl::setBinaryRetrievableHint(bool retrievable) {}
247
248void ProgramMtl::setSeparable(bool separable)
249{
250    UNIMPLEMENTED();
251}
252
253void ProgramMtl::prepareForLink(const gl::ShaderMap<ShaderImpl *> &shaders)
254{
255    for (gl::ShaderType shaderType : gl::AllShaderTypes())
256    {
257        mAttachedShaders[shaderType].reset();
258
259        if (shaders[shaderType] != nullptr)
260        {
261            const ShaderMtl *shaderMtl   = GetAs<ShaderMtl>(shaders[shaderType]);
262            mAttachedShaders[shaderType] = shaderMtl->getCompiledState();
263        }
264    }
265}
266
267angle::Result ProgramMtl::link(const gl::Context *context, std::shared_ptr<LinkTask> *linkTaskOut)
268{
269    DisplayMtl *displayMtl = mtl::GetImpl(context)->getDisplay();
270
271    *linkTaskOut = std::shared_ptr<LinkTask>(new LinkTaskMtl(displayMtl, this));
272    return angle::Result::Continue;
273}
274
275angle::Result ProgramMtl::linkJobImpl(mtl::Context *context,
276                                      const gl::ProgramLinkedResources &resources,
277                                      std::vector<std::shared_ptr<LinkSubTask>> *subTasksOut)
278{
279    ProgramExecutableMtl *executableMtl = getExecutable();
280
281    // Link resources before calling GetShaderSource to make sure they are ready for the set/binding
282    // assignment done in that function.
283    linkResources(resources);
284
285    ANGLE_TRY(executableMtl->initDefaultUniformBlocks(context, mState.getAttachedShaders()));
286    executableMtl->linkUpdateHasFlatAttributes(mState.getAttachedShader(gl::ShaderType::Vertex));
287
288    gl::ShaderMap<std::string> shaderSources;
289    mtl::MSLGetShaderSource(mState, resources, &shaderSources);
290
291    ANGLE_TRY(mtl::MTLGetMSL(context->getDisplay()->getFeatures(), mState.getExecutable(),
292                             shaderSources, mAttachedShaders,
293                             &executableMtl->mMslShaderTranslateInfo));
294    executableMtl->mMslXfbOnlyVertexShaderInfo =
295        executableMtl->mMslShaderTranslateInfo[gl::ShaderType::Vertex];
296
297    return compileMslShaderLibs(context, subTasksOut);
298}
299
300angle::Result ProgramMtl::compileMslShaderLibs(
301    mtl::Context *context,
302    std::vector<std::shared_ptr<LinkSubTask>> *subTasksOut)
303{
304    ANGLE_TRACE_EVENT0("gpu.angle", "ProgramMtl::compileMslShaderLibs");
305    gl::InfoLog &infoLog = mState.getExecutable().getInfoLog();
306
307    DisplayMtl *displayMtl              = context->getDisplay();
308    ProgramExecutableMtl *executableMtl = getExecutable();
309    bool asyncCompile = displayMtl->getFeatures().enableParallelMtlLibraryCompilation.enabled;
310    mtl::LibraryCache &libraryCache = displayMtl->getLibraryCache();
311
312    for (gl::ShaderType shaderType : gl::kAllGLES2ShaderTypes)
313    {
314        mtl::TranslatedShaderInfo *translateInfo =
315            &executableMtl->mMslShaderTranslateInfo[shaderType];
316        std::map<std::string, std::string> macros = GetDefaultSubstitutionDictionary();
317        const bool disableFastMath = displayMtl->getFeatures().intelDisableFastMath.enabled ||
318                                     translateInfo->hasIsnanOrIsinf;
319        const bool usesInvariance = translateInfo->hasInvariant;
320
321        // Check if the shader is already in the cache and use it instead of spawning a new thread
322        translateInfo->metalLibrary = libraryCache.get(translateInfo->metalShaderSource, macros,
323                                                       disableFastMath, usesInvariance);
324
325        if (!translateInfo->metalLibrary)
326        {
327            if (asyncCompile)
328            {
329                subTasksOut->emplace_back(new CompileMslTask(displayMtl, translateInfo, macros));
330            }
331            else
332            {
333                ANGLE_TRY(CreateMslShaderLib(context, infoLog, translateInfo, macros));
334            }
335        }
336    }
337
338    return angle::Result::Continue;
339}
340
341void ProgramMtl::linkResources(const gl::ProgramLinkedResources &resources)
342{
343    Std140BlockLayoutEncoderFactory std140EncoderFactory;
344    gl::ProgramLinkedResourcesLinker linker(&std140EncoderFactory);
345
346    linker.linkResources(mState, resources);
347}
348
349GLboolean ProgramMtl::validate(const gl::Caps &caps)
350{
351    // No-op. The spec is very vague about the behavior of validation.
352    return GL_TRUE;
353}
354
355}  // namespace rx
356