// // Copyright 2022 The ANGLE Project Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. //
// PixelLocalStorage.cpp: Defines the renderer-agnostic container classes // gl::PixelLocalStorage and gl::PixelLocalStoragePlane for // ANGLE_shader_pixel_local_storage.
void PixelLocalStoragePlane::onContextObjectsLost()
{ // We normally call deleteTexture on the memoryless plane texture ID, since we own it, but in // this case we can let it go.
mMemorylessTextureID = TextureID();
}
void PixelLocalStoragePlane::deinitialize(Context *context)
{
mInternalformat = GL_NONE;
mMemoryless = false; if (mMemorylessTextureID.value != 0)
{ // The app could have technically deleted mMemorylessTextureID by guessing its value and // calling glDeleteTextures, but it seems unnecessary to worry about that here. (Worst case // we delete one of their textures.) This also isn't a problem in WebGL.
context->deleteTexture(mMemorylessTextureID);
mMemorylessTextureID = TextureID();
} if (mTextureRef != nullptr)
{
mTextureRef->release(context);
mTextureRef = nullptr;
}
}
void PixelLocalStoragePlane::setMemoryless(Context *context, GLenum internalformat)
{
deinitialize(context);
mInternalformat = internalformat;
mMemoryless = true;
mTextureImageIndex = ImageIndex::MakeFromType(TextureType::_2D, 0, 0); // The backing texture will get allocated lazily, once we know what dimensions it should be.
ASSERT(mMemorylessTextureID.value == 0);
ASSERT(mTextureRef == nullptr);
}
bool PixelLocalStoragePlane::isTextureIDDeleted(const Context *context) const
{ // We can tell if the texture has been deleted by looking up mTextureRef's ID on the Context. If // they don't match, it's been deleted.
ASSERT(!isDeinitialized() || mTextureRef == nullptr); return mTextureRef != nullptr && context->getTexture(mTextureRef->id()) != mTextureRef;
}
GLint PixelLocalStoragePlane::getIntegeri(const Context *context, GLenum target, GLuint index) const
{ if (!isDeinitialized())
{ bool memoryless = isMemoryless() || isTextureIDDeleted(context); switch (target)
{ case GL_PIXEL_LOCAL_FORMAT_ANGLE: return mInternalformat; case GL_PIXEL_LOCAL_TEXTURE_NAME_ANGLE: return memoryless ? 0 : mTextureRef->id().value; case GL_PIXEL_LOCAL_TEXTURE_LEVEL_ANGLE: return memoryless ? 0 : mTextureImageIndex.getLevelIndex(); case GL_PIXEL_LOCAL_TEXTURE_LAYER_ANGLE: return memoryless ? 0 : mTextureImageIndex.getLayerIndex();
}
} // Since GL_NONE == 0, PLS queries all return 0 when the plane is deinitialized.
static_assert(GL_NONE == 0, "Expecting GL_NONE to be zero."); return 0;
}
void PixelLocalStoragePlane::ensureBackingIfMemoryless(Context *context, Extents plsExtents)
{
ASSERT(!isDeinitialized());
ASSERT(!isTextureIDDeleted(context)); // Convert to memoryless first in this case. if (!isMemoryless())
{
ASSERT(mTextureRef != nullptr); return;
}
// Do we need to allocate a new backing texture? if (mTextureRef == nullptr || static_cast<GLsizei>(mTextureRef->getWidth(TextureTarget::_2D, 0)) != plsExtents.width || static_cast<GLsizei>(mTextureRef->getHeight(TextureTarget::_2D, 0)) != plsExtents.height)
{ // Call setMemoryless() to release our current data.
setMemoryless(context, mInternalformat);
ASSERT(mTextureRef == nullptr);
ASSERT(mMemorylessTextureID.value == 0);
// Create a new texture that backs the memoryless plane.
context->genTextures(1, &mMemorylessTextureID);
{
ScopedBindTexture2D scopedBindTexture2D(context, mMemorylessTextureID);
context->bindTexture(TextureType::_2D, mMemorylessTextureID);
context->texStorage2D(TextureType::_2D, 1, mInternalformat, plsExtents.width,
plsExtents.height);
}
void PixelLocalStoragePlane::performLoadOperationClear(Context *context,
GLint drawBuffer,
GLenum loadop, constvoid *data)
{ // The GL scissor test must be disabled, since the intention is to clear the entire surface.
ASSERT(!context->getState().isScissorTestEnabled()); switch (mInternalformat)
{ case GL_RGBA8: case GL_R32F:
{
GLfloat clearValue[4]{}; if (loadop == GL_CLEAR_ANGLE)
{
memcpy(clearValue, data, sizeof(clearValue));
}
context->clearBufferfv(GL_COLOR, drawBuffer, clearValue); break;
} case GL_RGBA8I:
{
GLint clearValue[4]{}; if (loadop == GL_CLEAR_ANGLE)
{
memcpy(clearValue, data, sizeof(clearValue));
}
context->clearBufferiv(GL_COLOR, drawBuffer, clearValue); break;
} case GL_RGBA8UI: case GL_R32UI:
{
GLuint clearValue[4]{}; if (loadop == GL_CLEAR_ANGLE)
{
memcpy(clearValue, data, sizeof(clearValue));
}
context->clearBufferuiv(GL_COLOR, drawBuffer, clearValue); break;
} default: // Invalid PLS internalformats should not have made it this far.
UNREACHABLE();
}
}
void PixelLocalStoragePlane::bindToImage(Context *context,
Extents plsExtents,
GLuint unit, bool needsR32Packing)
{
ASSERT(!isDeinitialized());
ensureBackingIfMemoryless(context, plsExtents);
ASSERT(mTextureRef != nullptr);
GLenum imageBindingFormat = mInternalformat; if (needsR32Packing)
{ // D3D and ES require us to pack all PLS formats into r32f, r32i, or r32ui images. switch (imageBindingFormat)
{ case GL_RGBA8: case GL_RGBA8UI:
imageBindingFormat = GL_R32UI; break; case GL_RGBA8I:
imageBindingFormat = GL_R32I; break;
}
} if (mTextureRef->getType() != TextureType::_2D)
{ // TODO(anglebug.com/7279): Texture types other than GL_TEXTURE_2D will take a lot of // consideration to support on all backends. Hold of on fully implementing them until the // other backends are in place.
UNIMPLEMENTED();
}
context->bindImageTexture(unit, mTextureRef->id(), mTextureImageIndex.getLevelIndex(), GL_FALSE,
mTextureImageIndex.getLayerIndex(), GL_READ_WRITE,
imageBindingFormat);
}
void PixelLocalStorage::onFramebufferDestroyed(const Context *context)
{ if (context->getRefCount() == 0)
{ // If the Context's refcount is zero, we know it's in a teardown state and we can just let // go of our GL objects -- they get cleaned up as part of context teardown. Otherwise, the // Context should have called deleteContextObjects before reaching this point.
onContextObjectsLost(); for (PixelLocalStoragePlane &plane : mPlanes)
{
plane.onContextObjectsLost();
}
} for (PixelLocalStoragePlane &plane : mPlanes)
{
plane.onFramebufferDestroyed(context);
}
}
void PixelLocalStorage::begin(Context *context,
GLsizei n, const GLenum loadops[], constvoid *cleardata)
{ // Convert planes whose backing texture has been deleted to memoryless, and find the pixel local // storage rendering dimensions.
Extents plsExtents; bool hasPLSExtents = false; for (int i = 0; i < n; ++i)
{ if (loadops[i] == GL_DISABLE_ANGLE)
{ continue;
}
PixelLocalStoragePlane &plane = mPlanes[i]; if (plane.isTextureIDDeleted(context))
{ // [ANGLE_shader_pixel_local_storage] Section 4.4.2.X "Configuring Pixel Local Storage // on a Framebuffer": When a texture object is deleted, any pixel local storage plane to // which it was bound is automatically converted to a memoryless plane of matching // internalformat.
plane.setMemoryless(context, plane.getInternalformat());
} if (!hasPLSExtents && plane.getTextureImageExtents(context, &plsExtents))
{
hasPLSExtents = true;
}
} if (!hasPLSExtents)
{ // All PLS planes are memoryless. Use the rendering area of the framebuffer instead.
plsExtents =
context->getState().getDrawFramebuffer()->getState().getAttachmentExtentsIntersection();
ASSERT(plsExtents.depth == 0);
}
onBegin(context, n, loadops, reinterpret_cast<constchar *>(cleardata), plsExtents);
mNumActivePLSPlanes = n;
}
void onBegin(Context *context,
GLsizei n, const GLenum loadops[], constchar *cleardata,
Extents plsExtents) override
{ // Save the image bindings so we can restore them during onEnd(). const State &state = context->getState();
ASSERT(static_cast<size_t>(n) <= state.getImageUnits().size());
mSavedImageBindings.clear();
mSavedImageBindings.reserve(n); for (int i = 0; i < n; ++i)
{
mSavedImageBindings.emplace_back(state.getImageUnit(i));
}
// Save the default framebuffer width/height so we can restore it during onEnd().
Framebuffer *framebuffer = state.getDrawFramebuffer();
mSavedFramebufferDefaultWidth = framebuffer->getDefaultWidth();
mSavedFramebufferDefaultHeight = framebuffer->getDefaultHeight();
// Specify the framebuffer width/height explicitly in case we end up rendering exclusively // to shader images.
context->framebufferParameteri(GL_DRAW_FRAMEBUFFER, GL_FRAMEBUFFER_DEFAULT_WIDTH,
plsExtents.width);
context->framebufferParameteri(GL_DRAW_FRAMEBUFFER, GL_FRAMEBUFFER_DEFAULT_HEIGHT,
plsExtents.height);
// Guard GL state and bind a scratch framebuffer in case we need to reallocate or clear any // PLS planes. const size_t maxDrawBuffers = context->getCaps().maxDrawBuffers;
ScopedRestoreDrawFramebuffer ScopedRestoreDrawFramebuffer(context); if (mScratchFramebufferForClearing.value == 0)
{
context->genFramebuffers(1, &mScratchFramebufferForClearing);
context->bindFramebuffer(GL_DRAW_FRAMEBUFFER, mScratchFramebufferForClearing); // Turn on all draw buffers on the scratch framebuffer for clearing.
DrawBuffersVector<GLenum> drawBuffers(maxDrawBuffers);
std::iota(drawBuffers.begin(), drawBuffers.end(), GL_COLOR_ATTACHMENT0);
context->drawBuffers(static_cast<int>(drawBuffers.size()), drawBuffers.data());
} else
{
context->bindFramebuffer(GL_DRAW_FRAMEBUFFER, mScratchFramebufferForClearing);
}
ScopedDisableScissor scopedDisableScissor(context);
// Bind and clear the PLS planes.
size_t maxClearedAttachments = 0; for (int i = 0; i < n;)
{
angle::FixedVector<int, IMPLEMENTATION_MAX_DRAW_BUFFERS> pendingClears; for (; pendingClears.size() < maxDrawBuffers && i < n; ++i)
{
GLenum loadop = loadops[i]; if (loadop == GL_DISABLE_ANGLE)
{ continue;
}
PixelLocalStoragePlane &plane = getPlane(i);
ASSERT(!plane.isDeinitialized());
plane.bindToImage(context, plsExtents, i, mNeedsR32Packing); if (loadop == GL_ZERO || loadop == GL_CLEAR_ANGLE)
{
plane.attachToDrawFramebuffer(
context, plsExtents,
GL_COLOR_ATTACHMENT0 + static_cast<GLenum>(pendingClears.size()));
pendingClears.push_back(i); // Defer the clear for later.
}
} // Clear in batches to be more efficient with GL state. for (size_t drawBufferIdx = 0; drawBufferIdx < pendingClears.size(); ++drawBufferIdx)
{ int plsIdx = pendingClears[drawBufferIdx];
getPlane(plsIdx).performLoadOperationClear(
context, static_cast<GLint>(drawBufferIdx), loadops[plsIdx],
cleardata + plsIdx * 4 * 4);
}
maxClearedAttachments = std::max(maxClearedAttachments, pendingClears.size());
}
// Detach the cleared PLS textures from the scratch framebuffer. for (size_t i = 0; i < maxClearedAttachments; ++i)
{
context->framebufferTexture2D(GL_DRAW_FRAMEBUFFER,
GL_COLOR_ATTACHMENT0 + static_cast<GLenum>(i),
TextureTarget::_2D, TextureID(), 0);
}
// Unlike other barriers, GL_SHADER_IMAGE_ACCESS_BARRIER_BIT also synchronizes all types of // memory accesses that happened before the barrier: // // SHADER_IMAGE_ACCESS_BARRIER_BIT: Memory accesses using shader built-in image load and // store functions issued after the barrier will reflect data written by shaders prior to // the barrier. Additionally, image stores issued after the barrier will not execute until // all memory accesses (e.g., loads, stores, texture fetches, vertex fetches) initiated // prior to the barrier complete. // // So we don't any barriers other than GL_SHADER_IMAGE_ACCESS_BARRIER_BIT during begin().
context->memoryBarrier(GL_SHADER_IMAGE_ACCESS_BARRIER_BIT);
}
void onEnd(Context *context, GLsizei numActivePLSPlanes) override
{ // Restore the image bindings. Since glBindImageTexture and any commands that modify // textures are banned while PLS is active, these will all still be alive and valid.
ASSERT(mSavedImageBindings.size() == static_cast<size_t>(numActivePLSPlanes)); for (GLuint unit = 0; unit < mSavedImageBindings.size(); ++unit)
{
ImageUnit &binding = mSavedImageBindings[unit];
context->bindImageTexture(unit, binding.texture.id(), binding.level, binding.layered,
binding.layer, binding.access, binding.format);
// BindingPointers have to be explicitly cleaned up.
binding.texture.set(context, nullptr);
}
mSavedImageBindings.clear();
// We need ALL_BARRIER_BITS during end() because GL_SHADER_IMAGE_ACCESS_BARRIER_BIT doesn't // synchronize all types of memory accesses that can happen after the barrier.
context->memoryBarrier(GL_ALL_BARRIER_BITS);
}
private: // D3D and ES require us to pack all PLS formats into r32f, r32i, or r32ui images. constbool mNeedsR32Packing;
FramebufferID mScratchFramebufferForClearing{};
// Saved values to restore during onEnd().
GLint mSavedFramebufferDefaultWidth;
GLint mSavedFramebufferDefaultHeight;
std::vector<ImageUnit> mSavedImageBindings;
};
// Implements pixel local storage via framebuffer fetch. class PixelLocalStorageFramebufferFetch : public PixelLocalStorage
{ public: void onContextObjectsLost() override {}
// Remember the current draw buffer state so we can restore it during onEnd().
mSavedDrawBuffers.resize(appDrawBuffers.size());
std::copy(appDrawBuffers.begin(), appDrawBuffers.end(), mSavedDrawBuffers.begin());
// Set up new draw buffers for PLS. int firstPLSDrawBuffer = caps.maxCombinedDrawBuffersAndPixelLocalStoragePlanes - n; int numAppDrawBuffers =
std::min(static_cast<int>(appDrawBuffers.size()), firstPLSDrawBuffer);
DrawBuffersArray<GLenum> plsDrawBuffers;
std::copy(appDrawBuffers.begin(), appDrawBuffers.begin() + numAppDrawBuffers,
plsDrawBuffers.begin());
std::fill(plsDrawBuffers.begin() + numAppDrawBuffers,
plsDrawBuffers.begin() + firstPLSDrawBuffer, GL_NONE);
bool hasIndexedBlendAndColorMask = context->getExtensions().drawBuffersIndexedAny(); if (!hasIndexedBlendAndColorMask)
{ // We don't have indexed blend and color mask control. Disable them globally. (This also // means the app can't have its own draw buffers while PLS is active.)
ASSERT(caps.maxColorAttachmentsWithActivePixelLocalStorage == 0); if (state.isBlendEnabled())
{
context->disable(GL_BLEND);
mBlendsToReEnable.set(0);
}
std::array<bool, 4> &mask = mSavedColorMasks[0];
state.getBlendStateExt().getColorMaskIndexed(0, &mask[0], &mask[1], &mask[2], &mask[3]); if (!(mask[0] && mask[1] && mask[2] && mask[3]))
{
context->colorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
mColorMasksToRestore.set(0);
}
}
for (GLsizei i = 0; i < n; ++i)
{
GLuint drawBufferIdx = getDrawBufferIdx(caps, i);
GLenum loadop = loadops[i]; if (loadop == GL_DISABLE_ANGLE)
{
plsDrawBuffers[drawBufferIdx] = GL_NONE; continue;
}
// Attach our PLS texture to the framebuffer. Validation should have already ensured // nothing else was attached at this point.
GLenum colorAttachment = GL_COLOR_ATTACHMENT0 + drawBufferIdx;
ASSERT(!framebuffer->getAttachment(context, colorAttachment));
plane.attachToDrawFramebuffer(context, plsExtents, colorAttachment);
plsDrawBuffers[drawBufferIdx] = colorAttachment;
if (hasIndexedBlendAndColorMask)
{ // Ensure blend and color mask are disabled for this draw buffer. if (state.isBlendEnabledIndexed(drawBufferIdx))
{
context->disablei(GL_BLEND, drawBufferIdx);
mBlendsToReEnable.set(drawBufferIdx);
}
std::array<bool, 4> &mask = mSavedColorMasks[drawBufferIdx];
state.getBlendStateExt().getColorMaskIndexed(drawBufferIdx, &mask[0], &mask[1],
&mask[2], &mask[3]); if (!(mask[0] && mask[1] && mask[2] && mask[3]))
{
context->colorMaski(drawBufferIdx, GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
mColorMasksToRestore.set(drawBufferIdx);
}
}
if (plane.isMemoryless())
{ // Memoryless planes don't need to be preserved after glEndPixelLocalStorageANGLE().
mInvalidateList.push_back(colorAttachment);
}
needsClear = needsClear || (loadop != GL_KEEP);
}
// Turn on the PLS draw buffers.
context->drawBuffers(caps.maxCombinedDrawBuffersAndPixelLocalStoragePlanes,
plsDrawBuffers.data());
// Clear the non-KEEP PLS planes now that their draw buffers are turned on. if (needsClear)
{
ScopedDisableScissor scopedDisableScissor(context); for (GLsizei i = 0; i < n; ++i)
{
GLenum loadop = loadops[i]; if (loadop != GL_DISABLE_ANGLE && loadop != GL_KEEP)
{
GLuint drawBufferIdx = getDrawBufferIdx(caps, i);
getPlane(i).performLoadOperationClear(context, drawBufferIdx, loadop,
cleardata + i * 4 * 4);
}
}
}
if (!context->getExtensions().shaderPixelLocalStorageCoherentANGLE)
{ // Insert a barrier if we aren't coherent, since the textures may have been rendered to // previously.
barrier(context);
}
}
// Invalidate the memoryless PLS attachments. if (!mInvalidateList.empty())
{
context->invalidateFramebuffer(GL_DRAW_FRAMEBUFFER, static_cast<GLsizei>(mInvalidateList.size()),
mInvalidateList.data());
mInvalidateList.clear();
}
bool hasIndexedBlendAndColorMask = context->getExtensions().drawBuffersIndexedAny(); if (!hasIndexedBlendAndColorMask)
{ // Restore global blend and color mask. Validation should have ensured these didn't // change while pixel local storage was active. if (mBlendsToReEnable[0])
{
context->enable(GL_BLEND);
} if (mColorMasksToRestore[0])
{ const std::array<bool, 4> &mask = mSavedColorMasks[0];
context->colorMask(mask[0], mask[1], mask[2], mask[3]);
}
}
for (GLsizei i = 0; i < numActivePLSPlanes; ++i)
{ // Reset color attachments where PLS was attached. Validation should have already // ensured nothing was attached at these points when we activated pixel local storage, // and that nothing got attached during.
GLuint drawBufferIdx = getDrawBufferIdx(caps, i);
GLenum colorAttachment = GL_COLOR_ATTACHMENT0 + drawBufferIdx;
context->framebufferTexture2D(GL_DRAW_FRAMEBUFFER, colorAttachment, TextureTarget::_2D,
TextureID(), 0);
if (hasIndexedBlendAndColorMask)
{ // Restore this draw buffer's blend and color mask. Validation should have ensured // these did not change while pixel local storage was active. if (mBlendsToReEnable[drawBufferIdx])
{
context->enablei(GL_BLEND, drawBufferIdx);
} if (mColorMasksToRestore[drawBufferIdx])
{ const std::array<bool, 4> &mask = mSavedColorMasks[drawBufferIdx];
context->colorMaski(drawBufferIdx, mask[0], mask[1], mask[2], mask[3]);
}
}
}
// Restore the draw buffer state from before PLS was enabled.
context->drawBuffers(static_cast<GLsizei>(mSavedDrawBuffers.size()),
mSavedDrawBuffers.data());
mSavedDrawBuffers.clear();
}
private:
GLuint getDrawBufferIdx(const Caps &caps, GLuint plsPlaneIdx)
{ // Bind the PLS attachments in reverse order from the rear. This way, the shader translator // doesn't need to know how many planes are going to be active in order to figure out plane // indices. return caps.maxCombinedDrawBuffersAndPixelLocalStoragePlanes - plsPlaneIdx - 1;
}
std::unique_ptr<PixelLocalStorage> PixelLocalStorage::Make(const Context *context)
{ switch (context->getImplementation()->getNativePixelLocalStorageType())
{ case ShPixelLocalStorageType::ImageStoreR32PackedFormats: return std::make_unique<PixelLocalStorageImageLoadStore>(true); case ShPixelLocalStorageType::ImageStoreNativeFormats: return std::make_unique<PixelLocalStorageImageLoadStore>(false); case ShPixelLocalStorageType::FramebufferFetch: return std::make_unique<PixelLocalStorageFramebufferFetch>(); default:
UNREACHABLE(); return nullptr;
}
}
} // namespace gl
Messung V0.5
¤ Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.0.17Bemerkung:
(vorverarbeitet)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.