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