Commit aa6dd50d by Geoff Lang Committed by Commit Bot

Share scratch buffers between contexts.

The Display now owns scratch buffers and loans them out to contexts while they are current. This allows us to to only allocate one scratch buffer in a single-threaded use case. Tick the scratch buffers every time a new context is made current. Lower the lifetime from 1000 to 64 to ensure that in the worst case, the buffers are cleared after not being used for ~1 second. BUG=chromium:1030835 BUG=angleproject:4363 Change-Id: I83552424e2beac62b9e41152876b04fc84f53692 Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/2031698 Commit-Queue: Jamie Madill <jmadill@chromium.org> Reviewed-by: 's avatarZhenyao Mo <zmo@chromium.org>
parent 4546c5ce
......@@ -76,12 +76,32 @@ MemoryBuffer &MemoryBuffer::operator=(MemoryBuffer &&other)
return *this;
}
namespace
{
static constexpr uint32_t kDefaultScratchBufferLifetime = 1000u;
} // anonymous namespace
// ScratchBuffer implementation.
ScratchBuffer::ScratchBuffer() : ScratchBuffer(kDefaultScratchBufferLifetime) {}
ScratchBuffer::ScratchBuffer(uint32_t lifetime) : mLifetime(lifetime), mResetCounter(lifetime) {}
ScratchBuffer::~ScratchBuffer() {}
ScratchBuffer::ScratchBuffer(ScratchBuffer &&other)
{
*this = std::move(other);
}
ScratchBuffer &ScratchBuffer::operator=(ScratchBuffer &&other)
{
std::swap(mLifetime, other.mLifetime);
std::swap(mResetCounter, other.mResetCounter);
std::swap(mScratchMemory, other.mScratchMemory);
return *this;
}
bool ScratchBuffer::get(size_t requestedSize, MemoryBuffer **memoryBufferOut)
{
return getImpl(requestedSize, memoryBufferOut, Optional<uint8_t>::Invalid());
......@@ -110,9 +130,8 @@ bool ScratchBuffer::getImpl(size_t requestedSize,
tick();
}
if (mResetCounter == 0 || mScratchMemory.size() < requestedSize)
if (mScratchMemory.size() < requestedSize)
{
clear();
if (!mScratchMemory.resize(requestedSize))
{
return false;
......@@ -135,13 +154,20 @@ void ScratchBuffer::tick()
if (mResetCounter > 0)
{
--mResetCounter;
if (mResetCounter == 0)
{
clear();
}
}
}
void ScratchBuffer::clear()
{
mResetCounter = mLifetime;
mScratchMemory.clear();
if (mScratchMemory.size() > 0)
{
mScratchMemory.clear();
}
}
} // namespace angle
......@@ -62,9 +62,13 @@ class ScratchBuffer final : NonCopyable
// If we request a scratch buffer requesting a smaller size this many times, release and
// recreate the scratch buffer. This ensures we don't have a degenerate case where we are stuck
// hogging memory.
ScratchBuffer();
ScratchBuffer(uint32_t lifetime);
~ScratchBuffer();
ScratchBuffer(ScratchBuffer &&other);
ScratchBuffer &operator=(ScratchBuffer &&other);
// Returns true with a memory buffer of the requested size, or false on failure.
bool get(size_t requestedSize, MemoryBuffer **memoryBufferOut);
......@@ -79,7 +83,7 @@ class ScratchBuffer final : NonCopyable
private:
bool getImpl(size_t requestedSize, MemoryBuffer **memoryBufferOut, Optional<uint8_t> initValue);
const uint32_t mLifetime;
uint32_t mLifetime;
uint32_t mResetCounter;
MemoryBuffer mScratchMemory;
};
......
......@@ -43,10 +43,16 @@ struct Optional
}
void reset() { mValid = false; }
T &&release()
{
mValid = false;
return std::move(mValue);
}
static Optional Invalid() { return Optional(); }
bool valid() const { return mValid; }
T &value() { return mValue; }
const T &value() const { return mValue; }
bool operator==(const Optional &other) const
......
......@@ -347,8 +347,6 @@ Context::Context(egl::Display *display,
mVertexArrayObserverBinding(this, kVertexArraySubjectIndex),
mDrawFramebufferObserverBinding(this, kDrawFramebufferSubjectIndex),
mReadFramebufferObserverBinding(this, kReadFramebufferSubjectIndex),
mScratchBuffer(1000u),
mZeroFilledBuffer(1000u),
mThreadPool(nullptr),
mFrameCapture(new angle::FrameCapture),
mOverlay(mImplementation.get())
......@@ -706,7 +704,20 @@ egl::Error Context::unMakeCurrent(const egl::Display *display)
{
ANGLE_TRY(unsetDefaultFramebuffer());
return angle::ResultToEGL(mImplementation->onUnMakeCurrent(this));
ANGLE_TRY(angle::ResultToEGL(mImplementation->onUnMakeCurrent(this)));
// Return the scratch buffers to the display so they can be shared with other contexts while
// this one is not current.
if (mScratchBuffer.valid())
{
mDisplay->returnScratchBuffer(mScratchBuffer.release());
}
if (mZeroFilledBuffer.valid())
{
mDisplay->returnZeroFilledBuffer(mZeroFilledBuffer.release());
}
return egl::NoError();
}
BufferID Context::createBuffer()
......@@ -5757,13 +5768,36 @@ void Context::framebufferParameteri(GLenum target, GLenum pname, GLint param)
bool Context::getScratchBuffer(size_t requstedSizeBytes,
angle::MemoryBuffer **scratchBufferOut) const
{
return mScratchBuffer.get(requstedSizeBytes, scratchBufferOut);
if (!mScratchBuffer.valid())
{
mScratchBuffer = mDisplay->requestScratchBuffer();
}
ASSERT(mScratchBuffer.valid());
return mScratchBuffer.value().get(requstedSizeBytes, scratchBufferOut);
}
angle::ScratchBuffer *Context::getScratchBuffer() const
{
if (!mScratchBuffer.valid())
{
mScratchBuffer = mDisplay->requestScratchBuffer();
}
ASSERT(mScratchBuffer.valid());
return &mScratchBuffer.value();
}
bool Context::getZeroFilledBuffer(size_t requstedSizeBytes,
angle::MemoryBuffer **zeroBufferOut) const
{
return mZeroFilledBuffer.getInitialized(requstedSizeBytes, zeroBufferOut, 0);
if (!mZeroFilledBuffer.valid())
{
mZeroFilledBuffer = mDisplay->requestZeroFilledBuffer();
}
ASSERT(mZeroFilledBuffer.valid());
return mZeroFilledBuffer.value().getInitialized(requstedSizeBytes, zeroBufferOut, 0);
}
void Context::dispatchCompute(GLuint numGroupsX, GLuint numGroupsY, GLuint numGroupsZ)
......
......@@ -480,7 +480,7 @@ class Context final : public egl::LabeledObject, angle::NonCopyable, public angl
angle::MemoryBuffer **scratchBufferOut) const;
ANGLE_NO_DISCARD bool getZeroFilledBuffer(size_t requstedSizeBytes,
angle::MemoryBuffer **zeroBufferOut) const;
angle::ScratchBuffer *getScratchBuffer() const { return &mScratchBuffer; }
angle::ScratchBuffer *getScratchBuffer() const;
angle::Result prepareForCopyImage();
angle::Result prepareForDispatch();
......@@ -731,8 +731,8 @@ class Context final : public egl::LabeledObject, angle::NonCopyable, public angl
std::vector<angle::ObserverBinding> mImageObserverBindings;
// Not really a property of context state. The size and contexts change per-api-call.
mutable angle::ScratchBuffer mScratchBuffer;
mutable angle::ScratchBuffer mZeroFilledBuffer;
mutable Optional<angle::ScratchBuffer> mScratchBuffer;
mutable Optional<angle::ScratchBuffer> mZeroFilledBuffer;
std::shared_ptr<angle::WorkerThreadPool> mThreadPool;
......
......@@ -388,6 +388,8 @@ void ANGLESetDefaultDisplayPlatform(angle::EGLDisplayType display)
platformMethods->logInfo = Display_logInfo;
}
static constexpr uint32_t kScratchBufferLifetime = 64u;
} // anonymous namespace
DisplayState::DisplayState() : label(nullptr), featuresAllDisabled(false) {}
......@@ -1071,6 +1073,21 @@ Error Display::makeCurrent(const Thread *thread,
ANGLE_TRY(context->makeCurrent(this, drawSurface, readSurface));
}
// Tick all the scratch buffers to make sure they get cleaned up eventually if they stop being
// used.
{
std::lock_guard<std::mutex> lock(mScratchBufferMutex);
for (angle::ScratchBuffer &scatchBuffer : mScratchBuffers)
{
scatchBuffer.tick();
}
for (angle::ScratchBuffer &zeroFilledBuffer : mZeroFilledBuffers)
{
zeroFilledBuffer.tick();
}
}
return NoError();
}
......@@ -1663,4 +1680,46 @@ EGLAttrib Display::queryAttrib(const EGLint attribute)
}
return value;
}
angle::ScratchBuffer Display::requestScratchBuffer()
{
return requestScratchBufferImpl(&mScratchBuffers);
}
void Display::returnScratchBuffer(angle::ScratchBuffer scratchBuffer)
{
returnScratchBufferImpl(std::move(scratchBuffer), &mScratchBuffers);
}
angle::ScratchBuffer Display::requestZeroFilledBuffer()
{
return requestScratchBufferImpl(&mZeroFilledBuffers);
}
void Display::returnZeroFilledBuffer(angle::ScratchBuffer zeroFilledBuffer)
{
returnScratchBufferImpl(std::move(zeroFilledBuffer), &mZeroFilledBuffers);
}
angle::ScratchBuffer Display::requestScratchBufferImpl(
std::vector<angle::ScratchBuffer> *bufferVector)
{
std::lock_guard<std::mutex> lock(mScratchBufferMutex);
if (!bufferVector->empty())
{
angle::ScratchBuffer buffer = std::move(bufferVector->back());
bufferVector->pop_back();
return buffer;
}
return angle::ScratchBuffer(kScratchBufferLifetime);
}
void Display::returnScratchBufferImpl(angle::ScratchBuffer scratchBuffer,
std::vector<angle::ScratchBuffer> *bufferVector)
{
std::lock_guard<std::mutex> lock(mScratchBufferMutex);
bufferVector->push_back(std::move(scratchBuffer));
}
} // namespace egl
......@@ -11,6 +11,7 @@
#ifndef LIBANGLE_DISPLAY_H_
#define LIBANGLE_DISPLAY_H_
#include <mutex>
#include <set>
#include <vector>
......@@ -208,6 +209,12 @@ class Display final : public LabeledObject, angle::NonCopyable
EGLAttrib queryAttrib(const EGLint attribute);
angle::ScratchBuffer requestScratchBuffer();
void returnScratchBuffer(angle::ScratchBuffer scratchBuffer);
angle::ScratchBuffer requestZeroFilledBuffer();
void returnZeroFilledBuffer(angle::ScratchBuffer zeroFilledBuffer);
private:
Display(EGLenum platform, EGLNativeDisplayType displayId, Device *eglDevice);
......@@ -223,6 +230,10 @@ class Display final : public LabeledObject, angle::NonCopyable
void initVendorString();
void initializeFrontendFeatures();
angle::ScratchBuffer requestScratchBufferImpl(std::vector<angle::ScratchBuffer> *bufferVector);
void returnScratchBufferImpl(angle::ScratchBuffer scratchBuffer,
std::vector<angle::ScratchBuffer> *bufferVector);
DisplayState mState;
rx::DisplayImpl *mImplementation;
......@@ -265,6 +276,10 @@ class Display final : public LabeledObject, angle::NonCopyable
angle::FrontendFeatures mFrontendFeatures;
angle::FeatureList mFeatures;
std::mutex mScratchBufferMutex;
std::vector<angle::ScratchBuffer> mScratchBuffers;
std::vector<angle::ScratchBuffer> mZeroFilledBuffers;
};
} // namespace egl
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment