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 rasterSampleCount; 225 }; 226 227 enum class RenderPipelineRasterization : uint32_t 228 { 229 // This flag is used for vertex shader not writing any stage output (e.g gl_Position). 230 // This will disable fragment shader stage. This is useful for transform feedback ouput vertex 231 // shader. 232 Disabled, 233 234 // Fragment shader is enabled. 235 Enabled, 236 237 // This flag is for rasterization discard emulation when vertex shader still writes to stage 238 // output. Disabled flag cannot be used in this case since Metal doesn't allow that. The 239 // emulation would insert a code snippet to move gl_Position out of clip space's visible area to 240 // simulate the discard. 241 EmulatedDiscard, 242 243 EnumCount, 244 }; 245 246 template <typename T> 247 using RenderPipelineRasterStateMap = angle::PackedEnumMap<RenderPipelineRasterization, T>; 248 249 struct alignas(4) RenderPipelineDesc 250 { 251 RenderPipelineDesc(); 252 RenderPipelineDesc(const RenderPipelineDesc &src); 253 RenderPipelineDesc(RenderPipelineDesc &&src); 254 255 RenderPipelineDesc &operator=(const RenderPipelineDesc &src); 256 257 bool operator==(const RenderPipelineDesc &rhs) const; 258 size_t hash() const; 259 bool rasterizationEnabled() const; 260 261 AutoObjCPtr<MTLRenderPipelineDescriptor *> createMetalDesc( 262 id<MTLFunction> vertexShader, 263 id<MTLFunction> fragmentShader) const; 264 265 VertexDesc vertexDescriptor; 266 267 RenderPipelineOutputDesc outputDescriptor; 268 269 // Use uint8_t instead of MTLPrimitiveTopologyClass to compact space. 270 uint8_t inputPrimitiveTopology : 2; 271 272 bool alphaToCoverageEnabled : 1; 273 274 // These flags are for emulation and do not correspond to any flags in 275 // MTLRenderPipelineDescriptor descriptor. These flags should be used by 276 // RenderPipelineCacheSpecializeShaderFactory. 277 RenderPipelineRasterization rasterizationType : 2; 278 }; 279 280 struct alignas(4) ProvokingVertexComputePipelineDesc 281 { 282 ProvokingVertexComputePipelineDesc(); 283 ProvokingVertexComputePipelineDesc(const ProvokingVertexComputePipelineDesc &src); 284 ProvokingVertexComputePipelineDesc(ProvokingVertexComputePipelineDesc &&src); 285 286 ProvokingVertexComputePipelineDesc &operator=(const ProvokingVertexComputePipelineDesc &src); 287 288 bool operator==(const ProvokingVertexComputePipelineDesc &rhs) const; 289 bool operator!=(const ProvokingVertexComputePipelineDesc &rhs) const; 290 size_t hash() const; 291 292 gl::PrimitiveMode primitiveMode; 293 uint8_t elementType; 294 bool primitiveRestartEnabled; 295 bool generateIndices; 296 }; 297 298 struct RenderPassAttachmentDesc 299 { 300 RenderPassAttachmentDesc(); 301 // Set default values 302 void reset(); 303 304 bool equalIgnoreLoadStoreOptions(const RenderPassAttachmentDesc &other) const; 305 bool operator==(const RenderPassAttachmentDesc &other) const; 306 hasImplicitMSTextureRenderPassAttachmentDesc307 ANGLE_INLINE bool hasImplicitMSTexture() const { return implicitMSTexture.get(); } 308 getImplicitMSTextureIfAvailOrTextureRenderPassAttachmentDesc309 const TextureRef &getImplicitMSTextureIfAvailOrTexture() const 310 { 311 return hasImplicitMSTexture() ? implicitMSTexture : texture; 312 } 313 314 TextureRef texture; 315 // Implicit multisample texture that will be rendered into and discarded at the end of 316 // a render pass. Its result will be resolved into normal texture above. 317 TextureRef implicitMSTexture; 318 MipmapNativeLevel level; 319 uint32_t sliceOrDepth; 320 321 // This attachment is blendable or not. 322 bool blendable; 323 MTLLoadAction loadAction; 324 MTLStoreAction storeAction; 325 MTLStoreActionOptions storeActionOptions; 326 }; 327 328 struct RenderPassColorAttachmentDesc : public RenderPassAttachmentDesc 329 { 330 inline bool operator==(const RenderPassColorAttachmentDesc &other) const 331 { 332 return RenderPassAttachmentDesc::operator==(other) && clearColor == other.clearColor; 333 } 334 inline bool operator!=(const RenderPassColorAttachmentDesc &other) const 335 { 336 return !(*this == other); 337 } 338 MTLClearColor clearColor = {0, 0, 0, 0}; 339 }; 340 341 struct RenderPassDepthAttachmentDesc : public RenderPassAttachmentDesc 342 { 343 inline bool operator==(const RenderPassDepthAttachmentDesc &other) const 344 { 345 return RenderPassAttachmentDesc::operator==(other) && clearDepth == other.clearDepth; 346 } 347 inline bool operator!=(const RenderPassDepthAttachmentDesc &other) const 348 { 349 return !(*this == other); 350 } 351 352 double clearDepth = 1.0; 353 }; 354 355 struct RenderPassStencilAttachmentDesc : public RenderPassAttachmentDesc 356 { 357 inline bool operator==(const RenderPassStencilAttachmentDesc &other) const 358 { 359 return RenderPassAttachmentDesc::operator==(other) && clearStencil == other.clearStencil; 360 } 361 inline bool operator!=(const RenderPassStencilAttachmentDesc &other) const 362 { 363 return !(*this == other); 364 } 365 uint32_t clearStencil = 0; 366 }; 367 368 // 369 // This is C++ equivalent of Objective-C MTLRenderPassDescriptor. 370 // We could use MTLRenderPassDescriptor directly, however, using C++ struct has benefits of fast 371 // copy, stack allocation, inlined comparing function, etc. 372 // 373 struct RenderPassDesc 374 { 375 std::array<RenderPassColorAttachmentDesc, kMaxRenderTargets> colorAttachments; 376 RenderPassDepthAttachmentDesc depthAttachment; 377 RenderPassStencilAttachmentDesc stencilAttachment; 378 379 void convertToMetalDesc(MTLRenderPassDescriptor *objCDesc, 380 uint32_t deviceMaxRenderTargets) const; 381 382 // This will populate the RenderPipelineOutputDesc with default blend state and 383 // MTLColorWriteMaskAll 384 void populateRenderPipelineOutputDesc(RenderPipelineOutputDesc *outDesc) const; 385 // This will populate the RenderPipelineOutputDesc with default blend state and the specified 386 // MTLColorWriteMask 387 void populateRenderPipelineOutputDesc(const WriteMaskArray &writeMaskArray, 388 RenderPipelineOutputDesc *outDesc) const; 389 // This will populate the RenderPipelineOutputDesc with the specified blend state 390 void populateRenderPipelineOutputDesc(const BlendDescArray &blendDescArray, 391 RenderPipelineOutputDesc *outDesc) const; 392 393 bool equalIgnoreLoadStoreOptions(const RenderPassDesc &other) const; 394 bool operator==(const RenderPassDesc &other) const; 395 inline bool operator!=(const RenderPassDesc &other) const { return !(*this == other); } 396 397 uint32_t numColorAttachments = 0; 398 uint32_t rasterSampleCount = 1; 399 uint32_t defaultWidth = 0; 400 uint32_t defaultHeight = 0; 401 }; 402 403 } // namespace mtl 404 } // namespace rx 405 406 namespace std 407 { 408 409 template <> 410 struct hash<rx::mtl::DepthStencilDesc> 411 { 412 size_t operator()(const rx::mtl::DepthStencilDesc &key) const { return key.hash(); } 413 }; 414 415 template <> 416 struct hash<rx::mtl::SamplerDesc> 417 { 418 size_t operator()(const rx::mtl::SamplerDesc &key) const { return key.hash(); } 419 }; 420 421 template <> 422 struct hash<rx::mtl::RenderPipelineDesc> 423 { 424 size_t operator()(const rx::mtl::RenderPipelineDesc &key) const { return key.hash(); } 425 }; 426 427 template <> 428 struct hash<rx::mtl::ProvokingVertexComputePipelineDesc> 429 { 430 size_t operator()(const rx::mtl::ProvokingVertexComputePipelineDesc &key) const 431 { 432 return key.hash(); 433 } 434 }; 435 } // namespace std 436 437 namespace rx 438 { 439 namespace mtl 440 { 441 442 class StateCache final : angle::NonCopyable 443 { 444 public: 445 StateCache(const angle::FeaturesMtl &features); 446 ~StateCache(); 447 448 // Null depth stencil state has depth/stecil read & write disabled. 449 AutoObjCPtr<id<MTLDepthStencilState>> getNullDepthStencilState( 450 const mtl::ContextDevice &device); 451 AutoObjCPtr<id<MTLDepthStencilState>> getDepthStencilState(const mtl::ContextDevice &device, 452 const DepthStencilDesc &desc); 453 AutoObjCPtr<id<MTLSamplerState>> getSamplerState(const mtl::ContextDevice &device, 454 const SamplerDesc &desc); 455 // Null sampler state uses default SamplerDesc 456 AutoObjCPtr<id<MTLSamplerState>> getNullSamplerState(ContextMtl *context); 457 AutoObjCPtr<id<MTLSamplerState>> getNullSamplerState(const mtl::ContextDevice &device); 458 void clear(); 459 460 private: 461 const angle::FeaturesMtl &mFeatures; 462 463 AutoObjCPtr<id<MTLDepthStencilState>> mNullDepthStencilState = nil; 464 angle::HashMap<DepthStencilDesc, AutoObjCPtr<id<MTLDepthStencilState>>> mDepthStencilStates; 465 angle::HashMap<SamplerDesc, AutoObjCPtr<id<MTLSamplerState>>> mSamplerStates; 466 }; 467 468 } // namespace mtl 469 } // namespace rx 470 471 static inline bool operator==(const rx::mtl::VertexDesc &lhs, const rx::mtl::VertexDesc &rhs) 472 { 473 if (lhs.numAttribs != rhs.numAttribs || lhs.numBufferLayouts != rhs.numBufferLayouts) 474 { 475 return false; 476 } 477 for (uint8_t i = 0; i < lhs.numAttribs; ++i) 478 { 479 if (lhs.attributes[i] != rhs.attributes[i]) 480 { 481 return false; 482 } 483 } 484 for (uint8_t i = 0; i < lhs.numBufferLayouts; ++i) 485 { 486 if (lhs.layouts[i] != rhs.layouts[i]) 487 { 488 return false; 489 } 490 } 491 return true; 492 } 493 494 static inline bool operator==(const MTLClearColor &lhs, const MTLClearColor &rhs) 495 { 496 return lhs.red == rhs.red && lhs.green == rhs.green && lhs.blue == rhs.blue && 497 lhs.alpha == rhs.alpha; 498 } 499 500 #endif /* LIBANGLE_RENDERER_METAL_MTL_STATE_CACHE_H_ */ 501