• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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