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