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 class Buffer : public Resource {
28  static ContextMetricCount _bufferCPUCount;
29  static ContextMetricSize _bufferCPUMemSize;
30 
31 public:
32  using Flag = PageManager::Flag;
33 
34  // VKTODO: Make this independent of VkBufferUsageFlagBits.
35  // Flags match VkBufferUsageFlagBits for convenience... do not modify
36  enum Usage
37  {
38  // These values are unused in our API
39  //TransferSrc = 0x0001,
40  //TransferDst = 0x0002,
41  //UniformTexelBuffer = 0x0004,
42  //StorageTexelBuffer = 0x0008,
43  UniformBuffer = 0x0010,
44  ResourceBuffer = 0x0020,
45  IndexBuffer = 0x0040,
46  VertexBuffer = 0x0080,
47  IndirectBuffer = 0x0100,
48  AllFlags = 0x01F3
49  };
50 
51  class Update {
52  public:
53  Update(const Buffer& buffer);
54  Update(const Update& other);
55  Update(Update&& other);
56  void apply() const;
57 
58  private:
59  const Buffer& buffer;
60  size_t updateNumber;
61  Size size;
62  PageManager::Pages dirtyPages;
63  std::vector<uint8> dirtyData;
64  };
65 
66  static uint32_t getBufferCPUCount();
67  static Size getBufferCPUMemSize();
68 
69  // VKTODO: experiment with making buffer usage inferred from other commands on the batch.
70  Buffer(uint32_t usage, Size pageSize = PageManager::DEFAULT_PAGE_SIZE);
71 
72  template <typename T>
73  static Buffer* createBuffer(uint32_t usage, const std::vector<T>& v) {
74  return new Buffer(usage, sizeof(T) * v.size(), (const gpu::Byte*)v.data());
75  }
76 
77  Buffer(uint32_t usage, Size size, const Byte* bytes, Size pageSize = PageManager::DEFAULT_PAGE_SIZE);
78  Buffer(const Buffer& buf); // deep copy of the sysmem buffer
79  Buffer& operator=(const Buffer& buf); // deep copy of the sysmem buffer
80  ~Buffer();
81 
82  // The size in bytes of data stored in the buffer
83  Size getSize() const override;
84  template <typename T>
85  Size getNumTypedElements() const {
86  return getSize() / sizeof(T);
87  };
88 
89  const Byte* getData() const { return getSysmem().readData(); }
90 
91  uint32_t getUsage() const { return _usage; }
92 
93  // Resize the buffer
94  // Keep previous data [0 to min(pSize, mSize)]
95  Size resize(Size pSize);
96 
97  // Assign data bytes and size (allocate for size, then copy bytes if exists)
98  // \return the size of the buffer
99  Size setData(Size size, const Byte* data);
100 
101  // Assign data bytes and size (allocate for size, then copy bytes if exists)
102  // \return the number of bytes copied
103  Size setSubData(Size offset, Size size, const Byte* data);
104 
105  template <typename T>
106  Size setSubData(Size index, const T& t) {
107  Size offset = index * sizeof(T);
108  Size size = sizeof(T);
109  return setSubData(offset, size, reinterpret_cast<const Byte*>(&t));
110  }
111 
112  template <typename T>
113  Size setSubData(Size index, const std::vector<T>& t) {
114  if (t.empty()) {
115  return 0;
116  }
117  Size offset = index * sizeof(T);
118  Size size = t.size() * sizeof(T);
119  return setSubData(offset, size, reinterpret_cast<const Byte*>(&t[0]));
120  }
121 
122  // Append new data at the end of the current buffer
123  // do a resize( size + getSize) and copy the new data
124  // \return the number of bytes copied
125  Size append(Size size, const Byte* data);
126 
127  template <typename T>
128  Size append(const T& t) {
129  return append(sizeof(t), reinterpret_cast<const Byte*>(&t));
130  }
131 
132  template <typename T>
133  Size append(const std::vector<T>& t) {
134  if (t.empty()) {
135  return _end;
136  }
137  return append(sizeof(T) * t.size(), reinterpret_cast<const Byte*>(&t[0]));
138  }
139 
140  const GPUObjectPointer gpuObject{};
141 
142  // Access the sysmem object, limited to ourselves and GPUObject derived classes
143  const Sysmem& getSysmem() const { return _sysmem; }
144 
145  bool isDirty() const { return _pages(PageManager::DIRTY); }
146 
147  void applyUpdate(const Update& update);
148 
149  // Main thread operation to say that the buffer is ready to be used as a frame
150  Update getUpdate() const;
151 
152 protected:
153  // For use by the render thread to avoid the intermediate step of getUpdate/applyUpdate
154  void flush() const;
155 
156  // FIXME don't maintain a second buffer continuously. We should be able to apply updates
157  // directly to the GL object and discard _renderSysmem and _renderPages
158  mutable PageManager _renderPages;
159  mutable Sysmem _renderSysmem;
160 
161  mutable std::atomic<size_t> _getUpdateCount{ 0 };
162  mutable std::atomic<size_t> _applyUpdateCount{ 0 };
163 
164  void markDirty(Size offset, Size bytes);
165 
166  template <typename T>
167  void markDirty(Size index, Size count = 1) {
168  markDirty(sizeof(T) * index, sizeof(T) * count);
169  }
170 
171  Sysmem& editSysmem() { return _sysmem; }
172  Byte* editData() { return editSysmem().editData(); }
173 
174  mutable PageManager _pages;
175  Size _end{ 0 };
176  Sysmem _sysmem;
177  const uint32_t _usage{ 0 };
178 
179  friend class Serializer;
180  friend class Deserializer;
181  friend class BufferView;
182  friend class Frame;
183  friend class Batch;
184 
185  // FIXME find a more generic way to do this.
186  friend class ::gpu::vk::VKBuffer;
187  friend class ::gpu::vk::VKBackend;
188  friend class gl::GLBackend;
189  friend class gl::GLBuffer;
190  friend class gl41::GL41Buffer;
191  friend class gl45::GL45Buffer;
192  friend class gles::GLESBuffer;
193  friend class vk::VKBuffer;
194 };
195 
196 using BufferUpdates = std::vector<Buffer::Update>;
197 
198 typedef std::shared_ptr<Buffer> BufferPointer;
199 typedef std::vector<BufferPointer> Buffers;
200 
201 class BufferView {
202 protected:
203  static const Resource::Size DEFAULT_OFFSET{ 0 };
204  static const Element DEFAULT_ELEMENT;
205 
206 public:
207  using Size = Resource::Size;
208  using Index = int32_t;
209 
210  BufferPointer _buffer;
211  Size _offset{ 0 };
212  Size _size{ 0 };
213  Element _element{ DEFAULT_ELEMENT };
214  uint16 _stride{ 0 };
215 
216  BufferView(const BufferView& view) = default;
217  BufferView& operator=(const BufferView& view) = default;
218 
219  BufferView();
220  BufferView(const Element& element);
221  BufferView(Buffer* newBuffer, const Element& element = DEFAULT_ELEMENT);
222  BufferView(const BufferPointer& buffer, const Element& element = DEFAULT_ELEMENT);
223  BufferView(const BufferPointer& buffer, Size offset, Size size, const Element& element = DEFAULT_ELEMENT);
224  BufferView(const BufferPointer& buffer, Size offset, Size size, uint16 stride, const Element& element = DEFAULT_ELEMENT);
225 
226  Size getNumElements() const { return _size / _stride; }
227 
228  //Template iterator with random access on the buffer sysmem
229  template<typename T>
230  class Iterator {
231  public:
232  using iterator_category = std::random_access_iterator_tag;
233  using value_type = T;
234  using difference_type = Index;
235  using pointer = const value_type*;
236  using reference = const value_type&;
237 
238  Iterator(T* ptr = NULL, int stride = sizeof(T)): _ptr(ptr), _stride(stride) { }
239  Iterator(const Iterator<T>& iterator) = default;
240  ~Iterator() {}
241 
242  Iterator<T>& operator=(const Iterator<T>& iterator) = default;
243  Iterator<T>& operator=(T* ptr) {
244  _ptr = ptr;
245  // stride is left unchanged
246  return (*this);
247  }
248 
249  operator bool() const {
250  if (_ptr)
251  return true;
252  else
253  return false;
254  }
255 
256  bool operator==(const Iterator<T>& iterator) const { return (_ptr == iterator.getConstPtr()); }
257  bool operator!=(const Iterator<T>& iterator) const { return (_ptr != iterator.getConstPtr()); }
258  bool operator<(const Iterator<T>& iterator) const { return (_ptr < iterator.getConstPtr()); }
259  bool operator>(const Iterator<T>& iterator) const { return (_ptr > iterator.getConstPtr()); }
260 
261  void movePtr(const Index& movement) {
262  auto byteptr = ((Byte*)_ptr);
263  byteptr += _stride * movement;
264  _ptr = (T*)byteptr;
265  }
266 
267  Iterator<T>& operator+=(const Index& movement) {
268  movePtr(movement);
269  return (*this);
270  }
271  Iterator<T>& operator-=(const Index& movement) {
272  movePtr(-movement);
273  return (*this);
274  }
275  Iterator<T>& operator++() {
276  movePtr(1);
277  return (*this);
278  }
279  Iterator<T>& operator--() {
280  movePtr(-1);
281  return (*this);
282  }
283  Iterator<T> operator++(Index) {
284  auto temp(*this);
285  movePtr(1);
286  return temp;
287  }
288  Iterator<T> operator--(Index) {
289  auto temp(*this);
290  movePtr(-1);
291  return temp;
292  }
293  Iterator<T> operator+(const Index& movement) {
294  auto oldPtr = _ptr;
295  movePtr(movement);
296  auto temp(*this);
297  _ptr = oldPtr;
298  return temp;
299  }
300  Iterator<T> operator-(const Index& movement) {
301  auto oldPtr = _ptr;
302  movePtr(-movement);
303  auto temp(*this);
304  _ptr = oldPtr;
305  return temp;
306  }
307 
308  Index operator-(const Iterator<T>& iterator) { return (iterator.getPtr() - this->getPtr()) / sizeof(T); }
309 
310  T& operator*() { return *_ptr; }
311  const T& operator*() const { return *_ptr; }
312  T* operator->() { return _ptr; }
313 
314  T* getPtr() const { return _ptr; }
315  const T* getConstPtr() const { return _ptr; }
316 
317  protected:
318  T* _ptr;
319  int _stride;
320  };
321 
322 #if 0
323  // Direct memory access to the buffer contents is incompatible with the paging memory scheme
324  template <typename T> Iterator<T> begin() { return Iterator<T>(&edit<T>(0), _stride); }
325  template <typename T> Iterator<T> end() { return Iterator<T>(&edit<T>(getNum<T>()), _stride); }
326 #else
327  template <typename T>
328  Iterator<const T> begin() const {
329  return Iterator<const T>(&get<T>(), _stride);
330  }
331  template <typename T>
332  Iterator<const T> end() const {
333  // reimplement get<T> without bounds checking
334  Resource::Size elementOffset = getNum<T>() * _stride + _offset;
335  return Iterator<const T>((reinterpret_cast<const T*>(_buffer->getData() + elementOffset)), _stride);
336  }
337 #endif
338  template <typename T>
339  Iterator<const T> cbegin() const {
340  return Iterator<const T>(&get<T>(), _stride);
341  }
342  template <typename T>
343  Iterator<const T> cend() const {
344  // reimplement get<T> without bounds checking
345  Resource::Size elementOffset = getNum<T>() * _stride + _offset;
346  return Iterator<const T>((reinterpret_cast<const T*>(_buffer->getData() + elementOffset)), _stride);
347  }
348 
349  // the number of elements of the specified type fitting in the view size
350  template <typename T>
351  Index getNum() const {
352  return Index(_size / _stride);
353  }
354 
355  template <typename T>
356  const T& get() const {
357 #if _DEBUG
358  if (!_buffer) {
359  qCDebug(gpulogging) << "Accessing null gpu::buffer!";
360  }
361  if (sizeof(T) > (_buffer->getSize() - _offset)) {
362  qCDebug(gpulogging) << "Accessing buffer in non allocated memory, element size = " << sizeof(T)
363  << " available space in buffer at offset is = " << (_buffer->getSize() - _offset);
364  }
365  if (sizeof(T) > _size) {
366  qCDebug(gpulogging) << "Accessing buffer outside the BufferView range, element size = " << sizeof(T)
367  << " when bufferView size = " << _size;
368  }
369 #endif
370  const T* t = (reinterpret_cast<const T*>(_buffer->getData() + _offset));
371  return *(t);
372  }
373 
374  template <typename T>
375  T& edit() {
376 #if _DEBUG
377  if (!_buffer) {
378  qCDebug(gpulogging) << "Accessing null gpu::buffer!";
379  }
380  if (sizeof(T) > (_buffer->getSize() - _offset)) {
381  qCDebug(gpulogging) << "Accessing buffer in non allocated memory, element size = " << sizeof(T)
382  << " available space in buffer at offset is = " << (_buffer->getSize() - _offset);
383  }
384  if (sizeof(T) > _size) {
385  qCDebug(gpulogging) << "Accessing buffer outside the BufferView range, element size = " << sizeof(T)
386  << " when bufferView size = " << _size;
387  }
388 #endif
389  _buffer->markDirty(_offset, sizeof(T));
390  T* t = (reinterpret_cast<T*>(_buffer->editData() + _offset));
391  return *(t);
392  }
393 
394  template <typename T>
395  const T& get(const Index index) const {
396  Resource::Size elementOffset = index * _stride + _offset;
397 #if _DEBUG
398  if (!_buffer) {
399  qCDebug(gpulogging) << "Accessing null gpu::buffer!";
400  }
401  if (sizeof(T) > (_buffer->getSize() - elementOffset)) {
402  qCDebug(gpulogging) << "Accessing buffer in non allocated memory, index = " << index
403  << ", element size = " << sizeof(T)
404  << " available space in buffer at offset is = " << (_buffer->getSize() - elementOffset);
405  }
406  if (index > getNum<T>()) {
407  qCDebug(gpulogging) << "Accessing buffer outside the BufferView range, index = " << index
408  << " number elements = " << getNum<T>();
409  }
410 #endif
411  return *(reinterpret_cast<const T*>(_buffer->getData() + elementOffset));
412  }
413 
414  template <typename T>
415  T& edit(const Index index) const {
416  Resource::Size elementOffset = index * _stride + _offset;
417 #if _DEBUG
418  if (!_buffer) {
419  qCDebug(gpulogging) << "Accessing null gpu::buffer!";
420  }
421  if (sizeof(T) > (_buffer->getSize() - elementOffset)) {
422  qCDebug(gpulogging) << "Accessing buffer in non allocated memory, index = " << index
423  << ", element size = " << sizeof(T)
424  << " available space in buffer at offset is = " << (_buffer->getSize() - elementOffset);
425  }
426  if (index > getNum<T>()) {
427  qCDebug(gpulogging) << "Accessing buffer outside the BufferView range, index = " << index
428  << " number elements = " << getNum<T>();
429  }
430 #endif
431  _buffer->markDirty(elementOffset, sizeof(T));
432  return *(reinterpret_cast<T*>(_buffer->editData() + elementOffset));
433  }
434 };
435 
436 template <class T>
437 class StructBuffer : public gpu::BufferView {
438 public:
439  template <class U>
440  static BufferPointer makeBuffer(uint32_t usage = gpu::Buffer::UniformBuffer) {
441  U t;
442  return std::make_shared<gpu::Buffer>(usage, sizeof(U), (const gpu::Byte*)&t, sizeof(U));
443  }
444  ~StructBuffer(){};
445  StructBuffer() : gpu::BufferView(makeBuffer<T>()) {}
446 
447  T& edit() { return BufferView::edit<T>(0); }
448  const T& get() const { return BufferView::get<T>(0); }
449  const T* operator->() const { return &get(); }
450 };
451 }; // namespace gpu
452 
453 #endif
Base class for resources.
Definition: ResourceCache.h:415