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 // mtl_state_cache.h: 7 // Defines the class interface for StateCache, RenderPipelineCache and various 8 // C struct versions of Metal sampler, depth stencil, render pass, render pipeline descriptors. 9 // 10 11 #ifndef LIBANGLE_RENDERER_METAL_MTL_STATE_CACHE_H_ 12 #define LIBANGLE_RENDERER_METAL_MTL_STATE_CACHE_H_ 13 14 #import <Metal/Metal.h> 15 16 #include <unordered_map> 17 18 #include "libANGLE/State.h" 19 #include "libANGLE/angletypes.h" 20 #include "libANGLE/renderer/metal/mtl_common.h" 21 #include "libANGLE/renderer/metal/mtl_resources.h" 22 23 static inline bool operator==(const MTLClearColor &lhs, const MTLClearColor &rhs); 24 25 namespace angle 26 { 27 struct FeaturesMtl; 28 } 29 30 namespace rx 31 { 32 class ContextMtl; 33 34 namespace mtl 35 { 36 struct alignas(1) StencilDesc 37 { 38 bool operator==(const StencilDesc &rhs) const; 39 40 // Set default values 41 void reset(); 42 43 // Use uint8_t instead of MTLStencilOperation to compact space 44 uint8_t stencilFailureOperation : 3; 45 uint8_t depthFailureOperation : 3; 46 uint8_t depthStencilPassOperation : 3; 47 48 // Use uint8_t instead of MTLCompareFunction to compact space 49 uint8_t stencilCompareFunction : 3; 50 51 uint8_t readMask : 8; 52 uint8_t writeMask : 8; 53 }; 54 55 struct alignas(4) DepthStencilDesc 56 { 57 DepthStencilDesc(); 58 DepthStencilDesc(const DepthStencilDesc &src); 59 DepthStencilDesc(DepthStencilDesc &&src); 60 61 DepthStencilDesc &operator=(const DepthStencilDesc &src); 62 63 bool operator==(const DepthStencilDesc &rhs) const; 64 65 // Set default values. 66 // Default is depth/stencil test disabled. Depth/stencil write enabled. 67 void reset(); 68 69 size_t hash() const; 70 71 void updateDepthTestEnabled(const gl::DepthStencilState &dsState); 72 void updateDepthWriteEnabled(const gl::DepthStencilState &dsState); 73 void updateDepthCompareFunc(const gl::DepthStencilState &dsState); 74 void updateStencilTestEnabled(const gl::DepthStencilState &dsState); 75 void updateStencilFrontOps(const gl::DepthStencilState &dsState); 76 void updateStencilBackOps(const gl::DepthStencilState &dsState); 77 void updateStencilFrontFuncs(const gl::DepthStencilState &dsState); 78 void updateStencilBackFuncs(const gl::DepthStencilState &dsState); 79 void updateStencilFrontWriteMask(const gl::DepthStencilState &dsState); 80 void updateStencilBackWriteMask(const gl::DepthStencilState &dsState); 81 82 StencilDesc backFaceStencil; 83 StencilDesc frontFaceStencil; 84 85 // Use uint8_t instead of MTLCompareFunction to compact space 86 uint8_t depthCompareFunction : 3; 87 bool depthWriteEnabled : 1; 88 }; 89 90 struct alignas(4) SamplerDesc 91 { 92 SamplerDesc(); 93 SamplerDesc(const SamplerDesc &src); 94 SamplerDesc(SamplerDesc &&src); 95 96 explicit SamplerDesc(const gl::SamplerState &glState); 97 98 SamplerDesc &operator=(const SamplerDesc &src); 99 100 // Set default values. All filters are nearest, and addresModes are clamp to edge. 101 void reset(); 102 103 bool operator==(const SamplerDesc &rhs) const; 104 105 size_t hash() const; 106 107 // Use uint8_t instead of MTLSamplerAddressMode to compact space 108 uint8_t rAddressMode : 3; 109 uint8_t sAddressMode : 3; 110 uint8_t tAddressMode : 3; 111 112 // Use uint8_t instead of MTLSamplerMinMagFilter to compact space 113 uint8_t minFilter : 1; 114 uint8_t magFilter : 1; 115 uint8_t mipFilter : 2; 116 117 uint8_t maxAnisotropy : 5; 118 119 // Use uint8_t instead of MTLCompareFunction to compact space 120 uint8_t compareFunction : 3; 121 }; 122 123 struct VertexAttributeDesc 124 { 125 inline bool operator==(const VertexAttributeDesc &rhs) const 126 { 127 return format == rhs.format && offset == rhs.offset && bufferIndex == rhs.bufferIndex; 128 } 129 inline bool operator!=(const VertexAttributeDesc &rhs) const { return !(*this == rhs); } 130 131 // Use uint8_t instead of MTLVertexFormat to compact space 132 uint8_t format : 6; 133 // Offset is only used for default attributes buffer. So 8 bits are enough. 134 uint8_t offset : 8; 135 uint8_t bufferIndex : 5; 136 }; 137 138 struct VertexBufferLayoutDesc 139 { 140 inline bool operator==(const VertexBufferLayoutDesc &rhs) const 141 { 142 return stepFunction == rhs.stepFunction && stepRate == rhs.stepRate && stride == rhs.stride; 143 } 144 inline bool operator!=(const VertexBufferLayoutDesc &rhs) const { return !(*this == rhs); } 145 146 uint32_t stepRate; 147 uint32_t stride; 148 149 // Use uint8_t instead of MTLVertexStepFunction to compact space 150 uint8_t stepFunction; 151 }; 152 153 struct VertexDesc 154 { 155 VertexAttributeDesc attributes[kMaxVertexAttribs]; 156 VertexBufferLayoutDesc layouts[kMaxVertexAttribs]; 157 158 uint8_t numAttribs; 159 uint8_t numBufferLayouts; 160 }; 161 162 struct BlendDesc 163 { 164 bool operator==(const BlendDesc &rhs) const; 165 BlendDesc &operator=(const BlendDesc &src) = default; 166 167 // Set default values 168 void reset(); 169 void reset(MTLColorWriteMask writeMask); 170 171 void updateWriteMask(const uint8_t angleMask); 172 173 // Use uint8_t instead of MTLColorWriteMask to compact space 174 uint8_t writeMask : 4; 175 176 // Use uint8_t instead of MTLBlendOperation to compact space 177 uint8_t alphaBlendOperation : 3; 178 uint8_t rgbBlendOperation : 3; 179 180 // Use uint8_t instead of MTLBlendFactor to compact space 181 // NOTE(hqle): enum MTLBlendFactorSource1Color and above are unused. 182 uint8_t destinationAlphaBlendFactor : 4; 183 uint8_t destinationRGBBlendFactor : 4; 184 uint8_t sourceAlphaBlendFactor : 4; 185 uint8_t sourceRGBBlendFactor : 4; 186 187 bool blendingEnabled : 1; 188 }; 189 190 using BlendDescArray = std::array<BlendDesc, kMaxRenderTargets>; 191 using WriteMaskArray = std::array<uint8_t, kMaxRenderTargets>; 192 193 struct alignas(2) RenderPipelineColorAttachmentDesc : public BlendDesc 194 { 195 bool operator==(const RenderPipelineColorAttachmentDesc &rhs) const; 196 inline bool operator!=(const RenderPipelineColorAttachmentDesc &rhs) const 197 { 198 return !(*this == rhs); 199 } 200 201 // Set default values 202 void reset(); 203 void reset(MTLPixelFormat format); 204 void reset(MTLPixelFormat format, MTLColorWriteMask writeMask); 205 void reset(MTLPixelFormat format, const BlendDesc &blendDesc); 206 207 void update(const BlendDesc &blendDesc); 208 209 // Use uint16_t instead of MTLPixelFormat to compact space 210 uint16_t pixelFormat : 16; 211 }; 212 213 struct RenderPipelineOutputDesc 214 { 215 bool operator==(const RenderPipelineOutputDesc &rhs) const; 216 217 void updateEnabledDrawBuffers(gl::DrawBufferMask enabledBuffers); 218 219 RenderPipelineColorAttachmentDesc colorAttachments[kMaxRenderTargets]; 220 221 // Use uint16_t instead of MTLPixelFormat to compact space 222 uint16_t depthAttachmentPixelFormat : 16; 223 uint16_t stencilAttachmentPixelFormat : 16; 224 225 static_assert(kMaxRenderTargets <= 4, "kMaxRenderTargets must be <= 4"); 226 uint8_t numColorAttachments : 3; 227 uint8_t sampleCount : 5; 228 }; 229 230 // Some SDK levels don't declare MTLPrimitiveTopologyClass. Needs to do compile time check here: 231 #if !(TARGET_OS_OSX || TARGET_OS_MACCATALYST) && \ 232 (!defined(__IPHONE_12_0) || ANGLE_IOS_DEPLOY_TARGET < __IPHONE_12_0) 233 # define ANGLE_MTL_PRIMITIVE_TOPOLOGY_CLASS_AVAILABLE 0 234 using PrimitiveTopologyClass = uint32_t; 235 constexpr PrimitiveTopologyClass kPrimitiveTopologyClassTriangle = 0; 236 constexpr PrimitiveTopologyClass kPrimitiveTopologyClassPoint = 0; 237 #else 238 # define ANGLE_MTL_PRIMITIVE_TOPOLOGY_CLASS_AVAILABLE 1 239 using PrimitiveTopologyClass = MTLPrimitiveTopologyClass; 240 constexpr PrimitiveTopologyClass kPrimitiveTopologyClassTriangle = 241 MTLPrimitiveTopologyClassTriangle; 242 constexpr PrimitiveTopologyClass kPrimitiveTopologyClassPoint = MTLPrimitiveTopologyClassPoint; 243 #endif 244 245 enum class RenderPipelineRasterization : uint32_t 246 { 247 // This flag is used for vertex shader not writing any stage output (e.g gl_Position). 248 // This will disable fragment shader stage. This is useful for transform feedback ouput vertex 249 // shader. 250 Disabled, 251 252 // Fragment shader is enabled. 253 Enabled, 254 255 // This flag is for rasterization discard emulation when vertex shader still writes to stage 256 // output. Disabled flag cannot be used in this case since Metal doesn't allow that. The 257 // emulation would insert a code snippet to move gl_Position out of clip space's visible area to 258 // simulate the discard. 259 EmulatedDiscard, 260 261 EnumCount, 262 }; 263 264 template <typename T> 265 using RenderPipelineRasterStateMap = angle::PackedEnumMap<RenderPipelineRasterization, T>; 266 267 struct alignas(4) RenderPipelineDesc 268 { 269 RenderPipelineDesc(); 270 RenderPipelineDesc(const RenderPipelineDesc &src); 271 RenderPipelineDesc(RenderPipelineDesc &&src); 272 273 RenderPipelineDesc &operator=(const RenderPipelineDesc &src); 274 275 bool operator==(const RenderPipelineDesc &rhs) const; 276 size_t hash() const; 277 bool rasterizationEnabled() const; 278 279 VertexDesc vertexDescriptor; 280 281 RenderPipelineOutputDesc outputDescriptor; 282 283 // Use uint8_t instead of PrimitiveTopologyClass to compact space. 284 uint8_t inputPrimitiveTopology : 2; 285 286 bool alphaToCoverageEnabled : 1; 287 288 // These flags are for emulation and do not correspond to any flags in 289 // MTLRenderPipelineDescriptor descriptor. These flags should be used by 290 // RenderPipelineCacheSpecializeShaderFactory. 291 RenderPipelineRasterization rasterizationType : 2; 292 bool emulateCoverageMask : 1; 293 }; 294 295 struct RenderPassAttachmentDesc 296 { 297 RenderPassAttachmentDesc(); 298 // Set default values 299 void reset(); 300 301 bool equalIgnoreLoadStoreOptions(const RenderPassAttachmentDesc &other) const; 302 bool operator==(const RenderPassAttachmentDesc &other) const; 303 hasImplicitMSTextureRenderPassAttachmentDesc304 ANGLE_INLINE bool hasImplicitMSTexture() const { return implicitMSTexture.get(); } 305 306 TextureRef texture; 307 // Implicit multisample texture that will be rendered into and discarded at the end of 308 // a render pass. Its result will be resolved into normal texture above. 309 TextureRef implicitMSTexture; 310 MipmapNativeLevel level; 311 uint32_t sliceOrDepth; 312 313 // This attachment is blendable or not. 314 bool blendable; 315 MTLLoadAction loadAction; 316 MTLStoreAction storeAction; 317 MTLStoreActionOptions storeActionOptions; 318 }; 319 320 struct RenderPassColorAttachmentDesc : public RenderPassAttachmentDesc 321 { 322 inline bool operator==(const RenderPassColorAttachmentDesc &other) const 323 { 324 return RenderPassAttachmentDesc::operator==(other) && clearColor == other.clearColor; 325 } 326 inline bool operator!=(const RenderPassColorAttachmentDesc &other) const 327 { 328 return !(*this == other); 329 } 330 MTLClearColor clearColor = {0, 0, 0, 0}; 331 }; 332 333 struct RenderPassDepthAttachmentDesc : public RenderPassAttachmentDesc 334 { 335 inline bool operator==(const RenderPassDepthAttachmentDesc &other) const 336 { 337 return RenderPassAttachmentDesc::operator==(other) && clearDepth == other.clearDepth; 338 } 339 inline bool operator!=(const RenderPassDepthAttachmentDesc &other) const 340 { 341 return !(*this == other); 342 } 343 344 double clearDepth = 0; 345 }; 346 347 struct RenderPassStencilAttachmentDesc : public RenderPassAttachmentDesc 348 { 349 inline bool operator==(const RenderPassStencilAttachmentDesc &other) const 350 { 351 return RenderPassAttachmentDesc::operator==(other) && clearStencil == other.clearStencil; 352 } 353 inline bool operator!=(const RenderPassStencilAttachmentDesc &other) const 354 { 355 return !(*this == other); 356 } 357 uint32_t clearStencil = 0; 358 }; 359 360 // 361 // This is C++ equivalent of Objective-C MTLRenderPassDescriptor. 362 // We could use MTLRenderPassDescriptor directly, however, using C++ struct has benefits of fast 363 // copy, stack allocation, inlined comparing function, etc. 364 // 365 struct RenderPassDesc 366 { 367 RenderPassColorAttachmentDesc colorAttachments[kMaxRenderTargets]; 368 RenderPassDepthAttachmentDesc depthAttachment; 369 RenderPassStencilAttachmentDesc stencilAttachment; 370 371 void convertToMetalDesc(MTLRenderPassDescriptor *objCDesc) const; 372 373 // This will populate the RenderPipelineOutputDesc with default blend state and 374 // MTLColorWriteMaskAll 375 void populateRenderPipelineOutputDesc(RenderPipelineOutputDesc *outDesc) const; 376 // This will populate the RenderPipelineOutputDesc with default blend state and the specified 377 // MTLColorWriteMask 378 void populateRenderPipelineOutputDesc(const WriteMaskArray &writeMaskArray, 379 RenderPipelineOutputDesc *outDesc) const; 380 // This will populate the RenderPipelineOutputDesc with the specified blend state 381 void populateRenderPipelineOutputDesc(const BlendDescArray &blendDescArray, 382 RenderPipelineOutputDesc *outDesc) const; 383 384 bool equalIgnoreLoadStoreOptions(const RenderPassDesc &other) const; 385 bool operator==(const RenderPassDesc &other) const; 386 inline bool operator!=(const RenderPassDesc &other) const { return !(*this == other); } 387 388 uint32_t numColorAttachments = 0; 389 uint32_t sampleCount = 1; 390 }; 391 392 } // namespace mtl 393 } // namespace rx 394 395 namespace std 396 { 397 398 template <> 399 struct hash<rx::mtl::DepthStencilDesc> 400 { 401 size_t operator()(const rx::mtl::DepthStencilDesc &key) const { return key.hash(); } 402 }; 403 404 template <> 405 struct hash<rx::mtl::SamplerDesc> 406 { 407 size_t operator()(const rx::mtl::SamplerDesc &key) const { return key.hash(); } 408 }; 409 410 template <> 411 struct hash<rx::mtl::RenderPipelineDesc> 412 { 413 size_t operator()(const rx::mtl::RenderPipelineDesc &key) const { return key.hash(); } 414 }; 415 416 } // namespace std 417 418 namespace rx 419 { 420 namespace mtl 421 { 422 423 // Abstract factory to create specialized vertex & fragment shaders based on RenderPipelineDesc. 424 class RenderPipelineCacheSpecializeShaderFactory 425 { 426 public: 427 virtual ~RenderPipelineCacheSpecializeShaderFactory() = default; 428 // Get specialized shader for the render pipeline cache. 429 virtual angle::Result getSpecializedShader(Context *context, 430 gl::ShaderType shaderType, 431 const RenderPipelineDesc &renderPipelineDesc, 432 id<MTLFunction> *shaderOut) = 0; 433 // Check whether specialized shaders is required for the specified RenderPipelineDesc. 434 // If not, the render pipeline cache will use the supplied non-specialized shaders. 435 virtual bool hasSpecializedShader(gl::ShaderType shaderType, 436 const RenderPipelineDesc &renderPipelineDesc) = 0; 437 }; 438 439 // Render pipeline state cache per shader program. 440 class RenderPipelineCache final : angle::NonCopyable 441 { 442 public: 443 RenderPipelineCache(); 444 RenderPipelineCache(RenderPipelineCacheSpecializeShaderFactory *specializedShaderFactory); 445 ~RenderPipelineCache(); 446 447 // Set non-specialized vertex/fragment shader to be used by render pipeline cache to create 448 // render pipeline state. If the internal 449 // RenderPipelineCacheSpecializeShaderFactory.hasSpecializedShader() returns false for a 450 // particular RenderPipelineDesc, the render pipeline cache will use the non-specialized 451 // shaders. 452 void setVertexShader(Context *context, id<MTLFunction> shader); 453 void setFragmentShader(Context *context, id<MTLFunction> shader); 454 455 // Get non-specialized shaders supplied via set*Shader(). 456 id<MTLFunction> getVertexShader() { return mVertexShader; } 457 id<MTLFunction> getFragmentShader() { return mFragmentShader; } 458 459 AutoObjCPtr<id<MTLRenderPipelineState>> getRenderPipelineState(ContextMtl *context, 460 const RenderPipelineDesc &desc); 461 462 void clear(); 463 464 protected: 465 // Non-specialized vertex shader 466 AutoObjCPtr<id<MTLFunction>> mVertexShader; 467 // Non-specialized fragment shader 468 AutoObjCPtr<id<MTLFunction>> mFragmentShader; 469 470 private: 471 void clearPipelineStates(); 472 void recreatePipelineStates(Context *context); 473 AutoObjCPtr<id<MTLRenderPipelineState>> insertRenderPipelineState( 474 Context *context, 475 const RenderPipelineDesc &desc, 476 bool insertDefaultAttribLayout); 477 AutoObjCPtr<id<MTLRenderPipelineState>> createRenderPipelineState( 478 Context *context, 479 const RenderPipelineDesc &desc, 480 bool insertDefaultAttribLayout); 481 482 bool hasDefaultAttribs(const RenderPipelineDesc &desc) const; 483 484 // One table with default attrib and one table without. 485 angle::HashMap<RenderPipelineDesc, AutoObjCPtr<id<MTLRenderPipelineState>>> 486 mRenderPipelineStates[2]; 487 RenderPipelineCacheSpecializeShaderFactory *mSpecializedShaderFactory; 488 }; 489 490 class StateCache final : angle::NonCopyable 491 { 492 public: 493 StateCache(const angle::FeaturesMtl &features); 494 ~StateCache(); 495 496 // Null depth stencil state has depth/stecil read & write disabled. 497 inline AutoObjCPtr<id<MTLDepthStencilState>> getNullDepthStencilState(Context *context) 498 { 499 return getNullDepthStencilState(context->getMetalDevice()); 500 } 501 AutoObjCPtr<id<MTLDepthStencilState>> getNullDepthStencilState(id<MTLDevice> device); 502 AutoObjCPtr<id<MTLDepthStencilState>> getDepthStencilState(id<MTLDevice> device, 503 const DepthStencilDesc &desc); 504 AutoObjCPtr<id<MTLSamplerState>> getSamplerState(id<MTLDevice> device, const SamplerDesc &desc); 505 // Null sampler state uses default SamplerDesc 506 AutoObjCPtr<id<MTLSamplerState>> getNullSamplerState(Context *context); 507 AutoObjCPtr<id<MTLSamplerState>> getNullSamplerState(id<MTLDevice> device); 508 void clear(); 509 510 private: 511 const angle::FeaturesMtl &mFeatures; 512 513 AutoObjCPtr<id<MTLDepthStencilState>> mNullDepthStencilState = nil; 514 angle::HashMap<DepthStencilDesc, AutoObjCPtr<id<MTLDepthStencilState>>> mDepthStencilStates; 515 angle::HashMap<SamplerDesc, AutoObjCPtr<id<MTLSamplerState>>> mSamplerStates; 516 }; 517 518 } // namespace mtl 519 } // namespace rx 520 521 static inline bool operator==(const rx::mtl::VertexDesc &lhs, const rx::mtl::VertexDesc &rhs) 522 { 523 if (lhs.numAttribs != rhs.numAttribs || lhs.numBufferLayouts != rhs.numBufferLayouts) 524 { 525 return false; 526 } 527 for (uint8_t i = 0; i < lhs.numAttribs; ++i) 528 { 529 if (lhs.attributes[i] != rhs.attributes[i]) 530 { 531 return false; 532 } 533 } 534 for (uint8_t i = 0; i < lhs.numBufferLayouts; ++i) 535 { 536 if (lhs.layouts[i] != rhs.layouts[i]) 537 { 538 return false; 539 } 540 } 541 return true; 542 } 543 544 static inline bool operator==(const MTLClearColor &lhs, const MTLClearColor &rhs) 545 { 546 return lhs.red == rhs.red && lhs.green == rhs.green && lhs.blue == rhs.blue && 547 lhs.alpha == rhs.alpha; 548 } 549 550 #endif /* LIBANGLE_RENDERER_METAL_MTL_STATE_CACHE_H_ */ 551