Overte C++ Documentation
Buffer.h
1 //
2 // Created by Sam Gateau on 10/8/2014.
3 // Split from Resource.h/Resource.cpp by Bradley Austin Davis on 2016/08/07
4 // Copyright 2014 High Fidelity, Inc.
5 //
6 // Distributed under the Apache License, Version 2.0.
7 // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
8 //
9 #ifndef hifi_gpu_Buffer_h
10 #define hifi_gpu_Buffer_h
11 
12 #include <atomic>
13 
14 #if _DEBUG
15 #include <QtCore/QDebug>
16 #include "GPULogging.h"
17 #endif
18 
19 #include "Forward.h"
20 #include "Format.h"
21 #include "Resource.h"
22 #include "Sysmem.h"
23 #include "PageManager.h"
24 #include "Metric.h"
25 
26 namespace gpu {
27 
31 class Buffer : public Resource {
32 
33  // Incremented and decremented in buffer constructors.
34  static ContextMetricCount _bufferCPUCount;
35 
36  // Updated when buffer is resized or destroyed.
37  static ContextMetricSize _bufferCPUMemSize;
38 
39 public:
40  using Flag = PageManager::Flag;
41 
42  // VKTODO: Make this independent of VkBufferUsageFlagBits.
43  // Flags match VkBufferUsageFlagBits for convenience... do not modify
44  enum Usage
45  {
46  // These values are unused in our API
47  //TransferSrc = 0x0001,
48  //TransferDst = 0x0002,
49  //UniformTexelBuffer = 0x0004,
50  //StorageTexelBuffer = 0x0008,
51  UniformBuffer = 0x0010,
52  ResourceBuffer = 0x0020,
53  IndexBuffer = 0x0040,
54  VertexBuffer = 0x0080,
55  IndirectBuffer = 0x0100,
56  AllFlags = 0x01F3
57  };
58 
65  class Update {
66  public:
73  Update(const Buffer& buffer);
74 
79  Update(const Update& other);
80 
85  Update(Update&& other);
86 
94  void apply() const;
95 
96  private:
97  // Reference to the buffer this update is for.
98  const Buffer& buffer;
99 
100  // Each update has an index number that is incremented by one from the index of the previous update.
101  // It's used to ensure that updates are applied in correct order and that none are skipped.
102  size_t updateNumber;
103 
104  // It's equal to size of the all pages for the given buffer.
105  Size size;
106 
107  // Indices of pages that need to be updated.
108  PageManager::Pages dirtyPages;
109 
110  // Data for the pages that need to be updated.
111  std::vector<uint8> dirtyData;
112  };
113 
117  static uint32_t getBufferCPUCount();
118 
122  static Size getBufferCPUMemSize();
123 
124  // VKTODO: experiment with making buffer usage inferred from other commands on the batch.
129  Buffer(uint32_t usage, Size pageSize = PageManager::DEFAULT_PAGE_SIZE);
130 
138  template <typename T>
139  static Buffer* createBuffer(uint32_t usage, const std::vector<T>& v) {
140  return new Buffer(usage, sizeof(T) * v.size(), (const gpu::Byte*)v.data());
141  }
142 
150  Buffer(uint32_t usage, Size size, const Byte* bytes, Size pageSize = PageManager::DEFAULT_PAGE_SIZE);
151 
158  Buffer(const Buffer& buf);
159 
167  Buffer& operator=(const Buffer& buf);
168 
172  ~Buffer();
173 
177  Size getSize() const override;
178 
183  template <typename T>
184  Size getNumTypedElements() const {
185  return getSize() / sizeof(T);
186  };
187 
191  const Byte* getData() const { return getSysmem().readData(); }
192 
196  uint32_t getUsage() const { return _usage; }
197 
205  Size resize(Size pSize);
206 
213  Size setData(Size size, const Byte* data);
214 
215  // Assign data bytes and size (allocate for size, then copy bytes if exists)
216  // \return
226  Size setSubData(Size offset, Size size, const Byte* data);
227 
236  template <typename T>
237  Size setSubData(Size index, const T& t) {
238  Size offset = index * sizeof(T);
239  Size size = sizeof(T);
240  return setSubData(offset, size, reinterpret_cast<const Byte*>(&t));
241  }
242 
251  template <typename T>
252  Size setSubData(Size index, const std::vector<T>& t) {
253  if (t.empty()) {
254  return 0;
255  }
256  Size offset = index * sizeof(T);
257  Size size = t.size() * sizeof(T);
258  return setSubData(offset, size, reinterpret_cast<const Byte*>(&t[0]));
259  }
260 
269  Size append(Size size, const Byte* data);
270 
278  template <typename T>
279  Size append(const T& t) {
280  return append(sizeof(t), reinterpret_cast<const Byte*>(&t));
281  }
282 
290  template <typename T>
291  Size append(const std::vector<T>& t) {
292  if (t.empty()) {
293  return _end;
294  }
295  return append(sizeof(T) * t.size(), reinterpret_cast<const Byte*>(&t[0]));
296  }
297 
303 
311  const Sysmem& getSysmem() const { return _sysmem; }
312 
316  bool isDirty() const { return _pages(PageManager::DIRTY); }
317 
325  void applyUpdate(const Update& update);
326 
327  // Main thread operation to say that the buffer is ready to be used as a frame
334  Update getUpdate() const;
335 
336 protected:
339 
345  void flush() const;
346 
347  // FIXME don't maintain a second buffer continuously. We should be able to apply updates
348  // directly to the GL object and discard _renderSysmem and _renderPages
349 
354 
360 
364  mutable std::atomic<size_t> _getUpdateCount{ 0 };
365 
369  mutable std::atomic<size_t> _applyUpdateCount{ 0 };
370 
378  void markDirty(Size offset, Size bytes);
379 
387  template <typename T>
388  void markDirty(Size index, Size count = 1) {
389  markDirty(sizeof(T) * index, sizeof(T) * count);
390  }
391 
395  Sysmem& editSysmem() { return _sysmem; }
396 
401  Byte* editData() { return editSysmem().editData(); }
402 
409 
413  Size _end{ 0 };
414 
420 
425  const uint32_t _usage{ 0 };
426 
427  friend class Serializer;
428  friend class Deserializer;
429  friend class BufferView;
430  friend class Frame;
431  friend class Batch;
432 
433  // FIXME find a more generic way to do this.
434  friend class ::gpu::vk::VKBuffer;
435  friend class ::gpu::vk::VKBackend;
436  friend class gl::GLBackend;
437  friend class gl::GLBuffer;
438  friend class gl41::GL41Buffer;
439  friend class gl45::GL45Buffer;
440  friend class gles::GLESBuffer;
441  friend class vk::VKBuffer;
442 };
443 
444 using BufferUpdates = std::vector<Buffer::Update>;
445 
446 typedef std::shared_ptr<Buffer> BufferPointer;
447 typedef std::vector<BufferPointer> Buffers;
448 
452 class BufferView {
453 protected:
454  //
455  static const Resource::Size DEFAULT_OFFSET{ 0 };
456 
457  //
458  static const Element DEFAULT_ELEMENT;
459 
460 public:
461  using Size = Resource::Size;
462  using Index = int32_t;
463 
467  BufferPointer _buffer;
468 
472  Size _offset{ 0 };
473 
477  Size _size{ 0 };
478 
482  Element _element{ DEFAULT_ELEMENT };
483 
487  uint16 _stride{ 0 };
488 
489  BufferView(const BufferView& view) = default;
490  BufferView& operator=(const BufferView& view) = default;
491 
495  BufferView();
496 
501  BufferView(const Element& element);
502 
510  BufferView(Buffer* newBuffer, const Element& element = DEFAULT_ELEMENT);
511 
518  BufferView(const BufferPointer& buffer, const Element& element = DEFAULT_ELEMENT);
519 
528  BufferView(const BufferPointer& buffer, Size offset, Size size, const Element& element = DEFAULT_ELEMENT);
529 
538  BufferView(const BufferPointer& buffer, Size offset, Size size, uint16 stride, const Element& element = DEFAULT_ELEMENT);
539 
543  Size getNumElements() const { return _size / _stride; }
544 
549  template <typename T>
550  class Iterator {
551  public:
552  using iterator_category = std::random_access_iterator_tag;
553  using value_type = T;
554  using difference_type = Index;
555  using pointer = const value_type*;
556  using reference = const value_type&;
557 
558  Iterator(T* ptr = NULL, int stride = sizeof(T)): _ptr(ptr), _stride(stride) { }
559  Iterator(const Iterator<T>& iterator) = default;
560  ~Iterator() {}
561 
562  Iterator<T>& operator=(const Iterator<T>& iterator) = default;
563  Iterator<T>& operator=(T* ptr) {
564  _ptr = ptr;
565  // stride is left unchanged
566  return (*this);
567  }
568 
569  operator bool() const {
570  if (_ptr)
571  return true;
572  else
573  return false;
574  }
575 
576  bool operator==(const Iterator<T>& iterator) const { return (_ptr == iterator.getConstPtr()); }
577  bool operator!=(const Iterator<T>& iterator) const { return (_ptr != iterator.getConstPtr()); }
578  bool operator<(const Iterator<T>& iterator) const { return (_ptr < iterator.getConstPtr()); }
579  bool operator>(const Iterator<T>& iterator) const { return (_ptr > iterator.getConstPtr()); }
580 
581  void movePtr(const Index& movement) {
582  auto byteptr = ((Byte*)_ptr);
583  byteptr += _stride * movement;
584  _ptr = (T*)byteptr;
585  }
586 
587  Iterator<T>& operator+=(const Index& movement) {
588  movePtr(movement);
589  return (*this);
590  }
591  Iterator<T>& operator-=(const Index& movement) {
592  movePtr(-movement);
593  return (*this);
594  }
595  Iterator<T>& operator++() {
596  movePtr(1);
597  return (*this);
598  }
599  Iterator<T>& operator--() {
600  movePtr(-1);
601  return (*this);
602  }
603  Iterator<T> operator++(Index) {
604  auto temp(*this);
605  movePtr(1);
606  return temp;
607  }
608  Iterator<T> operator--(Index) {
609  auto temp(*this);
610  movePtr(-1);
611  return temp;
612  }
613  Iterator<T> operator+(const Index& movement) {
614  auto oldPtr = _ptr;
615  movePtr(movement);
616  auto temp(*this);
617  _ptr = oldPtr;
618  return temp;
619  }
620  Iterator<T> operator-(const Index& movement) {
621  auto oldPtr = _ptr;
622  movePtr(-movement);
623  auto temp(*this);
624  _ptr = oldPtr;
625  return temp;
626  }
627 
628  Index operator-(const Iterator<T>& iterator) { return (iterator.getPtr() - this->getPtr()) / sizeof(T); }
629 
630  T& operator*() { return *_ptr; }
631  const T& operator*() const { return *_ptr; }
632  T* operator->() { return _ptr; }
633 
634  T* getPtr() const { return _ptr; }
635  const T* getConstPtr() const { return _ptr; }
636 
637  protected:
638  T* _ptr;
639  int _stride;
640  };
641 
642 #if 0
643  // Direct memory access to the buffer contents is incompatible with the paging memory scheme
644  template <typename T> Iterator<T> begin() { return Iterator<T>(&edit<T>(0), _stride); }
645  template <typename T> Iterator<T> end() { return Iterator<T>(&edit<T>(getNum<T>()), _stride); }
646 #else
647  template <typename T>
648  Iterator<const T> begin() const {
649  return Iterator<const T>(&get<T>(), _stride);
650  }
651  template <typename T>
652  Iterator<const T> end() const {
653  // reimplement get<T> without bounds checking
654  Resource::Size elementOffset = getNum<T>() * _stride + _offset;
655  return Iterator<const T>((reinterpret_cast<const T*>(_buffer->getData() + elementOffset)), _stride);
656  }
657 #endif
658  template <typename T>
659  Iterator<const T> cbegin() const {
660  return Iterator<const T>(&get<T>(), _stride);
661  }
662  template <typename T>
663  Iterator<const T> cend() const {
664  // reimplement get<T> without bounds checking
665  Resource::Size elementOffset = getNum<T>() * _stride + _offset;
666  return Iterator<const T>((reinterpret_cast<const T*>(_buffer->getData() + elementOffset)), _stride);
667  }
668 
669  // the number of elements of the specified type fitting in the view size
670  template <typename T>
671  Index getNum() const {
672  return Index(_size / _stride);
673  }
674 
679  template <typename T>
680  const T& get() const {
681 #if _DEBUG
682  if (!_buffer) {
683  qCDebug(gpulogging) << "Accessing null gpu::buffer!";
684  }
685  if (sizeof(T) > (_buffer->getSize() - _offset)) {
686  qCDebug(gpulogging) << "Accessing buffer in non allocated memory, element size = " << sizeof(T)
687  << " available space in buffer at offset is = " << (_buffer->getSize() - _offset);
688  }
689  if (sizeof(T) > _size) {
690  qCDebug(gpulogging) << "Accessing buffer outside the BufferView range, element size = " << sizeof(T)
691  << " when bufferView size = " << _size;
692  }
693 #endif
694  const T* t = (reinterpret_cast<const T*>(_buffer->getData() + _offset));
695  return *(t);
696  }
697 
704  template <typename T>
705  T& edit() {
706 #if _DEBUG
707  if (!_buffer) {
708  qCDebug(gpulogging) << "Accessing null gpu::buffer!";
709  }
710  if (sizeof(T) > (_buffer->getSize() - _offset)) {
711  qCDebug(gpulogging) << "Accessing buffer in non allocated memory, element size = " << sizeof(T)
712  << " available space in buffer at offset is = " << (_buffer->getSize() - _offset);
713  }
714  if (sizeof(T) > _size) {
715  qCDebug(gpulogging) << "Accessing buffer outside the BufferView range, element size = " << sizeof(T)
716  << " when bufferView size = " << _size;
717  }
718 #endif
719  _buffer->markDirty(_offset, sizeof(T));
720  T* t = (reinterpret_cast<T*>(_buffer->editData() + _offset));
721  return *(t);
722  }
723 
729  template <typename T>
730  const T& get(const Index index) const {
731  Resource::Size elementOffset = index * _stride + _offset;
732 #if _DEBUG
733  if (!_buffer) {
734  qCDebug(gpulogging) << "Accessing null gpu::buffer!";
735  }
736  if (sizeof(T) > (_buffer->getSize() - elementOffset)) {
737  qCDebug(gpulogging) << "Accessing buffer in non allocated memory, index = " << index
738  << ", element size = " << sizeof(T)
739  << " available space in buffer at offset is = " << (_buffer->getSize() - elementOffset);
740  }
741  if (index > getNum<T>()) {
742  qCDebug(gpulogging) << "Accessing buffer outside the BufferView range, index = " << index
743  << " number elements = " << getNum<T>();
744  }
745 #endif
746  return *(reinterpret_cast<const T*>(_buffer->getData() + elementOffset));
747  }
748 
756  template <typename T>
757  T& edit(const Index index) const {
758  Resource::Size elementOffset = index * _stride + _offset;
759 #if _DEBUG
760  if (!_buffer) {
761  qCDebug(gpulogging) << "Accessing null gpu::buffer!";
762  }
763  if (sizeof(T) > (_buffer->getSize() - elementOffset)) {
764  qCDebug(gpulogging) << "Accessing buffer in non allocated memory, index = " << index
765  << ", element size = " << sizeof(T)
766  << " available space in buffer at offset is = " << (_buffer->getSize() - elementOffset);
767  }
768  if (index > getNum<T>()) {
769  qCDebug(gpulogging) << "Accessing buffer outside the BufferView range, index = " << index
770  << " number elements = " << getNum<T>();
771  }
772 #endif
773  _buffer->markDirty(elementOffset, sizeof(T));
774  return *(reinterpret_cast<T*>(_buffer->editData() + elementOffset));
775  }
776 };
777 
784 template <class T>
785 class StructBuffer : public gpu::BufferView {
786 public:
795  template <class U>
796  static BufferPointer makeBuffer(uint32_t usage = gpu::Buffer::UniformBuffer) {
797  U t{};
798  return std::make_shared<gpu::Buffer>(usage, sizeof(U), (const gpu::Byte*)&t, sizeof(U));
799  }
800  ~StructBuffer(){};
801  StructBuffer() : gpu::BufferView(makeBuffer<T>()) {}
802 
808  T& edit() { return BufferView::edit<T>(0); }
809 
813  const T& get() const { return BufferView::get<T>(0); }
814 
815  const T* operator->() const { return &get(); }
816 };
817 }; // namespace gpu
818 
819 #endif
Definition: Buffer.h:65
void apply() const
Applies the update to the buffer.
Definition: Buffer.cpp:127
Update(const Buffer &buffer)
Generates an update object based on current dirty pages in the buffer.
Definition: Buffer.cpp:111
Definition: Buffer.h:31
Size append(const T &t)
Append new data at the end of the current buffer from an object of a given type.
Definition: Buffer.h:279
const Sysmem & getSysmem() const
Access the sysmem object.
Definition: Buffer.h:311
Size setSubData(Size index, const std::vector< T > &t)
Set a subset of buffer's data using a vector of objects of a given type.
Definition: Buffer.h:252
void markDirty(Size index, Size count=1)
Marks pages on which data from a number of objects of a specified type range is stored as dirty.
Definition: Buffer.h:388
Sysmem & editSysmem()
Definition: Buffer.h:395
Byte * editData()
Returns pointer to data to be edited, but data still needs to be flagged as dirty with a separate mak...
Definition: Buffer.h:401
Size getNumTypedElements() const
Definition: Buffer.h:184
Size setData(Size size, const Byte *data)
Assign data bytes and size (allocate for size, then copy bytes if exists).
Definition: Buffer.cpp:166
const GPUObjectPointer gpuObject
Definition: Buffer.h:302
uint32_t getUsage() const
Definition: Buffer.h:196
PageManager _renderPages
Definition: Buffer.h:353
const Byte * getData() const
Definition: Buffer.h:191
Size append(const std::vector< T > &t)
Append new data at the end of the current buffer using a vector of objects of a given type.
Definition: Buffer.h:291
static Size getBufferCPUMemSize()
Definition: Buffer.cpp:43
void markDirty(Size offset, Size bytes)
Marks pages on which specified data range is stored as dirty.
Definition: Buffer.cpp:87
~Buffer()
Definition: Buffer.cpp:72
std::atomic< size_t > _applyUpdateCount
Definition: Buffer.h:369
std::atomic< size_t > _getUpdateCount
Definition: Buffer.h:364
Buffer(uint32_t usage, Size pageSize=PageManager::DEFAULT_PAGE_SIZE)
Definition: Buffer.cpp:47
PageManager _pages
Definition: Buffer.h:408
Size append(Size size, const Byte *data)
Append new data at the end of the current buffer.
Definition: Buffer.cpp:180
Sysmem _renderSysmem
Definition: Buffer.h:359
Size getSize() const override
Definition: Buffer.cpp:187
const uint32_t _usage
Definition: Buffer.h:425
Update getUpdate() const
Generate Update object from the current changes to the buffer.
Definition: Buffer.cpp:146
void incrementCreatedBufferCount()
Counter used for debugging excessive creation of buffers.
Definition: Buffer.cpp:19
Size _end
Definition: Buffer.h:413
static Buffer * createBuffer(uint32_t usage, const std::vector< T > &v)
Creates a buffer from std::vector.
Definition: Buffer.h:139
bool isDirty() const
Definition: Buffer.h:316
void applyUpdate(const Update &update)
Applies update to a buffer.
Definition: Buffer.cpp:150
Size resize(Size pSize)
Resizes buffer.
Definition: Buffer.cpp:77
void flush() const
Instantly applies current changes to a buffer without creating an Update object.
Definition: Buffer.cpp:154
static uint32_t getBufferCPUCount()
Definition: Buffer.cpp:39
Size setSubData(Size index, const T &t)
Set a subset of buffer's data using data of a given object.
Definition: Buffer.h:237
Buffer & operator=(const Buffer &buf)
Assignment constructor.
Definition: Buffer.cpp:66
Size setSubData(Size offset, Size size, const Byte *data)
Set a subset of buffer's data.
Definition: Buffer.cpp:172
Sysmem _sysmem
Definition: Buffer.h:419
Template iterator with random access on the buffer sysmem.
Definition: Buffer.h:550
Definition: gpu/src/gpu/Forward.h:134
Definition: Metric.h:22
Buffer and Texture inherits from this class.
Definition: Resource.h:19
A buffer contaning a single structure.
Definition: Buffer.h:785
const T & get() const
Definition: Buffer.h:813
T & edit()
Mark data as dirty and get a writeable reference to the stored structure.
Definition: Buffer.h:808
static BufferPointer makeBuffer(uint32_t usage=gpu::Buffer::UniformBuffer)
Creates a new buffer containing a give structure.
Definition: Buffer.h:796
Sysmem is the underneath cache for the data of a resource in CPU RAM.
Definition: Sysmem.h:18
Byte * editData()
Access the byte array for reading and writing.
Definition: Sysmem.h:99
const Byte * readData() const
Access the byte array.
Definition: Sysmem.h:93
Definition: PageManager.h:21