/ src / video_core / renderer_opengl / gl_stream_buffer.cpp
gl_stream_buffer.cpp
  1  // Copyright 2022 Citra Emulator Project
  2  // Licensed under GPLv2 or any later version
  3  // Refer to the license.txt file included.
  4  
  5  #include "common/alignment.h"
  6  #include "common/assert.h"
  7  #include "common/microprofile.h"
  8  #include "video_core/renderer_opengl/gl_driver.h"
  9  #include "video_core/renderer_opengl/gl_stream_buffer.h"
 10  
 11  MICROPROFILE_DEFINE(OpenGL_StreamBuffer, "OpenGL", "Stream Buffer Orphaning",
 12                      MP_RGB(128, 128, 192));
 13  
 14  namespace OpenGL {
 15  
 16  OGLStreamBuffer::OGLStreamBuffer(Driver& driver, GLenum target, GLsizeiptr size,
 17                                   bool prefer_coherent)
 18      : gl_target(target), buffer_size(size) {
 19      gl_buffer.Create();
 20      glBindBuffer(gl_target, gl_buffer.handle);
 21  
 22      GLsizeiptr allocate_size = size;
 23      if (driver.HasBug(DriverBug::VertexArrayOutOfBound) && target == GL_ARRAY_BUFFER) {
 24          allocate_size *= 2;
 25      }
 26  
 27      if (GLAD_GL_ARB_buffer_storage) {
 28          persistent = true;
 29          coherent = prefer_coherent;
 30          GLbitfield flags =
 31              GL_MAP_WRITE_BIT | GL_MAP_PERSISTENT_BIT | (coherent ? GL_MAP_COHERENT_BIT : 0);
 32          glBufferStorage(gl_target, allocate_size, nullptr, flags);
 33          mapped_ptr = static_cast<u8*>(glMapBufferRange(
 34              gl_target, 0, buffer_size, flags | (coherent ? 0 : GL_MAP_FLUSH_EXPLICIT_BIT)));
 35      } else {
 36          glBufferData(gl_target, allocate_size, nullptr, GL_STREAM_DRAW);
 37      }
 38  }
 39  
 40  OGLStreamBuffer::~OGLStreamBuffer() {
 41      if (persistent) {
 42          glBindBuffer(gl_target, gl_buffer.handle);
 43          glUnmapBuffer(gl_target);
 44      }
 45      gl_buffer.Release();
 46  }
 47  
 48  GLuint OGLStreamBuffer::GetHandle() const {
 49      return gl_buffer.handle;
 50  }
 51  
 52  GLsizeiptr OGLStreamBuffer::GetSize() const {
 53      return buffer_size;
 54  }
 55  
 56  std::tuple<u8*, GLintptr, bool> OGLStreamBuffer::Map(GLsizeiptr size, GLintptr alignment) {
 57      ASSERT_MSG(size <= buffer_size, "Requested size {} exceeds buffer size {}", size, buffer_size);
 58      ASSERT(alignment <= buffer_size);
 59      mapped_size = size;
 60  
 61      if (alignment > 0) {
 62          buffer_pos = Common::AlignUp<std::size_t>(buffer_pos, alignment);
 63      }
 64  
 65      bool invalidate = false;
 66      if (buffer_pos + size > buffer_size) {
 67          buffer_pos = 0;
 68          invalidate = true;
 69  
 70          if (persistent) {
 71              glUnmapBuffer(gl_target);
 72          }
 73      }
 74  
 75      if (invalidate || !persistent) {
 76          MICROPROFILE_SCOPE(OpenGL_StreamBuffer);
 77          GLbitfield flags = GL_MAP_WRITE_BIT | (persistent ? GL_MAP_PERSISTENT_BIT : 0) |
 78                             (coherent ? GL_MAP_COHERENT_BIT : GL_MAP_FLUSH_EXPLICIT_BIT) |
 79                             (invalidate ? GL_MAP_INVALIDATE_BUFFER_BIT : GL_MAP_UNSYNCHRONIZED_BIT);
 80          mapped_ptr = static_cast<u8*>(
 81              glMapBufferRange(gl_target, buffer_pos, buffer_size - buffer_pos, flags));
 82          mapped_offset = buffer_pos;
 83      }
 84  
 85      return std::make_tuple(mapped_ptr + buffer_pos - mapped_offset, buffer_pos, invalidate);
 86  }
 87  
 88  void OGLStreamBuffer::Unmap(GLsizeiptr size) {
 89      ASSERT(size <= mapped_size);
 90  
 91      if (!coherent && size > 0) {
 92          glFlushMappedBufferRange(gl_target, buffer_pos - mapped_offset, size);
 93      }
 94  
 95      if (!persistent) {
 96          glUnmapBuffer(gl_target);
 97      }
 98  
 99      buffer_pos += size;
100  }
101  
102  } // namespace OpenGL