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 "common/FixedVector.h"
15 #include "libANGLE/Context.h"
16 #include "libANGLE/Framebuffer.h"
17 #include "libANGLE/context_private_call.inl.h"
18 #include "libANGLE/renderer/ContextImpl.h"
19 #include "libANGLE/renderer/TextureImpl.h"
20
21 namespace gl
22 {
23 // RAII utilities for working with GL state.
24 namespace
25 {
26 class ScopedBindTexture2D : angle::NonCopyable
27 {
28 public:
ScopedBindTexture2D(Context * context,TextureID texture)29 ScopedBindTexture2D(Context *context, TextureID texture)
30 : mContext(context),
31 mSavedTexBinding2D(
32 mContext->getState().getSamplerTextureId(mContext->getState().getActiveSampler(),
33 TextureType::_2D))
34 {
35 mContext->bindTexture(TextureType::_2D, texture);
36 }
37
~ScopedBindTexture2D()38 ~ScopedBindTexture2D() { mContext->bindTexture(TextureType::_2D, mSavedTexBinding2D); }
39
40 private:
41 Context *const mContext;
42 TextureID mSavedTexBinding2D;
43 };
44
45 class ScopedRestoreDrawFramebuffer : angle::NonCopyable
46 {
47 public:
ScopedRestoreDrawFramebuffer(Context * context)48 ScopedRestoreDrawFramebuffer(Context *context)
49 : mContext(context), mSavedFramebuffer(mContext->getState().getDrawFramebuffer())
50 {
51 ASSERT(mSavedFramebuffer);
52 }
53
~ScopedRestoreDrawFramebuffer()54 ~ScopedRestoreDrawFramebuffer() { mContext->bindDrawFramebuffer(mSavedFramebuffer->id()); }
55
56 private:
57 Context *const mContext;
58 Framebuffer *const mSavedFramebuffer;
59 };
60
61 class ScopedDisableScissor : angle::NonCopyable
62 {
63 public:
ScopedDisableScissor(Context * context)64 ScopedDisableScissor(Context *context)
65 : mContext(context), mScissorTestEnabled(mContext->getState().isScissorTestEnabled())
66 {
67 if (mScissorTestEnabled)
68 {
69 ContextPrivateDisable(mContext->getMutablePrivateState(),
70 mContext->getMutablePrivateStateCache(), GL_SCISSOR_TEST);
71 }
72 }
73
~ScopedDisableScissor()74 ~ScopedDisableScissor()
75 {
76 if (mScissorTestEnabled)
77 {
78 ContextPrivateEnable(mContext->getMutablePrivateState(),
79 mContext->getMutablePrivateStateCache(), GL_SCISSOR_TEST);
80 }
81 }
82
83 private:
84 Context *const mContext;
85 const bool mScissorTestEnabled;
86 };
87
88 class ScopedDisableRasterizerDiscard : angle::NonCopyable
89 {
90 public:
ScopedDisableRasterizerDiscard(Context * context)91 ScopedDisableRasterizerDiscard(Context *context)
92 : mContext(context),
93 mRasterizerDiscardEnabled(mContext->getState().isRasterizerDiscardEnabled())
94 {
95 if (mRasterizerDiscardEnabled)
96 {
97 ContextPrivateDisable(mContext->getMutablePrivateState(),
98 mContext->getMutablePrivateStateCache(), GL_RASTERIZER_DISCARD);
99 }
100 }
101
~ScopedDisableRasterizerDiscard()102 ~ScopedDisableRasterizerDiscard()
103 {
104 if (mRasterizerDiscardEnabled)
105 {
106 ContextPrivateEnable(mContext->getMutablePrivateState(),
107 mContext->getMutablePrivateStateCache(), GL_RASTERIZER_DISCARD);
108 }
109 }
110
111 private:
112 Context *const mContext;
113 const bool mRasterizerDiscardEnabled;
114 };
115
116 class ScopedEnableColorMask : angle::NonCopyable
117 {
118 public:
ScopedEnableColorMask(Context * context,int firstDrawBuffer,int numDrawBuffers)119 ScopedEnableColorMask(Context *context, int firstDrawBuffer, int numDrawBuffers)
120 : mContext(context), mFirstDrawBuffer(firstDrawBuffer), mNumDrawBuffers(numDrawBuffers)
121 {
122 const State &state = mContext->getState();
123 mSavedColorMasks = state.getBlendStateExt().getColorMaskBits();
124 if (!mContext->getExtensions().drawBuffersIndexedAny())
125 {
126 const uint8_t colorMask =
127 BlendStateExt::ColorMaskStorage::GetValueIndexed(0, mSavedColorMasks);
128 if (colorMask != BlendStateExt::kColorMaskRGBA)
129 {
130 ContextPrivateColorMask(mContext->getMutablePrivateState(),
131 mContext->getMutablePrivateStateCache(), GL_TRUE, GL_TRUE,
132 GL_TRUE, GL_TRUE);
133 }
134 }
135 else
136 {
137 const int endDrawBuffer = mFirstDrawBuffer + mNumDrawBuffers;
138 for (int i = mFirstDrawBuffer; i < endDrawBuffer; ++i)
139 {
140 const uint8_t colorMask =
141 BlendStateExt::ColorMaskStorage::GetValueIndexed(i, mSavedColorMasks);
142 if (colorMask != BlendStateExt::kColorMaskRGBA)
143 {
144 ContextPrivateColorMaski(mContext->getMutablePrivateState(),
145 mContext->getMutablePrivateStateCache(), i, GL_TRUE,
146 GL_TRUE, GL_TRUE, GL_TRUE);
147 }
148 }
149 }
150 }
151
~ScopedEnableColorMask()152 ~ScopedEnableColorMask()
153 {
154 bool r, g, b, a;
155 if (!mContext->getExtensions().drawBuffersIndexedAny())
156 {
157 const uint8_t colorMask =
158 BlendStateExt::ColorMaskStorage::GetValueIndexed(0, mSavedColorMasks);
159 if (colorMask != BlendStateExt::kColorMaskRGBA)
160 {
161 BlendStateExt::UnpackColorMask(colorMask, &r, &g, &b, &a);
162 ContextPrivateColorMask(mContext->getMutablePrivateState(),
163 mContext->getMutablePrivateStateCache(), r, g, b, a);
164 }
165 }
166 else
167 {
168 const int endDrawBuffer = mFirstDrawBuffer + mNumDrawBuffers;
169 for (int i = mFirstDrawBuffer; i < endDrawBuffer; ++i)
170 {
171 const uint8_t colorMask =
172 BlendStateExt::ColorMaskStorage::GetValueIndexed(i, mSavedColorMasks);
173 if (colorMask != BlendStateExt::kColorMaskRGBA)
174 {
175 BlendStateExt::UnpackColorMask(colorMask, &r, &g, &b, &a);
176 ContextPrivateColorMaski(mContext->getMutablePrivateState(),
177 mContext->getMutablePrivateStateCache(), i, r, g, b,
178 a);
179 }
180 }
181 }
182 }
183
184 private:
185 Context *const mContext;
186 const int mFirstDrawBuffer;
187 const int mNumDrawBuffers;
188 BlendStateExt::ColorMaskStorage::Type mSavedColorMasks;
189 };
190 } // namespace
191
PixelLocalStoragePlane()192 PixelLocalStoragePlane::PixelLocalStoragePlane() : mTextureObserver(this, 0) {}
193
~PixelLocalStoragePlane()194 PixelLocalStoragePlane::~PixelLocalStoragePlane()
195 {
196 // Call deinitialize or onContextObjectsLost first!
197 // (PixelLocalStorage::deleteContextObjects calls deinitialize.)
198 ASSERT(isDeinitialized());
199 // We can always expect to receive angle::SubjectMessage::TextureIDDeleted, even if our texture
200 // isn't deleted until context teardown. For this reason, we don't need to hold a ref on the
201 // underlying texture that is the subject of mTextureObserver.
202 ASSERT(mTextureObserver.getSubject() == nullptr);
203 }
204
onContextObjectsLost()205 void PixelLocalStoragePlane::onContextObjectsLost()
206 {
207 // We normally call deleteTexture on the memoryless plane texture ID, since we own it, but in
208 // this case we can let it go.
209 mTextureID = TextureID();
210 deinitialize(nullptr);
211 }
212
deinitialize(Context * context)213 void PixelLocalStoragePlane::deinitialize(Context *context)
214 {
215 if (mMemoryless && mTextureID.value != 0)
216 {
217 ASSERT(context);
218 context->deleteTexture(mTextureID); // Will deinitialize the texture via observers.
219 }
220 else
221 {
222 mInternalformat = GL_NONE;
223 mMemoryless = false;
224 mTextureID = TextureID();
225 mTextureObserver.reset();
226 }
227 ASSERT(isDeinitialized());
228 }
229
setMemoryless(Context * context,GLenum internalformat)230 void PixelLocalStoragePlane::setMemoryless(Context *context, GLenum internalformat)
231 {
232 deinitialize(context);
233 mInternalformat = internalformat;
234 mMemoryless = true;
235 // The backing texture will get allocated lazily, once we know what dimensions it should be.
236 ASSERT(mTextureID.value == 0);
237 mTextureImageIndex = ImageIndex::MakeFromType(TextureType::_2D, 0, 0);
238 }
239
setTextureBacked(Context * context,Texture * tex,int level,int layer)240 void PixelLocalStoragePlane::setTextureBacked(Context *context, Texture *tex, int level, int layer)
241 {
242 deinitialize(context);
243 ASSERT(tex->getImmutableFormat());
244 mInternalformat = tex->getState().getBaseLevelDesc().format.info->internalFormat;
245 mMemoryless = false;
246 mTextureID = tex->id();
247 mTextureObserver.bind(tex);
248 mTextureImageIndex = ImageIndex::MakeFromType(tex->getType(), level, layer);
249 }
250
onSubjectStateChange(angle::SubjectIndex index,angle::SubjectMessage message)251 void PixelLocalStoragePlane::onSubjectStateChange(angle::SubjectIndex index,
252 angle::SubjectMessage message)
253 {
254 ASSERT(index == 0);
255 switch (message)
256 {
257 case angle::SubjectMessage::TextureIDDeleted:
258 // When a texture object is deleted, any pixel local storage plane to which it is bound
259 // is automatically deinitialized.
260 ASSERT(mTextureID.value != 0);
261 mTextureID = TextureID();
262 deinitialize(nullptr);
263 break;
264 default:
265 break;
266 }
267 }
268
isDeinitialized() const269 bool PixelLocalStoragePlane::isDeinitialized() const
270 {
271 if (mInternalformat == GL_NONE)
272 {
273 ASSERT(!isMemoryless());
274 ASSERT(mTextureID.value == 0);
275 ASSERT(mTextureObserver.getSubject() == nullptr);
276 return true;
277 }
278 return false;
279 }
280
getIntegeri(GLenum target) const281 GLint PixelLocalStoragePlane::getIntegeri(GLenum target) const
282 {
283 if (!isDeinitialized())
284 {
285 switch (target)
286 {
287 case GL_PIXEL_LOCAL_FORMAT_ANGLE:
288 return mInternalformat;
289 case GL_PIXEL_LOCAL_TEXTURE_NAME_ANGLE:
290 return isMemoryless() ? 0 : mTextureID.value;
291 case GL_PIXEL_LOCAL_TEXTURE_LEVEL_ANGLE:
292 return isMemoryless() ? 0 : mTextureImageIndex.getLevelIndex();
293 case GL_PIXEL_LOCAL_TEXTURE_LAYER_ANGLE:
294 return isMemoryless() ? 0 : mTextureImageIndex.getLayerIndex();
295 }
296 }
297 // Since GL_NONE == 0, PLS queries all return 0 when the plane is deinitialized.
298 static_assert(GL_NONE == 0, "Expecting GL_NONE to be zero.");
299 return 0;
300 }
301
getTextureImageExtents(const Context * context,Extents * extents) const302 bool PixelLocalStoragePlane::getTextureImageExtents(const Context *context, Extents *extents) const
303 {
304 ASSERT(!isDeinitialized());
305 if (isMemoryless())
306 {
307 return false;
308 }
309 Texture *tex = context->getTexture(mTextureID);
310 ASSERT(tex != nullptr);
311 *extents = tex->getExtents(mTextureImageIndex.getTarget(), mTextureImageIndex.getLevelIndex());
312 extents->depth = 0;
313 return true;
314 }
315
ensureBackingTextureIfMemoryless(Context * context,Extents plsExtents)316 void PixelLocalStoragePlane::ensureBackingTextureIfMemoryless(Context *context, Extents plsExtents)
317 {
318 ASSERT(!isDeinitialized());
319 if (!isMemoryless())
320 {
321 ASSERT(mTextureID.value != 0);
322 return;
323 }
324
325 // Internal textures backing memoryless planes are always 2D and not mipmapped.
326 ASSERT(mTextureImageIndex.getType() == TextureType::_2D);
327 ASSERT(mTextureImageIndex.getLevelIndex() == 0);
328 ASSERT(mTextureImageIndex.getLayerIndex() == 0);
329
330 Texture *tex = nullptr;
331 if (mTextureID.value != 0)
332 {
333 tex = context->getTexture(mTextureID);
334 ASSERT(tex != nullptr);
335 }
336
337 // Do we need to allocate a new backing texture?
338 if (tex == nullptr ||
339 static_cast<GLsizei>(tex->getWidth(TextureTarget::_2D, 0)) != plsExtents.width ||
340 static_cast<GLsizei>(tex->getHeight(TextureTarget::_2D, 0)) != plsExtents.height)
341 {
342 // Call setMemoryless() to release our current data, if any.
343 setMemoryless(context, mInternalformat);
344 ASSERT(mTextureID.value == 0);
345
346 // Create a new texture that backs the memoryless plane.
347 mTextureID = context->createTexture();
348 {
349 ScopedBindTexture2D scopedBindTexture2D(context, mTextureID);
350 context->bindTexture(TextureType::_2D, mTextureID);
351 context->texStorage2D(TextureType::_2D, 1, mInternalformat, plsExtents.width,
352 plsExtents.height);
353 }
354
355 tex = context->getTexture(mTextureID);
356 ASSERT(tex != nullptr);
357 ASSERT(tex->id() == mTextureID);
358 mTextureObserver.bind(tex);
359 }
360 }
361
attachToDrawFramebuffer(Context * context,GLenum colorAttachment) const362 void PixelLocalStoragePlane::attachToDrawFramebuffer(Context *context, GLenum colorAttachment) const
363 {
364 ASSERT(!isDeinitialized());
365 // Call ensureBackingTextureIfMemoryless() first!
366 ASSERT(mTextureID.value != 0 && context->getTexture(mTextureID) != nullptr);
367 if (mTextureImageIndex.usesTex3D()) // GL_TEXTURE_3D or GL_TEXTURE_2D_ARRAY.
368 {
369 context->framebufferTextureLayer(GL_DRAW_FRAMEBUFFER, colorAttachment, mTextureID,
370 mTextureImageIndex.getLevelIndex(),
371 mTextureImageIndex.getLayerIndex());
372 }
373 else
374 {
375 context->framebufferTexture2D(GL_DRAW_FRAMEBUFFER, colorAttachment,
376 mTextureImageIndex.getTarget(), mTextureID,
377 mTextureImageIndex.getLevelIndex());
378 }
379 }
380
381 // Clears the draw buffer at 0-based index 'drawBufferIdx' on the current framebuffer.
382 class ClearBufferCommands : public PixelLocalStoragePlane::ClearCommands
383 {
384 public:
ClearBufferCommands(Context * context)385 ClearBufferCommands(Context *context) : mContext(context) {}
386
clearfv(int drawBufferIdx,const GLfloat value[]) const387 void clearfv(int drawBufferIdx, const GLfloat value[]) const override
388 {
389 mContext->clearBufferfv(GL_COLOR, drawBufferIdx, value);
390 }
391
cleariv(int drawBufferIdx,const GLint value[]) const392 void cleariv(int drawBufferIdx, const GLint value[]) const override
393 {
394 mContext->clearBufferiv(GL_COLOR, drawBufferIdx, value);
395 }
396
clearuiv(int drawBufferIdx,const GLuint value[]) const397 void clearuiv(int drawBufferIdx, const GLuint value[]) const override
398 {
399 mContext->clearBufferuiv(GL_COLOR, drawBufferIdx, value);
400 }
401
402 private:
403 Context *const mContext;
404 };
405
406 template <typename T, size_t N>
ClampArray(std::array<T,N> & arr,T lo,T hi)407 void ClampArray(std::array<T, N> &arr, T lo, T hi)
408 {
409 for (T &x : arr)
410 {
411 x = std::clamp(x, lo, hi);
412 }
413 }
414
issueClearCommand(ClearCommands * clearCommands,int target,GLenum loadop) const415 void PixelLocalStoragePlane::issueClearCommand(ClearCommands *clearCommands,
416 int target,
417 GLenum loadop) const
418 {
419 switch (mInternalformat)
420 {
421 case GL_RGBA8:
422 case GL_R32F:
423 {
424 std::array<GLfloat, 4> clearValue = {0, 0, 0, 0};
425 if (loadop == GL_LOAD_OP_CLEAR_ANGLE)
426 {
427 clearValue = mClearValuef;
428 if (mInternalformat == GL_RGBA8)
429 {
430 ClampArray(clearValue, 0.f, 1.f);
431 }
432 }
433 clearCommands->clearfv(target, clearValue.data());
434 break;
435 }
436 case GL_RGBA8I:
437 {
438 std::array<GLint, 4> clearValue = {0, 0, 0, 0};
439 if (loadop == GL_LOAD_OP_CLEAR_ANGLE)
440 {
441 clearValue = mClearValuei;
442 ClampArray(clearValue, -128, 127);
443 }
444 clearCommands->cleariv(target, clearValue.data());
445 break;
446 }
447 case GL_RGBA8UI:
448 case GL_R32UI:
449 {
450 std::array<GLuint, 4> clearValue = {0, 0, 0, 0};
451 if (loadop == GL_LOAD_OP_CLEAR_ANGLE)
452 {
453 clearValue = mClearValueui;
454 if (mInternalformat == GL_RGBA8UI)
455 {
456 ClampArray(clearValue, 0u, 255u);
457 }
458 }
459 clearCommands->clearuiv(target, clearValue.data());
460 break;
461 }
462 default:
463 // Invalid PLS internalformats should not have made it this far.
464 UNREACHABLE();
465 }
466 }
467
bindToImage(Context * context,GLuint unit,bool needsR32Packing) const468 void PixelLocalStoragePlane::bindToImage(Context *context, GLuint unit, bool needsR32Packing) const
469 {
470 ASSERT(!isDeinitialized());
471 // Call ensureBackingTextureIfMemoryless() first!
472 ASSERT(mTextureID.value != 0 && context->getTexture(mTextureID) != nullptr);
473 GLenum imageBindingFormat = mInternalformat;
474 if (needsR32Packing)
475 {
476 // D3D and ES require us to pack all PLS formats into r32f, r32i, or r32ui images.
477 switch (imageBindingFormat)
478 {
479 case GL_RGBA8:
480 case GL_RGBA8UI:
481 imageBindingFormat = GL_R32UI;
482 break;
483 case GL_RGBA8I:
484 imageBindingFormat = GL_R32I;
485 break;
486 }
487 }
488 context->bindImageTexture(unit, mTextureID, mTextureImageIndex.getLevelIndex(), GL_FALSE,
489 mTextureImageIndex.getLayerIndex(), GL_READ_WRITE,
490 imageBindingFormat);
491 }
492
getBackingTexture(const Context * context) const493 const Texture *PixelLocalStoragePlane::getBackingTexture(const Context *context) const
494 {
495 ASSERT(!isDeinitialized());
496 ASSERT(!isMemoryless());
497 const Texture *tex = context->getTexture(mTextureID);
498 ASSERT(tex != nullptr);
499 return tex;
500 }
501
PixelLocalStorage(const ShPixelLocalStorageOptions & plsOptions,const Caps & caps)502 PixelLocalStorage::PixelLocalStorage(const ShPixelLocalStorageOptions &plsOptions, const Caps &caps)
503 : mPLSOptions(plsOptions), mPlanes(caps.maxPixelLocalStoragePlanes)
504 {}
505
~PixelLocalStorage()506 PixelLocalStorage::~PixelLocalStorage() {}
507
508 namespace
509 {
AllPlanesDeinitialized(const angle::FixedVector<PixelLocalStoragePlane,IMPLEMENTATION_MAX_PIXEL_LOCAL_STORAGE_PLANES> & planes,const Context * context)510 bool AllPlanesDeinitialized(
511 const angle::FixedVector<PixelLocalStoragePlane, IMPLEMENTATION_MAX_PIXEL_LOCAL_STORAGE_PLANES>
512 &planes,
513 const Context *context)
514 {
515 for (const PixelLocalStoragePlane &plane : planes)
516 {
517 if (!plane.isDeinitialized())
518 {
519 return false;
520 }
521 }
522 return true;
523 }
524 } // namespace
525
onFramebufferDestroyed(const Context * context)526 void PixelLocalStorage::onFramebufferDestroyed(const Context *context)
527 {
528 if (!context->isReferenced())
529 {
530 // If the Context's refcount is zero, we know it's in a teardown state and we can just let
531 // go of our GL objects -- they get cleaned up as part of context teardown. Otherwise, the
532 // Context should have called deleteContextObjects before reaching this point.
533 onContextObjectsLost();
534 for (PixelLocalStoragePlane &plane : mPlanes)
535 {
536 plane.onContextObjectsLost();
537 }
538 }
539 // Call deleteContextObjects() when a Framebuffer is destroyed outside of context teardown!
540 ASSERT(AllPlanesDeinitialized(mPlanes, context));
541 }
542
deleteContextObjects(Context * context)543 void PixelLocalStorage::deleteContextObjects(Context *context)
544 {
545 onDeleteContextObjects(context);
546 for (PixelLocalStoragePlane &plane : mPlanes)
547 {
548 plane.deinitialize(context);
549 }
550 }
551
begin(Context * context,GLsizei n,const GLenum loadops[])552 void PixelLocalStorage::begin(Context *context, GLsizei n, const GLenum loadops[])
553 {
554 // Find the pixel local storage rendering dimensions.
555 Extents plsExtents;
556 bool hasPLSExtents = false;
557 for (GLsizei i = 0; i < n; ++i)
558 {
559 PixelLocalStoragePlane &plane = mPlanes[i];
560 if (plane.getTextureImageExtents(context, &plsExtents))
561 {
562 hasPLSExtents = true;
563 break;
564 }
565 }
566 if (!hasPLSExtents)
567 {
568 // All PLS planes are memoryless. Use the rendering area of the framebuffer instead.
569 plsExtents =
570 context->getState().getDrawFramebuffer()->getState().getAttachmentExtentsIntersection();
571 ASSERT(plsExtents.depth == 0);
572 }
573 for (GLsizei i = 0; i < n; ++i)
574 {
575 PixelLocalStoragePlane &plane = mPlanes[i];
576 if (mPLSOptions.type == ShPixelLocalStorageType::ImageLoadStore ||
577 mPLSOptions.type == ShPixelLocalStorageType::FramebufferFetch)
578 {
579 plane.ensureBackingTextureIfMemoryless(context, plsExtents);
580 }
581 plane.markActive(true);
582 }
583
584 onBegin(context, n, loadops, plsExtents);
585 }
586
end(Context * context,GLsizei n,const GLenum storeops[])587 void PixelLocalStorage::end(Context *context, GLsizei n, const GLenum storeops[])
588 {
589 onEnd(context, n, storeops);
590
591 for (GLsizei i = 0; i < n; ++i)
592 {
593 mPlanes[i].markActive(false);
594 }
595 }
596
barrier(Context * context)597 void PixelLocalStorage::barrier(Context *context)
598 {
599 ASSERT(!context->getExtensions().shaderPixelLocalStorageCoherentANGLE);
600 onBarrier(context);
601 }
602
interrupt(Context * context)603 void PixelLocalStorage::interrupt(Context *context)
604 {
605 if (mInterruptCount == 0)
606 {
607 mActivePlanesAtInterrupt = context->getState().getPixelLocalStorageActivePlanes();
608 ASSERT(0 <= mActivePlanesAtInterrupt &&
609 mActivePlanesAtInterrupt <= IMPLEMENTATION_MAX_PIXEL_LOCAL_STORAGE_PLANES);
610 if (mActivePlanesAtInterrupt != 0)
611 {
612 context->endPixelLocalStorageImplicit();
613 }
614 }
615 ++mInterruptCount;
616 ASSERT(mInterruptCount > 0);
617 }
618
restore(Context * context)619 void PixelLocalStorage::restore(Context *context)
620 {
621 ASSERT(mInterruptCount > 0);
622 --mInterruptCount;
623 ASSERT(0 <= mActivePlanesAtInterrupt &&
624 mActivePlanesAtInterrupt <= IMPLEMENTATION_MAX_PIXEL_LOCAL_STORAGE_PLANES);
625 if (mInterruptCount == 0 && mActivePlanesAtInterrupt >= 1)
626 {
627 angle::FixedVector<GLenum, IMPLEMENTATION_MAX_PIXEL_LOCAL_STORAGE_PLANES> loadops(
628 mActivePlanesAtInterrupt);
629 for (GLsizei i = 0; i < mActivePlanesAtInterrupt; ++i)
630 {
631 loadops[i] = mPlanes[i].isMemoryless() ? GL_DONT_CARE : GL_LOAD_OP_LOAD_ANGLE;
632 }
633 context->beginPixelLocalStorage(mActivePlanesAtInterrupt, loadops.data());
634 }
635 }
636
637 namespace
638 {
639 // Implements pixel local storage with image load/store shader operations.
640 class PixelLocalStorageImageLoadStore : public PixelLocalStorage
641 {
642 public:
PixelLocalStorageImageLoadStore(const ShPixelLocalStorageOptions & plsOptions,const Caps & caps)643 PixelLocalStorageImageLoadStore(const ShPixelLocalStorageOptions &plsOptions, const Caps &caps)
644 : PixelLocalStorage(plsOptions, caps)
645 {
646 ASSERT(mPLSOptions.type == ShPixelLocalStorageType::ImageLoadStore);
647 }
648
649 // Call deleteContextObjects or onContextObjectsLost first!
~PixelLocalStorageImageLoadStore()650 ~PixelLocalStorageImageLoadStore() override
651 {
652 ASSERT(mScratchFramebufferForClearing.value == 0);
653 }
654
onContextObjectsLost()655 void onContextObjectsLost() override
656 {
657 mScratchFramebufferForClearing = FramebufferID(); // Let go of GL objects.
658 }
659
onDeleteContextObjects(Context * context)660 void onDeleteContextObjects(Context *context) override
661 {
662 if (mScratchFramebufferForClearing.value != 0)
663 {
664 context->deleteFramebuffer(mScratchFramebufferForClearing);
665 mScratchFramebufferForClearing = FramebufferID();
666 }
667 }
668
onBegin(Context * context,GLsizei n,const GLenum loadops[],Extents plsExtents)669 void onBegin(Context *context, GLsizei n, const GLenum loadops[], Extents plsExtents) override
670 {
671 // Save the image bindings so we can restore them during onEnd().
672 const State &state = context->getState();
673 ASSERT(static_cast<size_t>(n) <= state.getImageUnits().size());
674 mSavedImageBindings.clear();
675 mSavedImageBindings.reserve(n);
676 for (GLsizei i = 0; i < n; ++i)
677 {
678 mSavedImageBindings.emplace_back(state.getImageUnit(i));
679 }
680
681 Framebuffer *framebuffer = state.getDrawFramebuffer();
682 if (mPLSOptions.renderPassNeedsAMDRasterOrderGroupsWorkaround)
683 {
684 // anglebug.com/42266263 -- Metal [[raster_order_group()]] does not work for read_write
685 // textures on AMD when the render pass doesn't have a color attachment on slot 0. To
686 // work around this we attach one of the PLS textures to GL_COLOR_ATTACHMENT0, if there
687 // isn't one already.
688 // It's important to keep the attachment enabled so that it's set in the corresponding
689 // MTLRenderPassAttachmentDescriptor. As the fragment shader does not have any output
690 // bound to this attachment, set the color write mask to all-disabled.
691 // Note that the PLS extension disallows simultaneously binding a single texture image
692 // to a PLS plane and attaching it to the draw framebuffer. Enabling this workaround on
693 // any other platform would yield incorrect results.
694 // This flag is set to true iff the framebuffer has an attachment 0 and it is enabled.
695 mHadColorAttachment0 = framebuffer->getColorAttachment(0) != nullptr;
696 if (!mHadColorAttachment0)
697 {
698 // Indexed color masks are always available on Metal.
699 ASSERT(context->getExtensions().drawBuffersIndexedAny());
700 // Remember the current draw buffer 0 color mask and set it to all-disabled.
701 state.getBlendStateExt().getColorMaskIndexed(
702 0, &mSavedColorMask[0], &mSavedColorMask[1], &mSavedColorMask[2],
703 &mSavedColorMask[3]);
704 ContextPrivateColorMaski(context->getMutablePrivateState(),
705 context->getMutablePrivateStateCache(), 0, false, false,
706 false, false);
707
708 // Remember the current draw buffer state so we can restore it during onEnd().
709 const DrawBuffersVector<GLenum> &appDrawBuffers =
710 framebuffer->getDrawBufferStates();
711 mSavedDrawBuffers.resize(appDrawBuffers.size());
712 std::copy(appDrawBuffers.begin(), appDrawBuffers.end(), mSavedDrawBuffers.begin());
713
714 // Turn on draw buffer 0.
715 if (mSavedDrawBuffers[0] != GL_COLOR_ATTACHMENT0)
716 {
717 GLenum drawBuffer0 = mSavedDrawBuffers[0];
718 mSavedDrawBuffers[0] = GL_COLOR_ATTACHMENT0;
719 context->drawBuffers(static_cast<GLsizei>(mSavedDrawBuffers.size()),
720 mSavedDrawBuffers.data());
721 mSavedDrawBuffers[0] = drawBuffer0;
722 }
723
724 // Attach one of the PLS textures to GL_COLOR_ATTACHMENT0.
725 getPlane(0).attachToDrawFramebuffer(context, GL_COLOR_ATTACHMENT0);
726 }
727 }
728 else
729 {
730 // Save the default framebuffer width/height so we can restore it during onEnd().
731 mSavedFramebufferDefaultWidth = framebuffer->getDefaultWidth();
732 mSavedFramebufferDefaultHeight = framebuffer->getDefaultHeight();
733
734 // Specify the framebuffer width/height explicitly in case we end up rendering
735 // exclusively to shader images.
736 context->framebufferParameteri(GL_DRAW_FRAMEBUFFER, GL_FRAMEBUFFER_DEFAULT_WIDTH,
737 plsExtents.width);
738 context->framebufferParameteri(GL_DRAW_FRAMEBUFFER, GL_FRAMEBUFFER_DEFAULT_HEIGHT,
739 plsExtents.height);
740 }
741
742 // Guard GL state and bind a scratch framebuffer in case we need to reallocate or clear any
743 // PLS planes.
744 const size_t maxDrawBuffers = context->getCaps().maxDrawBuffers;
745 ScopedRestoreDrawFramebuffer ScopedRestoreDrawFramebuffer(context);
746 if (mScratchFramebufferForClearing.value == 0)
747 {
748 context->genFramebuffers(1, &mScratchFramebufferForClearing);
749 context->bindFramebuffer(GL_DRAW_FRAMEBUFFER, mScratchFramebufferForClearing);
750 // Turn on all draw buffers on the scratch framebuffer for clearing.
751 DrawBuffersVector<GLenum> drawBuffers(maxDrawBuffers);
752 std::iota(drawBuffers.begin(), drawBuffers.end(), GL_COLOR_ATTACHMENT0);
753 context->drawBuffers(static_cast<int>(drawBuffers.size()), drawBuffers.data());
754 }
755 else
756 {
757 context->bindFramebuffer(GL_DRAW_FRAMEBUFFER, mScratchFramebufferForClearing);
758 }
759 ScopedDisableScissor scopedDisableScissor(context);
760 ScopedDisableRasterizerDiscard scopedDisableRasterizerDiscard(context);
761
762 // Bind and clear the PLS planes.
763 size_t maxClearedAttachments = 0;
764 for (GLsizei i = 0; i < n;)
765 {
766 DrawBuffersVector<int> pendingClears;
767 for (; pendingClears.size() < maxDrawBuffers && i < n; ++i)
768 {
769 GLenum loadop = loadops[i];
770 const PixelLocalStoragePlane &plane = getPlane(i);
771 plane.bindToImage(context, i, !mPLSOptions.supportsNativeRGBA8ImageFormats);
772 if (loadop == GL_LOAD_OP_ZERO_ANGLE || loadop == GL_LOAD_OP_CLEAR_ANGLE)
773 {
774 plane.attachToDrawFramebuffer(
775 context, GL_COLOR_ATTACHMENT0 + static_cast<GLenum>(pendingClears.size()));
776 pendingClears.push_back(i); // Defer the clear for later.
777 }
778 }
779 // Clear in batches in order to be more efficient with GL state.
780 ScopedEnableColorMask scopedEnableColorMask(context, 0,
781 static_cast<int>(pendingClears.size()));
782 ClearBufferCommands clearBufferCommands(context);
783 for (size_t drawBufferIdx = 0; drawBufferIdx < pendingClears.size(); ++drawBufferIdx)
784 {
785 int plsIdx = pendingClears[drawBufferIdx];
786 getPlane(plsIdx).issueClearCommand(
787 &clearBufferCommands, static_cast<int>(drawBufferIdx), loadops[plsIdx]);
788 }
789 maxClearedAttachments = std::max(maxClearedAttachments, pendingClears.size());
790 }
791
792 // Detach the cleared PLS textures from the scratch framebuffer.
793 for (size_t i = 0; i < maxClearedAttachments; ++i)
794 {
795 context->framebufferTexture2D(GL_DRAW_FRAMEBUFFER,
796 GL_COLOR_ATTACHMENT0 + static_cast<GLenum>(i),
797 TextureTarget::_2D, TextureID(), 0);
798 }
799
800 // Unlike other barriers, GL_SHADER_IMAGE_ACCESS_BARRIER_BIT also synchronizes all types of
801 // memory accesses that happened before the barrier:
802 //
803 // SHADER_IMAGE_ACCESS_BARRIER_BIT: Memory accesses using shader built-in image load and
804 // store functions issued after the barrier will reflect data written by shaders prior to
805 // the barrier. Additionally, image stores issued after the barrier will not execute until
806 // all memory accesses (e.g., loads, stores, texture fetches, vertex fetches) initiated
807 // prior to the barrier complete.
808 //
809 // So we don't any barriers other than GL_SHADER_IMAGE_ACCESS_BARRIER_BIT during begin().
810 context->memoryBarrier(GL_SHADER_IMAGE_ACCESS_BARRIER_BIT);
811 }
812
onEnd(Context * context,GLsizei n,const GLenum storeops[])813 void onEnd(Context *context, GLsizei n, const GLenum storeops[]) override
814 {
815 // Restore the image bindings. Since glBindImageTexture and any commands that modify
816 // textures are banned while PLS is active, these will all still be alive and valid.
817 ASSERT(mSavedImageBindings.size() == static_cast<size_t>(n));
818 for (GLuint unit = 0; unit < mSavedImageBindings.size(); ++unit)
819 {
820 ImageUnit &binding = mSavedImageBindings[unit];
821 context->bindImageTexture(unit, binding.texture.id(), binding.level, binding.layered,
822 binding.layer, binding.access, binding.format);
823
824 // BindingPointers have to be explicitly cleaned up.
825 binding.texture.set(context, nullptr);
826 }
827 mSavedImageBindings.clear();
828
829 if (mPLSOptions.renderPassNeedsAMDRasterOrderGroupsWorkaround)
830 {
831 if (!mHadColorAttachment0)
832 {
833 // Detach the PLS texture we attached to GL_COLOR_ATTACHMENT0.
834 context->framebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
835 TextureTarget::_2D, TextureID(), 0);
836
837 // Restore the draw buffer state from before PLS was enabled.
838 if (mSavedDrawBuffers[0] != GL_COLOR_ATTACHMENT0)
839 {
840 context->drawBuffers(static_cast<GLsizei>(mSavedDrawBuffers.size()),
841 mSavedDrawBuffers.data());
842 }
843 mSavedDrawBuffers.clear();
844
845 // Restore the draw buffer 0 color mask.
846 ContextPrivateColorMaski(
847 context->getMutablePrivateState(), context->getMutablePrivateStateCache(), 0,
848 mSavedColorMask[0], mSavedColorMask[1], mSavedColorMask[2], mSavedColorMask[3]);
849 }
850 }
851 else
852 {
853 // Restore the default framebuffer width/height.
854 context->framebufferParameteri(GL_DRAW_FRAMEBUFFER, GL_FRAMEBUFFER_DEFAULT_WIDTH,
855 mSavedFramebufferDefaultWidth);
856 context->framebufferParameteri(GL_DRAW_FRAMEBUFFER, GL_FRAMEBUFFER_DEFAULT_HEIGHT,
857 mSavedFramebufferDefaultHeight);
858 }
859
860 // We need ALL_BARRIER_BITS during end() because GL_SHADER_IMAGE_ACCESS_BARRIER_BIT doesn't
861 // synchronize all types of memory accesses that can happen after the barrier.
862 context->memoryBarrier(GL_ALL_BARRIER_BITS);
863 }
864
onBarrier(Context * context)865 void onBarrier(Context *context) override
866 {
867 context->memoryBarrier(GL_SHADER_IMAGE_ACCESS_BARRIER_BIT);
868 }
869
870 private:
871 // D3D and ES require us to pack all PLS formats into r32f, r32i, or r32ui images.
872 FramebufferID mScratchFramebufferForClearing{};
873
874 // Saved values to restore during onEnd().
875 std::vector<ImageUnit> mSavedImageBindings;
876 // If mPLSOptions.plsRenderPassNeedsColorAttachmentWorkaround.
877 bool mHadColorAttachment0;
878 std::array<bool, 4> mSavedColorMask;
879 DrawBuffersVector<GLenum> mSavedDrawBuffers;
880 // If !mPLSOptions.plsRenderPassNeedsColorAttachmentWorkaround.
881 GLint mSavedFramebufferDefaultWidth;
882 GLint mSavedFramebufferDefaultHeight;
883 };
884
885 // Implements pixel local storage via framebuffer fetch.
886 class PixelLocalStorageFramebufferFetch : public PixelLocalStorage
887 {
888 public:
PixelLocalStorageFramebufferFetch(const ShPixelLocalStorageOptions & plsOptions,const Caps & caps)889 PixelLocalStorageFramebufferFetch(const ShPixelLocalStorageOptions &plsOptions,
890 const Caps &caps)
891 : PixelLocalStorage(plsOptions, caps)
892 {
893 ASSERT(mPLSOptions.type == ShPixelLocalStorageType::FramebufferFetch);
894 }
895
onContextObjectsLost()896 void onContextObjectsLost() override {}
897
onDeleteContextObjects(Context *)898 void onDeleteContextObjects(Context *) override {}
899
onBegin(Context * context,GLsizei n,const GLenum loadops[],Extents plsExtents)900 void onBegin(Context *context, GLsizei n, const GLenum loadops[], Extents plsExtents) override
901 {
902 const Caps &caps = context->getCaps();
903 Framebuffer *framebuffer = context->getState().getDrawFramebuffer();
904 const DrawBuffersVector<GLenum> &appDrawBuffers = framebuffer->getDrawBufferStates();
905
906 // Remember the current draw buffer state so we can restore it during onEnd().
907 mSavedDrawBuffers.resize(appDrawBuffers.size());
908 std::copy(appDrawBuffers.begin(), appDrawBuffers.end(), mSavedDrawBuffers.begin());
909
910 // Set up new draw buffers for PLS.
911 int firstPLSDrawBuffer = caps.maxCombinedDrawBuffersAndPixelLocalStoragePlanes - n;
912 int numAppDrawBuffers =
913 std::min(static_cast<int>(appDrawBuffers.size()), firstPLSDrawBuffer);
914 DrawBuffersArray<GLenum> plsDrawBuffers;
915 std::copy(appDrawBuffers.begin(), appDrawBuffers.begin() + numAppDrawBuffers,
916 plsDrawBuffers.begin());
917 std::fill(plsDrawBuffers.begin() + numAppDrawBuffers,
918 plsDrawBuffers.begin() + firstPLSDrawBuffer, GL_NONE);
919
920 bool needsClear = false;
921 for (GLsizei i = 0; i < n; ++i)
922 {
923 GLuint drawBufferIdx = GetDrawBufferIdx(caps, i);
924 GLenum loadop = loadops[i];
925 const PixelLocalStoragePlane &plane = getPlane(i);
926 ASSERT(!plane.isDeinitialized());
927
928 // Attach our PLS texture to the framebuffer. Validation should have already ensured
929 // nothing else was attached at this point.
930 GLenum colorAttachment = GL_COLOR_ATTACHMENT0 + drawBufferIdx;
931 ASSERT(!framebuffer->getAttachment(context, colorAttachment));
932 plane.attachToDrawFramebuffer(context, colorAttachment);
933 plsDrawBuffers[drawBufferIdx] = colorAttachment;
934
935 needsClear = needsClear || (loadop != GL_LOAD_OP_LOAD_ANGLE);
936 }
937
938 // Turn on the PLS draw buffers.
939 context->drawBuffers(caps.maxCombinedDrawBuffersAndPixelLocalStoragePlanes,
940 plsDrawBuffers.data());
941
942 // Clear the non-LOAD_OP_LOAD PLS planes now that their draw buffers are turned on.
943 if (needsClear)
944 {
945 ScopedDisableScissor scopedDisableScissor(context);
946 ScopedDisableRasterizerDiscard scopedDisableRasterizerDiscard(context);
947 ClearBufferCommands clearBufferCommands(context);
948 for (GLsizei i = 0; i < n; ++i)
949 {
950 GLenum loadop = loadops[i];
951 if (loadop != GL_LOAD_OP_LOAD_ANGLE)
952 {
953 GLuint drawBufferIdx = GetDrawBufferIdx(caps, i);
954 ScopedEnableColorMask scopedEnableColorMask(context, drawBufferIdx, 1);
955 getPlane(i).issueClearCommand(&clearBufferCommands, drawBufferIdx, loadop);
956 }
957 }
958 }
959
960 if (!context->getExtensions().shaderPixelLocalStorageCoherentANGLE)
961 {
962 // Insert a barrier if we aren't coherent, since the textures may have been rendered to
963 // previously.
964 barrier(context);
965 }
966 }
967
onEnd(Context * context,GLsizei n,const GLenum storeops[])968 void onEnd(Context *context, GLsizei n, const GLenum storeops[]) override
969 {
970 const Caps &caps = context->getCaps();
971
972 // Invalidate the non-preserved PLS attachments.
973 DrawBuffersVector<GLenum> invalidateList;
974 for (GLsizei i = n - 1; i >= 0; --i)
975 {
976 if (!getPlane(i).isActive())
977 {
978 continue;
979 }
980 if (storeops[i] != GL_STORE_OP_STORE_ANGLE || getPlane(i).isMemoryless())
981 {
982 int drawBufferIdx = GetDrawBufferIdx(caps, i);
983 invalidateList.push_back(GL_COLOR_ATTACHMENT0 + drawBufferIdx);
984 }
985 }
986 if (!invalidateList.empty())
987 {
988 context->invalidateFramebuffer(GL_DRAW_FRAMEBUFFER,
989 static_cast<GLsizei>(invalidateList.size()),
990 invalidateList.data());
991 }
992
993 for (GLsizei i = 0; i < n; ++i)
994 {
995 // Reset color attachments where PLS was attached. Validation should have already
996 // ensured nothing was attached at these points when we activated pixel local storage,
997 // and that nothing got attached during.
998 GLuint drawBufferIdx = GetDrawBufferIdx(caps, i);
999 GLenum colorAttachment = GL_COLOR_ATTACHMENT0 + drawBufferIdx;
1000 context->framebufferTexture2D(GL_DRAW_FRAMEBUFFER, colorAttachment, TextureTarget::_2D,
1001 TextureID(), 0);
1002 }
1003
1004 // Restore the draw buffer state from before PLS was enabled.
1005 context->drawBuffers(static_cast<GLsizei>(mSavedDrawBuffers.size()),
1006 mSavedDrawBuffers.data());
1007 mSavedDrawBuffers.clear();
1008 }
1009
onBarrier(Context * context)1010 void onBarrier(Context *context) override { context->framebufferFetchBarrier(); }
1011
1012 private:
GetDrawBufferIdx(const Caps & caps,GLuint plsPlaneIdx)1013 static GLuint GetDrawBufferIdx(const Caps &caps, GLuint plsPlaneIdx)
1014 {
1015 // Bind the PLS attachments in reverse order from the rear. This way, the shader translator
1016 // doesn't need to know how many planes are going to be active in order to figure out plane
1017 // indices.
1018 return caps.maxCombinedDrawBuffersAndPixelLocalStoragePlanes - plsPlaneIdx - 1;
1019 }
1020
1021 DrawBuffersVector<GLenum> mSavedDrawBuffers;
1022 };
1023
1024 } // namespace
1025
Make(const Context * context)1026 std::unique_ptr<PixelLocalStorage> PixelLocalStorage::Make(const Context *context)
1027 {
1028 const ShPixelLocalStorageOptions &plsOptions =
1029 context->getImplementation()->getNativePixelLocalStorageOptions();
1030 const Caps &caps = context->getState().getCaps();
1031 switch (plsOptions.type)
1032 {
1033 case ShPixelLocalStorageType::ImageLoadStore:
1034 return std::make_unique<PixelLocalStorageImageLoadStore>(plsOptions, caps);
1035 case ShPixelLocalStorageType::FramebufferFetch:
1036 return std::make_unique<PixelLocalStorageFramebufferFetch>(plsOptions, caps);
1037 default:
1038 UNREACHABLE();
1039 return nullptr;
1040 }
1041 }
1042 } // namespace gl
1043