/src/ogre/OgreMain/include/OgreHardwareBuffer.h
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | ----------------------------------------------------------------------------- |
3 | | This source file is part of OGRE |
4 | | (Object-oriented Graphics Rendering Engine) |
5 | | For the latest info, see http://www.ogre3d.org/ |
6 | | |
7 | | Copyright (c) 2000-2014 Torus Knot Software Ltd |
8 | | |
9 | | Permission is hereby granted, free of charge, to any person obtaining a copy |
10 | | of this software and associated documentation files (the "Software"), to deal |
11 | | in the Software without restriction, including without limitation the rights |
12 | | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
13 | | copies of the Software, and to permit persons to whom the Software is |
14 | | furnished to do so, subject to the following conditions: |
15 | | |
16 | | The above copyright notice and this permission notice shall be included in |
17 | | all copies or substantial portions of the Software. |
18 | | |
19 | | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
20 | | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
21 | | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
22 | | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
23 | | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
24 | | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN |
25 | | THE SOFTWARE. |
26 | | ----------------------------------------------------------------------------- |
27 | | */ |
28 | | #ifndef __HardwareBuffer__ |
29 | | #define __HardwareBuffer__ |
30 | | |
31 | | // Precompiler options |
32 | | #include "OgrePrerequisites.h" |
33 | | #include "OgreException.h" |
34 | | |
35 | | namespace Ogre { |
36 | | |
37 | | /** \addtogroup Core |
38 | | * @{ |
39 | | */ |
40 | | /** \addtogroup RenderSystem |
41 | | * @{ |
42 | | */ |
43 | | /// Enums describing buffer usage |
44 | | enum HardwareBufferUsage : uint8 |
45 | | { |
46 | | /** Memory mappable on host and cached |
47 | | * @par Usage |
48 | | * results of some computations, e.g. screen capture |
49 | | */ |
50 | | HBU_GPU_TO_CPU = 1, |
51 | | /** CPU (system) memory |
52 | | * This is the least optimal buffer setting. |
53 | | * @par Usage |
54 | | * Staging copy of resources used as transfer source. |
55 | | */ |
56 | | HBU_CPU_ONLY = 2, |
57 | | /** Indicates the application will never read the contents of the buffer back, |
58 | | it will only ever write data. Locking a buffer with this flag will ALWAYS |
59 | | return a pointer to new, blank memory rather than the memory associated |
60 | | with the contents of the buffer; this avoids DMA stalls because you can |
61 | | write to a new memory area while the previous one is being used. |
62 | | |
63 | | However, you may read from it’s shadow buffer if you set one up |
64 | | */ |
65 | | HBU_DETAIL_WRITE_ONLY = 4, |
66 | | /** Device-local GPU (video) memory. No need to be mappable on host. |
67 | | * This is the optimal buffer usage setting. |
68 | | * @par Usage |
69 | | * Resources transferred from host once (immutable) - e.g. most textures, vertex buffers |
70 | | */ |
71 | | HBU_GPU_ONLY = HBU_GPU_TO_CPU | HBU_DETAIL_WRITE_ONLY, |
72 | | /** Mappable on host and preferably fast to access by GPU. |
73 | | * @par Usage |
74 | | * Resources written frequently by host (dynamic) - e.g. uniform buffers updated every frame |
75 | | */ |
76 | | HBU_CPU_TO_GPU = HBU_CPU_ONLY | HBU_DETAIL_WRITE_ONLY, |
77 | | }; |
78 | | /** Abstract class defining common features of hardware buffers. |
79 | | |
80 | | A 'hardware buffer' is any area of memory held outside of core system ram, |
81 | | and in our case refers mostly to video ram, although in theory this class |
82 | | could be used with other memory areas such as sound card memory, custom |
83 | | coprocessor memory etc. |
84 | | @par |
85 | | This reflects the fact that memory held outside of main system RAM must |
86 | | be interacted with in a more formal fashion in order to promote |
87 | | cooperative and optimal usage of the buffers between the various |
88 | | processing units which manipulate them. |
89 | | @par |
90 | | This abstract class defines the core interface which is common to all |
91 | | buffers, whether it be vertex buffers, index buffers, texture memory |
92 | | or framebuffer memory etc. |
93 | | @par |
94 | | Buffers have the ability to be 'shadowed' in system memory, this is because |
95 | | the kinds of access allowed on hardware buffers is not always as flexible as |
96 | | that allowed for areas of system memory - for example it is often either |
97 | | impossible, or extremely undesirable from a performance standpoint to read from |
98 | | a hardware buffer; when writing to hardware buffers, you should also write every |
99 | | byte and do it sequentially. In situations where this is too restrictive, |
100 | | it is possible to create a hardware, write-only buffer (the most efficient kind) |
101 | | and to back it with a system memory 'shadow' copy which can be read and updated arbitrarily. |
102 | | Ogre handles synchronising this buffer with the real hardware buffer (which should still be |
103 | | created with the HBU_DYNAMIC flag if you intend to update it very frequently). Whilst this |
104 | | approach does have its own costs, such as increased memory overhead, these costs can |
105 | | often be outweighed by the performance benefits of using a more hardware efficient buffer. |
106 | | You should look for the 'useShadowBuffer' parameter on the creation methods used to create |
107 | | the buffer of the type you require (see HardwareBufferManager) to enable this feature. |
108 | | */ |
109 | | class _OgreExport HardwareBuffer : public BufferAlloc |
110 | | { |
111 | | |
112 | | public: |
113 | | typedef uint8 Usage; |
114 | | /// Rather use HardwareBufferUsage |
115 | | enum UsageEnum |
116 | | { |
117 | | /// same as #HBU_GPU_TO_CPU |
118 | | HBU_STATIC = HBU_GPU_TO_CPU, |
119 | | /// same as #HBU_CPU_ONLY |
120 | | HBU_DYNAMIC = HBU_CPU_ONLY, |
121 | | /// @deprecated use #HBU_DETAIL_WRITE_ONLY |
122 | | HBU_WRITE_ONLY = HBU_DETAIL_WRITE_ONLY, |
123 | | /// @deprecated do not use |
124 | | HBU_DISCARDABLE = 8, |
125 | | /// same as #HBU_GPU_ONLY |
126 | | HBU_STATIC_WRITE_ONLY = HBU_GPU_ONLY, |
127 | | /// same as #HBU_CPU_TO_GPU |
128 | | HBU_DYNAMIC_WRITE_ONLY = HBU_CPU_TO_GPU, |
129 | | /// @deprecated do not use |
130 | | HBU_DYNAMIC_WRITE_ONLY_DISCARDABLE = HBU_CPU_TO_GPU, |
131 | | }; |
132 | | /// Locking options |
133 | | enum LockOptions : uint8 |
134 | | { |
135 | | /** Normal mode, ie allows read/write and contents are preserved. |
136 | | This kind of lock allows reading and writing from the buffer - it’s also the least |
137 | | optimal because basically you’re telling the card you could be doing anything at |
138 | | all. If you’re not using a shadow buffer, it requires the buffer to be transferred |
139 | | from the card and back again. If you’re using a shadow buffer the effect is |
140 | | minimal. |
141 | | */ |
142 | | HBL_NORMAL, |
143 | | /** Discards the <em>entire</em> buffer while locking. |
144 | | This means you are happy for the card to discard the entire current contents of the |
145 | | buffer. Implicitly this means you are not going to read the data - it also means |
146 | | that the card can avoid any stalls if the buffer is currently being rendered from, |
147 | | because it will actually give you an entirely different one. Use this wherever |
148 | | possible when you are locking a buffer which was not created with a shadow buffer. |
149 | | If you are using a shadow buffer it matters less, although with a shadow buffer it’s |
150 | | preferable to lock the entire buffer at once, because that allows the shadow buffer |
151 | | to use HBL_DISCARD when it uploads the updated contents to the real buffer. |
152 | | @note Only useful on buffers created with the HBU_CPU_TO_GPU flag. |
153 | | */ |
154 | | HBL_DISCARD, |
155 | | /** Lock the buffer for reading only. Not allowed in buffers which are created with |
156 | | HBU_GPU_ONLY. |
157 | | Mandatory on static buffers, i.e. those created without the HBU_DYNAMIC flag. |
158 | | */ |
159 | | HBL_READ_ONLY, |
160 | | /** As HBL_WRITE_ONLY, except the application guarantees not to overwrite any |
161 | | region of the buffer which has already been used in this frame, can allow |
162 | | some optimisation on some APIs. |
163 | | @note Only useful on buffers with no shadow buffer.*/ |
164 | | HBL_NO_OVERWRITE, |
165 | | /** Lock the buffer for writing only.*/ |
166 | | HBL_WRITE_ONLY |
167 | | |
168 | | }; |
169 | | protected: |
170 | | size_t mSizeInBytes; |
171 | | size_t mLockStart; |
172 | | size_t mLockSize; |
173 | | std::unique_ptr<HardwareBuffer> mDelegate; |
174 | | std::unique_ptr<HardwareBuffer> mShadowBuffer; |
175 | | bool mShadowUpdated; |
176 | | bool mSuppressHardwareUpdate; |
177 | | bool mIsLocked; |
178 | | Usage mUsage; |
179 | | |
180 | | /// Internal implementation of lock() |
181 | | virtual void* lockImpl(size_t offset, size_t length, LockOptions options) |
182 | 0 | { |
183 | 0 | return mDelegate->lock(offset, length, options); |
184 | 0 | } |
185 | | /// Internal implementation of unlock() |
186 | 0 | virtual void unlockImpl(void) { mDelegate->unlock(); } |
187 | | |
188 | | public: |
189 | | /// Constructor, to be called by HardwareBufferManager only |
190 | | HardwareBuffer(Usage usage, bool useShadowBuffer) |
191 | | : mSizeInBytes(0), mLockStart(0), mLockSize(0), mShadowUpdated(false), mSuppressHardwareUpdate(false), |
192 | | mIsLocked(false), mUsage(usage) |
193 | 0 | { |
194 | 0 | // If use shadow buffer, upgrade to WRITE_ONLY on hardware side |
195 | 0 | if (useShadowBuffer && usage == HBU_CPU_ONLY) |
196 | 0 | { |
197 | 0 | mUsage = HBU_CPU_TO_GPU; |
198 | 0 | } |
199 | 0 | else if (useShadowBuffer && usage == HBU_GPU_TO_CPU) |
200 | 0 | { |
201 | 0 | mUsage = HBU_GPU_ONLY; |
202 | 0 | } |
203 | 0 | } |
204 | 0 | virtual ~HardwareBuffer() {} |
205 | | /** Lock the buffer for (potentially) reading / writing. |
206 | | @param offset The byte offset from the start of the buffer to lock |
207 | | @param length The size of the area to lock, in bytes |
208 | | @param options Locking options |
209 | | @return Pointer to the locked memory |
210 | | */ |
211 | | virtual void* lock(size_t offset, size_t length, LockOptions options) |
212 | 0 | { |
213 | 0 | OgreAssert(!isLocked(), "Cannot lock this buffer: it is already locked"); |
214 | 0 | OgreAssert((length + offset) <= mSizeInBytes, "Lock request out of bounds"); |
215 | | |
216 | 0 | void* ret = NULL; |
217 | 0 | if (mShadowBuffer) |
218 | 0 | { |
219 | | // we have to assume a read / write lock so we use the shadow buffer |
220 | | // and tag for sync on unlock() |
221 | 0 | mShadowUpdated = (options != HBL_READ_ONLY); |
222 | |
|
223 | 0 | ret = mShadowBuffer->lock(offset, length, options); |
224 | 0 | } |
225 | 0 | else |
226 | 0 | { |
227 | 0 | mIsLocked = true; |
228 | | // Lock the real buffer if there is no shadow buffer |
229 | 0 | ret = lockImpl(offset, length, options); |
230 | 0 | } |
231 | 0 | mLockStart = offset; |
232 | 0 | mLockSize = length; |
233 | 0 | return ret; |
234 | 0 | } |
235 | | |
236 | | /// @overload |
237 | | void* lock(LockOptions options) |
238 | 0 | { |
239 | 0 | return this->lock(0, mSizeInBytes, options); |
240 | 0 | } |
241 | | /** Releases the lock on this buffer. |
242 | | |
243 | | Locking and unlocking a buffer can, in some rare circumstances such as |
244 | | switching video modes whilst the buffer is locked, corrupt the |
245 | | contents of a buffer. This is pretty rare, but if it occurs, |
246 | | this method will throw an exception, meaning you |
247 | | must re-upload the data. |
248 | | @par |
249 | | Note that using the 'read' and 'write' forms of updating the buffer does not |
250 | | suffer from this problem, so if you want to be 100% sure your |
251 | | data will not be lost, use the 'read' and 'write' forms instead. |
252 | | */ |
253 | | void unlock(void) |
254 | 0 | { |
255 | 0 | OgreAssert(isLocked(), "Cannot unlock this buffer: it is not locked"); |
256 | | |
257 | | // If we used the shadow buffer this time... |
258 | 0 | if (mShadowBuffer && mShadowBuffer->isLocked()) |
259 | 0 | { |
260 | 0 | mShadowBuffer->unlock(); |
261 | | // Potentially update the 'real' buffer from the shadow buffer |
262 | 0 | _updateFromShadow(); |
263 | 0 | } |
264 | 0 | else |
265 | 0 | { |
266 | | // Otherwise, unlock the real one |
267 | 0 | unlockImpl(); |
268 | 0 | mIsLocked = false; |
269 | 0 | } |
270 | |
|
271 | 0 | } |
272 | | |
273 | | /** Reads data from the buffer and places it in the memory pointed to by pDest. |
274 | | @param offset The byte offset from the start of the buffer to read |
275 | | @param length The size of the area to read, in bytes |
276 | | @param pDest The area of memory in which to place the data, must be large enough to |
277 | | accommodate the data! |
278 | | */ |
279 | | virtual void readData(size_t offset, size_t length, void* pDest) |
280 | 0 | { |
281 | 0 | if (mShadowBuffer) |
282 | 0 | { |
283 | 0 | mShadowBuffer->readData(offset, length, pDest); |
284 | 0 | return; |
285 | 0 | } |
286 | 0 |
|
287 | 0 | mDelegate->readData(offset, length, pDest); |
288 | 0 | } |
289 | | /** Writes data to the buffer from an area of system memory; note that you must |
290 | | ensure that your buffer is big enough. |
291 | | @param offset The byte offset from the start of the buffer to start writing |
292 | | @param length The size of the data to write to, in bytes |
293 | | @param pSource The source of the data to be written |
294 | | @param discardWholeBuffer If true, this allows the driver to discard the entire buffer when writing, |
295 | | such that DMA stalls can be avoided; use if you can. |
296 | | */ |
297 | | virtual void writeData(size_t offset, size_t length, const void* pSource, |
298 | | bool discardWholeBuffer = false) |
299 | 0 | { |
300 | | // Update the shadow buffer |
301 | 0 | if (mShadowBuffer) |
302 | 0 | { |
303 | 0 | mShadowBuffer->writeData(offset, length, pSource, discardWholeBuffer); |
304 | 0 | } |
305 | |
|
306 | 0 | mDelegate->writeData(offset, length, pSource, discardWholeBuffer); |
307 | 0 | } |
308 | | |
309 | | /** Copy data from another buffer into this one. |
310 | | |
311 | | Note that the source buffer must not be created with the |
312 | | usage HBU_WRITE_ONLY otherwise this will fail. |
313 | | @param srcBuffer The buffer from which to read the copied data |
314 | | @param srcOffset Offset in the source buffer at which to start reading |
315 | | @param dstOffset Offset in the destination buffer to start writing |
316 | | @param length Length of the data to copy, in bytes. |
317 | | @param discardWholeBuffer If true, will discard the entire contents of this buffer before copying |
318 | | */ |
319 | | virtual void copyData(HardwareBuffer& srcBuffer, size_t srcOffset, |
320 | | size_t dstOffset, size_t length, bool discardWholeBuffer = false) |
321 | 0 | { |
322 | 0 | if(mDelegate && !srcBuffer.isSystemMemory()) |
323 | 0 | { |
324 | 0 | mDelegate->copyData(*srcBuffer.mDelegate, srcOffset, dstOffset, length, discardWholeBuffer); |
325 | 0 | return; |
326 | 0 | } |
327 | 0 | const void* srcData = srcBuffer.lock(srcOffset, length, HBL_READ_ONLY); |
328 | 0 | this->writeData(dstOffset, length, srcData, discardWholeBuffer); |
329 | 0 | srcBuffer.unlock(); |
330 | 0 | } |
331 | | |
332 | | /** Copy all data from another buffer into this one. |
333 | | |
334 | | Normally these buffers should be of identical size, but if they're |
335 | | not, the routine will use the smallest of the two sizes. |
336 | | */ |
337 | | void copyData(HardwareBuffer& srcBuffer) |
338 | 0 | { |
339 | 0 | size_t sz = std::min(getSizeInBytes(), srcBuffer.getSizeInBytes()); |
340 | 0 | copyData(srcBuffer, 0, 0, sz, true); |
341 | 0 | } |
342 | | |
343 | | /// Updates the real buffer from the shadow buffer, if required |
344 | | virtual void _updateFromShadow(void) |
345 | 0 | { |
346 | 0 | if (mShadowBuffer && mShadowUpdated && !mSuppressHardwareUpdate) |
347 | 0 | { |
348 | 0 | // Do this manually to avoid locking problems |
349 | 0 | const void* srcData = mShadowBuffer->lockImpl(mLockStart, mLockSize, HBL_READ_ONLY); |
350 | 0 | // Lock with discard if the whole buffer was locked, otherwise w/o |
351 | 0 | bool discardWholeBuffer = mLockStart == 0 && mLockSize == mSizeInBytes; |
352 | 0 | LockOptions lockOpt = discardWholeBuffer ? HBL_DISCARD : HBL_WRITE_ONLY; |
353 | 0 | void* destData = this->lockImpl(mLockStart, mLockSize, lockOpt); |
354 | 0 | // Copy shadow to real |
355 | 0 | memcpy(destData, srcData, mLockSize); |
356 | 0 | this->unlockImpl(); |
357 | 0 | mShadowBuffer->unlockImpl(); |
358 | 0 | mShadowUpdated = false; |
359 | 0 | } |
360 | 0 | } |
361 | | |
362 | | /// Returns the size of this buffer in bytes |
363 | 0 | size_t getSizeInBytes(void) const { return mSizeInBytes; } |
364 | | /// Returns the Usage flags with which this buffer was created |
365 | 0 | Usage getUsage(void) const { return mUsage; } |
366 | | /// Returns whether this buffer is held in system memory |
367 | 0 | virtual bool isSystemMemory(void) const { return mDelegate && mDelegate->isSystemMemory(); } |
368 | | /// Returns whether this buffer has a system memory shadow for quicker reading |
369 | 0 | bool hasShadowBuffer(void) const { return mShadowBuffer || (mDelegate && mDelegate->hasShadowBuffer()); } |
370 | | /// Returns whether or not this buffer is currently locked. |
371 | 0 | bool isLocked(void) const { |
372 | 0 | return mIsLocked || (mShadowBuffer && mShadowBuffer->isLocked()); |
373 | 0 | } |
374 | | /// Pass true to suppress hardware upload of shadow buffer changes |
375 | 0 | void suppressHardwareUpdate(bool suppress) { |
376 | 0 | mSuppressHardwareUpdate = suppress; |
377 | 0 | if (!suppress) |
378 | 0 | _updateFromShadow(); |
379 | 0 |
|
380 | 0 | if(mDelegate) |
381 | 0 | mDelegate->suppressHardwareUpdate(suppress); |
382 | 0 | } |
383 | | |
384 | | template <typename T> T* _getImpl() |
385 | | { |
386 | | return static_cast<T*>(mDelegate.get()); |
387 | | } |
388 | | }; |
389 | | |
390 | | typedef HardwareBuffer HardwareCounterBuffer; |
391 | | typedef HardwareBuffer HardwareUniformBuffer; |
392 | | |
393 | | /** Locking helper. Guaranteed unlocking even in case of exception. */ |
394 | | struct HardwareBufferLockGuard |
395 | | { |
396 | 0 | HardwareBufferLockGuard() : pBuf(0), pData(0) {} |
397 | | |
398 | | HardwareBufferLockGuard(HardwareBuffer* p, HardwareBuffer::LockOptions options) |
399 | 0 | : pBuf(0), pData(0) { lock(p, options); } |
400 | | |
401 | | HardwareBufferLockGuard(HardwareBuffer* p, size_t offset, size_t length, HardwareBuffer::LockOptions options) |
402 | 0 | : pBuf(0), pData(0) { lock(p, offset, length, options); } |
403 | | |
404 | | template <typename T> |
405 | | HardwareBufferLockGuard(const SharedPtr<T>& p, HardwareBuffer::LockOptions options) |
406 | | : pBuf(0), pData(0) { lock(p.get(), options); } |
407 | | |
408 | | template <typename T> |
409 | | HardwareBufferLockGuard(const SharedPtr<T>& p, size_t offset, size_t length, HardwareBuffer::LockOptions options) |
410 | | : pBuf(0), pData(0) { lock(p.get(), offset, length, options); } |
411 | | |
412 | 0 | ~HardwareBufferLockGuard() { unlock(); } |
413 | | |
414 | | void unlock() |
415 | 0 | { |
416 | 0 | if(pBuf) |
417 | 0 | { |
418 | 0 | pBuf->unlock(); |
419 | 0 | pBuf = 0; |
420 | 0 | pData = 0; |
421 | 0 | } |
422 | 0 | } |
423 | | |
424 | | void lock(HardwareBuffer* p, HardwareBuffer::LockOptions options) |
425 | 0 | { |
426 | 0 | assert(p); |
427 | 0 | unlock(); |
428 | 0 | pBuf = p; |
429 | 0 | pData = pBuf->lock(options); |
430 | 0 | } |
431 | | |
432 | | void lock(HardwareBuffer* p, size_t offset, size_t length, HardwareBuffer::LockOptions options) |
433 | 0 | { |
434 | 0 | assert(p); |
435 | 0 | unlock(); |
436 | 0 | pBuf = p; |
437 | 0 | pData = pBuf->lock(offset, length, options); |
438 | 0 | } |
439 | | |
440 | | template <typename T> |
441 | | void lock(const SharedPtr<T>& p, HardwareBuffer::LockOptions options) |
442 | | { lock(p.get(), options); } |
443 | | |
444 | | template <typename T> |
445 | | void lock(const SharedPtr<T>& p, size_t offset, size_t length, HardwareBuffer::LockOptions options) |
446 | | { lock(p.get(), offset, length, options); } |
447 | | |
448 | | HardwareBuffer* pBuf; |
449 | | void* pData; |
450 | | }; |
451 | | |
452 | | /** @} */ |
453 | | /** @} */ |
454 | | } |
455 | | #endif |
456 | | |
457 | | |