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