Skip to content

Commit 5556acc

Browse files
committed
Main: factor out generic Buffer interface to use for dumb buffers
1 parent e40a8b1 commit 5556acc

File tree

1 file changed

+152
-141
lines changed

1 file changed

+152
-141
lines changed

OgreMain/include/OgreHardwareBuffer.h

+152-141
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,140 @@ namespace Ogre {
7575
*/
7676
HBU_CPU_TO_GPU = HBU_CPU_ONLY | HBU_DETAIL_WRITE_ONLY,
7777
};
78+
/** Abstract class defining common features of unstructured memory
79+
80+
This class defines the core interface which is common to all
81+
buffers, whether it be vertex buffers, index buffers, texture memory
82+
or framebuffer memory etc.
83+
*/
84+
class _OgreExport Buffer
85+
{
86+
public:
87+
/// Locking options
88+
enum LockOptions
89+
{
90+
/** Normal mode, ie allows read/write and contents are preserved.
91+
This kind of lock allows reading and writing from the buffer - it’s also the least
92+
optimal because basically you’re telling the card you could be doing anything at
93+
all. If you’re not using a shadow buffer, it requires the buffer to be transferred
94+
from the card and back again. If you’re using a shadow buffer the effect is
95+
minimal.
96+
*/
97+
HBL_NORMAL,
98+
/** Discards the <em>entire</em> buffer while locking.
99+
This means you are happy for the card to discard the entire current contents of the
100+
buffer. Implicitly this means you are not going to read the data - it also means
101+
that the card can avoid any stalls if the buffer is currently being rendered from,
102+
because it will actually give you an entirely different one. Use this wherever
103+
possible when you are locking a buffer which was not created with a shadow buffer.
104+
If you are using a shadow buffer it matters less, although with a shadow buffer it’s
105+
preferable to lock the entire buffer at once, because that allows the shadow buffer
106+
to use HBL_DISCARD when it uploads the updated contents to the real buffer.
107+
@note Only useful on buffers created with the HBU_CPU_TO_GPU flag.
108+
*/
109+
HBL_DISCARD,
110+
/** Lock the buffer for reading only. Not allowed in buffers which are created with
111+
HBU_WRITE_ONLY.
112+
Mandatory on static buffers, i.e. those created without the HBU_DYNAMIC flag.
113+
*/
114+
HBL_READ_ONLY,
115+
/** As HBL_DISCARD, except the application guarantees not to overwrite any
116+
region of the buffer which has already been used in this frame, can allow
117+
some optimisation on some APIs.
118+
@note Only useful on buffers with no shadow buffer.*/
119+
HBL_NO_OVERWRITE,
120+
/** Lock the buffer for writing only.*/
121+
HBL_WRITE_ONLY
122+
};
123+
Buffer(size_t sizeInBytes, int usage) : mSizeInBytes(sizeInBytes), mUsage(usage), mIsLocked(false) {}
124+
125+
virtual ~Buffer() {}
126+
/** Reads data from the buffer and places it in the memory pointed to by pDest.
127+
@param offset The byte offset from the start of the buffer to read
128+
@param length The size of the area to read, in bytes
129+
@param pDest The area of memory in which to place the data, must be large enough to
130+
accommodate the data!
131+
*/
132+
virtual void readData(size_t offset, size_t length, void* pDest) /* const */ = 0;
133+
/** Writes data to the buffer from an area of system memory; note that you must
134+
ensure that your buffer is big enough.
135+
@param offset The byte offset from the start of the buffer to start writing
136+
@param length The size of the data to write to, in bytes
137+
@param pSource The source of the data to be written
138+
@param discardWholeBuffer If true, this allows the driver to discard the entire buffer when writing,
139+
such that DMA stalls can be avoided; use if you can.
140+
*/
141+
virtual void writeData(size_t offset, size_t length, const void* pSource, bool discardWholeBuffer = false) = 0;
142+
143+
/** Copy data from another buffer into this one.
144+
@remarks
145+
Note that the source buffer must not be created with the
146+
usage HBU_WRITE_ONLY otherwise this will fail.
147+
@param srcBuffer The buffer from which to read the copied data
148+
@param srcOffset Offset in the source buffer at which to start reading
149+
@param dstOffset Offset in the destination buffer to start writing
150+
@param length Length of the data to copy, in bytes.
151+
@param discardWholeBuffer If true, will discard the entire contents of this buffer before copying
152+
*/
153+
virtual void copyData(HardwareBuffer& _srcBuffer, size_t srcOffset, size_t dstOffset, size_t length,
154+
bool discardWholeBuffer = false)
155+
{
156+
auto& srcBuffer = (Buffer&)_srcBuffer; // backward compat
157+
const void* srcData = srcBuffer.lock(srcOffset, length, HBL_READ_ONLY);
158+
this->writeData(dstOffset, length, srcData, discardWholeBuffer);
159+
srcBuffer.unlock();
160+
}
161+
162+
/** Copy all data from another buffer into this one.
163+
@remarks
164+
Normally these buffers should be of identical size, but if they're
165+
not, the routine will use the smallest of the two sizes.
166+
*/
167+
void copyData(HardwareBuffer& _srcBuffer)
168+
{
169+
auto& srcBuffer = (Buffer&)_srcBuffer; // backward compat
170+
size_t sz = std::min(getSizeInBytes(), srcBuffer.getSizeInBytes());
171+
copyData(_srcBuffer, 0, 0, sz, true);
172+
}
173+
174+
/** Lock the buffer for (potentially) reading / writing.
175+
@param offset The byte offset from the start of the buffer to lock
176+
@param length The size of the area to lock, in bytes
177+
@param options Locking options
178+
@return Pointer to the locked memory
179+
*/
180+
virtual void* lock(size_t offset, size_t length, LockOptions options) = 0;
181+
182+
/// @overload
183+
void* lock(LockOptions options) { return this->lock(0, mSizeInBytes, options); }
184+
185+
/** Releases the lock on this buffer.
186+
@remarks
187+
Locking and unlocking a buffer can, in some rare circumstances such as
188+
switching video modes whilst the buffer is locked, corrupt the
189+
contents of a buffer. This is pretty rare, but if it occurs,
190+
this method will throw an exception, meaning you
191+
must re-upload the data.
192+
@par
193+
Note that using the 'read' and 'write' forms of updating the buffer does not
194+
suffer from this problem, so if you want to be 100% sure your
195+
data will not be lost, use the 'read' and 'write' forms instead.
196+
*/
197+
virtual void unlock() = 0;
198+
199+
/// Returns whether or not this buffer is currently locked.
200+
virtual bool isLocked() const { return mIsLocked; }
201+
/// Returns the size of this buffer in bytes
202+
size_t getSizeInBytes(void) const { return mSizeInBytes; }
203+
/// Returns the Usage flags with which this buffer was created
204+
int getUsage(void) const { return mUsage; }
205+
protected:
206+
size_t mSizeInBytes;
207+
int mUsage;
208+
bool mIsLocked;
209+
};
210+
211+
78212
/** Abstract class defining common features of hardware buffers.
79213
@remarks
80214
A 'hardware buffer' is any area of memory held outside of core system ram,
@@ -106,7 +240,7 @@ namespace Ogre {
106240
You should look for the 'useShadowBuffer' parameter on the creation methods used to create
107241
the buffer of the type you require (see HardwareBufferManager) to enable this feature.
108242
*/
109-
class _OgreExport HardwareBuffer : public BufferAlloc
243+
class _OgreExport HardwareBuffer : public Buffer
110244
{
111245

112246
public:
@@ -129,50 +263,10 @@ namespace Ogre {
129263
/// @deprecated do not use
130264
HBU_DYNAMIC_WRITE_ONLY_DISCARDABLE = HBU_CPU_TO_GPU,
131265
};
132-
/// Locking options
133-
enum LockOptions
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_WRITE_ONLY.
157-
Mandatory on static buffers, i.e. those created without the HBU_DYNAMIC flag.
158-
*/
159-
HBL_READ_ONLY,
160-
/** As HBL_DISCARD, 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-
};
169266
protected:
170-
size_t mSizeInBytes;
171-
Usage mUsage;
172-
bool mIsLocked;
173267
size_t mLockStart;
174268
size_t mLockSize;
175-
std::unique_ptr<HardwareBuffer> mShadowBuffer;
269+
std::unique_ptr<Buffer> mShadowBuffer;
176270
bool mSystemMemory;
177271
bool mUseShadowBuffer;
178272
bool mShadowUpdated;
@@ -186,7 +280,7 @@ namespace Ogre {
186280
public:
187281
/// Constructor, to be called by HardwareBufferManager only
188282
HardwareBuffer(Usage usage, bool systemMemory, bool useShadowBuffer)
189-
: mSizeInBytes(0), mUsage(usage), mIsLocked(false), mLockStart(0), mLockSize(0), mSystemMemory(systemMemory),
283+
: Buffer(0, usage), mLockStart(0), mLockSize(0), mSystemMemory(systemMemory),
190284
mUseShadowBuffer(useShadowBuffer), mShadowUpdated(false),
191285
mSuppressHardwareUpdate(false)
192286
{
@@ -200,14 +294,10 @@ namespace Ogre {
200294
mUsage = HBU_STATIC_WRITE_ONLY;
201295
}
202296
}
203-
virtual ~HardwareBuffer() {}
204-
/** Lock the buffer for (potentially) reading / writing.
205-
@param offset The byte offset from the start of the buffer to lock
206-
@param length The size of the area to lock, in bytes
207-
@param options Locking options
208-
@return Pointer to the locked memory
209-
*/
210-
virtual void* lock(size_t offset, size_t length, LockOptions options)
297+
298+
using Buffer::lock;
299+
300+
void* lock(size_t offset, size_t length, LockOptions options) override
211301
{
212302
OgreAssert(!isLocked(), "Cannot lock this buffer: it is already locked");
213303
OgreAssert((length + offset) <= mSizeInBytes, "Lock request out of bounds");
@@ -235,24 +325,7 @@ namespace Ogre {
235325
return ret;
236326
}
237327

238-
/// @overload
239-
void* lock(LockOptions options)
240-
{
241-
return this->lock(0, mSizeInBytes, options);
242-
}
243-
/** Releases the lock on this buffer.
244-
@remarks
245-
Locking and unlocking a buffer can, in some rare circumstances such as
246-
switching video modes whilst the buffer is locked, corrupt the
247-
contents of a buffer. This is pretty rare, but if it occurs,
248-
this method will throw an exception, meaning you
249-
must re-upload the data.
250-
@par
251-
Note that using the 'read' and 'write' forms of updating the buffer does not
252-
suffer from this problem, so if you want to be 100% sure your
253-
data will not be lost, use the 'read' and 'write' forms instead.
254-
*/
255-
virtual void unlock(void)
328+
void unlock(void) override
256329
{
257330
OgreAssert(isLocked(), "Cannot unlock this buffer: it is not locked");
258331

@@ -272,89 +345,27 @@ namespace Ogre {
272345

273346
}
274347

275-
/** Reads data from the buffer and places it in the memory pointed to by pDest.
276-
@param offset The byte offset from the start of the buffer to read
277-
@param length The size of the area to read, in bytes
278-
@param pDest The area of memory in which to place the data, must be large enough to
279-
accommodate the data!
280-
*/
281-
virtual void readData(size_t offset, size_t length, void* pDest) = 0;
282-
/** Writes data to the buffer from an area of system memory; note that you must
283-
ensure that your buffer is big enough.
284-
@param offset The byte offset from the start of the buffer to start writing
285-
@param length The size of the data to write to, in bytes
286-
@param pSource The source of the data to be written
287-
@param discardWholeBuffer If true, this allows the driver to discard the entire buffer when writing,
288-
such that DMA stalls can be avoided; use if you can.
289-
*/
290-
virtual void writeData(size_t offset, size_t length, const void* pSource,
291-
bool discardWholeBuffer = false) = 0;
292-
293-
/** Copy data from another buffer into this one.
294-
@remarks
295-
Note that the source buffer must not be created with the
296-
usage HBU_WRITE_ONLY otherwise this will fail.
297-
@param srcBuffer The buffer from which to read the copied data
298-
@param srcOffset Offset in the source buffer at which to start reading
299-
@param dstOffset Offset in the destination buffer to start writing
300-
@param length Length of the data to copy, in bytes.
301-
@param discardWholeBuffer If true, will discard the entire contents of this buffer before copying
302-
*/
303-
virtual void copyData(HardwareBuffer& srcBuffer, size_t srcOffset,
304-
size_t dstOffset, size_t length, bool discardWholeBuffer = false)
305-
{
306-
const void *srcData = srcBuffer.lock(
307-
srcOffset, length, HBL_READ_ONLY);
308-
this->writeData(dstOffset, length, srcData, discardWholeBuffer);
309-
srcBuffer.unlock();
310-
}
311-
312-
/** Copy all data from another buffer into this one.
313-
@remarks
314-
Normally these buffers should be of identical size, but if they're
315-
not, the routine will use the smallest of the two sizes.
316-
*/
317-
virtual void copyData(HardwareBuffer& srcBuffer)
318-
{
319-
size_t sz = std::min(getSizeInBytes(), srcBuffer.getSizeInBytes());
320-
copyData(srcBuffer, 0, 0, sz, true);
321-
}
322-
323348
/// Updates the real buffer from the shadow buffer, if required
324349
virtual void _updateFromShadow(void)
325350
{
326351
if (mUseShadowBuffer && mShadowUpdated && !mSuppressHardwareUpdate)
327352
{
328-
// Do this manually to avoid locking problems
329-
const void *srcData = mShadowBuffer->lockImpl(
330-
mLockStart, mLockSize, HBL_READ_ONLY);
331353
// Lock with discard if the whole buffer was locked, otherwise w/o
332-
LockOptions lockOpt;
333-
if (mLockStart == 0 && mLockSize == mSizeInBytes)
334-
lockOpt = HBL_DISCARD;
335-
else
336-
lockOpt = HBL_WRITE_ONLY;
337-
338-
void *destData = this->lockImpl(
339-
mLockStart, mLockSize, lockOpt);
354+
LockOptions lockOpt = mLockSize == mSizeInBytes ? HBL_DISCARD : HBL_WRITE_ONLY;
355+
// Do this manually to avoid locking problems
356+
void* destData = this->lockImpl(mLockStart, mLockSize, lockOpt);
340357
// Copy shadow to real
341-
memcpy(destData, srcData, mLockSize);
358+
mShadowBuffer->readData(mLockStart, mLockSize, destData);
342359
this->unlockImpl();
343-
mShadowBuffer->unlockImpl();
344360
mShadowUpdated = false;
345361
}
346362
}
347363

348-
/// Returns the size of this buffer in bytes
349-
size_t getSizeInBytes(void) const { return mSizeInBytes; }
350-
/// Returns the Usage flags with which this buffer was created
351-
Usage getUsage(void) const { return mUsage; }
352364
/// Returns whether this buffer is held in system memory
353365
bool isSystemMemory(void) const { return mSystemMemory; }
354366
/// Returns whether this buffer has a system memory shadow for quicker reading
355367
bool hasShadowBuffer(void) const { return mUseShadowBuffer; }
356-
/// Returns whether or not this buffer is currently locked.
357-
bool isLocked(void) const {
368+
bool isLocked(void) const override {
358369
return mIsLocked || (mUseShadowBuffer && mShadowBuffer->isLocked());
359370
}
360371
/// Pass true to suppress hardware upload of shadow buffer changes
@@ -375,10 +386,10 @@ namespace Ogre {
375386
{
376387
HardwareBufferLockGuard() : pBuf(0), pData(0) {}
377388

378-
HardwareBufferLockGuard(HardwareBuffer* p, HardwareBuffer::LockOptions options)
389+
HardwareBufferLockGuard(Buffer* p, HardwareBuffer::LockOptions options)
379390
: pBuf(0), pData(0) { lock(p, options); }
380391

381-
HardwareBufferLockGuard(HardwareBuffer* p, size_t offset, size_t length, HardwareBuffer::LockOptions options)
392+
HardwareBufferLockGuard(Buffer* p, size_t offset, size_t length, HardwareBuffer::LockOptions options)
382393
: pBuf(0), pData(0) { lock(p, offset, length, options); }
383394

384395
template <typename T>
@@ -401,15 +412,15 @@ namespace Ogre {
401412
}
402413
}
403414

404-
void lock(HardwareBuffer* p, HardwareBuffer::LockOptions options)
415+
void lock(Buffer* p, HardwareBuffer::LockOptions options)
405416
{
406417
assert(p);
407418
unlock();
408419
pBuf = p;
409420
pData = pBuf->lock(options);
410421
}
411422

412-
void lock(HardwareBuffer* p, size_t offset, size_t length, HardwareBuffer::LockOptions options)
423+
void lock(Buffer* p, size_t offset, size_t length, HardwareBuffer::LockOptions options)
413424
{
414425
assert(p);
415426
unlock();
@@ -425,7 +436,7 @@ namespace Ogre {
425436
void lock(const SharedPtr<T>& p, size_t offset, size_t length, HardwareBuffer::LockOptions options)
426437
{ lock(p.get(), offset, length, options); }
427438

428-
HardwareBuffer* pBuf;
439+
Buffer* pBuf;
429440
void* pData;
430441
};
431442

0 commit comments

Comments
 (0)