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// ContextMtl.mm: 7// Implements the class methods for ContextMtl. 8// 9 10#include "libANGLE/renderer/metal/ContextMtl.h" 11 12#include <TargetConditionals.h> 13#include <cstdint> 14 15#include "GLSLANG/ShaderLang.h" 16#include "common/debug.h" 17#include "image_util/loadimage.h" 18#include "libANGLE/Display.h" 19#include "libANGLE/Query.h" 20#include "libANGLE/TransformFeedback.h" 21#include "libANGLE/renderer/OverlayImpl.h" 22#include "libANGLE/renderer/metal/BufferMtl.h" 23#include "libANGLE/renderer/metal/CompilerMtl.h" 24#include "libANGLE/renderer/metal/DisplayMtl.h" 25#include "libANGLE/renderer/metal/FrameBufferMtl.h" 26#include "libANGLE/renderer/metal/ProgramExecutableMtl.h" 27#include "libANGLE/renderer/metal/ProgramMtl.h" 28#include "libANGLE/renderer/metal/QueryMtl.h" 29#include "libANGLE/renderer/metal/RenderBufferMtl.h" 30#include "libANGLE/renderer/metal/RenderTargetMtl.h" 31#include "libANGLE/renderer/metal/SamplerMtl.h" 32#include "libANGLE/renderer/metal/ShaderMtl.h" 33#include "libANGLE/renderer/metal/SyncMtl.h" 34#include "libANGLE/renderer/metal/TextureMtl.h" 35#include "libANGLE/renderer/metal/TransformFeedbackMtl.h" 36#include "libANGLE/renderer/metal/VertexArrayMtl.h" 37#include "libANGLE/renderer/metal/mtl_command_buffer.h" 38#include "libANGLE/renderer/metal/mtl_common.h" 39#include "libANGLE/renderer/metal/mtl_context_device.h" 40#include "libANGLE/renderer/metal/mtl_format_utils.h" 41#include "libANGLE/renderer/metal/mtl_utils.h" 42 43namespace rx 44{ 45 46namespace 47{ 48#if TARGET_OS_OSX 49// Unlimited triangle fan buffers 50constexpr uint32_t kMaxTriFanLineLoopBuffersPerFrame = 0; 51#else 52// Allow up to 10 buffers for trifan/line loop generation without stalling the GPU. 53constexpr uint32_t kMaxTriFanLineLoopBuffersPerFrame = 10; 54#endif 55 56#define ANGLE_MTL_XFB_DRAW(DRAW_PROC) \ 57 if (!mState.isTransformFeedbackActiveUnpaused()) \ 58 { \ 59 /* Normal draw call */ \ 60 DRAW_PROC(false); \ 61 } \ 62 else \ 63 { \ 64 /* First pass: write to XFB buffers in vertex shader, fragment shader inactive */ \ 65 bool rasterizationNotDisabled = \ 66 mRenderPipelineDesc.rasterizationType != mtl::RenderPipelineRasterization::Disabled; \ 67 if (rasterizationNotDisabled) \ 68 { \ 69 invalidateRenderPipeline(); \ 70 } \ 71 DRAW_PROC(true); \ 72 if (rasterizationNotDisabled) \ 73 { \ 74 /* Second pass: full rasterization: vertex shader + fragment shader are active. \ 75 Vertex shader writes to stage output but won't write to XFB buffers */ \ 76 invalidateRenderPipeline(); \ 77 DRAW_PROC(false); \ 78 } \ 79 } 80 81angle::Result AllocateTriangleFanBufferFromPool(ContextMtl *context, 82 GLsizei vertexCount, 83 mtl::BufferPool *pool, 84 mtl::BufferRef *bufferOut, 85 uint32_t *offsetOut, 86 uint32_t *numElemsOut) 87{ 88 uint32_t numIndices; 89 ANGLE_TRY(mtl::GetTriangleFanIndicesCount(context, vertexCount, &numIndices)); 90 91 size_t offset; 92 pool->releaseInFlightBuffers(context); 93 ANGLE_TRY(pool->allocate(context, numIndices * sizeof(uint32_t), nullptr, bufferOut, &offset, 94 nullptr)); 95 96 *offsetOut = static_cast<uint32_t>(offset); 97 *numElemsOut = numIndices; 98 99 return angle::Result::Continue; 100} 101 102angle::Result AllocateBufferFromPool(ContextMtl *context, 103 GLsizei indicesToReserve, 104 mtl::BufferPool *pool, 105 mtl::BufferRef *bufferOut, 106 uint32_t *offsetOut) 107{ 108 size_t offset; 109 pool->releaseInFlightBuffers(context); 110 ANGLE_TRY(pool->allocate(context, indicesToReserve * sizeof(uint32_t), nullptr, bufferOut, 111 &offset, nullptr)); 112 113 *offsetOut = static_cast<uint32_t>(offset); 114 115 return angle::Result::Continue; 116} 117 118bool NeedToInvertDepthRange(float near, float far) 119{ 120 return near > far; 121} 122 123bool IsTransformFeedbackOnly(const gl::State &glState) 124{ 125 return glState.isTransformFeedbackActiveUnpaused() && glState.isRasterizerDiscardEnabled(); 126} 127 128std::string ConvertMarkerToString(GLsizei length, const char *marker) 129{ 130 std::string cppString; 131 if (length == 0) 132 { 133 cppString = marker; 134 } 135 else 136 { 137 cppString.assign(marker, length); 138 } 139 return cppString; 140} 141 142// This class constructs line loop's last segment buffer inside begin() method 143// and perform the draw of the line loop's last segment inside destructor 144class LineLoopLastSegmentHelper 145{ 146 public: 147 LineLoopLastSegmentHelper() {} 148 149 ~LineLoopLastSegmentHelper() 150 { 151 if (!mLineLoopIndexBuffer) 152 { 153 return; 154 } 155 156 // Draw last segment of line loop here 157 mtl::RenderCommandEncoder *encoder = mContextMtl->getRenderCommandEncoder(); 158 ASSERT(encoder); 159 encoder->drawIndexed(MTLPrimitiveTypeLine, 2, MTLIndexTypeUInt32, mLineLoopIndexBuffer, 0); 160 } 161 162 angle::Result begin(const gl::Context *context, 163 mtl::BufferPool *indexBufferPool, 164 GLint firstVertex, 165 GLsizei vertexOrIndexCount, 166 gl::DrawElementsType indexTypeOrNone, 167 const void *indices) 168 { 169 mContextMtl = mtl::GetImpl(context); 170 171 indexBufferPool->releaseInFlightBuffers(mContextMtl); 172 173 ANGLE_TRY(indexBufferPool->allocate(mContextMtl, 2 * sizeof(uint32_t), nullptr, 174 &mLineLoopIndexBuffer, nullptr, nullptr)); 175 176 if (indexTypeOrNone == gl::DrawElementsType::InvalidEnum) 177 { 178 ANGLE_TRY(mContextMtl->getDisplay()->getUtils().generateLineLoopLastSegment( 179 mContextMtl, firstVertex, firstVertex + vertexOrIndexCount - 1, 180 mLineLoopIndexBuffer, 0)); 181 } 182 else 183 { 184 ASSERT(firstVertex == 0); 185 ANGLE_TRY( 186 mContextMtl->getDisplay()->getUtils().generateLineLoopLastSegmentFromElementsArray( 187 mContextMtl, 188 {indexTypeOrNone, vertexOrIndexCount, indices, mLineLoopIndexBuffer, 0})); 189 } 190 191 ANGLE_TRY(indexBufferPool->commit(mContextMtl)); 192 193 return angle::Result::Continue; 194 } 195 196 private: 197 ContextMtl *mContextMtl = nullptr; 198 mtl::BufferRef mLineLoopIndexBuffer; 199}; 200 201GLint GetOwnershipIdentity(const egl::AttributeMap &attribs) 202{ 203 return attribs.getAsInt(EGL_CONTEXT_METAL_OWNERSHIP_IDENTITY_ANGLE, 0); 204} 205 206} // namespace 207 208ContextMtl::ContextMtl(const gl::State &state, 209 gl::ErrorSet *errorSet, 210 const egl::AttributeMap &attribs, 211 DisplayMtl *display) 212 : ContextImpl(state, errorSet), 213 mtl::Context(display), 214 mCmdBuffer(&display->cmdQueue()), 215 mRenderEncoder(&mCmdBuffer, mOcclusionQueryPool), 216 mBlitEncoder(&mCmdBuffer), 217 mComputeEncoder(&mCmdBuffer), 218 mDriverUniforms{}, 219 mProvokingVertexHelper(this), 220 mContextDevice(GetOwnershipIdentity(attribs)) 221{ 222 if (@available(iOS 12.0, macOS 10.14, *)) 223 { 224 mHasMetalSharedEvents = true; 225 } 226} 227 228ContextMtl::~ContextMtl() {} 229 230angle::Result ContextMtl::initialize(const angle::ImageLoadContext &imageLoadContext) 231{ 232 for (mtl::BlendDesc &blendDesc : mBlendDescArray) 233 { 234 blendDesc.reset(); 235 } 236 237 mWriteMaskArray.fill(MTLColorWriteMaskAll); 238 239 mDepthStencilDesc.reset(); 240 241 mTriFanIndexBuffer.initialize(this, 0, mtl::kIndexBufferOffsetAlignment, 242 kMaxTriFanLineLoopBuffersPerFrame); 243 mLineLoopIndexBuffer.initialize(this, 0, mtl::kIndexBufferOffsetAlignment, 244 kMaxTriFanLineLoopBuffersPerFrame); 245 mLineLoopLastSegmentIndexBuffer.initialize(this, 2 * sizeof(uint32_t), 246 mtl::kIndexBufferOffsetAlignment, 247 kMaxTriFanLineLoopBuffersPerFrame); 248 249 mContextDevice.set(mDisplay->getMetalDevice()); 250 251 mImageLoadContext = imageLoadContext; 252 253 return angle::Result::Continue; 254} 255 256void ContextMtl::onDestroy(const gl::Context *context) 257{ 258 mTriFanIndexBuffer.destroy(this); 259 mLineLoopIndexBuffer.destroy(this); 260 mLineLoopLastSegmentIndexBuffer.destroy(this); 261 mOcclusionQueryPool.destroy(this); 262 263 mIncompleteTextures.onDestroy(context); 264 mProvokingVertexHelper.onDestroy(this); 265 mDummyXFBRenderTexture = nullptr; 266 267 mContextDevice.reset(); 268} 269 270// Flush and finish. 271angle::Result ContextMtl::flush(const gl::Context *context) 272{ 273 if (mHasMetalSharedEvents) 274 { 275 // MTLSharedEvent is available on these platforms, and callers 276 // are expected to use the EGL_ANGLE_metal_shared_event_sync 277 // extension to synchronize with ANGLE's Metal backend, if 278 // needed. This is typically required if two MTLDevices are 279 // operating on the same IOSurface. 280 flushCommandBuffer(mtl::NoWait); 281 } 282 else 283 { 284 // Older operating systems do not have this primitive available. 285 // Make every flush operation wait until it's scheduled in order to 286 // achieve callers' expected synchronization behavior. 287 flushCommandBuffer(mtl::WaitUntilScheduled); 288 } 289 return angle::Result::Continue; 290} 291angle::Result ContextMtl::finish(const gl::Context *context) 292{ 293 ANGLE_TRY(finishCommandBuffer()); 294 return angle::Result::Continue; 295} 296 297ANGLE_INLINE angle::Result ContextMtl::resyncDrawFramebufferIfNeeded(const gl::Context *context) 298{ 299 // Resync the draw framebuffer if 300 // - it has incompatible attachments; OR 301 // - it had incompatible attachments during the previous operation. 302 if (ANGLE_UNLIKELY(mIncompatibleAttachments.any() || mForceResyncDrawFramebuffer)) 303 { 304 if (mIncompatibleAttachments.any()) 305 { 306 ANGLE_PERF_WARNING(context->getState().getDebug(), GL_DEBUG_SEVERITY_LOW, 307 "Resyncing the draw framebuffer because it has active attachments " 308 "incompatible with the current program outputs."); 309 } 310 311 // Ensure sync on the next operation if the current state has incompatible attachments. 312 mForceResyncDrawFramebuffer = mIncompatibleAttachments.any(); 313 314 FramebufferMtl *fbo = mtl::GetImpl(getState().getDrawFramebuffer()); 315 ASSERT(fbo != nullptr); 316 return fbo->syncState(context, GL_DRAW_FRAMEBUFFER, gl::Framebuffer::DirtyBits(), 317 gl::Command::Draw); 318 } 319 return angle::Result::Continue; 320} 321 322// Drawing methods. 323angle::Result ContextMtl::drawTriFanArraysWithBaseVertex(const gl::Context *context, 324 GLint first, 325 GLsizei count, 326 GLsizei instances, 327 GLuint baseInstance) 328{ 329 ASSERT((getDisplay()->getFeatures().hasBaseVertexInstancedDraw.enabled)); 330 331 uint32_t genIndicesCount; 332 ANGLE_TRY(mtl::GetTriangleFanIndicesCount(this, count, &genIndicesCount)); 333 334 size_t indexBufferSize = genIndicesCount * sizeof(uint32_t); 335 // We can reuse the previously generated index buffer if it has more than enough indices 336 // data already. 337 if (mTriFanArraysIndexBuffer == nullptr || mTriFanArraysIndexBuffer->size() < indexBufferSize) 338 { 339 // Re-generate a new index buffer, which the first index will be zero. 340 ANGLE_TRY( 341 mtl::Buffer::MakeBuffer(this, indexBufferSize, nullptr, &mTriFanArraysIndexBuffer)); 342 ANGLE_TRY(getDisplay()->getUtils().generateTriFanBufferFromArrays( 343 this, {0, static_cast<uint32_t>(count), mTriFanArraysIndexBuffer, 0})); 344 } 345 346 ASSERT(!getState().isTransformFeedbackActiveUnpaused()); 347 bool isNoOp = false; 348 ANGLE_TRY(setupDraw(context, gl::PrimitiveMode::TriangleFan, first, count, instances, 349 gl::DrawElementsType::InvalidEnum, reinterpret_cast<const void *>(0), false, 350 &isNoOp)); 351 if (!isNoOp) 352 { 353 // Draw with the zero starting index buffer, shift the vertex index using baseVertex 354 // instanced draw: 355 mRenderEncoder.drawIndexedInstancedBaseVertexBaseInstance( 356 MTLPrimitiveTypeTriangle, genIndicesCount, MTLIndexTypeUInt32, mTriFanArraysIndexBuffer, 357 0, instances, first, baseInstance); 358 } 359 360 return angle::Result::Continue; 361} 362angle::Result ContextMtl::drawTriFanArraysLegacy(const gl::Context *context, 363 GLint first, 364 GLsizei count, 365 GLsizei instances) 366{ 367 // Legacy method is only used for GPU lacking instanced base vertex draw capabilities. 368 mtl::BufferRef genIdxBuffer; 369 uint32_t genIdxBufferOffset; 370 uint32_t genIndicesCount; 371 ANGLE_TRY(AllocateTriangleFanBufferFromPool(this, count, &mTriFanIndexBuffer, &genIdxBuffer, 372 &genIdxBufferOffset, &genIndicesCount)); 373 ANGLE_TRY(getDisplay()->getUtils().generateTriFanBufferFromArrays( 374 this, {static_cast<uint32_t>(first), static_cast<uint32_t>(count), genIdxBuffer, 375 genIdxBufferOffset})); 376 377 ANGLE_TRY(mTriFanIndexBuffer.commit(this)); 378 379 ASSERT(!getState().isTransformFeedbackActiveUnpaused()); 380 bool isNoOp = false; 381 ANGLE_TRY(setupDraw(context, gl::PrimitiveMode::TriangleFan, first, count, instances, 382 gl::DrawElementsType::InvalidEnum, reinterpret_cast<const void *>(0), false, 383 &isNoOp)); 384 if (!isNoOp) 385 { 386 mRenderEncoder.drawIndexedInstanced(MTLPrimitiveTypeTriangle, genIndicesCount, 387 MTLIndexTypeUInt32, genIdxBuffer, genIdxBufferOffset, 388 instances); 389 } 390 return angle::Result::Continue; 391} 392angle::Result ContextMtl::drawTriFanArrays(const gl::Context *context, 393 GLint first, 394 GLsizei count, 395 GLsizei instances, 396 GLuint baseInstance) 397{ 398 if (count <= 3 && baseInstance == 0) 399 { 400 return drawArraysImpl(context, gl::PrimitiveMode::Triangles, first, count, instances, 0); 401 } 402 if (getDisplay()->getFeatures().hasBaseVertexInstancedDraw.enabled) 403 { 404 return drawTriFanArraysWithBaseVertex(context, first, count, instances, baseInstance); 405 } 406 return drawTriFanArraysLegacy(context, first, count, instances); 407} 408 409angle::Result ContextMtl::drawLineLoopArraysNonInstanced(const gl::Context *context, 410 GLint first, 411 GLsizei count) 412{ 413 // Generate line loop's last segment. It will be rendered when this function exits. 414 LineLoopLastSegmentHelper lineloopHelper; 415 // Line loop helper needs to generate last segment indices before rendering command encoder 416 // starts. 417 ANGLE_TRY(lineloopHelper.begin(context, &mLineLoopLastSegmentIndexBuffer, first, count, 418 gl::DrawElementsType::InvalidEnum, nullptr)); 419 420 return drawArraysImpl(context, gl::PrimitiveMode::LineStrip, first, count, 0, 0); 421} 422 423angle::Result ContextMtl::drawLineLoopArrays(const gl::Context *context, 424 GLint first, 425 GLsizei count, 426 GLsizei instances, 427 GLuint baseInstance) 428{ 429 if (instances <= 1 && baseInstance == 0) 430 { 431 return drawLineLoopArraysNonInstanced(context, first, count); 432 } 433 434 mtl::BufferRef genIdxBuffer; 435 uint32_t genIdxBufferOffset; 436 uint32_t genIndicesCount = count + 1; 437 438 ANGLE_TRY(AllocateBufferFromPool(this, genIndicesCount, &mLineLoopIndexBuffer, &genIdxBuffer, 439 &genIdxBufferOffset)); 440 ANGLE_TRY(getDisplay()->getUtils().generateLineLoopBufferFromArrays( 441 this, {static_cast<uint32_t>(first), static_cast<uint32_t>(count), genIdxBuffer, 442 genIdxBufferOffset})); 443 444 ANGLE_TRY(mLineLoopIndexBuffer.commit(this)); 445 446 ASSERT(!getState().isTransformFeedbackActiveUnpaused()); 447 bool isNoOp = false; 448 ANGLE_TRY(setupDraw(context, gl::PrimitiveMode::LineLoop, first, count, instances, 449 gl::DrawElementsType::InvalidEnum, nullptr, false, &isNoOp)); 450 if (!isNoOp) 451 { 452 if (baseInstance == 0) 453 { 454 mRenderEncoder.drawIndexedInstanced(MTLPrimitiveTypeLineStrip, genIndicesCount, 455 MTLIndexTypeUInt32, genIdxBuffer, 456 genIdxBufferOffset, instances); 457 } 458 else 459 { 460 mRenderEncoder.drawIndexedInstancedBaseVertexBaseInstance( 461 MTLPrimitiveTypeLineStrip, genIndicesCount, MTLIndexTypeUInt32, genIdxBuffer, 462 genIdxBufferOffset, instances, 0, baseInstance); 463 } 464 } 465 466 return angle::Result::Continue; 467} 468 469angle::Result ContextMtl::drawArraysImpl(const gl::Context *context, 470 gl::PrimitiveMode mode, 471 GLint first, 472 GLsizei count, 473 GLsizei instances, 474 GLuint baseInstance) 475{ 476 // Real instances count. Zero means this is not instanced draw. 477 GLsizei instanceCount = instances ? instances : 1; 478 479 if (mCullAllPolygons && gl::IsPolygonMode(mode)) 480 { 481 return angle::Result::Continue; 482 } 483 if (requiresIndexRewrite(context->getState(), mode)) 484 { 485 return drawArraysProvokingVertexImpl(context, mode, first, count, instances, baseInstance); 486 } 487 if (mode == gl::PrimitiveMode::TriangleFan) 488 { 489 return drawTriFanArrays(context, first, count, instanceCount, baseInstance); 490 } 491 else if (mode == gl::PrimitiveMode::LineLoop) 492 { 493 return drawLineLoopArrays(context, first, count, instanceCount, baseInstance); 494 } 495 496 MTLPrimitiveType mtlType = mtl::GetPrimitiveType(mode); 497 498#define DRAW_GENERIC_ARRAY(xfbPass) \ 499 { \ 500 bool isNoOp = false; \ 501 ANGLE_TRY(setupDraw(context, mode, first, count, instances, \ 502 gl::DrawElementsType::InvalidEnum, nullptr, xfbPass, &isNoOp)); \ 503 if (!isNoOp) \ 504 { \ 505 \ 506 if (instances == 0) \ 507 { \ 508 /* This method is called from normal drawArrays() */ \ 509 mRenderEncoder.draw(mtlType, first, count); \ 510 } \ 511 else \ 512 { \ 513 if (baseInstance == 0) \ 514 { \ 515 mRenderEncoder.drawInstanced(mtlType, first, count, instanceCount); \ 516 } \ 517 else \ 518 { \ 519 mRenderEncoder.drawInstancedBaseInstance(mtlType, first, count, instanceCount, \ 520 baseInstance); \ 521 } \ 522 } \ 523 } \ 524 } 525 526 ANGLE_MTL_XFB_DRAW(DRAW_GENERIC_ARRAY) 527 528 return angle::Result::Continue; 529} 530 531angle::Result ContextMtl::drawArrays(const gl::Context *context, 532 gl::PrimitiveMode mode, 533 GLint first, 534 GLsizei count) 535{ 536 ANGLE_TRY(resyncDrawFramebufferIfNeeded(context)); 537 return drawArraysImpl(context, mode, first, count, 0, 0); 538} 539 540angle::Result ContextMtl::drawArraysInstanced(const gl::Context *context, 541 gl::PrimitiveMode mode, 542 GLint first, 543 GLsizei count, 544 GLsizei instances) 545{ 546 // Instanced draw calls with zero instances are skipped in the frontend. 547 // The drawArraysImpl function would treat them as non-instanced. 548 ASSERT(instances > 0); 549 ANGLE_TRY(resyncDrawFramebufferIfNeeded(context)); 550 return drawArraysImpl(context, mode, first, count, instances, 0); 551} 552 553angle::Result ContextMtl::drawArraysInstancedBaseInstance(const gl::Context *context, 554 gl::PrimitiveMode mode, 555 GLint first, 556 GLsizei count, 557 GLsizei instanceCount, 558 GLuint baseInstance) 559{ 560 // Instanced draw calls with zero instances are skipped in the frontend. 561 // The drawArraysImpl function would treat them as non-instanced. 562 ASSERT(instanceCount > 0); 563 ANGLE_TRY(resyncDrawFramebufferIfNeeded(context)); 564 return drawArraysImpl(context, mode, first, count, instanceCount, baseInstance); 565} 566 567angle::Result ContextMtl::drawTriFanElements(const gl::Context *context, 568 GLsizei count, 569 gl::DrawElementsType type, 570 const void *indices, 571 GLsizei instances, 572 GLint baseVertex, 573 GLuint baseInstance) 574{ 575 if (count > 3) 576 { 577 mtl::BufferRef genIdxBuffer; 578 uint32_t genIdxBufferOffset; 579 uint32_t genIndicesCount; 580 bool primitiveRestart = getState().isPrimitiveRestartEnabled(); 581 ANGLE_TRY(AllocateTriangleFanBufferFromPool(this, count, &mTriFanIndexBuffer, &genIdxBuffer, 582 &genIdxBufferOffset, &genIndicesCount)); 583 584 ANGLE_TRY(getDisplay()->getUtils().generateTriFanBufferFromElementsArray( 585 this, {type, count, indices, genIdxBuffer, genIdxBufferOffset, primitiveRestart}, 586 &genIndicesCount)); 587 588 ANGLE_TRY(mTriFanIndexBuffer.commit(this)); 589 bool isNoOp = false; 590 ANGLE_TRY(setupDraw(context, gl::PrimitiveMode::TriangleFan, 0, count, instances, type, 591 indices, false, &isNoOp)); 592 if (!isNoOp && genIndicesCount > 0) 593 { 594 if (baseVertex == 0 && baseInstance == 0) 595 { 596 mRenderEncoder.drawIndexedInstanced(MTLPrimitiveTypeTriangle, genIndicesCount, 597 MTLIndexTypeUInt32, genIdxBuffer, 598 genIdxBufferOffset, instances); 599 } 600 else 601 { 602 mRenderEncoder.drawIndexedInstancedBaseVertexBaseInstance( 603 MTLPrimitiveTypeTriangle, genIndicesCount, MTLIndexTypeUInt32, genIdxBuffer, 604 genIdxBufferOffset, instances, baseVertex, baseInstance); 605 } 606 } 607 608 return angle::Result::Continue; 609 } // if (count > 3) 610 return drawElementsImpl(context, gl::PrimitiveMode::Triangles, count, type, indices, instances, 611 baseVertex, baseInstance); 612} 613 614angle::Result ContextMtl::drawLineLoopElementsNonInstancedNoPrimitiveRestart( 615 const gl::Context *context, 616 GLsizei count, 617 gl::DrawElementsType type, 618 const void *indices) 619{ 620 // Generate line loop's last segment. It will be rendered when this function exits. 621 LineLoopLastSegmentHelper lineloopHelper; 622 // Line loop helper needs to generate index before rendering command encoder starts. 623 ANGLE_TRY( 624 lineloopHelper.begin(context, &mLineLoopLastSegmentIndexBuffer, 0, count, type, indices)); 625 626 return drawElementsImpl(context, gl::PrimitiveMode::LineStrip, count, type, indices, 0, 0, 0); 627} 628 629angle::Result ContextMtl::drawLineLoopElements(const gl::Context *context, 630 GLsizei count, 631 gl::DrawElementsType type, 632 const void *indices, 633 GLsizei instances, 634 GLint baseVertex, 635 GLuint baseInstance) 636{ 637 if (count >= 2) 638 { 639 bool primitiveRestart = getState().isPrimitiveRestartEnabled(); 640 if (instances <= 1 && !primitiveRestart && baseVertex == 0 && baseInstance == 0) 641 { 642 // Non instanced draw and no primitive restart, just use faster version. 643 return drawLineLoopElementsNonInstancedNoPrimitiveRestart(context, count, type, 644 indices); 645 } 646 647 mtl::BufferRef genIdxBuffer; 648 uint32_t genIdxBufferOffset; 649 uint32_t reservedIndices = count * 2; 650 uint32_t genIndicesCount; 651 ANGLE_TRY(AllocateBufferFromPool(this, reservedIndices, &mLineLoopIndexBuffer, 652 &genIdxBuffer, &genIdxBufferOffset)); 653 654 ANGLE_TRY(getDisplay()->getUtils().generateLineLoopBufferFromElementsArray( 655 this, {type, count, indices, genIdxBuffer, genIdxBufferOffset, primitiveRestart}, 656 &genIndicesCount)); 657 658 ANGLE_TRY(mLineLoopIndexBuffer.commit(this)); 659 bool isNoOp = false; 660 ANGLE_TRY(setupDraw(context, gl::PrimitiveMode::LineLoop, 0, count, instances, type, 661 indices, false, &isNoOp)); 662 if (!isNoOp && genIndicesCount > 0) 663 { 664 if (baseVertex == 0 && baseInstance == 0) 665 { 666 mRenderEncoder.drawIndexedInstanced(MTLPrimitiveTypeLineStrip, genIndicesCount, 667 MTLIndexTypeUInt32, genIdxBuffer, 668 genIdxBufferOffset, instances); 669 } 670 else 671 { 672 mRenderEncoder.drawIndexedInstancedBaseVertexBaseInstance( 673 MTLPrimitiveTypeLineStrip, genIndicesCount, MTLIndexTypeUInt32, genIdxBuffer, 674 genIdxBufferOffset, instances, baseVertex, baseInstance); 675 } 676 } 677 678 return angle::Result::Continue; 679 } // if (count >= 2) 680 return drawElementsImpl(context, gl::PrimitiveMode::Lines, count, type, indices, instances, 681 baseVertex, baseInstance); 682} 683 684angle::Result ContextMtl::drawArraysProvokingVertexImpl(const gl::Context *context, 685 gl::PrimitiveMode mode, 686 GLsizei first, 687 GLsizei count, 688 GLsizei instances, 689 GLuint baseInstance) 690{ 691 692 size_t outIndexCount = 0; 693 size_t outIndexOffset = 0; 694 gl::DrawElementsType convertedType = gl::DrawElementsType::UnsignedInt; 695 gl::PrimitiveMode outIndexMode = gl::PrimitiveMode::InvalidEnum; 696 697 mtl::BufferRef drawIdxBuffer; 698 ANGLE_TRY(mProvokingVertexHelper.generateIndexBuffer( 699 mtl::GetImpl(context), first, count, mode, convertedType, outIndexCount, outIndexOffset, 700 outIndexMode, drawIdxBuffer)); 701 GLsizei outIndexCounti32 = static_cast<GLsizei>(outIndexCount); 702 703 // Note: we don't need to pass the generated index buffer to ContextMtl::setupDraw. 704 // Because setupDraw only needs to operate on the original vertex buffers & PrimitiveMode. 705 // setupDraw might convert vertex attributes if the offset & alignment are not natively 706 // supported by Metal. However, the converted attributes have the same order as the original 707 // vertices. Hence the conversion doesn't need to know about the newly generated index buffer. 708#define DRAW_PROVOKING_VERTEX_ARRAY(xfbPass) \ 709 if (xfbPass) \ 710 { \ 711 bool isNoOp = false; \ 712 ANGLE_TRY(setupDraw(context, mode, first, count, instances, \ 713 gl::DrawElementsType::InvalidEnum, nullptr, xfbPass, &isNoOp)); \ 714 if (!isNoOp) \ 715 { \ 716 MTLPrimitiveType mtlType = mtl::GetPrimitiveType(mode); \ 717 if (instances == 0) \ 718 { \ 719 /* This method is called from normal drawArrays() */ \ 720 mRenderEncoder.draw(mtlType, first, count); \ 721 } \ 722 else \ 723 { \ 724 if (baseInstance == 0) \ 725 { \ 726 mRenderEncoder.drawInstanced(mtlType, first, count, instances); \ 727 } \ 728 else \ 729 { \ 730 mRenderEncoder.drawInstancedBaseInstance(mtlType, first, count, instances, \ 731 baseInstance); \ 732 } \ 733 } \ 734 } \ 735 } \ 736 else \ 737 { \ 738 bool isNoOp = false; \ 739 ANGLE_TRY(setupDraw(context, mode, first, count, instances, \ 740 gl::DrawElementsType::InvalidEnum, nullptr, xfbPass, &isNoOp)); \ 741 \ 742 if (!isNoOp) \ 743 { \ 744 MTLPrimitiveType mtlType = mtl::GetPrimitiveType(outIndexMode); \ 745 MTLIndexType mtlIdxType = mtl::GetIndexType(convertedType); \ 746 if (instances == 0) \ 747 { \ 748 mRenderEncoder.drawIndexed(mtlType, outIndexCounti32, mtlIdxType, drawIdxBuffer, \ 749 outIndexOffset); \ 750 } \ 751 else \ 752 { \ 753 if (baseInstance == 0) \ 754 { \ 755 mRenderEncoder.drawIndexedInstanced(mtlType, outIndexCounti32, mtlIdxType, \ 756 drawIdxBuffer, outIndexOffset, instances); \ 757 } \ 758 else \ 759 { \ 760 mRenderEncoder.drawIndexedInstancedBaseVertexBaseInstance( \ 761 mtlType, outIndexCounti32, mtlIdxType, drawIdxBuffer, outIndexOffset, \ 762 instances, 0, baseInstance); \ 763 } \ 764 } \ 765 } \ 766 } 767 768 ANGLE_MTL_XFB_DRAW(DRAW_PROVOKING_VERTEX_ARRAY) 769 return angle::Result::Continue; 770} 771 772angle::Result ContextMtl::drawElementsImpl(const gl::Context *context, 773 gl::PrimitiveMode mode, 774 GLsizei count, 775 gl::DrawElementsType type, 776 const void *indices, 777 GLsizei instances, 778 GLint baseVertex, 779 GLuint baseInstance) 780{ 781 // Real instances count. Zero means this is not instanced draw. 782 GLsizei instanceCount = instances ? instances : 1; 783 784 if (mCullAllPolygons && gl::IsPolygonMode(mode)) 785 { 786 return angle::Result::Continue; 787 } 788 789 if (mode == gl::PrimitiveMode::TriangleFan) 790 { 791 return drawTriFanElements(context, count, type, indices, instanceCount, baseVertex, 792 baseInstance); 793 } 794 else if (mode == gl::PrimitiveMode::LineLoop) 795 { 796 return drawLineLoopElements(context, count, type, indices, instanceCount, baseVertex, 797 baseInstance); 798 } 799 800 mtl::BufferRef idxBuffer; 801 mtl::BufferRef drawIdxBuffer; 802 size_t convertedOffset = 0; 803 gl::DrawElementsType convertedType = type; 804 805 ANGLE_TRY(mVertexArray->getIndexBuffer(context, type, count, indices, &idxBuffer, 806 &convertedOffset, &convertedType)); 807 808 ASSERT(idxBuffer); 809 ASSERT((convertedType == gl::DrawElementsType::UnsignedShort && (convertedOffset % 2) == 0) || 810 (convertedType == gl::DrawElementsType::UnsignedInt && (convertedOffset % 4) == 0)); 811 812 uint32_t convertedCounti32 = (uint32_t)count; 813 814 size_t provokingVertexAdditionalOffset = 0; 815 816 if (requiresIndexRewrite(context->getState(), mode)) 817 { 818 size_t outIndexCount = 0; 819 gl::PrimitiveMode newMode = gl::PrimitiveMode::InvalidEnum; 820 ANGLE_TRY(mProvokingVertexHelper.preconditionIndexBuffer( 821 mtl::GetImpl(context), idxBuffer, count, convertedOffset, 822 mState.isPrimitiveRestartEnabled(), mode, convertedType, outIndexCount, 823 provokingVertexAdditionalOffset, newMode, drawIdxBuffer)); 824 // Line strips and triangle strips are rewritten to flat line arrays and tri arrays. 825 convertedCounti32 = (uint32_t)outIndexCount; 826 mode = newMode; 827 } 828 else 829 { 830 drawIdxBuffer = idxBuffer; 831 } 832 // Draw commands will only be broken up if transform feedback is enabled, 833 // if the mode is a simple type, and if the buffer contained any restart 834 // indices. 835 // It's safe to use idxBuffer in this case, as it will contain the same count and restart ranges 836 // as drawIdxBuffer. 837 const std::vector<DrawCommandRange> drawCommands = mVertexArray->getDrawIndices( 838 context, type, convertedType, mode, idxBuffer, convertedCounti32, convertedOffset); 839 bool isNoOp = false; 840 ANGLE_TRY(setupDraw(context, mode, 0, count, instances, type, indices, false, &isNoOp)); 841 if (!isNoOp) 842 { 843 MTLPrimitiveType mtlType = mtl::GetPrimitiveType(mode); 844 845 MTLIndexType mtlIdxType = mtl::GetIndexType(convertedType); 846 847 if (instances == 0 && baseVertex == 0 && baseInstance == 0) 848 { 849 // Normal draw 850 for (auto &command : drawCommands) 851 { 852 mRenderEncoder.drawIndexed(mtlType, command.count, mtlIdxType, drawIdxBuffer, 853 command.offset + provokingVertexAdditionalOffset); 854 } 855 } 856 else 857 { 858 // Instanced draw 859 if (baseVertex == 0 && baseInstance == 0) 860 { 861 for (auto &command : drawCommands) 862 { 863 mRenderEncoder.drawIndexedInstanced( 864 mtlType, command.count, mtlIdxType, drawIdxBuffer, 865 command.offset + provokingVertexAdditionalOffset, instanceCount); 866 } 867 } 868 else 869 { 870 for (auto &command : drawCommands) 871 { 872 mRenderEncoder.drawIndexedInstancedBaseVertexBaseInstance( 873 mtlType, command.count, mtlIdxType, drawIdxBuffer, 874 command.offset + provokingVertexAdditionalOffset, instanceCount, baseVertex, 875 baseInstance); 876 } 877 } 878 } 879 } 880 return angle::Result::Continue; 881} 882 883angle::Result ContextMtl::drawElements(const gl::Context *context, 884 gl::PrimitiveMode mode, 885 GLsizei count, 886 gl::DrawElementsType type, 887 const void *indices) 888{ 889 ANGLE_TRY(resyncDrawFramebufferIfNeeded(context)); 890 return drawElementsImpl(context, mode, count, type, indices, 0, 0, 0); 891} 892 893angle::Result ContextMtl::drawElementsBaseVertex(const gl::Context *context, 894 gl::PrimitiveMode mode, 895 GLsizei count, 896 gl::DrawElementsType type, 897 const void *indices, 898 GLint baseVertex) 899{ 900 UNIMPLEMENTED(); 901 return angle::Result::Stop; 902} 903 904angle::Result ContextMtl::drawElementsInstanced(const gl::Context *context, 905 gl::PrimitiveMode mode, 906 GLsizei count, 907 gl::DrawElementsType type, 908 const void *indices, 909 GLsizei instanceCount) 910{ 911 ANGLE_TRY(resyncDrawFramebufferIfNeeded(context)); 912 // Instanced draw calls with zero instances are skipped in the frontend. 913 // The drawElementsImpl function would treat them as non-instanced. 914 ASSERT(instanceCount > 0); 915 return drawElementsImpl(context, mode, count, type, indices, instanceCount, 0, 0); 916} 917 918angle::Result ContextMtl::drawElementsInstancedBaseVertex(const gl::Context *context, 919 gl::PrimitiveMode mode, 920 GLsizei count, 921 gl::DrawElementsType type, 922 const void *indices, 923 GLsizei instanceCount, 924 GLint baseVertex) 925{ 926 ANGLE_TRY(resyncDrawFramebufferIfNeeded(context)); 927 // Instanced draw calls with zero instances are skipped in the frontend. 928 // The drawElementsImpl function would treat them as non-instanced. 929 ASSERT(instanceCount > 0); 930 return drawElementsImpl(context, mode, count, type, indices, instanceCount, baseVertex, 0); 931} 932 933angle::Result ContextMtl::drawElementsInstancedBaseVertexBaseInstance(const gl::Context *context, 934 gl::PrimitiveMode mode, 935 GLsizei count, 936 gl::DrawElementsType type, 937 const void *indices, 938 GLsizei instances, 939 GLint baseVertex, 940 GLuint baseInstance) 941{ 942 ANGLE_TRY(resyncDrawFramebufferIfNeeded(context)); 943 // Instanced draw calls with zero instances are skipped in the frontend. 944 // The drawElementsImpl function would treat them as non-instanced. 945 ASSERT(instances > 0); 946 return drawElementsImpl(context, mode, count, type, indices, instances, baseVertex, 947 baseInstance); 948} 949 950angle::Result ContextMtl::drawRangeElements(const gl::Context *context, 951 gl::PrimitiveMode mode, 952 GLuint start, 953 GLuint end, 954 GLsizei count, 955 gl::DrawElementsType type, 956 const void *indices) 957{ 958 ANGLE_TRY(resyncDrawFramebufferIfNeeded(context)); 959 return drawElementsImpl(context, mode, count, type, indices, 0, 0, 0); 960} 961 962angle::Result ContextMtl::drawRangeElementsBaseVertex(const gl::Context *context, 963 gl::PrimitiveMode mode, 964 GLuint start, 965 GLuint end, 966 GLsizei count, 967 gl::DrawElementsType type, 968 const void *indices, 969 GLint baseVertex) 970{ 971 // NOTE(hqle): ES 3.2 972 UNIMPLEMENTED(); 973 return angle::Result::Stop; 974} 975 976angle::Result ContextMtl::drawArraysIndirect(const gl::Context *context, 977 gl::PrimitiveMode mode, 978 const void *indirect) 979{ 980 // NOTE(hqle): ES 3.0 981 UNIMPLEMENTED(); 982 return angle::Result::Stop; 983} 984angle::Result ContextMtl::drawElementsIndirect(const gl::Context *context, 985 gl::PrimitiveMode mode, 986 gl::DrawElementsType type, 987 const void *indirect) 988{ 989 // NOTE(hqle): ES 3.0 990 UNIMPLEMENTED(); 991 return angle::Result::Stop; 992} 993 994angle::Result ContextMtl::multiDrawArrays(const gl::Context *context, 995 gl::PrimitiveMode mode, 996 const GLint *firsts, 997 const GLsizei *counts, 998 GLsizei drawcount) 999{ 1000 return rx::MultiDrawArraysGeneral(this, context, mode, firsts, counts, drawcount); 1001} 1002 1003angle::Result ContextMtl::multiDrawArraysInstanced(const gl::Context *context, 1004 gl::PrimitiveMode mode, 1005 const GLint *firsts, 1006 const GLsizei *counts, 1007 const GLsizei *instanceCounts, 1008 GLsizei drawcount) 1009{ 1010 return rx::MultiDrawArraysInstancedGeneral(this, context, mode, firsts, counts, instanceCounts, 1011 drawcount); 1012} 1013 1014angle::Result ContextMtl::multiDrawArraysIndirect(const gl::Context *context, 1015 gl::PrimitiveMode mode, 1016 const void *indirect, 1017 GLsizei drawcount, 1018 GLsizei stride) 1019{ 1020 return rx::MultiDrawArraysIndirectGeneral(this, context, mode, indirect, drawcount, stride); 1021} 1022 1023angle::Result ContextMtl::multiDrawElements(const gl::Context *context, 1024 gl::PrimitiveMode mode, 1025 const GLsizei *counts, 1026 gl::DrawElementsType type, 1027 const GLvoid *const *indices, 1028 GLsizei drawcount) 1029{ 1030 return rx::MultiDrawElementsGeneral(this, context, mode, counts, type, indices, drawcount); 1031} 1032 1033angle::Result ContextMtl::multiDrawElementsInstanced(const gl::Context *context, 1034 gl::PrimitiveMode mode, 1035 const GLsizei *counts, 1036 gl::DrawElementsType type, 1037 const GLvoid *const *indices, 1038 const GLsizei *instanceCounts, 1039 GLsizei drawcount) 1040{ 1041 return rx::MultiDrawElementsInstancedGeneral(this, context, mode, counts, type, indices, 1042 instanceCounts, drawcount); 1043} 1044 1045angle::Result ContextMtl::multiDrawElementsIndirect(const gl::Context *context, 1046 gl::PrimitiveMode mode, 1047 gl::DrawElementsType type, 1048 const void *indirect, 1049 GLsizei drawcount, 1050 GLsizei stride) 1051{ 1052 return rx::MultiDrawElementsIndirectGeneral(this, context, mode, type, indirect, drawcount, 1053 stride); 1054} 1055 1056angle::Result ContextMtl::multiDrawArraysInstancedBaseInstance(const gl::Context *context, 1057 gl::PrimitiveMode mode, 1058 const GLint *firsts, 1059 const GLsizei *counts, 1060 const GLsizei *instanceCounts, 1061 const GLuint *baseInstances, 1062 GLsizei drawcount) 1063{ 1064 return rx::MultiDrawArraysInstancedBaseInstanceGeneral( 1065 this, context, mode, firsts, counts, instanceCounts, baseInstances, drawcount); 1066} 1067 1068angle::Result ContextMtl::multiDrawElementsInstancedBaseVertexBaseInstance( 1069 const gl::Context *context, 1070 gl::PrimitiveMode mode, 1071 const GLsizei *counts, 1072 gl::DrawElementsType type, 1073 const GLvoid *const *indices, 1074 const GLsizei *instanceCounts, 1075 const GLint *baseVertices, 1076 const GLuint *baseInstances, 1077 GLsizei drawcount) 1078{ 1079 return rx::MultiDrawElementsInstancedBaseVertexBaseInstanceGeneral( 1080 this, context, mode, counts, type, indices, instanceCounts, baseVertices, baseInstances, 1081 drawcount); 1082} 1083 1084// Device loss 1085gl::GraphicsResetStatus ContextMtl::getResetStatus() 1086{ 1087 return gl::GraphicsResetStatus::NoError; 1088} 1089 1090// EXT_debug_marker 1091angle::Result ContextMtl::insertEventMarker(GLsizei length, const char *marker) 1092{ 1093 return angle::Result::Continue; 1094} 1095 1096angle::Result ContextMtl::pushGroupMarker(GLsizei length, const char *marker) 1097{ 1098 mCmdBuffer.pushDebugGroup(ConvertMarkerToString(length, marker)); 1099 return angle::Result::Continue; 1100} 1101 1102angle::Result ContextMtl::popGroupMarker() 1103{ 1104 mCmdBuffer.popDebugGroup(); 1105 return angle::Result::Continue; 1106} 1107 1108// KHR_debug 1109angle::Result ContextMtl::pushDebugGroup(const gl::Context *context, 1110 GLenum source, 1111 GLuint id, 1112 const std::string &message) 1113{ 1114 return angle::Result::Continue; 1115} 1116 1117angle::Result ContextMtl::popDebugGroup(const gl::Context *context) 1118{ 1119 return angle::Result::Continue; 1120} 1121 1122void ContextMtl::updateIncompatibleAttachments(const gl::State &glState) 1123{ 1124 const gl::ProgramExecutable *programExecutable = glState.getProgramExecutable(); 1125 gl::Framebuffer *drawFramebuffer = glState.getDrawFramebuffer(); 1126 if (programExecutable == nullptr || drawFramebuffer == nullptr) 1127 { 1128 mIncompatibleAttachments.reset(); 1129 return; 1130 } 1131 1132 // Cache a mask of incompatible attachments ignoring unused outputs and disabled draw buffers. 1133 mIncompatibleAttachments = 1134 gl::GetComponentTypeMaskDiff(drawFramebuffer->getDrawBufferTypeMask(), 1135 programExecutable->getFragmentOutputsTypeMask()) & 1136 drawFramebuffer->getDrawBufferMask() & programExecutable->getActiveOutputVariablesMask(); 1137} 1138 1139// State sync with dirty bits. 1140angle::Result ContextMtl::syncState(const gl::Context *context, 1141 const gl::state::DirtyBits dirtyBits, 1142 const gl::state::DirtyBits bitMask, 1143 const gl::state::ExtendedDirtyBits extendedDirtyBits, 1144 const gl::state::ExtendedDirtyBits extendedBitMask, 1145 gl::Command command) 1146{ 1147 const gl::State &glState = context->getState(); 1148 1149 // Metal's blend state is set at once, while ANGLE tracks separate dirty 1150 // bits: ENABLED, FUNCS, and EQUATIONS. Merge all three of them to the first one. 1151 // PS: these can not be statically initialized on some architectures as there is 1152 // no constuctor for DirtyBits that takes an int (which becomes BitSetArray<64>). 1153 gl::state::DirtyBits checkBlendBitsMask; 1154 checkBlendBitsMask.set(gl::state::DIRTY_BIT_BLEND_ENABLED); 1155 checkBlendBitsMask.set(gl::state::DIRTY_BIT_BLEND_FUNCS); 1156 checkBlendBitsMask.set(gl::state::DIRTY_BIT_BLEND_EQUATIONS); 1157 gl::state::DirtyBits resetBlendBitsMask; 1158 resetBlendBitsMask.set(gl::state::DIRTY_BIT_BLEND_FUNCS); 1159 resetBlendBitsMask.set(gl::state::DIRTY_BIT_BLEND_EQUATIONS); 1160 1161 gl::state::DirtyBits mergedDirtyBits = gl::state::DirtyBits(dirtyBits) & ~resetBlendBitsMask; 1162 mergedDirtyBits.set(gl::state::DIRTY_BIT_BLEND_ENABLED, (dirtyBits & checkBlendBitsMask).any()); 1163 1164 for (auto iter = mergedDirtyBits.begin(), endIter = mergedDirtyBits.end(); iter != endIter; 1165 ++iter) 1166 { 1167 size_t dirtyBit = *iter; 1168 switch (dirtyBit) 1169 { 1170 case gl::state::DIRTY_BIT_SCISSOR_TEST_ENABLED: 1171 case gl::state::DIRTY_BIT_SCISSOR: 1172 updateScissor(glState); 1173 break; 1174 case gl::state::DIRTY_BIT_VIEWPORT: 1175 { 1176 FramebufferMtl *framebufferMtl = mtl::GetImpl(glState.getDrawFramebuffer()); 1177 updateViewport(framebufferMtl, glState.getViewport(), glState.getNearPlane(), 1178 glState.getFarPlane()); 1179 // Update the scissor, which will be constrained to the viewport 1180 updateScissor(glState); 1181 break; 1182 } 1183 case gl::state::DIRTY_BIT_DEPTH_RANGE: 1184 updateDepthRange(glState.getNearPlane(), glState.getFarPlane()); 1185 break; 1186 case gl::state::DIRTY_BIT_BLEND_COLOR: 1187 mDirtyBits.set(DIRTY_BIT_BLEND_COLOR); 1188 break; 1189 case gl::state::DIRTY_BIT_BLEND_ENABLED: 1190 updateBlendDescArray(glState.getBlendStateExt()); 1191 break; 1192 case gl::state::DIRTY_BIT_COLOR_MASK: 1193 { 1194 const gl::BlendStateExt &blendStateExt = glState.getBlendStateExt(); 1195 size_t i = 0; 1196 for (; i < blendStateExt.getDrawBufferCount(); i++) 1197 { 1198 mBlendDescArray[i].updateWriteMask(blendStateExt.getColorMaskIndexed(i)); 1199 mWriteMaskArray[i] = mBlendDescArray[i].writeMask; 1200 } 1201 for (; i < mBlendDescArray.size(); i++) 1202 { 1203 mBlendDescArray[i].updateWriteMask(0); 1204 mWriteMaskArray[i] = mBlendDescArray[i].writeMask; 1205 } 1206 invalidateRenderPipeline(); 1207 break; 1208 } 1209 case gl::state::DIRTY_BIT_SAMPLE_ALPHA_TO_COVERAGE_ENABLED: 1210 if (getDisplay()->getFeatures().emulateAlphaToCoverage.enabled) 1211 { 1212 invalidateDriverUniforms(); 1213 } 1214 else 1215 { 1216 invalidateRenderPipeline(); 1217 } 1218 break; 1219 case gl::state::DIRTY_BIT_SAMPLE_COVERAGE_ENABLED: 1220 case gl::state::DIRTY_BIT_SAMPLE_COVERAGE: 1221 invalidateDriverUniforms(); 1222 break; 1223 case gl::state::DIRTY_BIT_SAMPLE_MASK_ENABLED: 1224 // NOTE(hqle): 3.1 MSAA support 1225 break; 1226 case gl::state::DIRTY_BIT_SAMPLE_MASK: 1227 // NOTE(hqle): 3.1 MSAA support 1228 break; 1229 case gl::state::DIRTY_BIT_DEPTH_TEST_ENABLED: 1230 mDepthStencilDesc.updateDepthTestEnabled(glState.getDepthStencilState()); 1231 mDirtyBits.set(DIRTY_BIT_DEPTH_STENCIL_DESC); 1232 break; 1233 case gl::state::DIRTY_BIT_DEPTH_FUNC: 1234 mDepthStencilDesc.updateDepthCompareFunc(glState.getDepthStencilState()); 1235 mDirtyBits.set(DIRTY_BIT_DEPTH_STENCIL_DESC); 1236 break; 1237 case gl::state::DIRTY_BIT_DEPTH_MASK: 1238 mDepthStencilDesc.updateDepthWriteEnabled(glState.getDepthStencilState()); 1239 mDirtyBits.set(DIRTY_BIT_DEPTH_STENCIL_DESC); 1240 break; 1241 case gl::state::DIRTY_BIT_STENCIL_TEST_ENABLED: 1242 mDepthStencilDesc.updateStencilTestEnabled(glState.getDepthStencilState()); 1243 mDirtyBits.set(DIRTY_BIT_DEPTH_STENCIL_DESC); 1244 break; 1245 case gl::state::DIRTY_BIT_STENCIL_FUNCS_FRONT: 1246 mDepthStencilDesc.updateStencilFrontFuncs(glState.getDepthStencilState()); 1247 mStencilRefFront = glState.getStencilRef(); // clamped on the frontend 1248 mDirtyBits.set(DIRTY_BIT_DEPTH_STENCIL_DESC); 1249 mDirtyBits.set(DIRTY_BIT_STENCIL_REF); 1250 break; 1251 case gl::state::DIRTY_BIT_STENCIL_FUNCS_BACK: 1252 mDepthStencilDesc.updateStencilBackFuncs(glState.getDepthStencilState()); 1253 mStencilRefBack = glState.getStencilBackRef(); // clamped on the frontend 1254 mDirtyBits.set(DIRTY_BIT_DEPTH_STENCIL_DESC); 1255 mDirtyBits.set(DIRTY_BIT_STENCIL_REF); 1256 break; 1257 case gl::state::DIRTY_BIT_STENCIL_OPS_FRONT: 1258 mDepthStencilDesc.updateStencilFrontOps(glState.getDepthStencilState()); 1259 mDirtyBits.set(DIRTY_BIT_DEPTH_STENCIL_DESC); 1260 break; 1261 case gl::state::DIRTY_BIT_STENCIL_OPS_BACK: 1262 mDepthStencilDesc.updateStencilBackOps(glState.getDepthStencilState()); 1263 mDirtyBits.set(DIRTY_BIT_DEPTH_STENCIL_DESC); 1264 break; 1265 case gl::state::DIRTY_BIT_STENCIL_WRITEMASK_FRONT: 1266 mDepthStencilDesc.updateStencilFrontWriteMask(glState.getDepthStencilState()); 1267 mDirtyBits.set(DIRTY_BIT_DEPTH_STENCIL_DESC); 1268 break; 1269 case gl::state::DIRTY_BIT_STENCIL_WRITEMASK_BACK: 1270 mDepthStencilDesc.updateStencilBackWriteMask(glState.getDepthStencilState()); 1271 mDirtyBits.set(DIRTY_BIT_DEPTH_STENCIL_DESC); 1272 break; 1273 case gl::state::DIRTY_BIT_CULL_FACE_ENABLED: 1274 case gl::state::DIRTY_BIT_CULL_FACE: 1275 updateCullMode(glState); 1276 break; 1277 case gl::state::DIRTY_BIT_FRONT_FACE: 1278 updateFrontFace(glState); 1279 break; 1280 case gl::state::DIRTY_BIT_POLYGON_OFFSET_FILL_ENABLED: 1281 case gl::state::DIRTY_BIT_POLYGON_OFFSET: 1282 mDirtyBits.set(DIRTY_BIT_DEPTH_BIAS); 1283 break; 1284 case gl::state::DIRTY_BIT_RASTERIZER_DISCARD_ENABLED: 1285 mDirtyBits.set(DIRTY_BIT_RASTERIZER_DISCARD); 1286 break; 1287 case gl::state::DIRTY_BIT_LINE_WIDTH: 1288 // Do nothing 1289 break; 1290 case gl::state::DIRTY_BIT_PRIMITIVE_RESTART_ENABLED: 1291 // NOTE(hqle): ES 3.0 feature. 1292 break; 1293 case gl::state::DIRTY_BIT_CLEAR_COLOR: 1294 mClearColor = mtl::ClearColorValue( 1295 glState.getColorClearValue().red, glState.getColorClearValue().green, 1296 glState.getColorClearValue().blue, glState.getColorClearValue().alpha); 1297 break; 1298 case gl::state::DIRTY_BIT_CLEAR_DEPTH: 1299 break; 1300 case gl::state::DIRTY_BIT_CLEAR_STENCIL: 1301 mClearStencil = glState.getStencilClearValue() & mtl::kStencilMaskAll; 1302 break; 1303 case gl::state::DIRTY_BIT_UNPACK_STATE: 1304 // This is a no-op, its only important to use the right unpack state when we do 1305 // setImage or setSubImage in TextureMtl, which is plumbed through the frontend call 1306 break; 1307 case gl::state::DIRTY_BIT_UNPACK_BUFFER_BINDING: 1308 break; 1309 case gl::state::DIRTY_BIT_PACK_STATE: 1310 // This is a no-op, its only important to use the right pack state when we do 1311 // call readPixels later on. 1312 break; 1313 case gl::state::DIRTY_BIT_PACK_BUFFER_BINDING: 1314 break; 1315 case gl::state::DIRTY_BIT_DITHER_ENABLED: 1316 break; 1317 case gl::state::DIRTY_BIT_READ_FRAMEBUFFER_BINDING: 1318 break; 1319 case gl::state::DIRTY_BIT_DRAW_FRAMEBUFFER_BINDING: 1320 updateIncompatibleAttachments(glState); 1321 updateDrawFrameBufferBinding(context); 1322 break; 1323 case gl::state::DIRTY_BIT_RENDERBUFFER_BINDING: 1324 break; 1325 case gl::state::DIRTY_BIT_VERTEX_ARRAY_BINDING: 1326 updateVertexArray(context); 1327 break; 1328 case gl::state::DIRTY_BIT_DRAW_INDIRECT_BUFFER_BINDING: 1329 break; 1330 case gl::state::DIRTY_BIT_DISPATCH_INDIRECT_BUFFER_BINDING: 1331 break; 1332 case gl::state::DIRTY_BIT_PROGRAM_BINDING: 1333 static_assert( 1334 gl::state::DIRTY_BIT_PROGRAM_EXECUTABLE > gl::state::DIRTY_BIT_PROGRAM_BINDING, 1335 "Dirty bit order"); 1336 iter.setLaterBit(gl::state::DIRTY_BIT_PROGRAM_EXECUTABLE); 1337 break; 1338 case gl::state::DIRTY_BIT_PROGRAM_EXECUTABLE: 1339 { 1340 updateIncompatibleAttachments(glState); 1341 const gl::ProgramExecutable *executable = mState.getProgramExecutable(); 1342 ASSERT(executable); 1343 mExecutable = mtl::GetImpl(executable); 1344 updateProgramExecutable(context); 1345 break; 1346 } 1347 case gl::state::DIRTY_BIT_TEXTURE_BINDINGS: 1348 invalidateCurrentTextures(); 1349 break; 1350 case gl::state::DIRTY_BIT_SAMPLER_BINDINGS: 1351 invalidateCurrentTextures(); 1352 break; 1353 case gl::state::DIRTY_BIT_TRANSFORM_FEEDBACK_BINDING: 1354 // Nothing to do. 1355 break; 1356 case gl::state::DIRTY_BIT_SHADER_STORAGE_BUFFER_BINDING: 1357 // NOTE(hqle): ES 3.0 feature. 1358 break; 1359 case gl::state::DIRTY_BIT_UNIFORM_BUFFER_BINDINGS: 1360 mDirtyBits.set(DIRTY_BIT_UNIFORM_BUFFERS_BINDING); 1361 break; 1362 case gl::state::DIRTY_BIT_ATOMIC_COUNTER_BUFFER_BINDING: 1363 break; 1364 case gl::state::DIRTY_BIT_IMAGE_BINDINGS: 1365 // NOTE(hqle): properly handle GLSL images. 1366 invalidateCurrentTextures(); 1367 break; 1368 case gl::state::DIRTY_BIT_MULTISAMPLING: 1369 // NOTE(hqle): MSAA on/off. 1370 break; 1371 case gl::state::DIRTY_BIT_SAMPLE_ALPHA_TO_ONE: 1372 // NOTE(hqle): this is part of EXT_multisample_compatibility. 1373 // NOTE(hqle): MSAA feature. 1374 break; 1375 case gl::state::DIRTY_BIT_COVERAGE_MODULATION: 1376 break; 1377 case gl::state::DIRTY_BIT_FRAMEBUFFER_SRGB_WRITE_CONTROL_MODE: 1378 break; 1379 case gl::state::DIRTY_BIT_CURRENT_VALUES: 1380 { 1381 invalidateDefaultAttributes(glState.getAndResetDirtyCurrentValues()); 1382 break; 1383 } 1384 case gl::state::DIRTY_BIT_PROVOKING_VERTEX: 1385 break; 1386 case gl::state::DIRTY_BIT_EXTENDED: 1387 updateExtendedState(glState, extendedDirtyBits); 1388 break; 1389 case gl::state::DIRTY_BIT_SAMPLE_SHADING: 1390 // Nothing to do until OES_sample_shading is implemented. 1391 break; 1392 case gl::state::DIRTY_BIT_PATCH_VERTICES: 1393 // Nothing to do until EXT_tessellation_shader is implemented. 1394 break; 1395 default: 1396 UNREACHABLE(); 1397 break; 1398 } 1399 } 1400 1401 return angle::Result::Continue; 1402} 1403 1404void ContextMtl::updateExtendedState(const gl::State &glState, 1405 const gl::state::ExtendedDirtyBits extendedDirtyBits) 1406{ 1407 for (size_t extendedDirtyBit : extendedDirtyBits) 1408 { 1409 switch (extendedDirtyBit) 1410 { 1411 case gl::state::EXTENDED_DIRTY_BIT_CLIP_CONTROL: 1412 updateFrontFace(glState); 1413 invalidateDriverUniforms(); 1414 break; 1415 case gl::state::EXTENDED_DIRTY_BIT_CLIP_DISTANCES: 1416 invalidateDriverUniforms(); 1417 break; 1418 case gl::state::EXTENDED_DIRTY_BIT_DEPTH_CLAMP_ENABLED: 1419 mDirtyBits.set(DIRTY_BIT_DEPTH_CLIP_MODE); 1420 break; 1421 case gl::state::EXTENDED_DIRTY_BIT_POLYGON_MODE: 1422 mDirtyBits.set(DIRTY_BIT_FILL_MODE); 1423 mDirtyBits.set(DIRTY_BIT_DEPTH_BIAS); 1424 break; 1425 case gl::state::EXTENDED_DIRTY_BIT_POLYGON_OFFSET_LINE_ENABLED: 1426 mDirtyBits.set(DIRTY_BIT_DEPTH_BIAS); 1427 break; 1428 default: 1429 break; 1430 } 1431 } 1432} 1433 1434// Disjoint timer queries 1435GLint ContextMtl::getGPUDisjoint() 1436{ 1437 // Implementation currently is not affected by this. 1438 return 0; 1439} 1440 1441GLint64 ContextMtl::getTimestamp() 1442{ 1443 // Timestamps are currently unsupported. An implementation 1444 // strategy is written up in anglebug.com/7828 if they're needed 1445 // in the future. 1446 return 0; 1447} 1448 1449// Context switching 1450angle::Result ContextMtl::onMakeCurrent(const gl::Context *context) 1451{ 1452 invalidateState(context); 1453 gl::Query *query = mState.getActiveQuery(gl::QueryType::TimeElapsed); 1454 if (query) 1455 { 1456 GetImplAs<QueryMtl>(query)->onContextMakeCurrent(context); 1457 } 1458 mBufferManager.incrementNumContextSwitches(); 1459 return angle::Result::Continue; 1460} 1461angle::Result ContextMtl::onUnMakeCurrent(const gl::Context *context) 1462{ 1463 flushCommandBuffer(mtl::WaitUntilScheduled); 1464 // Note: this 2nd flush is needed because if there is a query in progress 1465 // then during flush, new command buffers are allocated that also need 1466 // to be flushed. This is a temporary fix and we should probably refactor 1467 // this later. See TODO(anglebug.com/7138) 1468 flushCommandBuffer(mtl::WaitUntilScheduled); 1469 gl::Query *query = mState.getActiveQuery(gl::QueryType::TimeElapsed); 1470 if (query) 1471 { 1472 GetImplAs<QueryMtl>(query)->onContextUnMakeCurrent(context); 1473 } 1474 return angle::Result::Continue; 1475} 1476 1477// Native capabilities, unmodified by gl::Context. 1478gl::Caps ContextMtl::getNativeCaps() const 1479{ 1480 return getDisplay()->getNativeCaps(); 1481} 1482const gl::TextureCapsMap &ContextMtl::getNativeTextureCaps() const 1483{ 1484 return getDisplay()->getNativeTextureCaps(); 1485} 1486const gl::Extensions &ContextMtl::getNativeExtensions() const 1487{ 1488 return getDisplay()->getNativeExtensions(); 1489} 1490const gl::Limitations &ContextMtl::getNativeLimitations() const 1491{ 1492 return getDisplay()->getNativeLimitations(); 1493} 1494const ShPixelLocalStorageOptions &ContextMtl::getNativePixelLocalStorageOptions() const 1495{ 1496 return getDisplay()->getNativePixelLocalStorageOptions(); 1497} 1498 1499// Shader creation 1500CompilerImpl *ContextMtl::createCompiler() 1501{ 1502 return new CompilerMtl(); 1503} 1504ShaderImpl *ContextMtl::createShader(const gl::ShaderState &state) 1505{ 1506 return new ShaderMtl(state); 1507} 1508ProgramImpl *ContextMtl::createProgram(const gl::ProgramState &state) 1509{ 1510 return new ProgramMtl(state); 1511} 1512 1513ProgramExecutableImpl *ContextMtl::createProgramExecutable(const gl::ProgramExecutable *executable) 1514{ 1515 return new ProgramExecutableMtl(executable); 1516} 1517 1518// Framebuffer creation 1519FramebufferImpl *ContextMtl::createFramebuffer(const gl::FramebufferState &state) 1520{ 1521 return new FramebufferMtl(state, this, /* flipY */ false); 1522} 1523 1524// Texture creation 1525TextureImpl *ContextMtl::createTexture(const gl::TextureState &state) 1526{ 1527 return new TextureMtl(state); 1528} 1529 1530// Renderbuffer creation 1531RenderbufferImpl *ContextMtl::createRenderbuffer(const gl::RenderbufferState &state) 1532{ 1533 return new RenderbufferMtl(state); 1534} 1535 1536// Buffer creation 1537BufferImpl *ContextMtl::createBuffer(const gl::BufferState &state) 1538{ 1539 return new BufferMtl(state); 1540} 1541 1542// Vertex Array creation 1543VertexArrayImpl *ContextMtl::createVertexArray(const gl::VertexArrayState &state) 1544{ 1545 return new VertexArrayMtl(state, this); 1546} 1547 1548// Query and Fence creation 1549QueryImpl *ContextMtl::createQuery(gl::QueryType type) 1550{ 1551 return new QueryMtl(type); 1552} 1553FenceNVImpl *ContextMtl::createFenceNV() 1554{ 1555 return new FenceNVMtl(); 1556} 1557SyncImpl *ContextMtl::createSync() 1558{ 1559 return new SyncMtl(); 1560} 1561 1562// Transform Feedback creation 1563TransformFeedbackImpl *ContextMtl::createTransformFeedback(const gl::TransformFeedbackState &state) 1564{ 1565 // NOTE(hqle): ES 3.0 1566 return new TransformFeedbackMtl(state); 1567} 1568 1569// Sampler object creation 1570SamplerImpl *ContextMtl::createSampler(const gl::SamplerState &state) 1571{ 1572 return new SamplerMtl(state); 1573} 1574 1575// Program Pipeline object creation 1576ProgramPipelineImpl *ContextMtl::createProgramPipeline(const gl::ProgramPipelineState &data) 1577{ 1578 // NOTE(hqle): ES 3.0 1579 UNIMPLEMENTED(); 1580 return nullptr; 1581} 1582 1583// Memory object creation. 1584MemoryObjectImpl *ContextMtl::createMemoryObject() 1585{ 1586 UNIMPLEMENTED(); 1587 return nullptr; 1588} 1589 1590// Semaphore creation. 1591SemaphoreImpl *ContextMtl::createSemaphore() 1592{ 1593 UNIMPLEMENTED(); 1594 return nullptr; 1595} 1596 1597OverlayImpl *ContextMtl::createOverlay(const gl::OverlayState &state) 1598{ 1599 // Not implemented. 1600 return new OverlayImpl(state); 1601} 1602 1603angle::Result ContextMtl::dispatchCompute(const gl::Context *context, 1604 GLuint numGroupsX, 1605 GLuint numGroupsY, 1606 GLuint numGroupsZ) 1607{ 1608 // NOTE(hqle): ES 3.0 1609 UNIMPLEMENTED(); 1610 return angle::Result::Stop; 1611} 1612angle::Result ContextMtl::dispatchComputeIndirect(const gl::Context *context, GLintptr indirect) 1613{ 1614 // NOTE(hqle): ES 3.0 1615 UNIMPLEMENTED(); 1616 return angle::Result::Stop; 1617} 1618 1619angle::Result ContextMtl::memoryBarrier(const gl::Context *context, GLbitfield barriers) 1620{ 1621 if (barriers == 0) 1622 { 1623 return angle::Result::Continue; 1624 } 1625 if (context->getClientVersion() >= gl::Version{3, 1}) 1626 { 1627 // We expect ES 3.0, and as such we don't consider ES 3.1+ objects in this function yet. 1628 UNIMPLEMENTED(); 1629 return angle::Result::Stop; 1630 } 1631 mtl::BarrierScope scope; 1632 switch (barriers) 1633 { 1634 case GL_ALL_BARRIER_BITS: 1635 scope = MTLBarrierScopeTextures | MTLBarrierScopeBuffers; 1636 if (getDisplay()->hasFragmentMemoryBarriers()) 1637 { 1638 scope |= mtl::kBarrierScopeRenderTargets; 1639 } 1640 break; 1641 case GL_SHADER_IMAGE_ACCESS_BARRIER_BIT: 1642 scope = MTLBarrierScopeTextures; 1643 if (getDisplay()->hasFragmentMemoryBarriers()) 1644 { 1645 // SHADER_IMAGE_ACCESS_BARRIER_BIT (and SHADER_STORAGE_BARRIER_BIT) require that all 1646 // prior types of accesses are finished before writes to the resource. Since this is 1647 // the case, we also have to include render targets in our barrier to ensure any 1648 // rendering completes before an imageLoad(). 1649 // 1650 // NOTE: Apple Silicon doesn't support MTLBarrierScopeRenderTargets. This seems to 1651 // work anyway though, and on that hardware we use programmable blending for pixel 1652 // local storage instead of read_write textures anyway. 1653 scope |= mtl::kBarrierScopeRenderTargets; 1654 } 1655 break; 1656 default: 1657 UNIMPLEMENTED(); 1658 return angle::Result::Stop; 1659 } 1660 // The GL API doesn't provide a distinction between different shader stages. 1661 // ES 3.0 doesn't have compute. 1662 mtl::RenderStages stages = MTLRenderStageVertex; 1663 if (getDisplay()->hasFragmentMemoryBarriers()) 1664 { 1665 stages |= MTLRenderStageFragment; 1666 } 1667 mRenderEncoder.memoryBarrier(scope, stages, stages); 1668 return angle::Result::Continue; 1669} 1670 1671angle::Result ContextMtl::memoryBarrierByRegion(const gl::Context *context, GLbitfield barriers) 1672{ 1673 // NOTE(hqle): ES 3.0 1674 UNIMPLEMENTED(); 1675 return angle::Result::Stop; 1676} 1677 1678// override mtl::ErrorHandler 1679void ContextMtl::handleError(GLenum glErrorCode, 1680 const char *message, 1681 const char *file, 1682 const char *function, 1683 unsigned int line) 1684{ 1685 mErrors->handleError(glErrorCode, message, file, function, line); 1686} 1687 1688void ContextMtl::handleError(NSError *nserror, 1689 const char *message, 1690 const char *file, 1691 const char *function, 1692 unsigned int line) 1693{ 1694 if (!nserror) 1695 { 1696 return; 1697 } 1698 1699 mErrors->handleError(GL_INVALID_OPERATION, message, file, function, line); 1700} 1701 1702void ContextMtl::invalidateState(const gl::Context *context) 1703{ 1704 mDirtyBits.set(); 1705 1706 invalidateDefaultAttributes(context->getStateCache().getActiveDefaultAttribsMask()); 1707} 1708 1709void ContextMtl::invalidateDefaultAttribute(size_t attribIndex) 1710{ 1711 mDirtyDefaultAttribsMask.set(attribIndex); 1712 mDirtyBits.set(DIRTY_BIT_DEFAULT_ATTRIBS); 1713} 1714 1715void ContextMtl::invalidateDefaultAttributes(const gl::AttributesMask &dirtyMask) 1716{ 1717 if (dirtyMask.any()) 1718 { 1719 mDirtyDefaultAttribsMask |= dirtyMask; 1720 mDirtyBits.set(DIRTY_BIT_DEFAULT_ATTRIBS); 1721 } 1722 1723 // TODO(anglebug.com/5505): determine how to merge this. 1724#if 0 1725 if (getDisplay()->getFeatures().hasExplicitMemBarrier.enabled) 1726 { 1727 const gl::ProgramExecutable *executable = mState.getProgramExecutable(); 1728 ASSERT(executable); 1729 ASSERT(executable->hasTransformFeedbackOutput() || mState.isTransformFeedbackActive()); 1730 TransformFeedbackMtl *transformFeedbackMtl = mtl::GetImpl(mState.getCurrentTransformFeedback()); 1731 size_t bufferCount = executable->getTransformFeedbackBufferCount(); 1732 const gl::TransformFeedbackBuffersArray<BufferMtl *> &bufferHandles = 1733 transformFeedbackMtl->getBufferHandles(); 1734 for (size_t i = 0; i < bufferCount; i++) 1735 { 1736 const mtl::BufferRef & constBufferRef = bufferHandles[i]->getCurrentBuffer(); 1737 mRenderEncoder.memoryBarrierWithResource(constBufferRef, mtl::kRenderStageVertex, mtl::kRenderStageVertex); 1738 } 1739 } 1740 else 1741 { 1742 //End the command encoder, so any Transform Feedback changes are available to subsequent draw calls. 1743 endEncoding(false); 1744 } 1745#endif 1746} 1747 1748void ContextMtl::invalidateCurrentTextures() 1749{ 1750 mDirtyBits.set(DIRTY_BIT_TEXTURES); 1751} 1752 1753void ContextMtl::invalidateDriverUniforms() 1754{ 1755 mDirtyBits.set(DIRTY_BIT_DRIVER_UNIFORMS); 1756} 1757 1758void ContextMtl::invalidateRenderPipeline() 1759{ 1760 mDirtyBits.set(DIRTY_BIT_RENDER_PIPELINE); 1761} 1762 1763const mtl::ClearColorValue &ContextMtl::getClearColorValue() const 1764{ 1765 return mClearColor; 1766} 1767const mtl::WriteMaskArray &ContextMtl::getWriteMaskArray() const 1768{ 1769 return mWriteMaskArray; 1770} 1771float ContextMtl::getClearDepthValue() const 1772{ 1773 return getState().getDepthClearValue(); 1774} 1775uint32_t ContextMtl::getClearStencilValue() const 1776{ 1777 return mClearStencil; 1778} 1779uint32_t ContextMtl::getStencilMask() const 1780{ 1781 return getState().getDepthStencilState().stencilWritemask & mtl::kStencilMaskAll; 1782} 1783 1784bool ContextMtl::getDepthMask() const 1785{ 1786 return getState().getDepthStencilState().depthMask; 1787} 1788 1789const mtl::Format &ContextMtl::getPixelFormat(angle::FormatID angleFormatId) const 1790{ 1791 return getDisplay()->getPixelFormat(angleFormatId); 1792} 1793 1794// See mtl::FormatTable::getVertexFormat() 1795const mtl::VertexFormat &ContextMtl::getVertexFormat(angle::FormatID angleFormatId, 1796 bool tightlyPacked) const 1797{ 1798 return getDisplay()->getVertexFormat(angleFormatId, tightlyPacked); 1799} 1800 1801const mtl::FormatCaps &ContextMtl::getNativeFormatCaps(MTLPixelFormat mtlFormat) const 1802{ 1803 return getDisplay()->getNativeFormatCaps(mtlFormat); 1804} 1805 1806angle::Result ContextMtl::getIncompleteTexture(const gl::Context *context, 1807 gl::TextureType type, 1808 gl::SamplerFormat format, 1809 gl::Texture **textureOut) 1810{ 1811 return mIncompleteTextures.getIncompleteTexture(context, type, format, nullptr, textureOut); 1812} 1813 1814void ContextMtl::endRenderEncoding(mtl::RenderCommandEncoder *encoder) 1815{ 1816 // End any pending visibility query in the render pass 1817 if (mOcclusionQuery) 1818 { 1819 disableActiveOcclusionQueryInRenderPass(); 1820 } 1821 1822 if (mBlitEncoder.valid()) 1823 { 1824 mBlitEncoder.endEncoding(); 1825 } 1826 1827 encoder->endEncoding(); 1828 1829 // Resolve visibility results 1830 mOcclusionQueryPool.resolveVisibilityResults(this); 1831} 1832 1833void ContextMtl::endBlitAndComputeEncoding() 1834{ 1835 if (mBlitEncoder.valid()) 1836 { 1837 mBlitEncoder.endEncoding(); 1838 } 1839 1840 if (mComputeEncoder.valid()) 1841 { 1842 mComputeEncoder.endEncoding(); 1843 mProvokingVertexHelper.releaseInFlightBuffers(this); 1844 } 1845} 1846 1847void ContextMtl::endEncoding(bool forceSaveRenderPassContent) 1848{ 1849 endBlitAndComputeEncoding(); 1850 1851 if (mRenderEncoder.valid()) 1852 { 1853 if (forceSaveRenderPassContent) 1854 { 1855 // Save the work in progress. 1856 mRenderEncoder.setStoreAction(MTLStoreActionStore); 1857 } 1858 1859 endRenderEncoding(&mRenderEncoder); 1860 } 1861 // End blit encoder after render encoder, as endRenderEncoding() might create a 1862 // blit encoder to resolve the visibility results. 1863 if (mBlitEncoder.valid()) 1864 { 1865 mBlitEncoder.endEncoding(); 1866 } 1867} 1868 1869void ContextMtl::flushCommandBuffer(mtl::CommandBufferFinishOperation operation) 1870{ 1871 if (mCmdBuffer.ready()) 1872 { 1873 endEncoding(true); 1874 mCmdBuffer.commit(operation); 1875 mBufferManager.incrementNumCommandBufferCommits(); 1876 mRenderPassesSinceFlush = 0; 1877 } 1878 else 1879 { 1880 mCmdBuffer.wait(operation); 1881 } 1882} 1883 1884void ContextMtl::flushCommandBufferIfNeeded() 1885{ 1886 if (mRenderPassesSinceFlush >= mtl::kMaxRenderPassesPerCommandBuffer) 1887 { 1888#if defined(ANGLE_PLATFORM_MACOS) 1889 // Ensure that we don't accumulate too many unflushed render passes. Don't wait until they 1890 // are submitted, other components handle backpressure so don't create uneccessary CPU/GPU 1891 // synchronization. 1892 flushCommandBuffer(mtl::NoWait); 1893#else 1894 // WaitUntilScheduled is used on iOS to avoid regressing untested devices. 1895 flushCommandBuffer(mtl::WaitUntilScheduled); 1896#endif 1897 } 1898 else if (mCmdBuffer.needsFlushForDrawCallLimits()) 1899 { 1900 flushCommandBuffer(mtl::NoWait); 1901 } 1902} 1903 1904void ContextMtl::present(const gl::Context *context, id<CAMetalDrawable> presentationDrawable) 1905{ 1906 ensureCommandBufferReady(); 1907 1908 FramebufferMtl *currentframebuffer = mtl::GetImpl(getState().getDrawFramebuffer()); 1909 if (currentframebuffer) 1910 { 1911 currentframebuffer->onFrameEnd(context); 1912 } 1913 1914 endEncoding(false); 1915 mCmdBuffer.present(presentationDrawable); 1916 mCmdBuffer.commit(mtl::NoWait); 1917 mRenderPassesSinceFlush = 0; 1918} 1919 1920angle::Result ContextMtl::finishCommandBuffer() 1921{ 1922 flushCommandBuffer(mtl::WaitUntilFinished); 1923 return angle::Result::Continue; 1924} 1925 1926bool ContextMtl::hasStartedRenderPass(const mtl::RenderPassDesc &desc) 1927{ 1928 return mRenderEncoder.valid() && 1929 mRenderEncoder.renderPassDesc().equalIgnoreLoadStoreOptions(desc); 1930} 1931 1932bool ContextMtl::isCurrentRenderEncoderSerial(uint64_t serial) 1933{ 1934 if (!mRenderEncoder.valid()) 1935 { 1936 return false; 1937 } 1938 1939 return serial == mRenderEncoder.getSerial(); 1940} 1941 1942// Get current render encoder 1943mtl::RenderCommandEncoder *ContextMtl::getRenderCommandEncoder() 1944{ 1945 if (!mRenderEncoder.valid()) 1946 { 1947 return nullptr; 1948 } 1949 1950 return &mRenderEncoder; 1951} 1952 1953mtl::RenderCommandEncoder *ContextMtl::getRenderPassCommandEncoder(const mtl::RenderPassDesc &desc) 1954{ 1955 if (hasStartedRenderPass(desc)) 1956 { 1957 return &mRenderEncoder; 1958 } 1959 1960 endEncoding(false); 1961 1962 ensureCommandBufferReady(); 1963 ++mRenderPassesSinceFlush; 1964 1965 // Need to re-apply everything on next draw call. 1966 mDirtyBits.set(); 1967 1968 const mtl::ContextDevice &metalDevice = getMetalDevice(); 1969 if (mtl::DeviceHasMaximumRenderTargetSize(metalDevice)) 1970 { 1971 NSUInteger maxSize = mtl::GetMaxRenderTargetSizeForDeviceInBytes(metalDevice); 1972 NSUInteger renderTargetSize = 1973 ComputeTotalSizeUsedForMTLRenderPassDescriptor(desc, this, metalDevice); 1974 if (renderTargetSize > maxSize) 1975 { 1976 std::stringstream errorStream; 1977 errorStream << "This set of render targets requires " << renderTargetSize 1978 << " bytes of pixel storage. This device supports " << maxSize << " bytes."; 1979 ANGLE_MTL_HANDLE_ERROR(this, errorStream.str().c_str(), GL_INVALID_OPERATION); 1980 return nullptr; 1981 } 1982 } 1983 return &mRenderEncoder.restart(desc, getNativeCaps().maxColorAttachments); 1984} 1985 1986// Utilities to quickly create render command encoder to a specific texture: 1987// The previous content of texture will be loaded 1988mtl::RenderCommandEncoder *ContextMtl::getTextureRenderCommandEncoder( 1989 const mtl::TextureRef &textureTarget, 1990 const mtl::ImageNativeIndex &index) 1991{ 1992 ASSERT(textureTarget && textureTarget->valid()); 1993 1994 mtl::RenderPassDesc rpDesc; 1995 1996 rpDesc.colorAttachments[0].texture = textureTarget; 1997 rpDesc.colorAttachments[0].level = index.getNativeLevel(); 1998 rpDesc.colorAttachments[0].sliceOrDepth = index.hasLayer() ? index.getLayerIndex() : 0; 1999 rpDesc.numColorAttachments = 1; 2000 rpDesc.sampleCount = textureTarget->samples(); 2001 2002 return getRenderPassCommandEncoder(rpDesc); 2003} 2004 2005// The previous content of texture will be loaded if clearColor is not provided 2006mtl::RenderCommandEncoder *ContextMtl::getRenderTargetCommandEncoderWithClear( 2007 const RenderTargetMtl &renderTarget, 2008 const Optional<MTLClearColor> &clearColor) 2009{ 2010 ASSERT(renderTarget.getTexture()); 2011 2012 mtl::RenderPassDesc rpDesc; 2013 renderTarget.toRenderPassAttachmentDesc(&rpDesc.colorAttachments[0]); 2014 rpDesc.numColorAttachments = 1; 2015 rpDesc.sampleCount = renderTarget.getRenderSamples(); 2016 2017 if (clearColor.valid()) 2018 { 2019 rpDesc.colorAttachments[0].loadAction = MTLLoadActionClear; 2020 rpDesc.colorAttachments[0].clearColor = mtl::EmulatedAlphaClearColor( 2021 clearColor.value(), renderTarget.getTexture()->getColorWritableMask()); 2022 2023 endEncoding(true); 2024 } 2025 2026 return getRenderPassCommandEncoder(rpDesc); 2027} 2028// The previous content of texture will be loaded 2029mtl::RenderCommandEncoder *ContextMtl::getRenderTargetCommandEncoder( 2030 const RenderTargetMtl &renderTarget) 2031{ 2032 return getRenderTargetCommandEncoderWithClear(renderTarget, Optional<MTLClearColor>()); 2033} 2034 2035mtl::BlitCommandEncoder *ContextMtl::getBlitCommandEncoder() 2036{ 2037 if (mRenderEncoder.valid() || mComputeEncoder.valid()) 2038 { 2039 endEncoding(true); 2040 } 2041 2042 if (mBlitEncoder.valid()) 2043 { 2044 return &mBlitEncoder; 2045 } 2046 2047 endEncoding(true); 2048 ensureCommandBufferReady(); 2049 2050 return &mBlitEncoder.restart(); 2051} 2052 2053mtl::BlitCommandEncoder *ContextMtl::getBlitCommandEncoderWithoutEndingRenderEncoder() 2054{ 2055 if (mBlitEncoder.valid()) 2056 { 2057 return &mBlitEncoder; 2058 } 2059 2060 endBlitAndComputeEncoding(); 2061 ensureCommandBufferReady(); 2062 2063 return &mBlitEncoder.restart(); 2064} 2065 2066mtl::ComputeCommandEncoder *ContextMtl::getComputeCommandEncoder() 2067{ 2068 if (mRenderEncoder.valid() || mBlitEncoder.valid()) 2069 { 2070 endEncoding(true); 2071 } 2072 2073 if (mComputeEncoder.valid()) 2074 { 2075 return &mComputeEncoder; 2076 } 2077 2078 endEncoding(true); 2079 ensureCommandBufferReady(); 2080 2081 return &mComputeEncoder.restart(); 2082} 2083 2084mtl::ComputeCommandEncoder *ContextMtl::getComputeCommandEncoderWithoutEndingRenderEncoder() 2085{ 2086 if (mComputeEncoder.valid()) 2087 { 2088 return &mComputeEncoder; 2089 } 2090 2091 endBlitAndComputeEncoding(); 2092 ensureCommandBufferReady(); 2093 2094 return &mComputeEncoder.restart(); 2095} 2096 2097mtl::ComputeCommandEncoder *ContextMtl::getIndexPreprocessingCommandEncoder() 2098{ 2099 return getComputeCommandEncoder(); 2100} 2101 2102void ContextMtl::ensureCommandBufferReady() 2103{ 2104 flushCommandBufferIfNeeded(); 2105 2106 if (!mCmdBuffer.ready()) 2107 { 2108 mCmdBuffer.restart(); 2109 } 2110 2111 ASSERT(mCmdBuffer.ready()); 2112} 2113 2114void ContextMtl::updateViewport(FramebufferMtl *framebufferMtl, 2115 const gl::Rectangle &viewport, 2116 float nearPlane, 2117 float farPlane) 2118{ 2119 mViewport = mtl::GetViewport(viewport, framebufferMtl->getState().getDimensions().height, 2120 framebufferMtl->flipY(), nearPlane, farPlane); 2121 mDirtyBits.set(DIRTY_BIT_VIEWPORT); 2122 2123 invalidateDriverUniforms(); 2124} 2125 2126void ContextMtl::updateDepthRange(float nearPlane, float farPlane) 2127{ 2128 if (NeedToInvertDepthRange(nearPlane, farPlane)) 2129 { 2130 // We also need to invert the depth in shader later by using scale value stored in driver 2131 // uniform depthRange.reserved 2132 std::swap(nearPlane, farPlane); 2133 } 2134 mViewport.znear = nearPlane; 2135 mViewport.zfar = farPlane; 2136 mDirtyBits.set(DIRTY_BIT_VIEWPORT); 2137 2138 invalidateDriverUniforms(); 2139} 2140 2141void ContextMtl::updateBlendDescArray(const gl::BlendStateExt &blendStateExt) 2142{ 2143 for (size_t i = 0; i < mBlendDescArray.size(); i++) 2144 { 2145 mtl::BlendDesc &blendDesc = mBlendDescArray[i]; 2146 if (blendStateExt.getEnabledMask().test(i)) 2147 { 2148 blendDesc.blendingEnabled = true; 2149 2150 blendDesc.sourceRGBBlendFactor = 2151 mtl::GetBlendFactor(blendStateExt.getSrcColorIndexed(i)); 2152 blendDesc.sourceAlphaBlendFactor = 2153 mtl::GetBlendFactor(blendStateExt.getSrcAlphaIndexed(i)); 2154 blendDesc.destinationRGBBlendFactor = 2155 mtl::GetBlendFactor(blendStateExt.getDstColorIndexed(i)); 2156 blendDesc.destinationAlphaBlendFactor = 2157 mtl::GetBlendFactor(blendStateExt.getDstAlphaIndexed(i)); 2158 2159 blendDesc.rgbBlendOperation = mtl::GetBlendOp(blendStateExt.getEquationColorIndexed(i)); 2160 blendDesc.alphaBlendOperation = 2161 mtl::GetBlendOp(blendStateExt.getEquationAlphaIndexed(i)); 2162 } 2163 else 2164 { 2165 // Enforce default state when blending is disabled, 2166 blendDesc.reset(blendDesc.writeMask); 2167 } 2168 } 2169 invalidateRenderPipeline(); 2170} 2171 2172void ContextMtl::updateScissor(const gl::State &glState) 2173{ 2174 FramebufferMtl *framebufferMtl = mtl::GetImpl(glState.getDrawFramebuffer()); 2175 gl::Rectangle renderArea = framebufferMtl->getCompleteRenderArea(); 2176 2177 ANGLE_MTL_LOG("renderArea = %d,%d,%d,%d", renderArea.x, renderArea.y, renderArea.width, 2178 renderArea.height); 2179 2180 // Clip the render area to the viewport. 2181 gl::Rectangle viewportClippedRenderArea; 2182 if (!gl::ClipRectangle(renderArea, glState.getViewport(), &viewportClippedRenderArea)) 2183 { 2184 viewportClippedRenderArea = gl::Rectangle(); 2185 } 2186 2187 gl::Rectangle scissoredArea = ClipRectToScissor(getState(), viewportClippedRenderArea, false); 2188 if (framebufferMtl->flipY()) 2189 { 2190 scissoredArea.y = renderArea.height - scissoredArea.y - scissoredArea.height; 2191 } 2192 2193 ANGLE_MTL_LOG("scissoredArea = %d,%d,%d,%d", scissoredArea.x, scissoredArea.y, 2194 scissoredArea.width, scissoredArea.height); 2195 2196 mScissorRect = mtl::GetScissorRect(scissoredArea); 2197 mDirtyBits.set(DIRTY_BIT_SCISSOR); 2198} 2199 2200void ContextMtl::updateCullMode(const gl::State &glState) 2201{ 2202 const gl::RasterizerState &rasterState = glState.getRasterizerState(); 2203 2204 mCullAllPolygons = false; 2205 if (!rasterState.cullFace) 2206 { 2207 mCullMode = MTLCullModeNone; 2208 } 2209 else 2210 { 2211 switch (rasterState.cullMode) 2212 { 2213 case gl::CullFaceMode::Back: 2214 mCullMode = MTLCullModeBack; 2215 break; 2216 case gl::CullFaceMode::Front: 2217 mCullMode = MTLCullModeFront; 2218 break; 2219 case gl::CullFaceMode::FrontAndBack: 2220 mCullAllPolygons = true; 2221 break; 2222 default: 2223 UNREACHABLE(); 2224 break; 2225 } 2226 } 2227 2228 mDirtyBits.set(DIRTY_BIT_CULL_MODE); 2229} 2230 2231void ContextMtl::updateFrontFace(const gl::State &glState) 2232{ 2233 FramebufferMtl *framebufferMtl = mtl::GetImpl(glState.getDrawFramebuffer()); 2234 const bool upperLeftOrigin = mState.getClipOrigin() == gl::ClipOrigin::UpperLeft; 2235 mWinding = mtl::GetFrontfaceWinding(glState.getRasterizerState().frontFace, 2236 framebufferMtl->flipY() == upperLeftOrigin); 2237 mDirtyBits.set(DIRTY_BIT_WINDING); 2238} 2239 2240// Index rewrite is required if: 2241// Provkoing vertex mode is 'last' 2242// Program has at least one 'flat' attribute 2243// PrimitiveMode is not POINTS. 2244bool ContextMtl::requiresIndexRewrite(const gl::State &state, gl::PrimitiveMode mode) 2245{ 2246 return mode != gl::PrimitiveMode::Points && mExecutable->hasFlatAttribute() && 2247 (state.getProvokingVertex() == gl::ProvokingVertexConvention::LastVertexConvention); 2248} 2249 2250void ContextMtl::updateDrawFrameBufferBinding(const gl::Context *context) 2251{ 2252 const gl::State &glState = getState(); 2253 2254 FramebufferMtl *newDrawFramebuffer = mtl::GetImpl(glState.getDrawFramebuffer()); 2255 if (newDrawFramebuffer != mDrawFramebuffer) 2256 { 2257 // Reset this flag if the framebuffer has changed to not sync it twice 2258 mForceResyncDrawFramebuffer = false; 2259 } 2260 2261 mDrawFramebuffer = newDrawFramebuffer; 2262 2263 mDrawFramebuffer->onStartedDrawingToFrameBuffer(context); 2264 2265 onDrawFrameBufferChangedState(context, mDrawFramebuffer, true); 2266} 2267 2268void ContextMtl::onDrawFrameBufferChangedState(const gl::Context *context, 2269 FramebufferMtl *framebuffer, 2270 bool renderPassChanged) 2271{ 2272 const gl::State &glState = getState(); 2273 ASSERT(framebuffer == mtl::GetImpl(glState.getDrawFramebuffer())); 2274 2275 updateViewport(framebuffer, glState.getViewport(), glState.getNearPlane(), 2276 glState.getFarPlane()); 2277 updateFrontFace(glState); 2278 updateScissor(glState); 2279 2280 if (renderPassChanged) 2281 { 2282 // End any render encoding using the old render pass. 2283 endEncoding(false); 2284 // Need to re-apply state to RenderCommandEncoder 2285 invalidateState(context); 2286 } 2287 else 2288 { 2289 // Invalidate current pipeline only. 2290 invalidateRenderPipeline(); 2291 } 2292} 2293 2294void ContextMtl::onBackbufferResized(const gl::Context *context, WindowSurfaceMtl *backbuffer) 2295{ 2296 const gl::State &glState = getState(); 2297 FramebufferMtl *framebuffer = mtl::GetImpl(glState.getDrawFramebuffer()); 2298 if (framebuffer->getAttachedBackbuffer() != backbuffer) 2299 { 2300 return; 2301 } 2302 2303 onDrawFrameBufferChangedState(context, framebuffer, true); 2304} 2305 2306angle::Result ContextMtl::onOcclusionQueryBegin(const gl::Context *context, QueryMtl *query) 2307{ 2308 ASSERT(mOcclusionQuery == nullptr); 2309 mOcclusionQuery = query; 2310 2311 if (mRenderEncoder.valid()) 2312 { 2313 // if render pass has started, start the query in the encoder 2314 return startOcclusionQueryInRenderPass(query, true); 2315 } 2316 else 2317 { 2318 query->resetVisibilityResult(this); 2319 } 2320 2321 return angle::Result::Continue; 2322} 2323void ContextMtl::onOcclusionQueryEnd(const gl::Context *context, QueryMtl *query) 2324{ 2325 ASSERT(mOcclusionQuery == query); 2326 2327 if (mRenderEncoder.valid()) 2328 { 2329 // if render pass has started, end the query in the encoder 2330 disableActiveOcclusionQueryInRenderPass(); 2331 } 2332 2333 mOcclusionQuery = nullptr; 2334} 2335void ContextMtl::onOcclusionQueryDestroy(const gl::Context *context, QueryMtl *query) 2336{ 2337 if (query->getAllocatedVisibilityOffsets().empty()) 2338 { 2339 return; 2340 } 2341 if (mOcclusionQuery == query) 2342 { 2343 onOcclusionQueryEnd(context, query); 2344 } 2345 mOcclusionQueryPool.deallocateQueryOffset(this, query); 2346} 2347 2348void ContextMtl::disableActiveOcclusionQueryInRenderPass() 2349{ 2350 if (!mOcclusionQuery || mOcclusionQuery->getAllocatedVisibilityOffsets().empty()) 2351 { 2352 return; 2353 } 2354 2355 ASSERT(mRenderEncoder.valid()); 2356 mRenderEncoder.setVisibilityResultMode(MTLVisibilityResultModeDisabled, 2357 mOcclusionQuery->getAllocatedVisibilityOffsets().back()); 2358} 2359 2360angle::Result ContextMtl::restartActiveOcclusionQueryInRenderPass() 2361{ 2362 if (!mOcclusionQuery || mOcclusionQuery->getAllocatedVisibilityOffsets().empty()) 2363 { 2364 return angle::Result::Continue; 2365 } 2366 2367 return startOcclusionQueryInRenderPass(mOcclusionQuery, false); 2368} 2369 2370angle::Result ContextMtl::startOcclusionQueryInRenderPass(QueryMtl *query, bool clearOldValue) 2371{ 2372 ASSERT(mRenderEncoder.valid()); 2373 2374 ANGLE_TRY(mOcclusionQueryPool.allocateQueryOffset(this, query, clearOldValue)); 2375 2376 mRenderEncoder.setVisibilityResultMode(MTLVisibilityResultModeBoolean, 2377 query->getAllocatedVisibilityOffsets().back()); 2378 2379 // We need to mark the query's buffer as being written in this command buffer now. Since the 2380 // actual writing is deferred until the render pass ends and user could try to read the query 2381 // result before the render pass ends. 2382 mCmdBuffer.setWriteDependency(query->getVisibilityResultBuffer(), /*isRenderCommand=*/true); 2383 2384 return angle::Result::Continue; 2385} 2386 2387void ContextMtl::onTransformFeedbackActive(const gl::Context *context, TransformFeedbackMtl *xfb) 2388{ 2389 // NOTE(hqle): We have to end current render pass to enable synchronization before XFB 2390 // buffers could be used as vertex input. Consider a better approach. 2391 endEncoding(true); 2392} 2393 2394void ContextMtl::onTransformFeedbackInactive(const gl::Context *context, TransformFeedbackMtl *xfb) 2395{ 2396 // NOTE(hqle): We have to end current render pass to enable synchronization before XFB 2397 // buffers could be used as vertex input. Consider a better approach. 2398 endEncoding(true); 2399} 2400 2401#if ANGLE_MTL_EVENT_AVAILABLE 2402uint64_t ContextMtl::queueEventSignal(id<MTLEvent> event, uint64_t value) 2403{ 2404 ensureCommandBufferReady(); 2405 // Event is queued to be signaled after current render pass. If we have helper blit or 2406 // compute encoders, avoid queueing by stopping them immediately so we get to insert the event 2407 // right away. 2408 endBlitAndComputeEncoding(); 2409 return mCmdBuffer.queueEventSignal(event, value); 2410} 2411 2412void ContextMtl::serverWaitEvent(id<MTLEvent> event, uint64_t value) 2413{ 2414 ensureCommandBufferReady(); 2415 2416 // Event waiting cannot be encoded if there is active encoder. 2417 endEncoding(true); 2418 2419 mCmdBuffer.serverWaitEvent(event, value); 2420} 2421#endif 2422 2423void ContextMtl::updateProgramExecutable(const gl::Context *context) 2424{ 2425 // Need to rebind textures 2426 invalidateCurrentTextures(); 2427 // Need to re-upload default attributes 2428 invalidateDefaultAttributes(context->getStateCache().getActiveDefaultAttribsMask()); 2429 // Render pipeline need to be re-applied 2430 invalidateRenderPipeline(); 2431} 2432 2433void ContextMtl::updateVertexArray(const gl::Context *context) 2434{ 2435 const gl::State &glState = getState(); 2436 mVertexArray = mtl::GetImpl(glState.getVertexArray()); 2437 invalidateDefaultAttributes(context->getStateCache().getActiveDefaultAttribsMask()); 2438 invalidateRenderPipeline(); 2439} 2440 2441angle::Result ContextMtl::updateDefaultAttribute(size_t attribIndex) 2442{ 2443 const gl::State &glState = mState; 2444 const gl::VertexAttribCurrentValueData &defaultValue = 2445 glState.getVertexAttribCurrentValues()[attribIndex]; 2446 2447 constexpr size_t kDefaultGLAttributeValueSize = 2448 sizeof(gl::VertexAttribCurrentValueData::Values); 2449 2450 static_assert(kDefaultGLAttributeValueSize == mtl::kDefaultAttributeSize, 2451 "Unexpected default attribute size"); 2452 memcpy(mDefaultAttributes[attribIndex].values, &defaultValue.Values, 2453 mtl::kDefaultAttributeSize); 2454 2455 return angle::Result::Continue; 2456} 2457 2458static bool isDrawNoOp(const mtl::RenderPipelineDesc &descriptor, 2459 ContextMtl *context, 2460 const mtl::ContextDevice &device) 2461{ 2462 // Ensure there is at least one valid render target. 2463 bool hasValidRenderTarget = false; 2464 2465 const NSUInteger maxColorRenderTargets = GetMaxNumberOfRenderTargetsForDevice(device); 2466 for (NSUInteger i = 0; i < maxColorRenderTargets; ++i) 2467 { 2468 const auto &colorAttachment = descriptor.outputDescriptor.colorAttachments[i]; 2469 if (colorAttachment.pixelFormat != MTLPixelFormatInvalid) 2470 { 2471 hasValidRenderTarget = true; 2472 break; 2473 } 2474 } 2475 2476 if (!hasValidRenderTarget && 2477 descriptor.outputDescriptor.depthAttachmentPixelFormat != MTLPixelFormatInvalid) 2478 { 2479 hasValidRenderTarget = true; 2480 } 2481 2482 if (!hasValidRenderTarget && 2483 descriptor.outputDescriptor.stencilAttachmentPixelFormat != MTLPixelFormatInvalid) 2484 { 2485 hasValidRenderTarget = true; 2486 } 2487 2488 if (!hasValidRenderTarget) 2489 { 2490 FramebufferMtl *framebufferMtl = mtl::GetImpl(context->getState().getDrawFramebuffer()); 2491 hasValidRenderTarget = framebufferMtl->renderPassHasDefaultWidthOrHeight(); 2492 } 2493 2494 // Draw is no op if there is no valid render target, and we're not in a 2495 // rasterization-disabled draw. 2496 2497 bool noRenderTarget = !hasValidRenderTarget; 2498 bool rasterizationDisabled = !descriptor.rasterizationEnabled(); 2499 return !rasterizationDisabled && noRenderTarget; 2500} 2501 2502angle::Result ContextMtl::setupDraw(const gl::Context *context, 2503 gl::PrimitiveMode mode, 2504 GLint firstVertex, 2505 GLsizei vertexOrIndexCount, 2506 GLsizei instances, 2507 gl::DrawElementsType indexTypeOrNone, 2508 const void *indices, 2509 bool xfbPass, 2510 bool *isNoOp) 2511{ 2512 ANGLE_TRY(setupDrawImpl(context, mode, firstVertex, vertexOrIndexCount, instances, 2513 indexTypeOrNone, indices, xfbPass, isNoOp)); 2514 if (*isNoOp) 2515 { 2516 return angle::Result::Continue; 2517 } 2518 if (!mRenderEncoder.valid()) 2519 { 2520 // Flush occurred during setup, due to running out of memory while setting up the render 2521 // pass state. This would happen for example when there is no more space in the uniform 2522 // buffers in the uniform buffer pool. The rendering would be flushed to free the uniform 2523 // buffer memory for new usage. In this case, re-run the setup. 2524 ANGLE_TRY(setupDrawImpl(context, mode, firstVertex, vertexOrIndexCount, instances, 2525 indexTypeOrNone, indices, xfbPass, isNoOp)); 2526 2527 if (*isNoOp) 2528 { 2529 return angle::Result::Continue; 2530 } 2531 // Setup with flushed state should either produce a working encoder or fail with an error 2532 // result. 2533 ASSERT(mRenderEncoder.valid()); 2534 } 2535 return angle::Result::Continue; 2536} 2537 2538angle::Result ContextMtl::setupDrawImpl(const gl::Context *context, 2539 gl::PrimitiveMode mode, 2540 GLint firstVertex, 2541 GLsizei vertexOrIndexCount, 2542 GLsizei instances, 2543 gl::DrawElementsType indexTypeOrNone, 2544 const void *indices, 2545 bool xfbPass, 2546 bool *isNoOp) 2547{ 2548 ASSERT(mExecutable); 2549 *isNoOp = false; 2550 // instances=0 means no instanced draw. 2551 GLsizei instanceCount = instances ? instances : 1; 2552 2553 if (context->getStateCache().hasAnyActiveClientAttrib()) 2554 { 2555 ANGLE_TRY(mVertexArray->updateClientAttribs(context, firstVertex, vertexOrIndexCount, 2556 instanceCount, indexTypeOrNone, indices)); 2557 } 2558 2559 // This must be called before render command encoder is started. 2560 bool textureChanged = false; 2561 if (mDirtyBits.test(DIRTY_BIT_TEXTURES)) 2562 { 2563 textureChanged = true; 2564 ANGLE_TRY(handleDirtyActiveTextures(context)); 2565 } 2566 2567 if (mDirtyBits.test(DIRTY_BIT_RASTERIZER_DISCARD)) 2568 { 2569 if (getState().isTransformFeedbackActiveUnpaused()) 2570 { 2571 // If XFB is active we need to reset render pass since we could use a dummy render 2572 // target if only XFB is needed. 2573 invalidateState(context); 2574 } 2575 else 2576 { 2577 invalidateRenderPipeline(); 2578 } 2579 } 2580 2581 if (!mRenderEncoder.valid()) 2582 { 2583 // re-apply everything 2584 invalidateState(context); 2585 } 2586 2587 if (mDirtyBits.test(DIRTY_BIT_DRAW_FRAMEBUFFER)) 2588 { 2589 ANGLE_TRY(handleDirtyRenderPass(context)); 2590 } 2591 2592 if (mOcclusionQuery && mOcclusionQueryPool.getNumRenderPassAllocatedQueries() == 0) 2593 { 2594 // The occlusion query is still active, and a new render pass has started. 2595 // We need to continue the querying process in the new render encoder. 2596 ANGLE_TRY(startOcclusionQueryInRenderPass(mOcclusionQuery, false)); 2597 } 2598 2599 bool isPipelineDescChanged; 2600 ANGLE_TRY(checkIfPipelineChanged(context, mode, xfbPass, &isPipelineDescChanged)); 2601 2602 bool uniformBuffersDirty = false; 2603 2604 if (IsTransformFeedbackOnly(getState())) 2605 { 2606 // Filter out unneeded dirty bits 2607 filterOutXFBOnlyDirtyBits(context); 2608 } 2609 2610 for (size_t bit : mDirtyBits) 2611 { 2612 switch (bit) 2613 { 2614 case DIRTY_BIT_TEXTURES: 2615 // Already handled. 2616 break; 2617 case DIRTY_BIT_DEFAULT_ATTRIBS: 2618 ANGLE_TRY(handleDirtyDefaultAttribs(context)); 2619 break; 2620 case DIRTY_BIT_DRIVER_UNIFORMS: 2621 ANGLE_TRY(handleDirtyDriverUniforms(context, firstVertex, vertexOrIndexCount)); 2622 break; 2623 case DIRTY_BIT_DEPTH_STENCIL_DESC: 2624 ANGLE_TRY(handleDirtyDepthStencilState(context)); 2625 break; 2626 case DIRTY_BIT_DEPTH_BIAS: 2627 ANGLE_TRY(handleDirtyDepthBias(context)); 2628 break; 2629 case DIRTY_BIT_DEPTH_CLIP_MODE: 2630 mRenderEncoder.setDepthClipMode( 2631 mState.isDepthClampEnabled() ? MTLDepthClipModeClamp : MTLDepthClipModeClip); 2632 break; 2633 case DIRTY_BIT_STENCIL_REF: 2634 mRenderEncoder.setStencilRefVals(mStencilRefFront, mStencilRefBack); 2635 break; 2636 case DIRTY_BIT_BLEND_COLOR: 2637 mRenderEncoder.setBlendColor( 2638 mState.getBlendColor().red, mState.getBlendColor().green, 2639 mState.getBlendColor().blue, mState.getBlendColor().alpha); 2640 break; 2641 case DIRTY_BIT_VIEWPORT: 2642 mRenderEncoder.setViewport(mViewport); 2643 break; 2644 case DIRTY_BIT_SCISSOR: 2645 mRenderEncoder.setScissorRect(mScissorRect); 2646 break; 2647 case DIRTY_BIT_DRAW_FRAMEBUFFER: 2648 // Already handled. 2649 break; 2650 case DIRTY_BIT_CULL_MODE: 2651 mRenderEncoder.setCullMode(mCullMode); 2652 break; 2653 case DIRTY_BIT_FILL_MODE: 2654 mRenderEncoder.setTriangleFillMode(mState.getPolygonMode() == gl::PolygonMode::Fill 2655 ? MTLTriangleFillModeFill 2656 : MTLTriangleFillModeLines); 2657 break; 2658 case DIRTY_BIT_WINDING: 2659 mRenderEncoder.setFrontFacingWinding(mWinding); 2660 break; 2661 case DIRTY_BIT_RENDER_PIPELINE: 2662 // Already handled. See checkIfPipelineChanged(). 2663 break; 2664 case DIRTY_BIT_UNIFORM_BUFFERS_BINDING: 2665 uniformBuffersDirty = true; 2666 break; 2667 case DIRTY_BIT_RASTERIZER_DISCARD: 2668 // Already handled. 2669 break; 2670 default: 2671 UNREACHABLE(); 2672 break; 2673 } 2674 } 2675 2676 if (xfbPass && !mDirtyBits.test(DIRTY_BIT_DRIVER_UNIFORMS)) 2677 { 2678 // If handleDirtyDriverUniforms() was not called and this is XFB pass, we still need to 2679 // update XFB related uniforms 2680 ANGLE_TRY( 2681 fillDriverXFBUniforms(firstVertex, vertexOrIndexCount, /** skippedInstances */ 0)); 2682 mRenderEncoder.setVertexData(mDriverUniforms, mtl::kDriverUniformsBindingIndex); 2683 } 2684 2685 mDirtyBits.reset(); 2686 // Check to see if our state would lead to a no-op draw. 2687 // If so, skip program setup until we end up with a state that requires a program. 2688 if (isDrawNoOp(mRenderPipelineDesc, this, mContextDevice)) 2689 { 2690 *isNoOp = true; 2691 } 2692 else 2693 { 2694 ANGLE_TRY(mExecutable->setupDraw(context, &mRenderEncoder, mRenderPipelineDesc, 2695 isPipelineDescChanged, textureChanged, 2696 uniformBuffersDirty)); 2697 } 2698 2699 return angle::Result::Continue; 2700} 2701 2702void ContextMtl::filterOutXFBOnlyDirtyBits(const gl::Context *context) 2703{ 2704 ASSERT(IsTransformFeedbackOnly(getState())); 2705 2706 ASSERT(mRenderEncoder.renderPassDesc().colorAttachments[0].texture == mDummyXFBRenderTexture); 2707 2708 // In transform feedback only pass, only vertex shader's related states are needed. 2709 constexpr size_t kUnneededBits = 2710 angle::Bit<size_t>(DIRTY_BIT_DEPTH_STENCIL_DESC) | 2711 angle::Bit<size_t>(DIRTY_BIT_DEPTH_BIAS) | angle::Bit<size_t>(DIRTY_BIT_STENCIL_REF) | 2712 angle::Bit<size_t>(DIRTY_BIT_BLEND_COLOR) | angle::Bit<size_t>(DIRTY_BIT_VIEWPORT) | 2713 angle::Bit<size_t>(DIRTY_BIT_SCISSOR) | angle::Bit<size_t>(DIRTY_BIT_CULL_MODE) | 2714 angle::Bit<size_t>(DIRTY_BIT_FILL_MODE) | angle::Bit<size_t>(DIRTY_BIT_WINDING); 2715 2716 mDirtyBits &= ~kUnneededBits; 2717} 2718 2719angle::Result ContextMtl::handleDirtyRenderPass(const gl::Context *context) 2720{ 2721 if (!IsTransformFeedbackOnly(mState)) 2722 { 2723 // Start new render command encoder 2724 ANGLE_MTL_TRY(this, mDrawFramebuffer->ensureRenderPassStarted(context)); 2725 } 2726 else 2727 { 2728 // XFB is active and rasterization is disabled. Use dummy render target. 2729 // We currently need to end the render pass when XFB is activated/deactivated so using 2730 // a small dummy render target would make the render pass ending very cheap. 2731 if (!mDummyXFBRenderTexture) 2732 { 2733 ANGLE_TRY(mtl::Texture::Make2DTexture(this, 2734 getPixelFormat(angle::FormatID::R8G8B8A8_UNORM), 2735 1, 1, 1, true, false, &mDummyXFBRenderTexture)); 2736 } 2737 mtl::RenderCommandEncoder *encoder = getTextureRenderCommandEncoder( 2738 mDummyXFBRenderTexture, 2739 mtl::ImageNativeIndex::FromBaseZeroGLIndex(gl::ImageIndex::Make2D(0))); 2740 encoder->setColorLoadAction(MTLLoadActionDontCare, MTLClearColor(), 0); 2741 encoder->setColorStoreAction(MTLStoreActionDontCare); 2742 2743#ifndef NDEBUG 2744 encoder->setLabel(@"TransformFeedbackOnlyPass"); 2745#endif 2746 } 2747 2748 // re-apply everything 2749 invalidateState(context); 2750 2751 return angle::Result::Continue; 2752} 2753 2754angle::Result ContextMtl::handleDirtyActiveTextures(const gl::Context *context) 2755{ 2756 const gl::State &glState = mState; 2757 const gl::ProgramExecutable *executable = glState.getProgramExecutable(); 2758 2759 constexpr auto ensureTextureCreated = [](const gl::Context *context, 2760 gl::Texture *texture) -> angle::Result { 2761 if (texture == nullptr) 2762 { 2763 return angle::Result::Continue; 2764 } 2765 2766 TextureMtl *textureMtl = mtl::GetImpl(texture); 2767 2768 // Make sure texture's images update will be transferred to GPU. 2769 ANGLE_TRY(textureMtl->ensureTextureCreated(context)); 2770 2771 // The binding of this texture will be done by ProgramMtl. 2772 return angle::Result::Continue; 2773 }; 2774 2775 const gl::ActiveTexturesCache &textures = glState.getActiveTexturesCache(); 2776 const gl::ActiveTextureMask &activeTextures = executable->getActiveSamplersMask(); 2777 2778 for (size_t textureUnit : activeTextures) 2779 { 2780 ANGLE_TRY(ensureTextureCreated(context, textures[textureUnit])); 2781 } 2782 2783 for (size_t imageUnit : executable->getActiveImagesMask()) 2784 { 2785 ANGLE_TRY(ensureTextureCreated(context, glState.getImageUnit(imageUnit).texture.get())); 2786 } 2787 2788 return angle::Result::Continue; 2789} 2790 2791angle::Result ContextMtl::handleDirtyDefaultAttribs(const gl::Context *context) 2792{ 2793 for (size_t attribIndex : mDirtyDefaultAttribsMask) 2794 { 2795 ANGLE_TRY(updateDefaultAttribute(attribIndex)); 2796 } 2797 2798 ASSERT(mRenderEncoder.valid()); 2799 mRenderEncoder.setVertexData(mDefaultAttributes, mtl::kDefaultAttribsBindingIndex); 2800 2801 mDirtyDefaultAttribsMask.reset(); 2802 return angle::Result::Continue; 2803} 2804 2805angle::Result ContextMtl::handleDirtyDriverUniforms(const gl::Context *context, 2806 GLint drawCallFirstVertex, 2807 uint32_t verticesPerInstance) 2808{ 2809 mDriverUniforms.depthRange[0] = mState.getNearPlane(); 2810 mDriverUniforms.depthRange[1] = mState.getFarPlane(); 2811 2812 mDriverUniforms.renderArea = mDrawFramebuffer->getState().getDimensions().height << 16 | 2813 mDrawFramebuffer->getState().getDimensions().width; 2814 2815 const float flipX = 1.0; 2816 const float flipY = mDrawFramebuffer->flipY() ? -1.0f : 1.0f; 2817 mDriverUniforms.flipXY = gl::PackSnorm4x8( 2818 flipX, flipY, flipX, mState.getClipOrigin() == gl::ClipOrigin::LowerLeft ? -flipY : flipY); 2819 2820 // gl_ClipDistance 2821 const uint32_t enabledClipDistances = mState.getEnabledClipDistances().bits(); 2822 ASSERT((enabledClipDistances & ~sh::vk::kDriverUniformsMiscEnabledClipPlanesMask) == 0); 2823 2824 // GL_CLIP_DEPTH_MODE_EXT 2825 const uint32_t transformDepth = !mState.isClipDepthModeZeroToOne(); 2826 ASSERT((transformDepth & ~sh::vk::kDriverUniformsMiscTransformDepthMask) == 0); 2827 2828 // GL_SAMPLE_ALPHA_TO_COVERAGE 2829 const uint32_t alphaToCoverage = mState.isSampleAlphaToCoverageEnabled(); 2830 ASSERT((alphaToCoverage & ~sh::vk::kDriverUniformsMiscAlphaToCoverageMask) == 0); 2831 2832 mDriverUniforms.misc = 2833 (enabledClipDistances << sh::vk::kDriverUniformsMiscEnabledClipPlanesOffset) | 2834 (transformDepth << sh::vk::kDriverUniformsMiscTransformDepthOffset) | 2835 (alphaToCoverage << sh::vk::kDriverUniformsMiscAlphaToCoverageOffset); 2836 2837 // Sample coverage mask 2838 if (mState.isSampleCoverageEnabled()) 2839 { 2840 const uint32_t sampleBitCount = mDrawFramebuffer->getSamples(); 2841 ASSERT(sampleBitCount < 32); 2842 const uint32_t coverageSampleBitCount = 2843 static_cast<uint32_t>(std::round(mState.getSampleCoverageValue() * sampleBitCount)); 2844 uint32_t coverageMask = (1u << coverageSampleBitCount) - 1; 2845 if (mState.getSampleCoverageInvert()) 2846 { 2847 const uint32_t sampleMask = (1u << sampleBitCount) - 1; 2848 coverageMask = sampleMask & (~coverageMask); 2849 } 2850 mDriverUniforms.coverageMask = coverageMask; 2851 } 2852 else 2853 { 2854 mDriverUniforms.coverageMask = 0xFFFFFFFFu; 2855 } 2856 2857 ANGLE_TRY( 2858 fillDriverXFBUniforms(drawCallFirstVertex, verticesPerInstance, /** skippedInstances */ 0)); 2859 2860 ASSERT(mRenderEncoder.valid()); 2861 mRenderEncoder.setFragmentData(mDriverUniforms, mtl::kDriverUniformsBindingIndex); 2862 mRenderEncoder.setVertexData(mDriverUniforms, mtl::kDriverUniformsBindingIndex); 2863 2864 return angle::Result::Continue; 2865} 2866 2867angle::Result ContextMtl::fillDriverXFBUniforms(GLint drawCallFirstVertex, 2868 uint32_t verticesPerInstance, 2869 uint32_t skippedInstances) 2870{ 2871 gl::TransformFeedback *transformFeedback = getState().getCurrentTransformFeedback(); 2872 2873 bool xfbActiveUnpaused = getState().isTransformFeedbackActiveUnpaused(); 2874 if (!transformFeedback || !xfbActiveUnpaused) 2875 { 2876 return angle::Result::Continue; 2877 } 2878 2879 mDriverUniforms.xfbVerticesPerInstance = verticesPerInstance; 2880 2881 TransformFeedbackMtl *transformFeedbackMtl = mtl::GetImpl(transformFeedback); 2882 2883 return transformFeedbackMtl->getBufferOffsets(this, drawCallFirstVertex, 2884 verticesPerInstance * skippedInstances, 2885 mDriverUniforms.xfbBufferOffsets); 2886} 2887 2888angle::Result ContextMtl::handleDirtyDepthStencilState(const gl::Context *context) 2889{ 2890 ASSERT(mRenderEncoder.valid()); 2891 2892 // Need to handle the case when render pass doesn't have depth/stencil attachment. 2893 mtl::DepthStencilDesc dsDesc = mDepthStencilDesc; 2894 const mtl::RenderPassDesc &renderPassDesc = mRenderEncoder.renderPassDesc(); 2895 2896 if (!renderPassDesc.depthAttachment.texture) 2897 { 2898 dsDesc.depthWriteEnabled = false; 2899 dsDesc.depthCompareFunction = MTLCompareFunctionAlways; 2900 } 2901 2902 if (!renderPassDesc.stencilAttachment.texture) 2903 { 2904 dsDesc.frontFaceStencil.reset(); 2905 dsDesc.backFaceStencil.reset(); 2906 } 2907 2908 // Apply depth stencil state 2909 mRenderEncoder.setDepthStencilState( 2910 getDisplay()->getStateCache().getDepthStencilState(getMetalDevice(), dsDesc)); 2911 2912 return angle::Result::Continue; 2913} 2914 2915angle::Result ContextMtl::handleDirtyDepthBias(const gl::Context *context) 2916{ 2917 const gl::RasterizerState &rasterState = mState.getRasterizerState(); 2918 ASSERT(mRenderEncoder.valid()); 2919 if (!mState.isPolygonOffsetEnabled()) 2920 { 2921 mRenderEncoder.setDepthBias(0, 0, 0); 2922 } 2923 else 2924 { 2925 mRenderEncoder.setDepthBias(rasterState.polygonOffsetUnits, rasterState.polygonOffsetFactor, 2926 rasterState.polygonOffsetClamp); 2927 } 2928 2929 return angle::Result::Continue; 2930} 2931 2932angle::Result ContextMtl::checkIfPipelineChanged(const gl::Context *context, 2933 gl::PrimitiveMode primitiveMode, 2934 bool xfbPass, 2935 bool *isPipelineDescChanged) 2936{ 2937 ASSERT(mRenderEncoder.valid()); 2938 mtl::PrimitiveTopologyClass topologyClass = mtl::GetPrimitiveTopologyClass(primitiveMode); 2939 2940 bool rppChange = mDirtyBits.test(DIRTY_BIT_RENDER_PIPELINE) || 2941 topologyClass != mRenderPipelineDesc.inputPrimitiveTopology; 2942 2943 // Obtain RenderPipelineDesc's vertex array descriptor. 2944 ANGLE_TRY(mVertexArray->setupDraw(context, &mRenderEncoder, &rppChange, 2945 &mRenderPipelineDesc.vertexDescriptor)); 2946 2947 if (rppChange) 2948 { 2949 const mtl::RenderPassDesc &renderPassDesc = mRenderEncoder.renderPassDesc(); 2950 // Obtain RenderPipelineDesc's output descriptor. 2951 renderPassDesc.populateRenderPipelineOutputDesc(mBlendDescArray, 2952 &mRenderPipelineDesc.outputDescriptor); 2953 2954 if (xfbPass) 2955 { 2956 // In XFB pass, we disable fragment shader. 2957 mRenderPipelineDesc.rasterizationType = mtl::RenderPipelineRasterization::Disabled; 2958 } 2959 else if (mState.isRasterizerDiscardEnabled()) 2960 { 2961 // If XFB is not active and rasterizer discard is enabled, we need to emulate the 2962 // discard. Because in this case, vertex shader might write to stage output values and 2963 // Metal doesn't allow rasterization to be disabled. 2964 mRenderPipelineDesc.rasterizationType = 2965 mtl::RenderPipelineRasterization::EmulatedDiscard; 2966 } 2967 else 2968 { 2969 mRenderPipelineDesc.rasterizationType = mtl::RenderPipelineRasterization::Enabled; 2970 } 2971 mRenderPipelineDesc.inputPrimitiveTopology = topologyClass; 2972 mRenderPipelineDesc.alphaToCoverageEnabled = 2973 mState.isSampleAlphaToCoverageEnabled() && 2974 mRenderPipelineDesc.outputDescriptor.sampleCount > 1 && 2975 !getDisplay()->getFeatures().emulateAlphaToCoverage.enabled; 2976 2977 mRenderPipelineDesc.outputDescriptor.updateEnabledDrawBuffers( 2978 mDrawFramebuffer->getState().getEnabledDrawBuffers()); 2979 } 2980 2981 *isPipelineDescChanged = rppChange; 2982 2983 return angle::Result::Continue; 2984} 2985 2986} // namespace rx 2987