Merge pull request #10688 from unknownbrackets/gl-buffers

GLES: Support more buffer mapping strategies
This commit is contained in:
Henrik Rydgård 2018-03-05 09:15:41 +01:00 committed by GitHub
commit d45ae9e67a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 75 additions and 19 deletions

View File

@ -59,6 +59,33 @@ GLRenderManager::~GLRenderManager() {
void GLRenderManager::ThreadStart() {
queueRunner_.CreateDeviceObjects();
threadFrame_ = threadInitFrame_;
bool mapBuffers = (gl_extensions.bugs & BUG_ANY_MAP_BUFFER_RANGE_SLOW) == 0;
bool hasBufferStorage = gl_extensions.ARB_buffer_storage || gl_extensions.EXT_buffer_storage;
if (!gl_extensions.VersionGEThan(3, 0, 0) && gl_extensions.IsGLES && !hasBufferStorage) {
// Force disable if it wouldn't work anyway.
mapBuffers = false;
}
// Notes on buffer mapping:
// NVIDIA GTX 9xx / 2017-10 drivers - mapping improves speed, basic unmap seems best.
// PowerVR GX6xxx / iOS 10.3 - mapping has little improvement, explicit flush is slower.
if (mapBuffers) {
switch (gl_extensions.gpuVendor) {
case GPU_VENDOR_NVIDIA:
bufferStrategy_ = GLBufferStrategy::FRAME_UNMAP;
break;
case GPU_VENDOR_QUALCOMM:
bufferStrategy_ = GLBufferStrategy::FLUSH_INVALIDATE_UNMAP;
break;
default:
bufferStrategy_ = GLBufferStrategy::SUBDATA;
}
} else {
bufferStrategy_ = GLBufferStrategy::SUBDATA;
}
}
void GLRenderManager::ThreadEnd() {
@ -436,7 +463,7 @@ void GLRenderManager::Run(int frame) {
initStepsOnThread.clear();
for (auto iter : frameData.activePushBuffers) {
iter->MapDevice();
iter->MapDevice(bufferStrategy_);
}
switch (frameData.type) {
@ -543,6 +570,7 @@ void GLPushBuffer::Map() {
// Force alignment. This is needed for PushAligned() to work as expected.
while ((intptr_t)writePtr_ & 15) {
writePtr_++;
offset_++;
info.flushOffset++;
}
assert(writePtr_);
@ -579,7 +607,7 @@ void GLPushBuffer::Flush() {
}
// For device memory, we flush all buffers here.
if (gl_extensions.VersionGEThan(3, 0, 0)) {
if ((strategy_ & GLBufferStrategy::MASK_FLUSH) != 0) {
for (auto &info : buffers_) {
if (info.flushOffset == 0 || !info.deviceMemory)
continue;
@ -663,17 +691,20 @@ size_t GLPushBuffer::GetTotalSize() const {
return sum;
}
void GLPushBuffer::MapDevice() {
void GLPushBuffer::MapDevice(GLBufferStrategy strategy) {
strategy_ = strategy;
if (strategy_ == GLBufferStrategy::SUBDATA) {
return;
}
bool mapChanged = false;
for (auto &info : buffers_) {
if (!info.buffer->buffer) {
// Can't map - no device buffer associated yet.
if (!info.buffer->buffer || info.deviceMemory) {
// Can't map - no device buffer associated yet or already mapped.
continue;
}
assert(!info.deviceMemory);
// TODO: Can we use GL_WRITE_ONLY?
info.deviceMemory = (uint8_t *)info.buffer->Map(GL_MAP_WRITE_BIT | GL_MAP_FLUSH_EXPLICIT_BIT);
info.deviceMemory = (uint8_t *)info.buffer->Map(strategy_);
mapChanged = mapChanged || info.deviceMemory != nullptr;
if (!info.deviceMemory && !info.localMemory) {
@ -702,27 +733,30 @@ void GLPushBuffer::UnmapDevice() {
}
}
void *GLRBuffer::Map(GLbitfield access) {
void *GLRBuffer::Map(GLBufferStrategy strategy) {
assert(buffer != 0);
// Notes on buffer mapping:
// NVIDIA GTX 9xx / 2017-10 drivers - mapping improves speed, explicit flush zero cost/benefit.
// PowerVR GX6xxx / iOS 10.3 - mapping has little improvement, explicit flush is slower.
// AMD / unknown - mapping causes black screens and flickering?
// Mali / unknown - mapping causes black screens and flickering?
GLbitfield access = GL_MAP_WRITE_BIT;
if ((strategy & GLBufferStrategy::MASK_FLUSH) != 0) {
access |= GL_MAP_FLUSH_EXPLICIT_BIT;
}
if ((strategy & GLBufferStrategy::MASK_INVALIDATE) != 0) {
access |= GL_MAP_INVALIDATE_BUFFER_BIT;
}
void *p = nullptr;
bool allowNativeBuffer = (gl_extensions.bugs & BUG_ANY_MAP_BUFFER_RANGE_SLOW) == 0;
bool allowNativeBuffer = strategy != GLBufferStrategy::SUBDATA;
if (allowNativeBuffer) {
glBindBuffer(target_, buffer);
if (gl_extensions.ARB_buffer_storage || gl_extensions.EXT_buffer_storage) {
#ifndef IOS
if (!hasStorage_) {
GLbitfield storageFlags = access & ~(GL_MAP_INVALIDATE_BUFFER_BIT | GL_MAP_FLUSH_EXPLICIT_BIT);
#ifdef USING_GLES2
glBufferStorageEXT(target_, size_, nullptr, access & (GL_MAP_READ_BIT | GL_MAP_WRITE_BIT));
glBufferStorageEXT(target_, size_, nullptr, storageFlags);
#else
glBufferStorage(target_, size_, nullptr, access & (GL_MAP_READ_BIT | GL_MAP_WRITE_BIT));
glBufferStorage(target_, size_, nullptr, storageFlags);
#endif
hasStorage_ = true;
}

View File

@ -130,6 +130,26 @@ public:
std::unordered_map<std::string, UniformInfo> uniformCache_;
};
enum class GLBufferStrategy {
SUBDATA = 0,
MASK_FLUSH = 0x10,
MASK_INVALIDATE = 0x20,
// Map/unmap the buffer each frame.
FRAME_UNMAP = 1,
// Map/unmap and also invalidate the buffer on map.
INVALIDATE_UNMAP = MASK_INVALIDATE,
// Map/unmap and explicitly flushed changed ranges.
FLUSH_UNMAP = MASK_FLUSH,
// Map/unmap, invalidate on map, and explicit flush.
FLUSH_INVALIDATE_UNMAP = MASK_FLUSH | MASK_INVALIDATE,
};
static inline int operator &(const GLBufferStrategy &lhs, const GLBufferStrategy &rhs) {
return (int)lhs & (int)rhs;
}
class GLRBuffer {
public:
GLRBuffer(GLuint target, size_t size) : target_(target), size_((int)size) {}
@ -139,7 +159,7 @@ public:
}
}
void *Map(GLbitfield access);
void *Map(GLBufferStrategy strategy);
bool Unmap();
bool Mapped() {
@ -745,6 +765,7 @@ private:
int curFrame_ = 0;
std::function<void()> swapFunction_;
GLBufferStrategy bufferStrategy_ = GLBufferStrategy::SUBDATA;
int targetWidth_ = 0;
int targetHeight_ = 0;
@ -849,7 +870,7 @@ public:
void Flush();
protected:
void MapDevice();
void MapDevice(GLBufferStrategy strategy);
void UnmapDevice();
private:
@ -864,6 +885,7 @@ private:
size_t size_ = 0;
uint8_t *writePtr_ = nullptr;
GLuint target_;
GLBufferStrategy strategy_ = GLBufferStrategy::SUBDATA;
friend class GLRenderManager;
};