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.mm: 7// Implements StateCache, RenderPipelineCache and various 8// C struct versions of Metal sampler, depth stencil, render pass, render pipeline descriptors. 9// 10 11#include "libANGLE/renderer/metal/mtl_state_cache.h" 12 13#include <sstream> 14 15#include "common/debug.h" 16#include "common/hash_utils.h" 17#include "libANGLE/renderer/metal/ContextMtl.h" 18#include "libANGLE/renderer/metal/mtl_resources.h" 19#include "libANGLE/renderer/metal/mtl_utils.h" 20#include "platform/FeaturesMtl.h" 21 22#define ANGLE_OBJC_CP_PROPERTY(DST, SRC, PROPERTY) \ 23 (DST).PROPERTY = static_cast<__typeof__((DST).PROPERTY)>(ToObjC((SRC).PROPERTY)) 24 25#define ANGLE_PROP_EQ(LHS, RHS, PROP) ((LHS).PROP == (RHS).PROP) 26 27namespace rx 28{ 29namespace mtl 30{ 31 32namespace 33{ 34 35template <class T> 36inline T ToObjC(const T p) 37{ 38 return p; 39} 40 41inline MTLStencilDescriptor *ToObjC(const StencilDesc &desc) 42{ 43 MTLStencilDescriptor *objCDesc = [[MTLStencilDescriptor alloc] init]; 44 45 ANGLE_OBJC_CP_PROPERTY(objCDesc, desc, stencilFailureOperation); 46 ANGLE_OBJC_CP_PROPERTY(objCDesc, desc, depthFailureOperation); 47 ANGLE_OBJC_CP_PROPERTY(objCDesc, desc, depthStencilPassOperation); 48 ANGLE_OBJC_CP_PROPERTY(objCDesc, desc, stencilCompareFunction); 49 ANGLE_OBJC_CP_PROPERTY(objCDesc, desc, readMask); 50 ANGLE_OBJC_CP_PROPERTY(objCDesc, desc, writeMask); 51 52 return [objCDesc ANGLE_MTL_AUTORELEASE]; 53} 54 55MTLDepthStencilDescriptor *ToObjC(const DepthStencilDesc &desc) 56{ 57 MTLDepthStencilDescriptor *objCDesc = [[MTLDepthStencilDescriptor alloc] init]; 58 59 ANGLE_OBJC_CP_PROPERTY(objCDesc, desc, backFaceStencil); 60 ANGLE_OBJC_CP_PROPERTY(objCDesc, desc, frontFaceStencil); 61 ANGLE_OBJC_CP_PROPERTY(objCDesc, desc, depthCompareFunction); 62 ANGLE_OBJC_CP_PROPERTY(objCDesc, desc, depthWriteEnabled); 63 64 return [objCDesc ANGLE_MTL_AUTORELEASE]; 65} 66 67MTLSamplerDescriptor *ToObjC(const SamplerDesc &desc) 68{ 69 MTLSamplerDescriptor *objCDesc = [[MTLSamplerDescriptor alloc] init]; 70 71 ANGLE_OBJC_CP_PROPERTY(objCDesc, desc, rAddressMode); 72 ANGLE_OBJC_CP_PROPERTY(objCDesc, desc, sAddressMode); 73 ANGLE_OBJC_CP_PROPERTY(objCDesc, desc, tAddressMode); 74 ANGLE_OBJC_CP_PROPERTY(objCDesc, desc, minFilter); 75 ANGLE_OBJC_CP_PROPERTY(objCDesc, desc, magFilter); 76 ANGLE_OBJC_CP_PROPERTY(objCDesc, desc, mipFilter); 77 ANGLE_OBJC_CP_PROPERTY(objCDesc, desc, maxAnisotropy); 78 ANGLE_OBJC_CP_PROPERTY(objCDesc, desc, compareFunction); 79 80 return [objCDesc ANGLE_MTL_AUTORELEASE]; 81} 82 83MTLVertexAttributeDescriptor *ToObjC(const VertexAttributeDesc &desc) 84{ 85 MTLVertexAttributeDescriptor *objCDesc = [[MTLVertexAttributeDescriptor alloc] init]; 86 87 ANGLE_OBJC_CP_PROPERTY(objCDesc, desc, format); 88 ANGLE_OBJC_CP_PROPERTY(objCDesc, desc, offset); 89 ANGLE_OBJC_CP_PROPERTY(objCDesc, desc, bufferIndex); 90 91 ASSERT(desc.bufferIndex >= kVboBindingIndexStart); 92 93 return [objCDesc ANGLE_MTL_AUTORELEASE]; 94} 95 96MTLVertexBufferLayoutDescriptor *ToObjC(const VertexBufferLayoutDesc &desc) 97{ 98 MTLVertexBufferLayoutDescriptor *objCDesc = [[MTLVertexBufferLayoutDescriptor alloc] init]; 99 100 ANGLE_OBJC_CP_PROPERTY(objCDesc, desc, stepFunction); 101 ANGLE_OBJC_CP_PROPERTY(objCDesc, desc, stepRate); 102 ANGLE_OBJC_CP_PROPERTY(objCDesc, desc, stride); 103 104 return [objCDesc ANGLE_MTL_AUTORELEASE]; 105} 106 107MTLVertexDescriptor *ToObjC(const VertexDesc &desc) 108{ 109 MTLVertexDescriptor *objCDesc = [[MTLVertexDescriptor alloc] init]; 110 [objCDesc reset]; 111 112 for (uint8_t i = 0; i < desc.numAttribs; ++i) 113 { 114 [objCDesc.attributes setObject:ToObjC(desc.attributes[i]) atIndexedSubscript:i]; 115 } 116 117 for (uint8_t i = 0; i < desc.numBufferLayouts; ++i) 118 { 119 // Ignore if stepFunction is kVertexStepFunctionInvalid. 120 // If we don't set this slot, it will apparently be disabled by metal runtime. 121 if (desc.layouts[i].stepFunction != kVertexStepFunctionInvalid) 122 { 123 [objCDesc.layouts setObject:ToObjC(desc.layouts[i]) atIndexedSubscript:i]; 124 } 125 } 126 127 return [objCDesc ANGLE_MTL_AUTORELEASE]; 128} 129 130MTLRenderPipelineColorAttachmentDescriptor *ToObjC(const RenderPipelineColorAttachmentDesc &desc) 131{ 132 MTLRenderPipelineColorAttachmentDescriptor *objCDesc = 133 [[MTLRenderPipelineColorAttachmentDescriptor alloc] init]; 134 135 ANGLE_OBJC_CP_PROPERTY(objCDesc, desc, pixelFormat); 136 ANGLE_OBJC_CP_PROPERTY(objCDesc, desc, writeMask); 137 ANGLE_OBJC_CP_PROPERTY(objCDesc, desc, alphaBlendOperation); 138 ANGLE_OBJC_CP_PROPERTY(objCDesc, desc, rgbBlendOperation); 139 ANGLE_OBJC_CP_PROPERTY(objCDesc, desc, destinationAlphaBlendFactor); 140 ANGLE_OBJC_CP_PROPERTY(objCDesc, desc, destinationRGBBlendFactor); 141 ANGLE_OBJC_CP_PROPERTY(objCDesc, desc, sourceAlphaBlendFactor); 142 ANGLE_OBJC_CP_PROPERTY(objCDesc, desc, sourceRGBBlendFactor); 143 ANGLE_OBJC_CP_PROPERTY(objCDesc, desc, blendingEnabled); 144 145 return [objCDesc ANGLE_MTL_AUTORELEASE]; 146} 147 148MTLRenderPipelineDescriptor *ToObjC(id<MTLFunction> vertexShader, 149 id<MTLFunction> fragmentShader, 150 const RenderPipelineDesc &desc) 151{ 152 MTLRenderPipelineDescriptor *objCDesc = [[MTLRenderPipelineDescriptor alloc] init]; 153 [objCDesc reset]; 154 155 ANGLE_OBJC_CP_PROPERTY(objCDesc, desc, vertexDescriptor); 156 157 for (uint8_t i = 0; i < desc.outputDescriptor.numColorAttachments; ++i) 158 { 159 [objCDesc.colorAttachments setObject:ToObjC(desc.outputDescriptor.colorAttachments[i]) 160 atIndexedSubscript:i]; 161 } 162 ANGLE_OBJC_CP_PROPERTY(objCDesc, desc.outputDescriptor, depthAttachmentPixelFormat); 163 ANGLE_OBJC_CP_PROPERTY(objCDesc, desc.outputDescriptor, stencilAttachmentPixelFormat); 164 ANGLE_OBJC_CP_PROPERTY(objCDesc, desc.outputDescriptor, sampleCount); 165 166#if ANGLE_MTL_PRIMITIVE_TOPOLOGY_CLASS_AVAILABLE 167 ANGLE_OBJC_CP_PROPERTY(objCDesc, desc, inputPrimitiveTopology); 168#endif 169 ANGLE_OBJC_CP_PROPERTY(objCDesc, desc, alphaToCoverageEnabled); 170 171 // rasterizationEnabled will be true for both EmulatedDiscard & Enabled. 172 objCDesc.rasterizationEnabled = desc.rasterizationEnabled(); 173 174 objCDesc.vertexFunction = vertexShader; 175 objCDesc.fragmentFunction = objCDesc.rasterizationEnabled ? fragmentShader : nil; 176 177 return [objCDesc ANGLE_MTL_AUTORELEASE]; 178} 179 180id<MTLTexture> ToObjC(const TextureRef &texture) 181{ 182 auto textureRef = texture; 183 return textureRef ? textureRef->get() : nil; 184} 185 186void BaseRenderPassAttachmentDescToObjC(const RenderPassAttachmentDesc &src, 187 MTLRenderPassAttachmentDescriptor *dst) 188{ 189 const TextureRef &implicitMsTexture = src.implicitMSTexture; 190 191 if (implicitMsTexture) 192 { 193 dst.texture = ToObjC(implicitMsTexture); 194 dst.level = 0; 195 dst.slice = 0; 196 dst.depthPlane = 0; 197 dst.resolveTexture = ToObjC(src.texture); 198 dst.resolveLevel = src.level.get(); 199 if (dst.resolveTexture.textureType == MTLTextureType3D) 200 { 201 dst.resolveDepthPlane = src.sliceOrDepth; 202 dst.resolveSlice = 0; 203 } 204 else 205 { 206 dst.resolveSlice = src.sliceOrDepth; 207 dst.resolveDepthPlane = 0; 208 } 209 } 210 else 211 { 212 dst.texture = ToObjC(src.texture); 213 dst.level = src.level.get(); 214 if (dst.texture.textureType == MTLTextureType3D) 215 { 216 dst.depthPlane = src.sliceOrDepth; 217 dst.slice = 0; 218 } 219 else 220 { 221 dst.slice = src.sliceOrDepth; 222 dst.depthPlane = 0; 223 } 224 dst.resolveTexture = nil; 225 dst.resolveLevel = 0; 226 dst.resolveSlice = 0; 227 dst.resolveDepthPlane = 0; 228 } 229 230 ANGLE_OBJC_CP_PROPERTY(dst, src, loadAction); 231 ANGLE_OBJC_CP_PROPERTY(dst, src, storeAction); 232 ANGLE_OBJC_CP_PROPERTY(dst, src, storeActionOptions); 233} 234 235void ToObjC(const RenderPassColorAttachmentDesc &desc, 236 MTLRenderPassColorAttachmentDescriptor *objCDesc) 237{ 238 BaseRenderPassAttachmentDescToObjC(desc, objCDesc); 239 240 ANGLE_OBJC_CP_PROPERTY(objCDesc, desc, clearColor); 241} 242 243void ToObjC(const RenderPassDepthAttachmentDesc &desc, 244 MTLRenderPassDepthAttachmentDescriptor *objCDesc) 245{ 246 BaseRenderPassAttachmentDescToObjC(desc, objCDesc); 247 248 ANGLE_OBJC_CP_PROPERTY(objCDesc, desc, clearDepth); 249} 250 251void ToObjC(const RenderPassStencilAttachmentDesc &desc, 252 MTLRenderPassStencilAttachmentDescriptor *objCDesc) 253{ 254 BaseRenderPassAttachmentDescToObjC(desc, objCDesc); 255 256 ANGLE_OBJC_CP_PROPERTY(objCDesc, desc, clearStencil); 257} 258 259} // namespace 260 261// StencilDesc implementation 262bool StencilDesc::operator==(const StencilDesc &rhs) const 263{ 264 return ANGLE_PROP_EQ(*this, rhs, stencilFailureOperation) && 265 ANGLE_PROP_EQ(*this, rhs, depthFailureOperation) && 266 ANGLE_PROP_EQ(*this, rhs, depthStencilPassOperation) && 267 268 ANGLE_PROP_EQ(*this, rhs, stencilCompareFunction) && 269 270 ANGLE_PROP_EQ(*this, rhs, readMask) && ANGLE_PROP_EQ(*this, rhs, writeMask); 271} 272 273void StencilDesc::reset() 274{ 275 stencilFailureOperation = depthFailureOperation = depthStencilPassOperation = 276 MTLStencilOperationKeep; 277 278 stencilCompareFunction = MTLCompareFunctionAlways; 279 readMask = writeMask = std::numeric_limits<uint32_t>::max() & mtl::kStencilMaskAll; 280} 281 282// DepthStencilDesc implementation 283DepthStencilDesc::DepthStencilDesc() 284{ 285 memset(this, 0, sizeof(*this)); 286} 287DepthStencilDesc::DepthStencilDesc(const DepthStencilDesc &src) 288{ 289 memcpy(this, &src, sizeof(*this)); 290} 291DepthStencilDesc::DepthStencilDesc(DepthStencilDesc &&src) 292{ 293 memcpy(this, &src, sizeof(*this)); 294} 295 296DepthStencilDesc &DepthStencilDesc::operator=(const DepthStencilDesc &src) 297{ 298 memcpy(this, &src, sizeof(*this)); 299 return *this; 300} 301 302bool DepthStencilDesc::operator==(const DepthStencilDesc &rhs) const 303{ 304 return ANGLE_PROP_EQ(*this, rhs, backFaceStencil) && 305 ANGLE_PROP_EQ(*this, rhs, frontFaceStencil) && 306 307 ANGLE_PROP_EQ(*this, rhs, depthCompareFunction) && 308 309 ANGLE_PROP_EQ(*this, rhs, depthWriteEnabled); 310} 311 312void DepthStencilDesc::reset() 313{ 314 frontFaceStencil.reset(); 315 backFaceStencil.reset(); 316 317 depthCompareFunction = MTLCompareFunctionAlways; 318 depthWriteEnabled = true; 319} 320 321void DepthStencilDesc::updateDepthTestEnabled(const gl::DepthStencilState &dsState) 322{ 323 if (!dsState.depthTest) 324 { 325 depthCompareFunction = MTLCompareFunctionAlways; 326 depthWriteEnabled = false; 327 } 328 else 329 { 330 updateDepthCompareFunc(dsState); 331 updateDepthWriteEnabled(dsState); 332 } 333} 334 335void DepthStencilDesc::updateDepthWriteEnabled(const gl::DepthStencilState &dsState) 336{ 337 depthWriteEnabled = dsState.depthTest && dsState.depthMask; 338} 339 340void DepthStencilDesc::updateDepthCompareFunc(const gl::DepthStencilState &dsState) 341{ 342 if (!dsState.depthTest) 343 { 344 return; 345 } 346 depthCompareFunction = GetCompareFunc(dsState.depthFunc); 347} 348 349void DepthStencilDesc::updateStencilTestEnabled(const gl::DepthStencilState &dsState) 350{ 351 if (!dsState.stencilTest) 352 { 353 frontFaceStencil.stencilCompareFunction = MTLCompareFunctionAlways; 354 frontFaceStencil.depthFailureOperation = MTLStencilOperationKeep; 355 frontFaceStencil.depthStencilPassOperation = MTLStencilOperationKeep; 356 frontFaceStencil.writeMask = 0; 357 358 backFaceStencil.stencilCompareFunction = MTLCompareFunctionAlways; 359 backFaceStencil.depthFailureOperation = MTLStencilOperationKeep; 360 backFaceStencil.depthStencilPassOperation = MTLStencilOperationKeep; 361 backFaceStencil.writeMask = 0; 362 } 363 else 364 { 365 updateStencilFrontFuncs(dsState); 366 updateStencilFrontOps(dsState); 367 updateStencilFrontWriteMask(dsState); 368 updateStencilBackFuncs(dsState); 369 updateStencilBackOps(dsState); 370 updateStencilBackWriteMask(dsState); 371 } 372} 373 374void DepthStencilDesc::updateStencilFrontOps(const gl::DepthStencilState &dsState) 375{ 376 if (!dsState.stencilTest) 377 { 378 return; 379 } 380 frontFaceStencil.stencilFailureOperation = GetStencilOp(dsState.stencilFail); 381 frontFaceStencil.depthFailureOperation = GetStencilOp(dsState.stencilPassDepthFail); 382 frontFaceStencil.depthStencilPassOperation = GetStencilOp(dsState.stencilPassDepthPass); 383} 384 385void DepthStencilDesc::updateStencilBackOps(const gl::DepthStencilState &dsState) 386{ 387 if (!dsState.stencilTest) 388 { 389 return; 390 } 391 backFaceStencil.stencilFailureOperation = GetStencilOp(dsState.stencilBackFail); 392 backFaceStencil.depthFailureOperation = GetStencilOp(dsState.stencilBackPassDepthFail); 393 backFaceStencil.depthStencilPassOperation = GetStencilOp(dsState.stencilBackPassDepthPass); 394} 395 396void DepthStencilDesc::updateStencilFrontFuncs(const gl::DepthStencilState &dsState) 397{ 398 if (!dsState.stencilTest) 399 { 400 return; 401 } 402 frontFaceStencil.stencilCompareFunction = GetCompareFunc(dsState.stencilFunc); 403 frontFaceStencil.readMask = dsState.stencilMask & mtl::kStencilMaskAll; 404} 405 406void DepthStencilDesc::updateStencilBackFuncs(const gl::DepthStencilState &dsState) 407{ 408 if (!dsState.stencilTest) 409 { 410 return; 411 } 412 backFaceStencil.stencilCompareFunction = GetCompareFunc(dsState.stencilBackFunc); 413 backFaceStencil.readMask = dsState.stencilBackMask & mtl::kStencilMaskAll; 414} 415 416void DepthStencilDesc::updateStencilFrontWriteMask(const gl::DepthStencilState &dsState) 417{ 418 if (!dsState.stencilTest) 419 { 420 return; 421 } 422 frontFaceStencil.writeMask = dsState.stencilWritemask & mtl::kStencilMaskAll; 423} 424 425void DepthStencilDesc::updateStencilBackWriteMask(const gl::DepthStencilState &dsState) 426{ 427 if (!dsState.stencilTest) 428 { 429 return; 430 } 431 backFaceStencil.writeMask = dsState.stencilBackWritemask & mtl::kStencilMaskAll; 432} 433 434size_t DepthStencilDesc::hash() const 435{ 436 return angle::ComputeGenericHash(*this); 437} 438 439// SamplerDesc implementation 440SamplerDesc::SamplerDesc() 441{ 442 memset(this, 0, sizeof(*this)); 443} 444SamplerDesc::SamplerDesc(const SamplerDesc &src) 445{ 446 memcpy(this, &src, sizeof(*this)); 447} 448SamplerDesc::SamplerDesc(SamplerDesc &&src) 449{ 450 memcpy(this, &src, sizeof(*this)); 451} 452 453SamplerDesc::SamplerDesc(const gl::SamplerState &glState) : SamplerDesc() 454{ 455 rAddressMode = GetSamplerAddressMode(glState.getWrapR()); 456 sAddressMode = GetSamplerAddressMode(glState.getWrapS()); 457 tAddressMode = GetSamplerAddressMode(glState.getWrapT()); 458 459 minFilter = GetFilter(glState.getMinFilter()); 460 magFilter = GetFilter(glState.getMagFilter()); 461 mipFilter = GetMipmapFilter(glState.getMinFilter()); 462 463 maxAnisotropy = static_cast<uint32_t>(glState.getMaxAnisotropy()); 464 465 compareFunction = GetCompareFunc(glState.getCompareFunc()); 466} 467 468SamplerDesc &SamplerDesc::operator=(const SamplerDesc &src) 469{ 470 memcpy(this, &src, sizeof(*this)); 471 return *this; 472} 473 474void SamplerDesc::reset() 475{ 476 rAddressMode = MTLSamplerAddressModeClampToEdge; 477 sAddressMode = MTLSamplerAddressModeClampToEdge; 478 tAddressMode = MTLSamplerAddressModeClampToEdge; 479 480 minFilter = MTLSamplerMinMagFilterNearest; 481 magFilter = MTLSamplerMinMagFilterNearest; 482 mipFilter = MTLSamplerMipFilterNearest; 483 484 maxAnisotropy = 1; 485 486 compareFunction = MTLCompareFunctionNever; 487} 488 489bool SamplerDesc::operator==(const SamplerDesc &rhs) const 490{ 491 return ANGLE_PROP_EQ(*this, rhs, rAddressMode) && ANGLE_PROP_EQ(*this, rhs, sAddressMode) && 492 ANGLE_PROP_EQ(*this, rhs, tAddressMode) && 493 494 ANGLE_PROP_EQ(*this, rhs, minFilter) && ANGLE_PROP_EQ(*this, rhs, magFilter) && 495 ANGLE_PROP_EQ(*this, rhs, mipFilter) && 496 497 ANGLE_PROP_EQ(*this, rhs, maxAnisotropy) && 498 499 ANGLE_PROP_EQ(*this, rhs, compareFunction); 500} 501 502size_t SamplerDesc::hash() const 503{ 504 return angle::ComputeGenericHash(*this); 505} 506 507// BlendDesc implementation 508bool BlendDesc::operator==(const BlendDesc &rhs) const 509{ 510 return ANGLE_PROP_EQ(*this, rhs, writeMask) && 511 512 ANGLE_PROP_EQ(*this, rhs, alphaBlendOperation) && 513 ANGLE_PROP_EQ(*this, rhs, rgbBlendOperation) && 514 515 ANGLE_PROP_EQ(*this, rhs, destinationAlphaBlendFactor) && 516 ANGLE_PROP_EQ(*this, rhs, destinationRGBBlendFactor) && 517 ANGLE_PROP_EQ(*this, rhs, sourceAlphaBlendFactor) && 518 ANGLE_PROP_EQ(*this, rhs, sourceRGBBlendFactor) && 519 520 ANGLE_PROP_EQ(*this, rhs, blendingEnabled); 521} 522 523void BlendDesc::reset() 524{ 525 reset(MTLColorWriteMaskAll); 526} 527 528void BlendDesc::reset(MTLColorWriteMask _writeMask) 529{ 530 writeMask = _writeMask; 531 532 blendingEnabled = false; 533 alphaBlendOperation = rgbBlendOperation = MTLBlendOperationAdd; 534 535 destinationAlphaBlendFactor = destinationRGBBlendFactor = MTLBlendFactorZero; 536 sourceAlphaBlendFactor = sourceRGBBlendFactor = MTLBlendFactorOne; 537} 538 539void BlendDesc::updateWriteMask(const uint8_t angleMask) 540{ 541 ASSERT(angleMask == (angleMask & 0xF)); 542 543// ANGLE's packed color mask is abgr (matches Vulkan & D3D11), while Metal expects rgba. 544#if defined(__aarch64__) 545 // ARM64 can reverse bits in a single instruction 546 writeMask = __builtin_bitreverse8(angleMask) >> 4; 547#else 548 /* On other architectures, Clang generates a polyfill that uses more 549 instructions than the following expression optimized for a 4-bit value. 550 551 (abgr * 0x41) & 0x14A: 552 .......abgr + 553 .abgr...... & 554 00101001010 = 555 ..b.r..a.g. 556 557 (b.r..a.g.) * 0x111: 558 b.r..a.g. + 559 b.r..a.g..... + 560 b.r..a.g......... = 561 b.r.bargbarg.a.g. 562 ^^^^ 563 */ 564 writeMask = ((((angleMask * 0x41) & 0x14A) * 0x111) >> 7) & 0xF; 565#endif 566} 567 568// RenderPipelineColorAttachmentDesc implementation 569bool RenderPipelineColorAttachmentDesc::operator==( 570 const RenderPipelineColorAttachmentDesc &rhs) const 571{ 572 if (!BlendDesc::operator==(rhs)) 573 { 574 return false; 575 } 576 return ANGLE_PROP_EQ(*this, rhs, pixelFormat); 577} 578 579void RenderPipelineColorAttachmentDesc::reset() 580{ 581 reset(MTLPixelFormatInvalid); 582} 583 584void RenderPipelineColorAttachmentDesc::reset(MTLPixelFormat format) 585{ 586 reset(format, MTLColorWriteMaskAll); 587} 588 589void RenderPipelineColorAttachmentDesc::reset(MTLPixelFormat format, MTLColorWriteMask _writeMask) 590{ 591 this->pixelFormat = format; 592 593 BlendDesc::reset(_writeMask); 594} 595 596void RenderPipelineColorAttachmentDesc::reset(MTLPixelFormat format, const BlendDesc &blendDesc) 597{ 598 this->pixelFormat = format; 599 600 BlendDesc::operator=(blendDesc); 601} 602 603void RenderPipelineColorAttachmentDesc::update(const BlendDesc &blendDesc) 604{ 605 BlendDesc::operator=(blendDesc); 606} 607 608// RenderPipelineOutputDesc implementation 609bool RenderPipelineOutputDesc::operator==(const RenderPipelineOutputDesc &rhs) const 610{ 611 if (numColorAttachments != rhs.numColorAttachments) 612 { 613 return false; 614 } 615 616 for (uint8_t i = 0; i < numColorAttachments; ++i) 617 { 618 if (colorAttachments[i] != rhs.colorAttachments[i]) 619 { 620 return false; 621 } 622 } 623 624 return ANGLE_PROP_EQ(*this, rhs, depthAttachmentPixelFormat) && 625 ANGLE_PROP_EQ(*this, rhs, stencilAttachmentPixelFormat); 626} 627 628void RenderPipelineOutputDesc::updateEnabledDrawBuffers(gl::DrawBufferMask enabledBuffers) 629{ 630 for (uint32_t colorIndex = 0; colorIndex < this->numColorAttachments; ++colorIndex) 631 { 632 if (!enabledBuffers.test(colorIndex)) 633 { 634 this->colorAttachments[colorIndex].writeMask = MTLColorWriteMaskNone; 635 } 636 } 637} 638 639// RenderPipelineDesc implementation 640RenderPipelineDesc::RenderPipelineDesc() 641{ 642 memset(this, 0, sizeof(*this)); 643 outputDescriptor.sampleCount = 1; 644 rasterizationType = RenderPipelineRasterization::Enabled; 645} 646 647RenderPipelineDesc::RenderPipelineDesc(const RenderPipelineDesc &src) 648{ 649 memcpy(this, &src, sizeof(*this)); 650} 651 652RenderPipelineDesc::RenderPipelineDesc(RenderPipelineDesc &&src) 653{ 654 memcpy(this, &src, sizeof(*this)); 655} 656 657RenderPipelineDesc &RenderPipelineDesc::operator=(const RenderPipelineDesc &src) 658{ 659 memcpy(this, &src, sizeof(*this)); 660 return *this; 661} 662 663bool RenderPipelineDesc::operator==(const RenderPipelineDesc &rhs) const 664{ 665 // NOTE(hqle): Use a faster way to compare, i.e take into account 666 // the number of active vertex attributes & render targets. 667 // If that way is used, hash() method must be changed also. 668 return memcmp(this, &rhs, sizeof(*this)) == 0; 669} 670 671size_t RenderPipelineDesc::hash() const 672{ 673 return angle::ComputeGenericHash(*this); 674} 675 676bool RenderPipelineDesc::rasterizationEnabled() const 677{ 678 return rasterizationType != RenderPipelineRasterization::Disabled; 679} 680 681// RenderPassDesc implementation 682RenderPassAttachmentDesc::RenderPassAttachmentDesc() 683{ 684 reset(); 685} 686 687void RenderPassAttachmentDesc::reset() 688{ 689 texture.reset(); 690 implicitMSTexture.reset(); 691 level = mtl::kZeroNativeMipLevel; 692 sliceOrDepth = 0; 693 blendable = false; 694 loadAction = MTLLoadActionLoad; 695 storeAction = MTLStoreActionStore; 696 storeActionOptions = MTLStoreActionOptionNone; 697} 698 699bool RenderPassAttachmentDesc::equalIgnoreLoadStoreOptions( 700 const RenderPassAttachmentDesc &other) const 701{ 702 return texture == other.texture && implicitMSTexture == other.implicitMSTexture && 703 level == other.level && sliceOrDepth == other.sliceOrDepth && 704 blendable == other.blendable; 705} 706 707bool RenderPassAttachmentDesc::operator==(const RenderPassAttachmentDesc &other) const 708{ 709 if (!equalIgnoreLoadStoreOptions(other)) 710 { 711 return false; 712 } 713 714 return loadAction == other.loadAction && storeAction == other.storeAction && 715 storeActionOptions == other.storeActionOptions; 716} 717 718void RenderPassDesc::populateRenderPipelineOutputDesc(RenderPipelineOutputDesc *outDesc) const 719{ 720 WriteMaskArray writeMaskArray; 721 writeMaskArray.fill(MTLColorWriteMaskAll); 722 populateRenderPipelineOutputDesc(writeMaskArray, outDesc); 723} 724 725void RenderPassDesc::populateRenderPipelineOutputDesc(const WriteMaskArray &writeMaskArray, 726 RenderPipelineOutputDesc *outDesc) const 727{ 728 // Default blend state with replaced color write masks. 729 BlendDescArray blendDescArray; 730 for (size_t i = 0; i < blendDescArray.size(); i++) 731 { 732 blendDescArray[i].reset(writeMaskArray[i]); 733 } 734 populateRenderPipelineOutputDesc(blendDescArray, outDesc); 735} 736 737void RenderPassDesc::populateRenderPipelineOutputDesc(const BlendDescArray &blendDescArray, 738 RenderPipelineOutputDesc *outDesc) const 739{ 740 RenderPipelineOutputDesc &outputDescriptor = *outDesc; 741 outputDescriptor.numColorAttachments = this->numColorAttachments; 742 outputDescriptor.sampleCount = this->sampleCount; 743 for (uint32_t i = 0; i < this->numColorAttachments; ++i) 744 { 745 auto &renderPassColorAttachment = this->colorAttachments[i]; 746 auto texture = renderPassColorAttachment.texture; 747 748 if (texture) 749 { 750 if (renderPassColorAttachment.blendable) 751 { 752 // Copy parameters from blend state 753 outputDescriptor.colorAttachments[i].reset(texture->pixelFormat(), 754 blendDescArray[i]); 755 } 756 else 757 { 758 // Disable blending if the attachment's render target doesn't support blending. 759 // Force default blending state to reduce the number of unique states. 760 outputDescriptor.colorAttachments[i].reset(texture->pixelFormat(), 761 blendDescArray[i].writeMask); 762 } 763 764 // Combine the masks. This is useful when the texture is not supposed to have alpha 765 // channel such as GL_RGB8, however, Metal doesn't natively support 24 bit RGB, so 766 // we need to use RGBA texture, and then disable alpha write to this texture 767 outputDescriptor.colorAttachments[i].writeMask &= texture->getColorWritableMask(); 768 } 769 else 770 { 771 772 outputDescriptor.colorAttachments[i].blendingEnabled = false; 773 outputDescriptor.colorAttachments[i].pixelFormat = MTLPixelFormatInvalid; 774 } 775 } 776 777 // Reset the unused output slots to ensure consistent hash value 778 for (uint32_t i = this->numColorAttachments; i < kMaxRenderTargets; ++i) 779 { 780 outputDescriptor.colorAttachments[i].reset(); 781 } 782 783 auto depthTexture = this->depthAttachment.texture; 784 outputDescriptor.depthAttachmentPixelFormat = 785 depthTexture ? depthTexture->pixelFormat() : MTLPixelFormatInvalid; 786 787 auto stencilTexture = this->stencilAttachment.texture; 788 outputDescriptor.stencilAttachmentPixelFormat = 789 stencilTexture ? stencilTexture->pixelFormat() : MTLPixelFormatInvalid; 790} 791 792bool RenderPassDesc::equalIgnoreLoadStoreOptions(const RenderPassDesc &other) const 793{ 794 if (numColorAttachments != other.numColorAttachments) 795 { 796 return false; 797 } 798 799 for (uint32_t i = 0; i < numColorAttachments; ++i) 800 { 801 auto &renderPassColorAttachment = colorAttachments[i]; 802 auto &otherRPAttachment = other.colorAttachments[i]; 803 if (!renderPassColorAttachment.equalIgnoreLoadStoreOptions(otherRPAttachment)) 804 { 805 return false; 806 } 807 } 808 809 return depthAttachment.equalIgnoreLoadStoreOptions(other.depthAttachment) && 810 stencilAttachment.equalIgnoreLoadStoreOptions(other.stencilAttachment); 811} 812 813bool RenderPassDesc::operator==(const RenderPassDesc &other) const 814{ 815 if (numColorAttachments != other.numColorAttachments) 816 { 817 return false; 818 } 819 820 for (uint32_t i = 0; i < numColorAttachments; ++i) 821 { 822 auto &renderPassColorAttachment = colorAttachments[i]; 823 auto &otherRPAttachment = other.colorAttachments[i]; 824 if (renderPassColorAttachment != (otherRPAttachment)) 825 { 826 return false; 827 } 828 } 829 830 return depthAttachment == other.depthAttachment && stencilAttachment == other.stencilAttachment; 831} 832 833// Convert to Metal object 834void RenderPassDesc::convertToMetalDesc(MTLRenderPassDescriptor *objCDesc) const 835{ 836 ANGLE_MTL_OBJC_SCOPE 837 { 838 for (uint32_t i = 0; i < numColorAttachments; ++i) 839 { 840 ToObjC(colorAttachments[i], objCDesc.colorAttachments[i]); 841 } 842 for (uint32_t i = numColorAttachments; i < kMaxRenderTargets; ++i) 843 { 844 // Inactive render target 845 objCDesc.colorAttachments[i].texture = nil; 846 objCDesc.colorAttachments[i].level = 0; 847 objCDesc.colorAttachments[i].slice = 0; 848 objCDesc.colorAttachments[i].depthPlane = 0; 849 objCDesc.colorAttachments[i].loadAction = MTLLoadActionDontCare; 850 objCDesc.colorAttachments[i].storeAction = MTLStoreActionDontCare; 851 } 852 853 ToObjC(depthAttachment, objCDesc.depthAttachment); 854 ToObjC(stencilAttachment, objCDesc.stencilAttachment); 855 } 856} 857 858// RenderPipelineCache implementation 859RenderPipelineCache::RenderPipelineCache() : RenderPipelineCache(nullptr) {} 860 861RenderPipelineCache::RenderPipelineCache( 862 RenderPipelineCacheSpecializeShaderFactory *specializedShaderFactory) 863 : mSpecializedShaderFactory(specializedShaderFactory) 864{} 865 866RenderPipelineCache::~RenderPipelineCache() {} 867 868void RenderPipelineCache::setVertexShader(ContextMtl *context, id<MTLFunction> shader) 869{ 870 mVertexShader.retainAssign(shader); 871 872 if (!shader) 873 { 874 clearPipelineStates(); 875 return; 876 } 877 878 recreatePipelineStates(context); 879} 880 881void RenderPipelineCache::setFragmentShader(ContextMtl *context, id<MTLFunction> shader) 882{ 883 mFragmentShader.retainAssign(shader); 884 885 if (!shader) 886 { 887 clearPipelineStates(); 888 return; 889 } 890 891 recreatePipelineStates(context); 892} 893 894bool RenderPipelineCache::hasDefaultAttribs(const RenderPipelineDesc &rpdesc) const 895{ 896 const VertexDesc &desc = rpdesc.vertexDescriptor; 897 for (uint8_t i = 0; i < desc.numAttribs; ++i) 898 { 899 if (desc.attributes[i].bufferIndex == kDefaultAttribsBindingIndex) 900 { 901 return true; 902 } 903 } 904 905 return false; 906} 907 908AutoObjCPtr<id<MTLRenderPipelineState>> RenderPipelineCache::getRenderPipelineState( 909 ContextMtl *context, 910 const RenderPipelineDesc &desc) 911{ 912 auto insertDefaultAttribLayout = hasDefaultAttribs(desc); 913 int tableIdx = insertDefaultAttribLayout ? 1 : 0; 914 auto &table = mRenderPipelineStates[tableIdx]; 915 auto ite = table.find(desc); 916 if (ite == table.end()) 917 { 918 return insertRenderPipelineState(context, desc, insertDefaultAttribLayout); 919 } 920 921 return ite->second; 922} 923 924AutoObjCPtr<id<MTLRenderPipelineState>> RenderPipelineCache::insertRenderPipelineState( 925 ContextMtl *context, 926 const RenderPipelineDesc &desc, 927 bool insertDefaultAttribLayout) 928{ 929 AutoObjCPtr<id<MTLRenderPipelineState>> newState = 930 createRenderPipelineState(context, desc, insertDefaultAttribLayout); 931 if (!newState) 932 { 933 return nil; 934 } 935 936 int tableIdx = insertDefaultAttribLayout ? 1 : 0; 937 auto re = mRenderPipelineStates[tableIdx].insert(std::make_pair(desc, newState)); 938 if (!re.second) 939 { 940 return nil; 941 } 942 943 return re.first->second; 944} 945 946AutoObjCPtr<id<MTLRenderPipelineState>> RenderPipelineCache::createRenderPipelineState( 947 ContextMtl *context, 948 const RenderPipelineDesc &originalDesc, 949 bool insertDefaultAttribLayout) 950{ 951 ANGLE_MTL_OBJC_SCOPE 952 { 953 // Disable coverage if the render pipeline's sample count is only 1. 954 RenderPipelineDesc desc = originalDesc; 955 if (desc.outputDescriptor.sampleCount == 1) 956 { 957 // Disable sample coverage if the output is not multisample 958 desc.emulateCoverageMask = false; 959 desc.alphaToCoverageEnabled = false; 960 } 961 962 // Choose shader variant 963 id<MTLFunction> vertShader = nil; 964 id<MTLFunction> fragShader = nil; 965 if (mSpecializedShaderFactory && 966 mSpecializedShaderFactory->hasSpecializedShader(gl::ShaderType::Vertex, desc)) 967 { 968 if (IsError(mSpecializedShaderFactory->getSpecializedShader( 969 context, gl::ShaderType::Vertex, desc, &vertShader))) 970 { 971 return nil; 972 } 973 } 974 else 975 { 976 // Non-specialized version 977 vertShader = mVertexShader; 978 } 979 980 if (mSpecializedShaderFactory && 981 mSpecializedShaderFactory->hasSpecializedShader(gl::ShaderType::Fragment, desc)) 982 { 983 if (IsError(mSpecializedShaderFactory->getSpecializedShader( 984 context, gl::ShaderType::Fragment, desc, &fragShader))) 985 { 986 return nil; 987 } 988 } 989 else 990 { 991 // Non-specialized version 992 fragShader = mFragmentShader; 993 } 994 995 if (!vertShader) 996 { 997 // Render pipeline without vertex shader is invalid. 998 context->handleError(GL_INVALID_OPERATION, __FILE__, ANGLE_FUNCTION, __LINE__); 999 return nil; 1000 } 1001 1002 const mtl::ContextDevice &metalDevice = context->getMetalDevice(); 1003 1004 // Convert to Objective-C desc: 1005 AutoObjCObj<MTLRenderPipelineDescriptor> objCDesc = ToObjC(vertShader, fragShader, desc); 1006 1007 // Validate Render Pipeline State: 1008 if (DeviceHasMaximumRenderTargetSize(metalDevice)) 1009 { 1010 // TODO: Is the use of NSUInteger in 32 bit systems ok without any overflow checking? 1011 NSUInteger maxSize = GetMaxRenderTargetSizeForDeviceInBytes(metalDevice); 1012 NSUInteger renderTargetSize = 1013 ComputeTotalSizeUsedForMTLRenderPipelineDescriptor(objCDesc, context, metalDevice); 1014 if (renderTargetSize > maxSize) 1015 { 1016 NSString *errorString = [NSString 1017 stringWithFormat:@"This set of render targets requires %lu bytes of " 1018 @"pixel storage. This device supports %lu bytes.", 1019 (unsigned long)renderTargetSize, (unsigned long)maxSize]; 1020 NSError *err = [NSError errorWithDomain:@"MTLValidationError" 1021 code:-1 1022 userInfo:@{NSLocalizedDescriptionKey : errorString}]; 1023 context->handleError(err, __FILE__, ANGLE_FUNCTION, __LINE__); 1024 return nil; 1025 } 1026 } 1027 1028 // Special attribute slot for default attribute 1029 if (insertDefaultAttribLayout) 1030 { 1031 MTLVertexBufferLayoutDescriptor *defaultAttribLayoutObjCDesc = 1032 [[MTLVertexBufferLayoutDescriptor alloc] init]; 1033 defaultAttribLayoutObjCDesc.stepFunction = MTLVertexStepFunctionConstant; 1034 defaultAttribLayoutObjCDesc.stepRate = 0; 1035 defaultAttribLayoutObjCDesc.stride = kDefaultAttributeSize * kMaxVertexAttribs; 1036 1037 [objCDesc.get().vertexDescriptor.layouts 1038 setObject:[defaultAttribLayoutObjCDesc ANGLE_MTL_AUTORELEASE] 1039 atIndexedSubscript:kDefaultAttribsBindingIndex]; 1040 } 1041 // Create pipeline state 1042 NSError *err = nil; 1043 auto newState = metalDevice.newRenderPipelineStateWithDescriptor(objCDesc, &err); 1044 if (err) 1045 { 1046 context->handleError(err, __FILE__, ANGLE_FUNCTION, __LINE__); 1047 return nil; 1048 } 1049 1050 return newState; 1051 } 1052} 1053 1054void RenderPipelineCache::recreatePipelineStates(ContextMtl *context) 1055{ 1056 for (int hasDefaultAttrib = 0; hasDefaultAttrib <= 1; ++hasDefaultAttrib) 1057 { 1058 for (auto &ite : mRenderPipelineStates[hasDefaultAttrib]) 1059 { 1060 if (ite.second == nil) 1061 { 1062 continue; 1063 } 1064 1065 ite.second = createRenderPipelineState(context, ite.first, hasDefaultAttrib); 1066 } 1067 } 1068} 1069 1070void RenderPipelineCache::clear() 1071{ 1072 mVertexShader = nil; 1073 mFragmentShader = nil; 1074 clearPipelineStates(); 1075} 1076 1077void RenderPipelineCache::clearPipelineStates() 1078{ 1079 mRenderPipelineStates[0].clear(); 1080 mRenderPipelineStates[1].clear(); 1081} 1082 1083// ProvokingVertexPipelineDesc 1084ProvokingVertexComputePipelineDesc::ProvokingVertexComputePipelineDesc() 1085{ 1086 memset(this, 0, sizeof(*this)); 1087} 1088ProvokingVertexComputePipelineDesc::ProvokingVertexComputePipelineDesc( 1089 const ProvokingVertexComputePipelineDesc &src) 1090{ 1091 memcpy(this, &src, sizeof(*this)); 1092} 1093ProvokingVertexComputePipelineDesc::ProvokingVertexComputePipelineDesc( 1094 const ProvokingVertexComputePipelineDesc &&src) 1095{ 1096 memcpy(this, &src, sizeof(*this)); 1097} 1098ProvokingVertexComputePipelineDesc &ProvokingVertexComputePipelineDesc::operator=( 1099 const ProvokingVertexComputePipelineDesc &src) 1100{ 1101 memcpy(this, &src, sizeof(*this)); 1102 return *this; 1103} 1104bool ProvokingVertexComputePipelineDesc::operator==( 1105 const ProvokingVertexComputePipelineDesc &rhs) const 1106{ 1107 return memcmp(this, &rhs, sizeof(*this)) == 0; 1108} 1109bool ProvokingVertexComputePipelineDesc::operator!=( 1110 const ProvokingVertexComputePipelineDesc &rhs) const 1111{ 1112 return !(*this == rhs); 1113} 1114size_t ProvokingVertexComputePipelineDesc::hash() const 1115{ 1116 return angle::ComputeGenericHash(*this); 1117} 1118 1119ProvokingVertexComputePipelineCache::ProvokingVertexComputePipelineCache() : mComputeShader(nullptr) 1120{} 1121 1122ProvokingVertexComputePipelineCache::ProvokingVertexComputePipelineCache( 1123 ProvokingVertexCacheSpecializeShaderFactory *specializedShaderFactory) 1124 : mComputeShader(nullptr), mSpecializedShaderFactory(specializedShaderFactory) 1125{} 1126 1127void ProvokingVertexComputePipelineCache::setComputeShader(ContextMtl *context, 1128 id<MTLFunction> shader) 1129{ 1130 mComputeShader.retainAssign(shader); 1131 if (!shader) 1132 { 1133 clearPipelineStates(); 1134 return; 1135 } 1136 1137 recreatePipelineStates(context); 1138} 1139 1140void ProvokingVertexComputePipelineCache::clearPipelineStates() 1141{ 1142 mComputePipelineStates.clear(); 1143} 1144 1145void ProvokingVertexComputePipelineCache::clear() 1146{ 1147 clearPipelineStates(); 1148} 1149 1150AutoObjCPtr<id<MTLComputePipelineState>> 1151ProvokingVertexComputePipelineCache::getComputePipelineState( 1152 ContextMtl *context, 1153 const ProvokingVertexComputePipelineDesc &desc) 1154{ 1155 auto &table = mComputePipelineStates; 1156 auto ite = table.find(desc); 1157 if (ite == table.end()) 1158 { 1159 return insertComputePipelineState(context, desc); 1160 } 1161 1162 return ite->second; 1163} 1164 1165AutoObjCPtr<id<MTLComputePipelineState>> 1166ProvokingVertexComputePipelineCache::insertComputePipelineState( 1167 ContextMtl *context, 1168 const ProvokingVertexComputePipelineDesc &desc) 1169{ 1170 AutoObjCPtr<id<MTLComputePipelineState>> newState = createComputePipelineState(context, desc); 1171 1172 auto re = mComputePipelineStates.insert(std::make_pair(desc, newState)); 1173 if (!re.second) 1174 { 1175 return nil; 1176 } 1177 1178 return re.first->second; 1179} 1180 1181void ProvokingVertexComputePipelineCache::recreatePipelineStates(ContextMtl *context) 1182{ 1183 1184 for (auto &ite : mComputePipelineStates) 1185 { 1186 if (ite.second == nil) 1187 { 1188 continue; 1189 } 1190 1191 ite.second = createComputePipelineState(context, ite.first); 1192 } 1193} 1194 1195AutoObjCPtr<id<MTLComputePipelineState>> 1196ProvokingVertexComputePipelineCache::createComputePipelineState( 1197 ContextMtl *context, 1198 const ProvokingVertexComputePipelineDesc &originalDesc) 1199{ 1200 ANGLE_MTL_OBJC_SCOPE 1201 { 1202 // Disable coverage if the render pipeline's sample count is only 1. 1203 ProvokingVertexComputePipelineDesc desc = originalDesc; 1204 1205 id<MTLFunction> computeFunction = nil; 1206 // Special case for transform feedback shader, we've already created it! See 1207 // getTransformFeedbackRenderPipeline 1208 if (mSpecializedShaderFactory && 1209 mSpecializedShaderFactory->hasSpecializedShader(gl::ShaderType::Compute, desc)) 1210 { 1211 if (IsError(mSpecializedShaderFactory->getSpecializedShader( 1212 context, gl::ShaderType::Compute, desc, &computeFunction))) 1213 { 1214 return nil; 1215 } 1216 } 1217 else 1218 { 1219 // Non-specialized version 1220 computeFunction = mComputeShader; 1221 } 1222 1223 if (!computeFunction) 1224 { 1225 // Render pipeline without vertex shader is invalid. 1226 context->handleError(GL_INVALID_OPERATION, __FILE__, ANGLE_FUNCTION, __LINE__); 1227 return nil; 1228 } 1229 1230 const mtl::ContextDevice &metalDevice = context->getMetalDevice(); 1231 1232 // Convert to Objective-C desc: 1233 NSError *err = nil; 1234 auto newState = metalDevice.newComputePipelineStateWithFunction(computeFunction, &err); 1235 if (err) 1236 { 1237 context->handleError(err, __FILE__, ANGLE_FUNCTION, __LINE__); 1238 return nil; 1239 } 1240 1241 return newState; 1242 } 1243} 1244 1245ProvokingVertexComputePipelineCache::~ProvokingVertexComputePipelineCache() {} 1246 1247// StateCache implementation 1248StateCache::StateCache(const angle::FeaturesMtl &features) : mFeatures(features) {} 1249 1250StateCache::~StateCache() {} 1251 1252AutoObjCPtr<id<MTLDepthStencilState>> StateCache::getNullDepthStencilState( 1253 const mtl::ContextDevice &device) 1254{ 1255 if (!mNullDepthStencilState) 1256 { 1257 DepthStencilDesc desc; 1258 desc.reset(); 1259 ASSERT(desc.frontFaceStencil.stencilCompareFunction == MTLCompareFunctionAlways); 1260 desc.depthWriteEnabled = false; 1261 mNullDepthStencilState = getDepthStencilState(device, desc); 1262 } 1263 return mNullDepthStencilState; 1264} 1265 1266AutoObjCPtr<id<MTLDepthStencilState>> StateCache::getDepthStencilState( 1267 const mtl::ContextDevice &device, 1268 const DepthStencilDesc &desc) 1269{ 1270 ANGLE_MTL_OBJC_SCOPE 1271 { 1272 auto ite = mDepthStencilStates.find(desc); 1273 if (ite == mDepthStencilStates.end()) 1274 { 1275 auto re = mDepthStencilStates.insert( 1276 std::make_pair(desc, device.newDepthStencilStateWithDescriptor(ToObjC(desc)))); 1277 if (!re.second) 1278 { 1279 return nil; 1280 } 1281 1282 ite = re.first; 1283 } 1284 1285 return ite->second; 1286 } 1287} 1288 1289AutoObjCPtr<id<MTLSamplerState>> StateCache::getSamplerState(const mtl::ContextDevice &device, 1290 const SamplerDesc &desc) 1291{ 1292 ANGLE_MTL_OBJC_SCOPE 1293 { 1294 auto ite = mSamplerStates.find(desc); 1295 if (ite == mSamplerStates.end()) 1296 { 1297 AutoObjCObj<MTLSamplerDescriptor> objCDesc = ToObjC(desc); 1298 if (!mFeatures.allowRuntimeSamplerCompareMode.enabled) 1299 { 1300 // Runtime sampler compare mode is not supported, fallback to never. 1301 objCDesc.get().compareFunction = MTLCompareFunctionNever; 1302 } 1303 auto re = mSamplerStates.insert( 1304 std::make_pair(desc, device.newSamplerStateWithDescriptor(objCDesc))); 1305 if (!re.second) 1306 return nil; 1307 1308 ite = re.first; 1309 } 1310 1311 return ite->second; 1312 } 1313} 1314 1315AutoObjCPtr<id<MTLSamplerState>> StateCache::getNullSamplerState(ContextMtl *context) 1316{ 1317 return getNullSamplerState(context->getMetalDevice()); 1318} 1319 1320AutoObjCPtr<id<MTLSamplerState>> StateCache::getNullSamplerState(const mtl::ContextDevice &device) 1321{ 1322 SamplerDesc desc; 1323 desc.reset(); 1324 1325 return getSamplerState(device, desc); 1326} 1327 1328void StateCache::clear() 1329{ 1330 mNullDepthStencilState = nil; 1331 mDepthStencilStates.clear(); 1332 mSamplerStates.clear(); 1333} 1334 1335} // namespace mtl 1336} // namespace rx 1337