persistent_buffer.h 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193
  1. #pragma once
  2. #include "platform_gl.h"
  3. #include "render_constants.h"
  4. #include <QDebug>
  5. #include <QOpenGLContext>
  6. #include <QOpenGLExtraFunctions>
  7. #include <cstddef>
  8. #include <cstring>
  9. namespace Render::GL {
  10. template <typename T>
  11. class PersistentRingBuffer : protected QOpenGLExtraFunctions {
  12. public:
  13. PersistentRingBuffer() = default;
  14. ~PersistentRingBuffer() { destroy(); }
  15. PersistentRingBuffer(const PersistentRingBuffer &) = delete;
  16. auto
  17. operator=(const PersistentRingBuffer &) -> PersistentRingBuffer & = delete;
  18. auto
  19. initialize(std::size_t capacity,
  20. int buffersInFlight = BufferCapacity::BuffersInFlight) -> bool {
  21. if (m_buffer != 0) {
  22. return false;
  23. }
  24. initializeOpenGLFunctions();
  25. if (!hasOpenGLFeature(QOpenGLFunctions::Buffers)) {
  26. return false;
  27. }
  28. m_capacity = capacity;
  29. m_buffersInFlight = buffersInFlight;
  30. m_totalSize = capacity * sizeof(T) * buffersInFlight;
  31. m_currentFrame = 0;
  32. m_frameOffset = 0;
  33. glGenBuffers(1, &m_buffer);
  34. glBindBuffer(GL_ARRAY_BUFFER, m_buffer);
  35. QOpenGLContext *ctx = QOpenGLContext::currentContext();
  36. if (ctx == nullptr) {
  37. qWarning() << "PersistentRingBuffer: No current OpenGL context";
  38. glBindBuffer(GL_ARRAY_BUFFER, 0);
  39. glDeleteBuffers(1, &m_buffer);
  40. m_buffer = 0;
  41. return false;
  42. }
  43. Platform::BufferStorageHelper::Mode mode;
  44. if (!Platform::BufferStorageHelper::createBuffer(m_buffer, m_totalSize,
  45. &mode)) {
  46. qWarning() << "PersistentRingBuffer: Failed to create buffer storage";
  47. glBindBuffer(GL_ARRAY_BUFFER, 0);
  48. glDeleteBuffers(1, &m_buffer);
  49. m_buffer = 0;
  50. return false;
  51. }
  52. m_bufferMode = mode;
  53. m_mappedPtr = Platform::BufferStorageHelper::map_buffer(m_totalSize, mode);
  54. if (m_mappedPtr == nullptr) {
  55. qWarning() << "PersistentRingBuffer: Failed to map buffer";
  56. glBindBuffer(GL_ARRAY_BUFFER, 0);
  57. destroy();
  58. return false;
  59. }
  60. if (mode == Platform::BufferStorageHelper::Mode::Fallback) {
  61. qInfo() << "PersistentRingBuffer: Running in fallback mode "
  62. "(non-persistent mapping)";
  63. glUnmapBuffer(GL_ARRAY_BUFFER);
  64. m_mappedPtr = nullptr;
  65. }
  66. glBindBuffer(GL_ARRAY_BUFFER, 0);
  67. return true;
  68. }
  69. void destroy() {
  70. if (m_buffer == 0) {
  71. return;
  72. }
  73. if (QOpenGLContext::currentContext() == nullptr) {
  74. m_buffer = 0;
  75. m_mappedPtr = nullptr;
  76. m_capacity = 0;
  77. m_totalSize = 0;
  78. return;
  79. }
  80. initializeOpenGLFunctions();
  81. if (m_mappedPtr != nullptr) {
  82. glBindBuffer(GL_ARRAY_BUFFER, m_buffer);
  83. glUnmapBuffer(GL_ARRAY_BUFFER);
  84. glBindBuffer(GL_ARRAY_BUFFER, 0);
  85. m_mappedPtr = nullptr;
  86. }
  87. glDeleteBuffers(1, &m_buffer);
  88. m_buffer = 0;
  89. m_capacity = 0;
  90. m_totalSize = 0;
  91. }
  92. void begin_frame() {
  93. m_currentFrame = (m_currentFrame + 1) % m_buffersInFlight;
  94. m_frameOffset = m_currentFrame * m_capacity * sizeof(T);
  95. m_currentCount = 0;
  96. }
  97. auto write(const T *data, std::size_t count) -> std::size_t {
  98. if (count == 0 || count > m_capacity || m_buffer == 0) {
  99. return 0;
  100. }
  101. if (m_bufferMode == Platform::BufferStorageHelper::Mode::Fallback) {
  102. glBindBuffer(GL_ARRAY_BUFFER, m_buffer);
  103. std::size_t const writeOffset =
  104. m_frameOffset + m_currentCount * sizeof(T);
  105. void *ptr =
  106. glMapBufferRange(GL_ARRAY_BUFFER, writeOffset, count * sizeof(T),
  107. GL_MAP_WRITE_BIT | GL_MAP_INVALIDATE_RANGE_BIT);
  108. if (ptr == nullptr) {
  109. qWarning() << "PersistentRingBuffer: Failed to map buffer for write";
  110. glBindBuffer(GL_ARRAY_BUFFER, 0);
  111. return 0;
  112. }
  113. std::memcpy(ptr, data, count * sizeof(T));
  114. glUnmapBuffer(GL_ARRAY_BUFFER);
  115. glBindBuffer(GL_ARRAY_BUFFER, 0);
  116. std::size_t const elementOffset = m_currentCount;
  117. m_currentCount += count;
  118. return elementOffset;
  119. }
  120. if (m_mappedPtr == nullptr) {
  121. return 0;
  122. }
  123. std::size_t const writeOffset = m_frameOffset + m_currentCount * sizeof(T);
  124. void *dest = static_cast<char *>(m_mappedPtr) + writeOffset;
  125. std::memcpy(dest, data, count * sizeof(T));
  126. std::size_t const elementOffset = m_currentCount;
  127. m_currentCount += count;
  128. return elementOffset;
  129. }
  130. [[nodiscard]] auto buffer() const -> GLuint { return m_buffer; }
  131. [[nodiscard]] auto current_offset() const -> std::size_t {
  132. return m_frameOffset;
  133. }
  134. [[nodiscard]] auto capacity() const -> std::size_t { return m_capacity; }
  135. [[nodiscard]] auto count() const -> std::size_t { return m_currentCount; }
  136. [[nodiscard]] auto is_valid() const -> bool {
  137. return m_buffer != 0 &&
  138. (m_bufferMode == Platform::BufferStorageHelper::Mode::Fallback ||
  139. m_mappedPtr != nullptr);
  140. }
  141. private:
  142. GLuint m_buffer = 0;
  143. void *m_mappedPtr = nullptr;
  144. std::size_t m_capacity = 0;
  145. std::size_t m_totalSize = 0;
  146. std::size_t m_frameOffset = 0;
  147. std::size_t m_currentCount = 0;
  148. int m_buffersInFlight = BufferCapacity::BuffersInFlight;
  149. int m_currentFrame = 0;
  150. Platform::BufferStorageHelper::Mode m_bufferMode =
  151. Platform::BufferStorageHelper::Mode::Persistent;
  152. };
  153. } // namespace Render::GL