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