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_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 // Disable blend and enable the full color mask on the draw buffers reserved for PLS.
538 const Caps &caps = context->getCaps();
539 GLint firstPLSDrawBuffer = FirstOverriddenDrawBuffer(caps, n);
540 PrivateState *privateState = context->getMutablePrivateState();
541 if (firstPLSDrawBuffer == 0)
542 {
543 privateState->setBlend(false);
544 privateState->setColorMask(true, true, true, true);
545 }
546 else
547 {
548 ASSERT(context->getExtensions().drawBuffersIndexedAny());
549 for (GLint i = firstPLSDrawBuffer; i < caps.maxDrawBuffers; ++i)
550 {
551 privateState->setBlendIndexed(false, i);
552 privateState->setColorMaskIndexed(true, true, true, true, i);
553 }
554 }
555
556 onBegin(context, n, loadops, plsExtents);
557 }
558
end(Context * context,GLsizei n,const GLenum storeops[])559 void PixelLocalStorage::end(Context *context, GLsizei n, const GLenum storeops[])
560 {
561 onEnd(context, n, storeops);
562
563 for (GLsizei i = 0; i < n; ++i)
564 {
565 mPlanes[i].markActive(false);
566 }
567 }
568
barrier(Context * context)569 void PixelLocalStorage::barrier(Context *context)
570 {
571 ASSERT(!context->getExtensions().shaderPixelLocalStorageCoherentANGLE);
572 onBarrier(context);
573 }
574
interrupt(Context * context)575 void PixelLocalStorage::interrupt(Context *context)
576 {
577 if (mInterruptCount == 0)
578 {
579 mActivePlanesAtInterrupt = context->getState().getPixelLocalStorageActivePlanes();
580 ASSERT(0 <= mActivePlanesAtInterrupt &&
581 mActivePlanesAtInterrupt <= IMPLEMENTATION_MAX_PIXEL_LOCAL_STORAGE_PLANES);
582 if (mActivePlanesAtInterrupt != 0)
583 {
584 context->endPixelLocalStorageImplicit();
585 }
586 }
587 ++mInterruptCount;
588 ASSERT(mInterruptCount > 0);
589 }
590
restore(Context * context)591 void PixelLocalStorage::restore(Context *context)
592 {
593 ASSERT(mInterruptCount > 0);
594 --mInterruptCount;
595 ASSERT(0 <= mActivePlanesAtInterrupt &&
596 mActivePlanesAtInterrupt <= IMPLEMENTATION_MAX_PIXEL_LOCAL_STORAGE_PLANES);
597 if (mInterruptCount == 0 && mActivePlanesAtInterrupt >= 1)
598 {
599 angle::FixedVector<GLenum, IMPLEMENTATION_MAX_PIXEL_LOCAL_STORAGE_PLANES> loadops(
600 mActivePlanesAtInterrupt);
601 for (GLsizei i = 0; i < mActivePlanesAtInterrupt; ++i)
602 {
603 loadops[i] = mPlanes[i].isMemoryless() ? GL_DONT_CARE : GL_LOAD_OP_LOAD_ANGLE;
604 }
605 context->beginPixelLocalStorage(mActivePlanesAtInterrupt, loadops.data());
606 }
607 }
608
609 namespace
610 {
611 // Implements pixel local storage with image load/store shader operations.
612 class PixelLocalStorageImageLoadStore : public PixelLocalStorage
613 {
614 public:
PixelLocalStorageImageLoadStore(const ShPixelLocalStorageOptions & plsOptions,const Caps & caps)615 PixelLocalStorageImageLoadStore(const ShPixelLocalStorageOptions &plsOptions, const Caps &caps)
616 : PixelLocalStorage(plsOptions, caps)
617 {
618 ASSERT(mPLSOptions.type == ShPixelLocalStorageType::ImageLoadStore);
619 }
620
621 // Call deleteContextObjects or onContextObjectsLost first!
~PixelLocalStorageImageLoadStore()622 ~PixelLocalStorageImageLoadStore() override
623 {
624 ASSERT(mScratchFramebufferForClearing.value == 0);
625 }
626
onContextObjectsLost()627 void onContextObjectsLost() override
628 {
629 mScratchFramebufferForClearing = FramebufferID(); // Let go of GL objects.
630 }
631
onDeleteContextObjects(Context * context)632 void onDeleteContextObjects(Context *context) override
633 {
634 if (mScratchFramebufferForClearing.value != 0)
635 {
636 context->deleteFramebuffer(mScratchFramebufferForClearing);
637 mScratchFramebufferForClearing = FramebufferID();
638 }
639 }
640
onBegin(Context * context,GLsizei n,const GLenum loadops[],Extents plsExtents)641 void onBegin(Context *context, GLsizei n, const GLenum loadops[], Extents plsExtents) override
642 {
643 // Save the image bindings so we can restore them during onEnd().
644 const State &state = context->getState();
645 ASSERT(static_cast<size_t>(n) <= state.getImageUnits().size());
646 mSavedImageBindings.clear();
647 mSavedImageBindings.reserve(n);
648 for (GLsizei i = 0; i < n; ++i)
649 {
650 mSavedImageBindings.emplace_back(state.getImageUnit(i));
651 }
652
653 Framebuffer *framebuffer = state.getDrawFramebuffer();
654 if (mPLSOptions.renderPassNeedsAMDRasterOrderGroupsWorkaround)
655 {
656 // anglebug.com/42266263 -- Metal [[raster_order_group()]] does not work for read_write
657 // textures on AMD when the render pass doesn't have a color attachment on slot 0. To
658 // work around this we attach one of the PLS textures to GL_COLOR_ATTACHMENT0, if there
659 // isn't one already.
660 // It's important to keep the attachment enabled so that it's set in the corresponding
661 // MTLRenderPassAttachmentDescriptor. As the fragment shader does not have any output
662 // bound to this attachment, set the color write mask to all-disabled.
663 // Note that the PLS extension disallows simultaneously binding a single texture image
664 // to a PLS plane and attaching it to the draw framebuffer. Enabling this workaround on
665 // any other platform would yield incorrect results.
666 // This flag is set to true iff the framebuffer has an attachment 0 and it is enabled.
667 mHadColorAttachment0 = framebuffer->getDrawBufferMask().test(0);
668 if (!mHadColorAttachment0)
669 {
670 // Indexed color masks are always available on Metal.
671 ASSERT(context->getExtensions().drawBuffersIndexedAny());
672 // Remember the current draw buffer 0 color mask and set it to all-disabled.
673 state.getBlendStateExt().getColorMaskIndexed(
674 0, &mSavedColorMask[0], &mSavedColorMask[1], &mSavedColorMask[2],
675 &mSavedColorMask[3]);
676 ContextPrivateColorMaski(context->getMutablePrivateState(),
677 context->getMutablePrivateStateCache(), 0, false, false,
678 false, false);
679
680 // Remember the current draw buffer state so we can restore it during onEnd().
681 const DrawBuffersVector<GLenum> &appDrawBuffers =
682 framebuffer->getDrawBufferStates();
683 mSavedDrawBuffers.resize(appDrawBuffers.size());
684 std::copy(appDrawBuffers.begin(), appDrawBuffers.end(), mSavedDrawBuffers.begin());
685
686 // Turn on draw buffer 0.
687 if (mSavedDrawBuffers[0] != GL_COLOR_ATTACHMENT0)
688 {
689 GLenum drawBuffer0 = mSavedDrawBuffers[0];
690 mSavedDrawBuffers[0] = GL_COLOR_ATTACHMENT0;
691 context->drawBuffers(static_cast<GLsizei>(mSavedDrawBuffers.size()),
692 mSavedDrawBuffers.data());
693 mSavedDrawBuffers[0] = drawBuffer0;
694 }
695
696 // Attach one of the PLS textures to GL_COLOR_ATTACHMENT0.
697 getPlane(0).attachToDrawFramebuffer(context, GL_COLOR_ATTACHMENT0);
698 }
699 }
700 else
701 {
702 // Save the default framebuffer width/height so we can restore it during onEnd().
703 mSavedFramebufferDefaultWidth = framebuffer->getDefaultWidth();
704 mSavedFramebufferDefaultHeight = framebuffer->getDefaultHeight();
705
706 // Specify the framebuffer width/height explicitly in case we end up rendering
707 // exclusively to shader images.
708 context->framebufferParameteri(GL_DRAW_FRAMEBUFFER, GL_FRAMEBUFFER_DEFAULT_WIDTH,
709 plsExtents.width);
710 context->framebufferParameteri(GL_DRAW_FRAMEBUFFER, GL_FRAMEBUFFER_DEFAULT_HEIGHT,
711 plsExtents.height);
712 }
713
714 // Guard GL state and bind a scratch framebuffer in case we need to reallocate or clear any
715 // PLS planes.
716 const size_t maxDrawBuffers = context->getCaps().maxDrawBuffers;
717 ScopedRestoreDrawFramebuffer ScopedRestoreDrawFramebuffer(context);
718 if (mScratchFramebufferForClearing.value == 0)
719 {
720 context->genFramebuffers(1, &mScratchFramebufferForClearing);
721 context->bindFramebuffer(GL_DRAW_FRAMEBUFFER, mScratchFramebufferForClearing);
722 // Turn on all draw buffers on the scratch framebuffer for clearing.
723 DrawBuffersVector<GLenum> drawBuffers(maxDrawBuffers);
724 std::iota(drawBuffers.begin(), drawBuffers.end(), GL_COLOR_ATTACHMENT0);
725 context->drawBuffers(static_cast<int>(drawBuffers.size()), drawBuffers.data());
726 }
727 else
728 {
729 context->bindFramebuffer(GL_DRAW_FRAMEBUFFER, mScratchFramebufferForClearing);
730 }
731 ScopedDisableScissor scopedDisableScissor(context);
732
733 // Bind and clear the PLS planes.
734 size_t maxClearedAttachments = 0;
735 for (GLsizei i = 0; i < n;)
736 {
737 DrawBuffersVector<int> pendingClears;
738 for (; pendingClears.size() < maxDrawBuffers && i < n; ++i)
739 {
740 GLenum loadop = loadops[i];
741 const PixelLocalStoragePlane &plane = getPlane(i);
742 plane.bindToImage(context, i, !mPLSOptions.supportsNativeRGBA8ImageFormats);
743 if (loadop == GL_LOAD_OP_ZERO_ANGLE || loadop == GL_LOAD_OP_CLEAR_ANGLE)
744 {
745 plane.attachToDrawFramebuffer(
746 context, GL_COLOR_ATTACHMENT0 + static_cast<GLenum>(pendingClears.size()));
747 pendingClears.push_back(i); // Defer the clear for later.
748 }
749 }
750 // Clear in batches in order to be more efficient with GL state.
751 ScopedEnableColorMask scopedEnableColorMask(context,
752 static_cast<int>(pendingClears.size()));
753 ClearBufferCommands clearBufferCommands(context);
754 for (size_t drawBufferIdx = 0; drawBufferIdx < pendingClears.size(); ++drawBufferIdx)
755 {
756 int plsIdx = pendingClears[drawBufferIdx];
757 getPlane(plsIdx).issueClearCommand(
758 &clearBufferCommands, static_cast<int>(drawBufferIdx), loadops[plsIdx]);
759 }
760 maxClearedAttachments = std::max(maxClearedAttachments, pendingClears.size());
761 }
762
763 // Detach the cleared PLS textures from the scratch framebuffer.
764 for (size_t i = 0; i < maxClearedAttachments; ++i)
765 {
766 context->framebufferTexture2D(GL_DRAW_FRAMEBUFFER,
767 GL_COLOR_ATTACHMENT0 + static_cast<GLenum>(i),
768 TextureTarget::_2D, TextureID(), 0);
769 }
770
771 // Unlike other barriers, GL_SHADER_IMAGE_ACCESS_BARRIER_BIT also synchronizes all types of
772 // memory accesses that happened before the barrier:
773 //
774 // SHADER_IMAGE_ACCESS_BARRIER_BIT: Memory accesses using shader built-in image load and
775 // store functions issued after the barrier will reflect data written by shaders prior to
776 // the barrier. Additionally, image stores issued after the barrier will not execute until
777 // all memory accesses (e.g., loads, stores, texture fetches, vertex fetches) initiated
778 // prior to the barrier complete.
779 //
780 // So we don't any barriers other than GL_SHADER_IMAGE_ACCESS_BARRIER_BIT during begin().
781 context->memoryBarrier(GL_SHADER_IMAGE_ACCESS_BARRIER_BIT);
782 }
783
onEnd(Context * context,GLsizei n,const GLenum storeops[])784 void onEnd(Context *context, GLsizei n, const GLenum storeops[]) override
785 {
786 // Restore the image bindings. Since glBindImageTexture and any commands that modify
787 // textures are banned while PLS is active, these will all still be alive and valid.
788 ASSERT(mSavedImageBindings.size() == static_cast<size_t>(n));
789 for (GLuint unit = 0; unit < mSavedImageBindings.size(); ++unit)
790 {
791 ImageUnit &binding = mSavedImageBindings[unit];
792 context->bindImageTexture(unit, binding.texture.id(), binding.level, binding.layered,
793 binding.layer, binding.access, binding.format);
794
795 // BindingPointers have to be explicitly cleaned up.
796 binding.texture.set(context, nullptr);
797 }
798 mSavedImageBindings.clear();
799
800 if (mPLSOptions.renderPassNeedsAMDRasterOrderGroupsWorkaround)
801 {
802 if (!mHadColorAttachment0)
803 {
804 // Detach the PLS texture we attached to GL_COLOR_ATTACHMENT0.
805 context->framebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
806 TextureTarget::_2D, TextureID(), 0);
807
808 // Restore the draw buffer state from before PLS was enabled.
809 if (mSavedDrawBuffers[0] != GL_COLOR_ATTACHMENT0)
810 {
811 context->drawBuffers(static_cast<GLsizei>(mSavedDrawBuffers.size()),
812 mSavedDrawBuffers.data());
813 }
814 mSavedDrawBuffers.clear();
815
816 // Restore the draw buffer 0 color mask.
817 ContextPrivateColorMaski(
818 context->getMutablePrivateState(), context->getMutablePrivateStateCache(), 0,
819 mSavedColorMask[0], mSavedColorMask[1], mSavedColorMask[2], mSavedColorMask[3]);
820 }
821 }
822 else
823 {
824 // Restore the default framebuffer width/height.
825 context->framebufferParameteri(GL_DRAW_FRAMEBUFFER, GL_FRAMEBUFFER_DEFAULT_WIDTH,
826 mSavedFramebufferDefaultWidth);
827 context->framebufferParameteri(GL_DRAW_FRAMEBUFFER, GL_FRAMEBUFFER_DEFAULT_HEIGHT,
828 mSavedFramebufferDefaultHeight);
829 }
830
831 // We need ALL_BARRIER_BITS during end() because GL_SHADER_IMAGE_ACCESS_BARRIER_BIT doesn't
832 // synchronize all types of memory accesses that can happen after the barrier.
833 context->memoryBarrier(GL_ALL_BARRIER_BITS);
834 }
835
onBarrier(Context * context)836 void onBarrier(Context *context) override
837 {
838 context->memoryBarrier(GL_SHADER_IMAGE_ACCESS_BARRIER_BIT);
839 }
840
841 private:
842 // D3D and ES require us to pack all PLS formats into r32f, r32i, or r32ui images.
843 FramebufferID mScratchFramebufferForClearing{};
844
845 // Saved values to restore during onEnd().
846 std::vector<ImageUnit> mSavedImageBindings;
847 // If mPLSOptions.plsRenderPassNeedsColorAttachmentWorkaround.
848 bool mHadColorAttachment0;
849 std::array<bool, 4> mSavedColorMask;
850 DrawBuffersVector<GLenum> mSavedDrawBuffers;
851 // If !mPLSOptions.plsRenderPassNeedsColorAttachmentWorkaround.
852 GLint mSavedFramebufferDefaultWidth;
853 GLint mSavedFramebufferDefaultHeight;
854 };
855
856 // Implements pixel local storage via framebuffer fetch.
857 class PixelLocalStorageFramebufferFetch : public PixelLocalStorage
858 {
859 public:
PixelLocalStorageFramebufferFetch(const ShPixelLocalStorageOptions & plsOptions,const Caps & caps)860 PixelLocalStorageFramebufferFetch(const ShPixelLocalStorageOptions &plsOptions,
861 const Caps &caps)
862 : PixelLocalStorage(plsOptions, caps)
863 {
864 ASSERT(mPLSOptions.type == ShPixelLocalStorageType::FramebufferFetch);
865 }
866
onContextObjectsLost()867 void onContextObjectsLost() override {}
868
onDeleteContextObjects(Context *)869 void onDeleteContextObjects(Context *) override {}
870
onBegin(Context * context,GLsizei n,const GLenum loadops[],Extents plsExtents)871 void onBegin(Context *context, GLsizei n, const GLenum loadops[], Extents plsExtents) override
872 {
873 const Caps &caps = context->getCaps();
874 Framebuffer *framebuffer = context->getState().getDrawFramebuffer();
875 const DrawBuffersVector<GLenum> &appDrawBuffers = framebuffer->getDrawBufferStates();
876
877 // Remember the current draw buffer state so we can restore it during onEnd().
878 mSavedDrawBuffers.resize(appDrawBuffers.size());
879 std::copy(appDrawBuffers.begin(), appDrawBuffers.end(), mSavedDrawBuffers.begin());
880
881 // Set up new draw buffers for PLS.
882 int firstPLSDrawBuffer = caps.maxCombinedDrawBuffersAndPixelLocalStoragePlanes - n;
883 int numAppDrawBuffers =
884 std::min(static_cast<int>(appDrawBuffers.size()), firstPLSDrawBuffer);
885 DrawBuffersArray<GLenum> plsDrawBuffers;
886 std::copy(appDrawBuffers.begin(), appDrawBuffers.begin() + numAppDrawBuffers,
887 plsDrawBuffers.begin());
888 std::fill(plsDrawBuffers.begin() + numAppDrawBuffers,
889 plsDrawBuffers.begin() + firstPLSDrawBuffer, GL_NONE);
890
891 bool needsClear = false;
892 for (GLsizei i = 0; i < n; ++i)
893 {
894 GLuint drawBufferIdx = GetDrawBufferIdx(caps, i);
895 GLenum loadop = loadops[i];
896 const PixelLocalStoragePlane &plane = getPlane(i);
897 ASSERT(!plane.isDeinitialized());
898
899 // Attach our PLS texture to the framebuffer. Validation should have already ensured
900 // nothing else was attached at this point.
901 GLenum colorAttachment = GL_COLOR_ATTACHMENT0 + drawBufferIdx;
902 ASSERT(!framebuffer->getAttachment(context, colorAttachment));
903 plane.attachToDrawFramebuffer(context, colorAttachment);
904 plsDrawBuffers[drawBufferIdx] = colorAttachment;
905
906 needsClear = needsClear || (loadop != GL_LOAD_OP_LOAD_ANGLE);
907 }
908
909 // Turn on the PLS draw buffers.
910 context->drawBuffers(caps.maxCombinedDrawBuffersAndPixelLocalStoragePlanes,
911 plsDrawBuffers.data());
912
913 // Clear the non-LOAD_OP_LOAD PLS planes now that their draw buffers are turned on.
914 if (needsClear)
915 {
916 ScopedDisableScissor scopedDisableScissor(context);
917 ClearBufferCommands clearBufferCommands(context);
918 for (GLsizei i = 0; i < n; ++i)
919 {
920 GLenum loadop = loadops[i];
921 if (loadop != GL_LOAD_OP_LOAD_ANGLE)
922 {
923 GLuint drawBufferIdx = GetDrawBufferIdx(caps, i);
924 getPlane(i).issueClearCommand(&clearBufferCommands, drawBufferIdx, loadop);
925 }
926 }
927 }
928
929 if (!context->getExtensions().shaderPixelLocalStorageCoherentANGLE)
930 {
931 // Insert a barrier if we aren't coherent, since the textures may have been rendered to
932 // previously.
933 barrier(context);
934 }
935 }
936
onEnd(Context * context,GLsizei n,const GLenum storeops[])937 void onEnd(Context *context, GLsizei n, const GLenum storeops[]) override
938 {
939 const Caps &caps = context->getCaps();
940
941 // Invalidate the non-preserved PLS attachments.
942 DrawBuffersVector<GLenum> invalidateList;
943 for (GLsizei i = n - 1; i >= 0; --i)
944 {
945 if (!getPlane(i).isActive())
946 {
947 continue;
948 }
949 if (storeops[i] != GL_STORE_OP_STORE_ANGLE || getPlane(i).isMemoryless())
950 {
951 int drawBufferIdx = GetDrawBufferIdx(caps, i);
952 invalidateList.push_back(GL_COLOR_ATTACHMENT0 + drawBufferIdx);
953 }
954 }
955 if (!invalidateList.empty())
956 {
957 context->invalidateFramebuffer(GL_DRAW_FRAMEBUFFER,
958 static_cast<GLsizei>(invalidateList.size()),
959 invalidateList.data());
960 }
961
962 for (GLsizei i = 0; i < n; ++i)
963 {
964 // Reset color attachments where PLS was attached. Validation should have already
965 // ensured nothing was attached at these points when we activated pixel local storage,
966 // and that nothing got attached during.
967 GLuint drawBufferIdx = GetDrawBufferIdx(caps, i);
968 GLenum colorAttachment = GL_COLOR_ATTACHMENT0 + drawBufferIdx;
969 context->framebufferTexture2D(GL_DRAW_FRAMEBUFFER, colorAttachment, TextureTarget::_2D,
970 TextureID(), 0);
971 }
972
973 // Restore the draw buffer state from before PLS was enabled.
974 context->drawBuffers(static_cast<GLsizei>(mSavedDrawBuffers.size()),
975 mSavedDrawBuffers.data());
976 mSavedDrawBuffers.clear();
977 }
978
onBarrier(Context * context)979 void onBarrier(Context *context) override { context->framebufferFetchBarrier(); }
980
981 private:
GetDrawBufferIdx(const Caps & caps,GLuint plsPlaneIdx)982 static GLuint GetDrawBufferIdx(const Caps &caps, GLuint plsPlaneIdx)
983 {
984 // Bind the PLS attachments in reverse order from the rear. This way, the shader translator
985 // doesn't need to know how many planes are going to be active in order to figure out plane
986 // indices.
987 return caps.maxCombinedDrawBuffersAndPixelLocalStoragePlanes - plsPlaneIdx - 1;
988 }
989
990 DrawBuffersVector<GLenum> mSavedDrawBuffers;
991 };
992
993 } // namespace
994
Make(const Context * context)995 std::unique_ptr<PixelLocalStorage> PixelLocalStorage::Make(const Context *context)
996 {
997 const ShPixelLocalStorageOptions &plsOptions =
998 context->getImplementation()->getNativePixelLocalStorageOptions();
999 const Caps &caps = context->getState().getCaps();
1000 switch (plsOptions.type)
1001 {
1002 case ShPixelLocalStorageType::ImageLoadStore:
1003 return std::make_unique<PixelLocalStorageImageLoadStore>(plsOptions, caps);
1004 case ShPixelLocalStorageType::FramebufferFetch:
1005 return std::make_unique<PixelLocalStorageFramebufferFetch>(plsOptions, caps);
1006 default:
1007 UNREACHABLE();
1008 return nullptr;
1009 }
1010 }
1011 } // namespace gl
1012