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