tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

PixelLocalStorage.cpp (32448B)


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