PixelLocalStorage.cpp (32448B)
1 // 2 // Copyright 2022 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 7 // PixelLocalStorage.cpp: Defines the renderer-agnostic container classes 8 // gl::PixelLocalStorage and gl::PixelLocalStoragePlane for 9 // ANGLE_shader_pixel_local_storage. 10 11 #include "libANGLE/PixelLocalStorage.h" 12 13 #include <numeric> 14 #include "libANGLE/Context.h" 15 #include "libANGLE/Framebuffer.h" 16 #include "libANGLE/Texture.h" 17 #include "libANGLE/renderer/ContextImpl.h" 18 19 namespace gl 20 { 21 // RAII utilities for working with GL state. 22 namespace 23 { 24 class ScopedBindTexture2D 25 { 26 public: 27 ScopedBindTexture2D(Context *context, TextureID texture) 28 : mContext(context), 29 mSavedTexBinding2D( 30 mContext->getState().getSamplerTextureId(mContext->getState().getActiveSampler(), 31 TextureType::_2D)) 32 { 33 mContext->bindTexture(TextureType::_2D, texture); 34 } 35 36 ~ScopedBindTexture2D() { mContext->bindTexture(TextureType::_2D, mSavedTexBinding2D); } 37 38 private: 39 Context *const mContext; 40 TextureID mSavedTexBinding2D; 41 }; 42 43 class ScopedRestoreDrawFramebuffer 44 { 45 public: 46 ScopedRestoreDrawFramebuffer(Context *context) 47 : mContext(context), mSavedFramebuffer(mContext->getState().getDrawFramebuffer()) 48 { 49 ASSERT(mSavedFramebuffer); 50 } 51 52 ~ScopedRestoreDrawFramebuffer() { mContext->bindDrawFramebuffer(mSavedFramebuffer->id()); } 53 54 private: 55 Context *const mContext; 56 Framebuffer *const mSavedFramebuffer; 57 }; 58 59 class ScopedDisableScissor 60 { 61 public: 62 ScopedDisableScissor(Context *context) 63 : mContext(context), mScissorTestEnabled(mContext->getState().isScissorTestEnabled()) 64 { 65 if (mScissorTestEnabled) 66 { 67 mContext->disable(GL_SCISSOR_TEST); 68 } 69 } 70 71 ~ScopedDisableScissor() 72 { 73 if (mScissorTestEnabled) 74 { 75 mContext->enable(GL_SCISSOR_TEST); 76 } 77 } 78 79 private: 80 Context *const mContext; 81 const GLint mScissorTestEnabled; 82 }; 83 } // namespace 84 85 PixelLocalStoragePlane::~PixelLocalStoragePlane() 86 { 87 // Call deinitialize or onContextObjectsLost first! 88 ASSERT(mMemorylessTextureID.value == 0); 89 // Call deinitialize or onFramebufferDestroyed first! 90 ASSERT(mTextureRef == nullptr); 91 } 92 93 void PixelLocalStoragePlane::onContextObjectsLost() 94 { 95 // We normally call deleteTexture on the memoryless plane texture ID, since we own it, but in 96 // this case we can let it go. 97 mMemorylessTextureID = TextureID(); 98 } 99 100 void PixelLocalStoragePlane::onFramebufferDestroyed(const Context *context) 101 { 102 if (mTextureRef != nullptr) 103 { 104 mTextureRef->release(context); 105 mTextureRef = nullptr; 106 } 107 } 108 109 void PixelLocalStoragePlane::deinitialize(Context *context) 110 { 111 mInternalformat = GL_NONE; 112 mMemoryless = false; 113 if (mMemorylessTextureID.value != 0) 114 { 115 // The app could have technically deleted mMemorylessTextureID by guessing its value and 116 // calling glDeleteTextures, but it seems unnecessary to worry about that here. (Worst case 117 // we delete one of their textures.) This also isn't a problem in WebGL. 118 context->deleteTexture(mMemorylessTextureID); 119 mMemorylessTextureID = TextureID(); 120 } 121 if (mTextureRef != nullptr) 122 { 123 mTextureRef->release(context); 124 mTextureRef = nullptr; 125 } 126 } 127 128 void PixelLocalStoragePlane::setMemoryless(Context *context, GLenum internalformat) 129 { 130 deinitialize(context); 131 mInternalformat = internalformat; 132 mMemoryless = true; 133 mTextureImageIndex = ImageIndex::MakeFromType(TextureType::_2D, 0, 0); 134 // The backing texture will get allocated lazily, once we know what dimensions it should be. 135 ASSERT(mMemorylessTextureID.value == 0); 136 ASSERT(mTextureRef == nullptr); 137 } 138 139 void PixelLocalStoragePlane::setTextureBacked(Context *context, Texture *tex, int level, int layer) 140 { 141 deinitialize(context); 142 ASSERT(tex->getImmutableFormat()); 143 mInternalformat = tex->getState().getBaseLevelDesc().format.info->internalFormat; 144 mMemoryless = false; 145 mTextureImageIndex = ImageIndex::MakeFromType(tex->getType(), level, layer); 146 mTextureRef = tex; 147 mTextureRef->addRef(); 148 } 149 150 bool PixelLocalStoragePlane::isTextureIDDeleted(const Context *context) const 151 { 152 // We can tell if the texture has been deleted by looking up mTextureRef's ID on the Context. If 153 // they don't match, it's been deleted. 154 ASSERT(!isDeinitialized() || mTextureRef == nullptr); 155 return mTextureRef != nullptr && context->getTexture(mTextureRef->id()) != mTextureRef; 156 } 157 158 GLint PixelLocalStoragePlane::getIntegeri(const Context *context, GLenum target, GLuint index) const 159 { 160 if (!isDeinitialized()) 161 { 162 bool memoryless = isMemoryless() || isTextureIDDeleted(context); 163 switch (target) 164 { 165 case GL_PIXEL_LOCAL_FORMAT_ANGLE: 166 return mInternalformat; 167 case GL_PIXEL_LOCAL_TEXTURE_NAME_ANGLE: 168 return memoryless ? 0 : mTextureRef->id().value; 169 case GL_PIXEL_LOCAL_TEXTURE_LEVEL_ANGLE: 170 return memoryless ? 0 : mTextureImageIndex.getLevelIndex(); 171 case GL_PIXEL_LOCAL_TEXTURE_LAYER_ANGLE: 172 return memoryless ? 0 : mTextureImageIndex.getLayerIndex(); 173 } 174 } 175 // Since GL_NONE == 0, PLS queries all return 0 when the plane is deinitialized. 176 static_assert(GL_NONE == 0, "Expecting GL_NONE to be zero."); 177 return 0; 178 } 179 180 bool PixelLocalStoragePlane::getTextureImageExtents(const Context *context, Extents *extents) const 181 { 182 if (isDeinitialized() || isMemoryless() || isTextureIDDeleted(context)) 183 { 184 return false; 185 } 186 ASSERT(mTextureRef != nullptr); 187 *extents = 188 mTextureRef->getExtents(mTextureImageIndex.getTarget(), mTextureImageIndex.getLevelIndex()); 189 extents->depth = 0; 190 return true; 191 } 192 193 void PixelLocalStoragePlane::ensureBackingIfMemoryless(Context *context, Extents plsExtents) 194 { 195 ASSERT(!isDeinitialized()); 196 ASSERT(!isTextureIDDeleted(context)); // Convert to memoryless first in this case. 197 if (!isMemoryless()) 198 { 199 ASSERT(mTextureRef != nullptr); 200 return; 201 } 202 203 // Internal textures backing memoryless planes are always 2D and not mipmapped. 204 ASSERT(mTextureImageIndex.getType() == TextureType::_2D); 205 ASSERT(mTextureImageIndex.getLevelIndex() == 0); 206 ASSERT(mTextureImageIndex.getLayerIndex() == 0); 207 const bool hasMemorylessTextureId = mMemorylessTextureID.value != 0; 208 const bool hasTextureRef = mTextureRef != nullptr; 209 ASSERT(hasMemorylessTextureId == hasTextureRef); 210 211 // Do we need to allocate a new backing texture? 212 if (mTextureRef == nullptr || 213 static_cast<GLsizei>(mTextureRef->getWidth(TextureTarget::_2D, 0)) != plsExtents.width || 214 static_cast<GLsizei>(mTextureRef->getHeight(TextureTarget::_2D, 0)) != plsExtents.height) 215 { 216 // Call setMemoryless() to release our current data. 217 setMemoryless(context, mInternalformat); 218 ASSERT(mTextureRef == nullptr); 219 ASSERT(mMemorylessTextureID.value == 0); 220 221 // Create a new texture that backs the memoryless plane. 222 context->genTextures(1, &mMemorylessTextureID); 223 { 224 ScopedBindTexture2D scopedBindTexture2D(context, mMemorylessTextureID); 225 context->bindTexture(TextureType::_2D, mMemorylessTextureID); 226 context->texStorage2D(TextureType::_2D, 1, mInternalformat, plsExtents.width, 227 plsExtents.height); 228 } 229 230 mTextureRef = context->getTexture(mMemorylessTextureID); 231 ASSERT(mTextureRef != nullptr); 232 ASSERT(mTextureRef->id() == mMemorylessTextureID); 233 mTextureRef->addRef(); 234 } 235 } 236 237 void PixelLocalStoragePlane::attachToDrawFramebuffer(Context *context, 238 Extents plsExtents, 239 GLenum colorAttachment) 240 { 241 ASSERT(!isDeinitialized()); 242 ensureBackingIfMemoryless(context, plsExtents); 243 ASSERT(mTextureRef != nullptr); 244 if (mTextureImageIndex.usesTex3D()) // GL_TEXTURE_3D or GL_TEXTURE_2D_ARRAY. 245 { 246 context->framebufferTextureLayer(GL_DRAW_FRAMEBUFFER, colorAttachment, mTextureRef->id(), 247 mTextureImageIndex.getLevelIndex(), 248 mTextureImageIndex.getLayerIndex()); 249 } 250 else 251 { 252 context->framebufferTexture2D(GL_DRAW_FRAMEBUFFER, colorAttachment, 253 mTextureImageIndex.getTarget(), mTextureRef->id(), 254 mTextureImageIndex.getLevelIndex()); 255 } 256 } 257 258 void PixelLocalStoragePlane::performLoadOperationClear(Context *context, 259 GLint drawBuffer, 260 GLenum loadop, 261 const void *data) 262 { 263 // The GL scissor test must be disabled, since the intention is to clear the entire surface. 264 ASSERT(!context->getState().isScissorTestEnabled()); 265 switch (mInternalformat) 266 { 267 case GL_RGBA8: 268 case GL_R32F: 269 { 270 GLfloat clearValue[4]{}; 271 if (loadop == GL_CLEAR_ANGLE) 272 { 273 memcpy(clearValue, data, sizeof(clearValue)); 274 } 275 context->clearBufferfv(GL_COLOR, drawBuffer, clearValue); 276 break; 277 } 278 case GL_RGBA8I: 279 { 280 GLint clearValue[4]{}; 281 if (loadop == GL_CLEAR_ANGLE) 282 { 283 memcpy(clearValue, data, sizeof(clearValue)); 284 } 285 context->clearBufferiv(GL_COLOR, drawBuffer, clearValue); 286 break; 287 } 288 case GL_RGBA8UI: 289 case GL_R32UI: 290 { 291 GLuint clearValue[4]{}; 292 if (loadop == GL_CLEAR_ANGLE) 293 { 294 memcpy(clearValue, data, sizeof(clearValue)); 295 } 296 context->clearBufferuiv(GL_COLOR, drawBuffer, clearValue); 297 break; 298 } 299 default: 300 // Invalid PLS internalformats should not have made it this far. 301 UNREACHABLE(); 302 } 303 } 304 305 void PixelLocalStoragePlane::bindToImage(Context *context, 306 Extents plsExtents, 307 GLuint unit, 308 bool needsR32Packing) 309 { 310 ASSERT(!isDeinitialized()); 311 ensureBackingIfMemoryless(context, plsExtents); 312 ASSERT(mTextureRef != nullptr); 313 GLenum imageBindingFormat = mInternalformat; 314 if (needsR32Packing) 315 { 316 // D3D and ES require us to pack all PLS formats into r32f, r32i, or r32ui images. 317 switch (imageBindingFormat) 318 { 319 case GL_RGBA8: 320 case GL_RGBA8UI: 321 imageBindingFormat = GL_R32UI; 322 break; 323 case GL_RGBA8I: 324 imageBindingFormat = GL_R32I; 325 break; 326 } 327 } 328 if (mTextureRef->getType() != TextureType::_2D) 329 { 330 // TODO(anglebug.com/7279): Texture types other than GL_TEXTURE_2D will take a lot of 331 // consideration to support on all backends. Hold of on fully implementing them until the 332 // other backends are in place. 333 UNIMPLEMENTED(); 334 } 335 context->bindImageTexture(unit, mTextureRef->id(), mTextureImageIndex.getLevelIndex(), GL_FALSE, 336 mTextureImageIndex.getLayerIndex(), GL_READ_WRITE, 337 imageBindingFormat); 338 } 339 340 PixelLocalStorage::PixelLocalStorage() {} 341 PixelLocalStorage::~PixelLocalStorage() {} 342 343 void PixelLocalStorage::onFramebufferDestroyed(const Context *context) 344 { 345 if (context->getRefCount() == 0) 346 { 347 // If the Context's refcount is zero, we know it's in a teardown state and we can just let 348 // go of our GL objects -- they get cleaned up as part of context teardown. Otherwise, the 349 // Context should have called deleteContextObjects before reaching this point. 350 onContextObjectsLost(); 351 for (PixelLocalStoragePlane &plane : mPlanes) 352 { 353 plane.onContextObjectsLost(); 354 } 355 } 356 for (PixelLocalStoragePlane &plane : mPlanes) 357 { 358 plane.onFramebufferDestroyed(context); 359 } 360 } 361 362 void PixelLocalStorage::deleteContextObjects(Context *context) 363 { 364 onDeleteContextObjects(context); 365 for (PixelLocalStoragePlane &plane : mPlanes) 366 { 367 plane.deinitialize(context); 368 } 369 } 370 371 void PixelLocalStorage::begin(Context *context, 372 GLsizei n, 373 const GLenum loadops[], 374 const void *cleardata) 375 { 376 // Convert planes whose backing texture has been deleted to memoryless, and find the pixel local 377 // storage rendering dimensions. 378 Extents plsExtents; 379 bool hasPLSExtents = false; 380 for (int i = 0; i < n; ++i) 381 { 382 if (loadops[i] == GL_DISABLE_ANGLE) 383 { 384 continue; 385 } 386 PixelLocalStoragePlane &plane = mPlanes[i]; 387 if (plane.isTextureIDDeleted(context)) 388 { 389 // [ANGLE_shader_pixel_local_storage] Section 4.4.2.X "Configuring Pixel Local Storage 390 // on a Framebuffer": When a texture object is deleted, any pixel local storage plane to 391 // which it was bound is automatically converted to a memoryless plane of matching 392 // internalformat. 393 plane.setMemoryless(context, plane.getInternalformat()); 394 } 395 if (!hasPLSExtents && plane.getTextureImageExtents(context, &plsExtents)) 396 { 397 hasPLSExtents = true; 398 } 399 } 400 if (!hasPLSExtents) 401 { 402 // All PLS planes are memoryless. Use the rendering area of the framebuffer instead. 403 plsExtents = 404 context->getState().getDrawFramebuffer()->getState().getAttachmentExtentsIntersection(); 405 ASSERT(plsExtents.depth == 0); 406 } 407 408 onBegin(context, n, loadops, reinterpret_cast<const char *>(cleardata), plsExtents); 409 mNumActivePLSPlanes = n; 410 } 411 412 void PixelLocalStorage::end(Context *context) 413 { 414 onEnd(context, mNumActivePLSPlanes); 415 mNumActivePLSPlanes = 0; 416 } 417 418 void PixelLocalStorage::barrier(Context *context) 419 { 420 ASSERT(!context->getExtensions().shaderPixelLocalStorageCoherentANGLE); 421 onBarrier(context); 422 } 423 424 namespace 425 { 426 // Implements pixel local storage with image load/store shader operations. 427 class PixelLocalStorageImageLoadStore : public PixelLocalStorage 428 { 429 public: 430 PixelLocalStorageImageLoadStore(bool needsR32Packing) : mNeedsR32Packing(needsR32Packing) {} 431 432 // Call deleteContextObjects or onContextObjectsLost first! 433 ~PixelLocalStorageImageLoadStore() override 434 { 435 ASSERT(mScratchFramebufferForClearing.value == 0); 436 } 437 438 void onContextObjectsLost() override 439 { 440 mScratchFramebufferForClearing = FramebufferID(); // Let go of GL objects. 441 } 442 443 void onDeleteContextObjects(Context *context) override 444 { 445 if (mScratchFramebufferForClearing.value != 0) 446 { 447 context->deleteFramebuffer(mScratchFramebufferForClearing); 448 mScratchFramebufferForClearing = FramebufferID(); 449 } 450 } 451 452 void onBegin(Context *context, 453 GLsizei n, 454 const GLenum loadops[], 455 const char *cleardata, 456 Extents plsExtents) override 457 { 458 // Save the image bindings so we can restore them during onEnd(). 459 const State &state = context->getState(); 460 ASSERT(static_cast<size_t>(n) <= state.getImageUnits().size()); 461 mSavedImageBindings.clear(); 462 mSavedImageBindings.reserve(n); 463 for (int i = 0; i < n; ++i) 464 { 465 mSavedImageBindings.emplace_back(state.getImageUnit(i)); 466 } 467 468 // Save the default framebuffer width/height so we can restore it during onEnd(). 469 Framebuffer *framebuffer = state.getDrawFramebuffer(); 470 mSavedFramebufferDefaultWidth = framebuffer->getDefaultWidth(); 471 mSavedFramebufferDefaultHeight = framebuffer->getDefaultHeight(); 472 473 // Specify the framebuffer width/height explicitly in case we end up rendering exclusively 474 // to shader images. 475 context->framebufferParameteri(GL_DRAW_FRAMEBUFFER, GL_FRAMEBUFFER_DEFAULT_WIDTH, 476 plsExtents.width); 477 context->framebufferParameteri(GL_DRAW_FRAMEBUFFER, GL_FRAMEBUFFER_DEFAULT_HEIGHT, 478 plsExtents.height); 479 480 // Guard GL state and bind a scratch framebuffer in case we need to reallocate or clear any 481 // PLS planes. 482 const size_t maxDrawBuffers = context->getCaps().maxDrawBuffers; 483 ScopedRestoreDrawFramebuffer ScopedRestoreDrawFramebuffer(context); 484 if (mScratchFramebufferForClearing.value == 0) 485 { 486 context->genFramebuffers(1, &mScratchFramebufferForClearing); 487 context->bindFramebuffer(GL_DRAW_FRAMEBUFFER, mScratchFramebufferForClearing); 488 // Turn on all draw buffers on the scratch framebuffer for clearing. 489 DrawBuffersVector<GLenum> drawBuffers(maxDrawBuffers); 490 std::iota(drawBuffers.begin(), drawBuffers.end(), GL_COLOR_ATTACHMENT0); 491 context->drawBuffers(static_cast<int>(drawBuffers.size()), drawBuffers.data()); 492 } 493 else 494 { 495 context->bindFramebuffer(GL_DRAW_FRAMEBUFFER, mScratchFramebufferForClearing); 496 } 497 ScopedDisableScissor scopedDisableScissor(context); 498 499 // Bind and clear the PLS planes. 500 size_t maxClearedAttachments = 0; 501 for (int i = 0; i < n;) 502 { 503 angle::FixedVector<int, IMPLEMENTATION_MAX_DRAW_BUFFERS> pendingClears; 504 for (; pendingClears.size() < maxDrawBuffers && i < n; ++i) 505 { 506 GLenum loadop = loadops[i]; 507 if (loadop == GL_DISABLE_ANGLE) 508 { 509 continue; 510 } 511 PixelLocalStoragePlane &plane = getPlane(i); 512 ASSERT(!plane.isDeinitialized()); 513 plane.bindToImage(context, plsExtents, i, mNeedsR32Packing); 514 if (loadop == GL_ZERO || loadop == GL_CLEAR_ANGLE) 515 { 516 plane.attachToDrawFramebuffer( 517 context, plsExtents, 518 GL_COLOR_ATTACHMENT0 + static_cast<GLenum>(pendingClears.size())); 519 pendingClears.push_back(i); // Defer the clear for later. 520 } 521 } 522 // Clear in batches to be more efficient with GL state. 523 for (size_t drawBufferIdx = 0; drawBufferIdx < pendingClears.size(); ++drawBufferIdx) 524 { 525 int plsIdx = pendingClears[drawBufferIdx]; 526 getPlane(plsIdx).performLoadOperationClear( 527 context, static_cast<GLint>(drawBufferIdx), loadops[plsIdx], 528 cleardata + plsIdx * 4 * 4); 529 } 530 maxClearedAttachments = std::max(maxClearedAttachments, pendingClears.size()); 531 } 532 533 // Detach the cleared PLS textures from the scratch framebuffer. 534 for (size_t i = 0; i < maxClearedAttachments; ++i) 535 { 536 context->framebufferTexture2D(GL_DRAW_FRAMEBUFFER, 537 GL_COLOR_ATTACHMENT0 + static_cast<GLenum>(i), 538 TextureTarget::_2D, TextureID(), 0); 539 } 540 541 // Unlike other barriers, GL_SHADER_IMAGE_ACCESS_BARRIER_BIT also synchronizes all types of 542 // memory accesses that happened before the barrier: 543 // 544 // SHADER_IMAGE_ACCESS_BARRIER_BIT: Memory accesses using shader built-in image load and 545 // store functions issued after the barrier will reflect data written by shaders prior to 546 // the barrier. Additionally, image stores issued after the barrier will not execute until 547 // all memory accesses (e.g., loads, stores, texture fetches, vertex fetches) initiated 548 // prior to the barrier complete. 549 // 550 // So we don't any barriers other than GL_SHADER_IMAGE_ACCESS_BARRIER_BIT during begin(). 551 context->memoryBarrier(GL_SHADER_IMAGE_ACCESS_BARRIER_BIT); 552 } 553 554 void onEnd(Context *context, GLsizei numActivePLSPlanes) override 555 { 556 // Restore the image bindings. Since glBindImageTexture and any commands that modify 557 // textures are banned while PLS is active, these will all still be alive and valid. 558 ASSERT(mSavedImageBindings.size() == static_cast<size_t>(numActivePLSPlanes)); 559 for (GLuint unit = 0; unit < mSavedImageBindings.size(); ++unit) 560 { 561 ImageUnit &binding = mSavedImageBindings[unit]; 562 context->bindImageTexture(unit, binding.texture.id(), binding.level, binding.layered, 563 binding.layer, binding.access, binding.format); 564 565 // BindingPointers have to be explicitly cleaned up. 566 binding.texture.set(context, nullptr); 567 } 568 mSavedImageBindings.clear(); 569 570 // Restore the default framebuffer width/height. 571 context->framebufferParameteri(GL_DRAW_FRAMEBUFFER, GL_FRAMEBUFFER_DEFAULT_WIDTH, 572 mSavedFramebufferDefaultWidth); 573 context->framebufferParameteri(GL_DRAW_FRAMEBUFFER, GL_FRAMEBUFFER_DEFAULT_HEIGHT, 574 mSavedFramebufferDefaultHeight); 575 576 // We need ALL_BARRIER_BITS during end() because GL_SHADER_IMAGE_ACCESS_BARRIER_BIT doesn't 577 // synchronize all types of memory accesses that can happen after the barrier. 578 context->memoryBarrier(GL_ALL_BARRIER_BITS); 579 } 580 581 void onBarrier(Context *context) override 582 { 583 context->memoryBarrier(GL_SHADER_IMAGE_ACCESS_BARRIER_BIT); 584 } 585 586 private: 587 // D3D and ES require us to pack all PLS formats into r32f, r32i, or r32ui images. 588 const bool mNeedsR32Packing; 589 FramebufferID mScratchFramebufferForClearing{}; 590 591 // Saved values to restore during onEnd(). 592 GLint mSavedFramebufferDefaultWidth; 593 GLint mSavedFramebufferDefaultHeight; 594 std::vector<ImageUnit> mSavedImageBindings; 595 }; 596 597 // Implements pixel local storage via framebuffer fetch. 598 class PixelLocalStorageFramebufferFetch : public PixelLocalStorage 599 { 600 public: 601 void onContextObjectsLost() override {} 602 603 void onDeleteContextObjects(Context *) override {} 604 605 void onBegin(Context *context, 606 GLsizei n, 607 const GLenum loadops[], 608 const char *cleardata, 609 Extents plsExtents) override 610 { 611 const State &state = context->getState(); 612 const Caps &caps = context->getCaps(); 613 Framebuffer *framebuffer = context->getState().getDrawFramebuffer(); 614 const DrawBuffersVector<GLenum> &appDrawBuffers = framebuffer->getDrawBufferStates(); 615 616 // Remember the current draw buffer state so we can restore it during onEnd(). 617 mSavedDrawBuffers.resize(appDrawBuffers.size()); 618 std::copy(appDrawBuffers.begin(), appDrawBuffers.end(), mSavedDrawBuffers.begin()); 619 620 // Set up new draw buffers for PLS. 621 int firstPLSDrawBuffer = caps.maxCombinedDrawBuffersAndPixelLocalStoragePlanes - n; 622 int numAppDrawBuffers = 623 std::min(static_cast<int>(appDrawBuffers.size()), firstPLSDrawBuffer); 624 DrawBuffersArray<GLenum> plsDrawBuffers; 625 std::copy(appDrawBuffers.begin(), appDrawBuffers.begin() + numAppDrawBuffers, 626 plsDrawBuffers.begin()); 627 std::fill(plsDrawBuffers.begin() + numAppDrawBuffers, 628 plsDrawBuffers.begin() + firstPLSDrawBuffer, GL_NONE); 629 630 mBlendsToReEnable.reset(); 631 mColorMasksToRestore.reset(); 632 mInvalidateList.clear(); 633 bool needsClear = false; 634 635 bool hasIndexedBlendAndColorMask = context->getExtensions().drawBuffersIndexedAny(); 636 if (!hasIndexedBlendAndColorMask) 637 { 638 // We don't have indexed blend and color mask control. Disable them globally. (This also 639 // means the app can't have its own draw buffers while PLS is active.) 640 ASSERT(caps.maxColorAttachmentsWithActivePixelLocalStorage == 0); 641 if (state.isBlendEnabled()) 642 { 643 context->disable(GL_BLEND); 644 mBlendsToReEnable.set(0); 645 } 646 std::array<bool, 4> &mask = mSavedColorMasks[0]; 647 state.getBlendStateExt().getColorMaskIndexed(0, &mask[0], &mask[1], &mask[2], &mask[3]); 648 if (!(mask[0] && mask[1] && mask[2] && mask[3])) 649 { 650 context->colorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); 651 mColorMasksToRestore.set(0); 652 } 653 } 654 655 for (GLsizei i = 0; i < n; ++i) 656 { 657 GLuint drawBufferIdx = getDrawBufferIdx(caps, i); 658 GLenum loadop = loadops[i]; 659 if (loadop == GL_DISABLE_ANGLE) 660 { 661 plsDrawBuffers[drawBufferIdx] = GL_NONE; 662 continue; 663 } 664 665 PixelLocalStoragePlane &plane = getPlane(i); 666 ASSERT(!plane.isDeinitialized()); 667 668 // Attach our PLS texture to the framebuffer. Validation should have already ensured 669 // nothing else was attached at this point. 670 GLenum colorAttachment = GL_COLOR_ATTACHMENT0 + drawBufferIdx; 671 ASSERT(!framebuffer->getAttachment(context, colorAttachment)); 672 plane.attachToDrawFramebuffer(context, plsExtents, colorAttachment); 673 plsDrawBuffers[drawBufferIdx] = colorAttachment; 674 675 if (hasIndexedBlendAndColorMask) 676 { 677 // Ensure blend and color mask are disabled for this draw buffer. 678 if (state.isBlendEnabledIndexed(drawBufferIdx)) 679 { 680 context->disablei(GL_BLEND, drawBufferIdx); 681 mBlendsToReEnable.set(drawBufferIdx); 682 } 683 std::array<bool, 4> &mask = mSavedColorMasks[drawBufferIdx]; 684 state.getBlendStateExt().getColorMaskIndexed(drawBufferIdx, &mask[0], &mask[1], 685 &mask[2], &mask[3]); 686 if (!(mask[0] && mask[1] && mask[2] && mask[3])) 687 { 688 context->colorMaski(drawBufferIdx, GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); 689 mColorMasksToRestore.set(drawBufferIdx); 690 } 691 } 692 693 if (plane.isMemoryless()) 694 { 695 // Memoryless planes don't need to be preserved after glEndPixelLocalStorageANGLE(). 696 mInvalidateList.push_back(colorAttachment); 697 } 698 699 needsClear = needsClear || (loadop != GL_KEEP); 700 } 701 702 // Turn on the PLS draw buffers. 703 context->drawBuffers(caps.maxCombinedDrawBuffersAndPixelLocalStoragePlanes, 704 plsDrawBuffers.data()); 705 706 // Clear the non-KEEP PLS planes now that their draw buffers are turned on. 707 if (needsClear) 708 { 709 ScopedDisableScissor scopedDisableScissor(context); 710 for (GLsizei i = 0; i < n; ++i) 711 { 712 GLenum loadop = loadops[i]; 713 if (loadop != GL_DISABLE_ANGLE && loadop != GL_KEEP) 714 { 715 GLuint drawBufferIdx = getDrawBufferIdx(caps, i); 716 getPlane(i).performLoadOperationClear(context, drawBufferIdx, loadop, 717 cleardata + i * 4 * 4); 718 } 719 } 720 } 721 722 if (!context->getExtensions().shaderPixelLocalStorageCoherentANGLE) 723 { 724 // Insert a barrier if we aren't coherent, since the textures may have been rendered to 725 // previously. 726 barrier(context); 727 } 728 } 729 730 void onEnd(Context *context, GLint numActivePLSPlanes) override 731 { 732 733 const Caps &caps = context->getCaps(); 734 735 // Invalidate the memoryless PLS attachments. 736 if (!mInvalidateList.empty()) 737 { 738 context->invalidateFramebuffer(GL_DRAW_FRAMEBUFFER, 739 static_cast<GLsizei>(mInvalidateList.size()), 740 mInvalidateList.data()); 741 mInvalidateList.clear(); 742 } 743 744 bool hasIndexedBlendAndColorMask = context->getExtensions().drawBuffersIndexedAny(); 745 if (!hasIndexedBlendAndColorMask) 746 { 747 // Restore global blend and color mask. Validation should have ensured these didn't 748 // change while pixel local storage was active. 749 if (mBlendsToReEnable[0]) 750 { 751 context->enable(GL_BLEND); 752 } 753 if (mColorMasksToRestore[0]) 754 { 755 const std::array<bool, 4> &mask = mSavedColorMasks[0]; 756 context->colorMask(mask[0], mask[1], mask[2], mask[3]); 757 } 758 } 759 760 for (GLsizei i = 0; i < numActivePLSPlanes; ++i) 761 { 762 // Reset color attachments where PLS was attached. Validation should have already 763 // ensured nothing was attached at these points when we activated pixel local storage, 764 // and that nothing got attached during. 765 GLuint drawBufferIdx = getDrawBufferIdx(caps, i); 766 GLenum colorAttachment = GL_COLOR_ATTACHMENT0 + drawBufferIdx; 767 context->framebufferTexture2D(GL_DRAW_FRAMEBUFFER, colorAttachment, TextureTarget::_2D, 768 TextureID(), 0); 769 770 if (hasIndexedBlendAndColorMask) 771 { 772 // Restore this draw buffer's blend and color mask. Validation should have ensured 773 // these did not change while pixel local storage was active. 774 if (mBlendsToReEnable[drawBufferIdx]) 775 { 776 context->enablei(GL_BLEND, drawBufferIdx); 777 } 778 if (mColorMasksToRestore[drawBufferIdx]) 779 { 780 const std::array<bool, 4> &mask = mSavedColorMasks[drawBufferIdx]; 781 context->colorMaski(drawBufferIdx, mask[0], mask[1], mask[2], mask[3]); 782 } 783 } 784 } 785 786 // Restore the draw buffer state from before PLS was enabled. 787 context->drawBuffers(static_cast<GLsizei>(mSavedDrawBuffers.size()), 788 mSavedDrawBuffers.data()); 789 mSavedDrawBuffers.clear(); 790 } 791 792 void onBarrier(Context *context) override { context->framebufferFetchBarrier(); } 793 794 private: 795 GLuint getDrawBufferIdx(const Caps &caps, GLuint plsPlaneIdx) 796 { 797 // Bind the PLS attachments in reverse order from the rear. This way, the shader translator 798 // doesn't need to know how many planes are going to be active in order to figure out plane 799 // indices. 800 return caps.maxCombinedDrawBuffersAndPixelLocalStoragePlanes - plsPlaneIdx - 1; 801 } 802 803 DrawBuffersVector<GLenum> mSavedDrawBuffers; 804 DrawBufferMask mBlendsToReEnable; 805 DrawBufferMask mColorMasksToRestore; 806 DrawBuffersArray<std::array<bool, 4>> mSavedColorMasks; 807 DrawBuffersVector<GLenum> mInvalidateList; 808 }; 809 } // namespace 810 811 std::unique_ptr<PixelLocalStorage> PixelLocalStorage::Make(const Context *context) 812 { 813 switch (context->getImplementation()->getNativePixelLocalStorageType()) 814 { 815 case ShPixelLocalStorageType::ImageStoreR32PackedFormats: 816 return std::make_unique<PixelLocalStorageImageLoadStore>(true); 817 case ShPixelLocalStorageType::ImageStoreNativeFormats: 818 return std::make_unique<PixelLocalStorageImageLoadStore>(false); 819 case ShPixelLocalStorageType::FramebufferFetch: 820 return std::make_unique<PixelLocalStorageFramebufferFetch>(); 821 default: 822 UNREACHABLE(); 823 return nullptr; 824 } 825 } 826 } // namespace gl