persistent_buffer.h 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184
  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::mapBuffer(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. initializeOpenGLFunctions();
  74. if (m_mappedPtr != nullptr) {
  75. glBindBuffer(GL_ARRAY_BUFFER, m_buffer);
  76. glUnmapBuffer(GL_ARRAY_BUFFER);
  77. glBindBuffer(GL_ARRAY_BUFFER, 0);
  78. m_mappedPtr = nullptr;
  79. }
  80. glDeleteBuffers(1, &m_buffer);
  81. m_buffer = 0;
  82. m_capacity = 0;
  83. m_totalSize = 0;
  84. }
  85. void beginFrame() {
  86. m_currentFrame = (m_currentFrame + 1) % m_buffersInFlight;
  87. m_frameOffset = m_currentFrame * m_capacity * sizeof(T);
  88. m_currentCount = 0;
  89. }
  90. auto write(const T *data, std::size_t count) -> std::size_t {
  91. if (count == 0 || count > m_capacity || m_buffer == 0) {
  92. return 0;
  93. }
  94. if (m_bufferMode == Platform::BufferStorageHelper::Mode::Fallback) {
  95. glBindBuffer(GL_ARRAY_BUFFER, m_buffer);
  96. std::size_t const writeOffset =
  97. m_frameOffset + m_currentCount * sizeof(T);
  98. void *ptr =
  99. glMapBufferRange(GL_ARRAY_BUFFER, writeOffset, count * sizeof(T),
  100. GL_MAP_WRITE_BIT | GL_MAP_INVALIDATE_RANGE_BIT);
  101. if (ptr == nullptr) {
  102. qWarning() << "PersistentRingBuffer: Failed to map buffer for write";
  103. glBindBuffer(GL_ARRAY_BUFFER, 0);
  104. return 0;
  105. }
  106. std::memcpy(ptr, data, count * sizeof(T));
  107. glUnmapBuffer(GL_ARRAY_BUFFER);
  108. glBindBuffer(GL_ARRAY_BUFFER, 0);
  109. std::size_t const elementOffset = m_currentCount;
  110. m_currentCount += count;
  111. return elementOffset;
  112. }
  113. if (m_mappedPtr == nullptr) {
  114. return 0;
  115. }
  116. std::size_t const writeOffset = m_frameOffset + m_currentCount * sizeof(T);
  117. void *dest = static_cast<char *>(m_mappedPtr) + writeOffset;
  118. std::memcpy(dest, data, count * sizeof(T));
  119. std::size_t const elementOffset = m_currentCount;
  120. m_currentCount += count;
  121. return elementOffset;
  122. }
  123. [[nodiscard]] auto buffer() const -> GLuint { return m_buffer; }
  124. [[nodiscard]] auto currentOffset() const -> std::size_t {
  125. return m_frameOffset;
  126. }
  127. [[nodiscard]] auto capacity() const -> std::size_t { return m_capacity; }
  128. [[nodiscard]] auto count() const -> std::size_t { return m_currentCount; }
  129. [[nodiscard]] auto isValid() const -> bool {
  130. return m_buffer != 0 &&
  131. (m_bufferMode == Platform::BufferStorageHelper::Mode::Fallback ||
  132. m_mappedPtr != nullptr);
  133. }
  134. private:
  135. GLuint m_buffer = 0;
  136. void *m_mappedPtr = nullptr;
  137. std::size_t m_capacity = 0;
  138. std::size_t m_totalSize = 0;
  139. std::size_t m_frameOffset = 0;
  140. std::size_t m_currentCount = 0;
  141. int m_buffersInFlight = BufferCapacity::BuffersInFlight;
  142. int m_currentFrame = 0;
  143. Platform::BufferStorageHelper::Mode m_bufferMode =
  144. Platform::BufferStorageHelper::Mode::Persistent;
  145. };
  146. } // namespace Render::GL