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