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