mirror of
https://github.com/baldurk/renderdoc.git
synced 2026-08-07 23:31:13 +00:00
Persistent Map()s part 4 - implement persistent map interception
* Should support coherent & non-coherent persistent maps (coherent coming with a fair performance hit). * Slightly changed the method from the one planned - coherent maps don't need to actually be mapped coherently. Since we're inserting a manual sync & copy point all over the place, we can map the underlying GL buffer as non-coherent and flush explicit, then just do the flushes when we do the copies. * Still need to add all those implicit sync point calls, and I'm sure there are plenty of bugs in this implementation.
This commit is contained in:
@@ -155,6 +155,24 @@ class WrappedOpenGL
|
||||
|
||||
set<ResourceId> m_HighTrafficResources;
|
||||
|
||||
// we store two separate sets of maps, since for an explicit glMemoryBarrier
|
||||
// we need to flush both types of maps, but for implicit sync points we only
|
||||
// want to consider coherent maps, and since that happens often we want it to
|
||||
// be as efficient as possible.
|
||||
set<GLResourceRecord*> m_CoherentMaps;
|
||||
set<GLResourceRecord*> m_PersistentMaps;
|
||||
|
||||
// this function iterates over all the maps, checking for any changes between
|
||||
// the shadow pointers, and propogates that to 'real' GL
|
||||
void PersistentMapMemoryBarrier(const set<GLResourceRecord *> &maps);
|
||||
|
||||
// this function is called at any point that could possibly pick up a change
|
||||
// in a coherent persistent mapped buffer, to propogate changes across. In most
|
||||
// cases hopefully m_CoherentMaps will be empty so this will amount to an inlined
|
||||
// check and jump
|
||||
inline void CoherentMapImplicitBarrier()
|
||||
{ if(!m_CoherentMaps.empty()) PersistentMapMemoryBarrier(m_CoherentMaps); }
|
||||
|
||||
vector<FetchFrameRecord> m_FrameRecord;
|
||||
|
||||
const FetchDrawcall *GetDrawcall(const FetchDrawcall *draw, uint32_t eventID);
|
||||
@@ -1140,6 +1158,8 @@ class WrappedOpenGL
|
||||
void Common_glTextureParameterIivEXT(GLResourceRecord *record, GLenum target, GLenum pname, const GLint *params);
|
||||
void Common_glTextureParameterIuivEXT(GLResourceRecord *record, GLenum target, GLenum pname, const GLuint *params);
|
||||
|
||||
void Common_glNamedBufferStorageEXT(ResourceId id, GLsizeiptr size, const void *data, GLbitfield flags);
|
||||
|
||||
IMPLEMENT_FUNCTION_SERIALISED(GLenum, glCheckNamedFramebufferStatusEXT(GLuint framebuffer, GLenum target));
|
||||
IMPLEMENT_FUNCTION_SERIALISED(void, glCompressedTextureImage1DEXT(GLuint texture, GLenum target, GLint level, GLenum internalformat, GLsizei width, GLint border, GLsizei imageSize, const void *bits));
|
||||
IMPLEMENT_FUNCTION_SERIALISED(void, glCompressedTextureImage2DEXT(GLuint texture, GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLsizei imageSize, const void *bits));
|
||||
|
||||
@@ -138,6 +138,7 @@ struct GLResourceRecord : public ResourceRecord
|
||||
usage(eGL_NONE)
|
||||
{
|
||||
RDCEraseEl(ShadowPtr);
|
||||
RDCEraseEl(Map);
|
||||
}
|
||||
|
||||
~GLResourceRecord()
|
||||
@@ -161,6 +162,9 @@ struct GLResourceRecord : public ResourceRecord
|
||||
MapStatus status;
|
||||
bool invalidate;
|
||||
byte *ptr;
|
||||
|
||||
byte *persistentPtr;
|
||||
int64_t persistentMaps; // counter indicating how many coherent maps are 'live'
|
||||
} Map;
|
||||
|
||||
void VerifyDataType(GLenum target)
|
||||
|
||||
@@ -343,17 +343,15 @@ bool WrappedOpenGL::Serialise_glNamedBufferStorageEXT(GLuint buffer, GLsizeiptr
|
||||
return true;
|
||||
}
|
||||
|
||||
void WrappedOpenGL::glNamedBufferStorageEXT(GLuint buffer, GLsizeiptr size, const void *data, GLbitfield flags)
|
||||
void WrappedOpenGL::Common_glNamedBufferStorageEXT(ResourceId id, GLsizeiptr size, const void *data, GLbitfield flags)
|
||||
{
|
||||
m_Real.glNamedBufferStorageEXT(buffer, size, data, flags);
|
||||
|
||||
if(m_State >= WRITING)
|
||||
{
|
||||
GLResourceRecord *record = GetResourceManager()->GetResourceRecord(BufferRes(GetCtx(), buffer));
|
||||
GLResourceRecord *record = GetResourceManager()->GetResourceRecord(id);
|
||||
RDCASSERT(record);
|
||||
|
||||
SCOPED_SERIALISE_CONTEXT(BUFFERSTORAGE);
|
||||
Serialise_glNamedBufferStorageEXT(buffer, size, data, flags);
|
||||
Serialise_glNamedBufferStorageEXT(record->Resource.name, size, data, flags);
|
||||
|
||||
// for satisfying GL_MIN_MAP_BUFFER_ALIGNMENT
|
||||
scope.SetAlignment(64);
|
||||
@@ -365,13 +363,35 @@ void WrappedOpenGL::glNamedBufferStorageEXT(GLuint buffer, GLsizeiptr size, cons
|
||||
record->SetDataPtr(chunk->GetData());
|
||||
record->Length = (int32_t)size;
|
||||
}
|
||||
|
||||
// We immediately map the whole range with appropriate flags, to be copied into whenever we
|
||||
// need to propogate changes. Note: Coherent buffers are not mapped coherent, but this is
|
||||
// because the user code isn't writing into them anyway and we're inserting invisible sync
|
||||
// points - so there's no need for it to be coherently mapped (and there's no requirement
|
||||
// that a buffer declared as coherent must ALWAYS be mapped as coherent).
|
||||
if(flags & GL_MAP_PERSISTENT_BIT)
|
||||
{
|
||||
record->Map.persistentPtr = (byte *)m_Real.glMapNamedBufferRangeEXT(record->Resource.name, 0, size,
|
||||
GL_MAP_WRITE_BIT|GL_MAP_FLUSH_EXPLICIT_BIT|GL_MAP_PERSISTENT_BIT);
|
||||
RDCASSERT(record->Map.persistentPtr);
|
||||
|
||||
// persistent maps always need both sets of shadow storage, so allocate up front.
|
||||
record->AllocShadowStorage(size, 64);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
m_Buffers[GetResourceManager()->GetID(BufferRes(GetCtx(), buffer))].size = size;
|
||||
m_Buffers[id].size = size;
|
||||
}
|
||||
}
|
||||
|
||||
void WrappedOpenGL::glNamedBufferStorageEXT(GLuint buffer, GLsizeiptr size, const void *data, GLbitfield flags)
|
||||
{
|
||||
m_Real.glNamedBufferStorageEXT(buffer, size, data, flags);
|
||||
|
||||
Common_glNamedBufferStorageEXT(GetResourceManager()->GetID(BufferRes(GetCtx(), buffer)), size, data, flags);
|
||||
}
|
||||
|
||||
void WrappedOpenGL::glNamedBufferStorage(GLuint buffer, GLsizei size, const void *data, GLbitfield flags)
|
||||
{
|
||||
// only difference to EXT function is size parameter, so just upcast
|
||||
@@ -382,32 +402,10 @@ void WrappedOpenGL::glBufferStorage(GLenum target, GLsizeiptr size, const void *
|
||||
{
|
||||
m_Real.glBufferStorage(target, size, data, flags);
|
||||
|
||||
size_t idx = BufferIdx(target);
|
||||
|
||||
if(m_State >= WRITING)
|
||||
{
|
||||
GLResourceRecord *record = GetCtxData().m_BufferRecord[BufferIdx(target)];
|
||||
RDCASSERT(record);
|
||||
|
||||
SCOPED_SERIALISE_CONTEXT(BUFFERSTORAGE);
|
||||
Serialise_glNamedBufferStorageEXT(record->Resource.name,
|
||||
size, data, flags);
|
||||
|
||||
// for satisfying GL_MIN_MAP_BUFFER_ALIGNMENT
|
||||
scope.SetAlignment(64);
|
||||
|
||||
Chunk *chunk = scope.Get();
|
||||
|
||||
{
|
||||
record->AddChunk(chunk);
|
||||
record->SetDataPtr(chunk->GetData());
|
||||
record->Length = (int32_t)size;
|
||||
}
|
||||
}
|
||||
Common_glNamedBufferStorageEXT(GetCtxData().m_BufferRecord[BufferIdx(target)]->GetResourceID(), size, data, flags);
|
||||
else
|
||||
{
|
||||
RDCERR("Internal buffers should be allocated via dsa interfaces");
|
||||
}
|
||||
}
|
||||
|
||||
bool WrappedOpenGL::Serialise_glNamedBufferDataEXT(GLuint buffer, GLsizeiptr size, const void *data, GLenum usage)
|
||||
@@ -1485,14 +1483,8 @@ void WrappedOpenGL::glInvalidateBufferSubData(GLuint buffer, GLintptr offset, GL
|
||||
* bit is passed to glMemoryBarrier, we manually call into glFlushMappedNamedBufferRangeEXT() with the
|
||||
* appropriate parameters and handling is otherwise identical.
|
||||
*
|
||||
* The final piece of the puzzle is coherent mapped buffers. Since coherent maps have a cost (there may
|
||||
* only be a limited amount of memory available to act as coherent mapped memory, in addition to it
|
||||
* having a performance cost) we don't do the same as above where we map immediately on creation.
|
||||
* Instead we perform real maps in tandem with user maps, but otherwise behave the same as above - the
|
||||
* pointer returned to the user is a pointer to our backing store, and we keep the pointer to the 'real'
|
||||
* mapped memory internal and perform the copies as above. Note we map the whole buffer even if user
|
||||
* code only maps a range, to simplify the case where a second map comes along for a non-overlapping
|
||||
* range (the pointer can be reused).
|
||||
* The final piece of the puzzle is coherent mapped buffers. Since we must break the coherency carefully
|
||||
* (see below), we map coherent buffers as non-coherent at creation time, the same as above.
|
||||
*
|
||||
* To satisfy the demands of being coherent, we need to transparently propogate any changes between the
|
||||
* user written data and the 'real' memory, without any call to intercept - there would be no need to
|
||||
@@ -1502,6 +1494,9 @@ void WrappedOpenGL::glInvalidateBufferSubData(GLuint buffer, GLintptr offset, GL
|
||||
* call that could depend on the results of the buffer. We then check if any write/change has happened
|
||||
* by comparing to the shadow storage, and if so we perform a manual flush of that changed region and
|
||||
* update the shadow storage for next time.
|
||||
*
|
||||
* This "fake coherency" is the reason we can map the buffer as non-coherent, since we will be performing
|
||||
* copies and flushes manually to emulate the coherency to allow our interception in the middle.
|
||||
*
|
||||
* By definition, there will be *many* of these places where the buffer results could be used, not least
|
||||
* any buffer copy, any texture copy (since a texture buffer could be created), any draw or dispatch,
|
||||
@@ -1519,6 +1514,16 @@ void WrappedOpenGL::glInvalidateBufferSubData(GLuint buffer, GLintptr offset, GL
|
||||
* performing a read-back at every sync point to find every change. Which by itself may also hide race
|
||||
* conditions anyway.
|
||||
*
|
||||
*
|
||||
* Implementation notes:
|
||||
*
|
||||
* The record->Map.ptr is the *offsetted* pointer, ie. a pointer to the beginning of the mapped region,
|
||||
* at record->Map.offset bytes from the start of the buffer.
|
||||
*
|
||||
* record->Map.persistentPtr points to the *base* of the buffer, not offsetted by any current map.
|
||||
*
|
||||
* Likewise the shadow storage pointers point to the base of a buffer-sized allocation each.
|
||||
*
|
||||
************************************************************************/
|
||||
|
||||
void *WrappedOpenGL::glMapNamedBufferRangeEXT(GLuint buffer, GLintptr offset, GLsizeiptr length, GLbitfield access)
|
||||
@@ -1543,8 +1548,14 @@ void *WrappedOpenGL::glMapNamedBufferRangeEXT(GLuint buffer, GLintptr offset, GL
|
||||
|
||||
// if this map is writing and doesn't invalidate, or is flush explicit, map directly
|
||||
if(!directMap && (!invalidateMap || flushExplicitMap) && (access & GL_MAP_WRITE_BIT) && m_State != WRITING_CAPFRAME)
|
||||
{
|
||||
directMap = true;
|
||||
|
||||
// persistent maps must ALWAYS be intercepted
|
||||
if(access & GL_MAP_PERSISTENT_BIT)
|
||||
directMap = false;
|
||||
|
||||
if(directMap)
|
||||
{
|
||||
m_HighTrafficResources.insert(record->GetResourceID());
|
||||
GetResourceManager()->MarkDirtyResource(record->GetResourceID());
|
||||
}
|
||||
@@ -1553,9 +1564,15 @@ void *WrappedOpenGL::glMapNamedBufferRangeEXT(GLuint buffer, GLintptr offset, GL
|
||||
record->Map.length = length;
|
||||
record->Map.access = access;
|
||||
record->Map.invalidate = invalidateMap;
|
||||
|
||||
if((access & (GL_MAP_COHERENT_BIT|GL_MAP_PERSISTENT_BIT)) != 0)
|
||||
RDCUNIMPLEMENTED("haven't implemented persistant glMap calls");
|
||||
|
||||
// store a list of all persistent maps, and subset of all coherent maps
|
||||
if(access & GL_MAP_PERSISTENT_BIT)
|
||||
{
|
||||
Atomic::Inc64(&record->Map.persistentMaps);
|
||||
m_PersistentMaps.insert(record);
|
||||
if(record->Map.access & GL_MAP_COHERENT_BIT)
|
||||
m_CoherentMaps.insert(record);
|
||||
}
|
||||
|
||||
// if we're doing a direct map, pass onto GL and return
|
||||
if(directMap)
|
||||
@@ -1571,12 +1588,16 @@ void *WrappedOpenGL::glMapNamedBufferRangeEXT(GLuint buffer, GLintptr offset, GL
|
||||
{
|
||||
byte *ptr = record->GetDataPtr();
|
||||
|
||||
if(record->Map.persistentPtr)
|
||||
ptr = record->GetShadowPtr(0);
|
||||
|
||||
RDCASSERT(ptr);
|
||||
|
||||
ptr += offset;
|
||||
|
||||
m_Real.glGetNamedBufferSubDataEXT(buffer, offset, length, ptr);
|
||||
|
||||
|
||||
record->Map.ptr = ptr;
|
||||
record->Map.status = GLResourceRecord::Mapped_Read;
|
||||
|
||||
return ptr;
|
||||
@@ -1587,14 +1608,25 @@ void *WrappedOpenGL::glMapNamedBufferRangeEXT(GLuint buffer, GLintptr offset, GL
|
||||
|
||||
RDCASSERT(ptr);
|
||||
{
|
||||
// flush explicit maps are handled particularly:
|
||||
// if we're idle, we just return the backing pointer and treat it no differently to a normal write map,
|
||||
// as modified-but-unflushed ranges are "undefined", so we can easily just let them
|
||||
// be modified.
|
||||
// if we're capframing, we won't create a normal unmap chunk that copies over all the data with a
|
||||
// given diff range. Instead we'll create a flush chunk for every glFlushMappedBufferRange.
|
||||
// persistent maps get particular handling
|
||||
if(access & GL_MAP_PERSISTENT_BIT)
|
||||
{
|
||||
// persistent pointers are always into the shadow storage, this way we can use the backing
|
||||
// store for 'initial' buffer contents as with any other buffer. We also need to keep a
|
||||
// comparison & modified buffer in case the application calls glMemoryBarrier(..) at any
|
||||
// time.
|
||||
|
||||
if(m_State == WRITING_CAPFRAME)
|
||||
// if we're invalidating, mark the whole range as 0xcc
|
||||
if(invalidateMap)
|
||||
{
|
||||
memset(record->GetShadowPtr(0)+offset, 0xcc, length);
|
||||
memset(record->GetShadowPtr(1)+offset, 0xcc, length);
|
||||
}
|
||||
|
||||
record->Map.ptr = ptr = record->GetShadowPtr(0)+offset;
|
||||
record->Map.status = GLResourceRecord::Mapped_Write;
|
||||
}
|
||||
else if(m_State == WRITING_CAPFRAME)
|
||||
{
|
||||
byte *shadow = (byte *)record->GetShadowPtr(0);
|
||||
|
||||
@@ -1637,7 +1669,7 @@ void *WrappedOpenGL::glMapNamedBufferRangeEXT(GLuint buffer, GLintptr offset, GL
|
||||
record->Map.ptr = ptr = shadow;
|
||||
record->Map.status = GLResourceRecord::Mapped_Write;
|
||||
}
|
||||
else // if(m_State == WRITING_IDLE)
|
||||
else if(m_State == WRITING_IDLE)
|
||||
{
|
||||
// return buffer backing store pointer, offsetted
|
||||
ptr += offset;
|
||||
@@ -1800,9 +1832,21 @@ bool WrappedOpenGL::Serialise_glUnmapNamedBufferEXT(GLuint buffer)
|
||||
|
||||
if(DiffEnd > DiffStart)
|
||||
{
|
||||
void *ptr = m_Real.glMapNamedBufferRangeEXT(buffer, (GLintptr)(offs+DiffStart), GLsizeiptr(DiffEnd-DiffStart), GL_MAP_WRITE_BIT);
|
||||
memcpy(ptr, data, size_t(DiffEnd-DiffStart));
|
||||
m_Real.glUnmapNamedBufferEXT(buffer);
|
||||
if(record && record->Map.persistentPtr)
|
||||
{
|
||||
// if we have a persistent mapped pointer, copy the range into the 'real' memory and
|
||||
// do a flush. Note the persistent pointer is always to the base of the buffer so we
|
||||
// need to account for the offset
|
||||
|
||||
memcpy(record->Map.persistentPtr+offs+DiffStart, record->Map.ptr+DiffStart, DiffEnd-DiffStart);
|
||||
m_Real.glFlushMappedNamedBufferRangeEXT(buffer, GLintptr(offs+DiffStart), DiffEnd-DiffStart);
|
||||
}
|
||||
else
|
||||
{
|
||||
void *ptr = m_Real.glMapNamedBufferRangeEXT(buffer, (GLintptr)(offs+DiffStart), GLsizeiptr(DiffEnd-DiffStart), GL_MAP_WRITE_BIT);
|
||||
memcpy(ptr, data, size_t(DiffEnd-DiffStart));
|
||||
m_Real.glUnmapNamedBufferEXT(buffer);
|
||||
}
|
||||
}
|
||||
|
||||
if(m_State < WRITING)
|
||||
@@ -1838,33 +1882,60 @@ GLboolean WrappedOpenGL::glUnmapNamedBufferEXT(GLuint buffer)
|
||||
break;
|
||||
case GLResourceRecord::Mapped_Write:
|
||||
{
|
||||
if(m_State == WRITING_CAPFRAME)
|
||||
if(record->Map.access & GL_MAP_FLUSH_EXPLICIT_BIT)
|
||||
{
|
||||
if(record->Map.access & GL_MAP_FLUSH_EXPLICIT_BIT)
|
||||
// do nothing, any flushes that happened were handled,
|
||||
// and we won't do any other updates here or make a chunk.
|
||||
}
|
||||
else if(m_State == WRITING_CAPFRAME)
|
||||
{
|
||||
SCOPED_SERIALISE_CONTEXT(UNMAP);
|
||||
Serialise_glUnmapNamedBufferEXT(buffer);
|
||||
m_ContextRecord->AddChunk(scope.Get());
|
||||
}
|
||||
else if(m_State == WRITING_IDLE)
|
||||
{
|
||||
if(record->Map.persistentPtr)
|
||||
{
|
||||
// do nothing, any flushes that happened were handled,
|
||||
// and we won't do any other updates here or make a chunk.
|
||||
// if we have a persistent mapped pointer, copy the range into the 'real' memory and
|
||||
// do a flush. Note the persistent pointer is always to the base of the buffer so we
|
||||
// need to account for the offset
|
||||
|
||||
memcpy(record->Map.persistentPtr+record->Map.offset, record->Map.ptr, record->Map.length);
|
||||
m_Real.glFlushMappedNamedBufferRangeEXT(buffer, record->Map.offset, record->Map.length);
|
||||
|
||||
// update shadow storage
|
||||
memcpy(record->GetShadowPtr(1)+record->Map.offset, record->Map.ptr, record->Map.length);
|
||||
|
||||
GetResourceManager()->MarkDirtyResource(record->GetResourceID());
|
||||
}
|
||||
else
|
||||
{
|
||||
SCOPED_SERIALISE_CONTEXT(UNMAP);
|
||||
Serialise_glUnmapNamedBufferEXT(buffer);
|
||||
m_ContextRecord->AddChunk(scope.Get());
|
||||
// if we are here for WRITING_IDLE, the app wrote directly into our backing
|
||||
// store memory. Just need to copy the data across to GL, no other work needed
|
||||
void *ptr = m_Real.glMapNamedBufferRangeEXT(buffer, (GLintptr)record->Map.offset, GLsizeiptr(record->Map.length), GL_MAP_WRITE_BIT);
|
||||
memcpy(ptr, record->Map.ptr, record->Map.length);
|
||||
m_Real.glUnmapNamedBufferEXT(buffer);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// if we are here for WRITING_IDLE, the app wrote directly into our backing
|
||||
// store memory. Just need to copy the data across to GL, no other work needed
|
||||
void *ptr = m_Real.glMapNamedBufferRangeEXT(buffer, (GLintptr)record->Map.offset, GLsizeiptr(record->Map.length), GL_MAP_WRITE_BIT);
|
||||
memcpy(ptr, record->Map.ptr, record->Map.length);
|
||||
m_Real.glUnmapNamedBufferEXT(buffer);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// keep list of persistent & coherent maps up to date if we've
|
||||
// made the last unmap to a buffer
|
||||
if(record->Map.access & GL_MAP_PERSISTENT_BIT)
|
||||
{
|
||||
int64_t ref = Atomic::Dec64(&record->Map.persistentMaps);
|
||||
if(ref == 0)
|
||||
{
|
||||
m_PersistentMaps.erase(record);
|
||||
if(record->Map.access & GL_MAP_COHERENT_BIT)
|
||||
m_CoherentMaps.erase(record);
|
||||
}
|
||||
}
|
||||
|
||||
record->Map.status = GLResourceRecord::Unmapped;
|
||||
|
||||
return ret;
|
||||
@@ -1920,11 +1991,23 @@ bool WrappedOpenGL::Serialise_glFlushMappedNamedBufferRangeEXT(GLuint buffer, GL
|
||||
res = GetResourceManager()->GetLiveResource(ID);
|
||||
else
|
||||
res = GetResourceManager()->GetCurrentResource(ID);
|
||||
|
||||
if(record && record->Map.persistentPtr)
|
||||
{
|
||||
// if we have a persistent mapped pointer, copy the range into the 'real' memory and
|
||||
// do a flush. Note the persistent pointer is always to the base of the buffer so we
|
||||
// need to account for the offset
|
||||
|
||||
// perform a map of the range and copy the data, to emulate the modified region being flushed
|
||||
void *ptr = m_Real.glMapNamedBufferRangeEXT(res.name, (GLintptr)offs, (GLsizeiptr)len, GL_MAP_WRITE_BIT);
|
||||
memcpy(ptr, data, (size_t)len);
|
||||
m_Real.glUnmapNamedBufferEXT(res.name);
|
||||
memcpy(record->Map.persistentPtr+offs, record->Map.ptr - record->Map.offset + offs, (size_t)len);
|
||||
m_Real.glFlushMappedNamedBufferRangeEXT(buffer, (GLintptr)offs, (GLsizeiptr)len);
|
||||
}
|
||||
else
|
||||
{
|
||||
// perform a map of the range and copy the data, to emulate the modified region being flushed
|
||||
void *ptr = m_Real.glMapNamedBufferRangeEXT(res.name, (GLintptr)offs, (GLsizeiptr)len, GL_MAP_WRITE_BIT);
|
||||
memcpy(ptr, data, (size_t)len);
|
||||
m_Real.glUnmapNamedBufferEXT(res.name);
|
||||
}
|
||||
|
||||
if(m_State < WRITING)
|
||||
SAFE_DELETE_ARRAY(data);
|
||||
@@ -1991,6 +2074,18 @@ void WrappedOpenGL::glFlushMappedNamedBufferRangeEXT(GLuint buffer, GLintptr off
|
||||
// other statuses is GLResourceRecord::Mapped_Read
|
||||
}
|
||||
}
|
||||
else if(m_State == WRITING_IDLE)
|
||||
{
|
||||
// if this is a flush of a persistent map, we need to copy through to
|
||||
// the real pointer and perform a real flush.
|
||||
if(record && record->Map.persistentPtr)
|
||||
{
|
||||
memcpy(record->Map.persistentPtr+offset, record->Map.ptr - record->Map.offset + offset, length);
|
||||
m_Real.glFlushMappedNamedBufferRangeEXT(buffer, offset, length);
|
||||
|
||||
GetResourceManager()->MarkDirtyResource(record->GetResourceID());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void WrappedOpenGL::glFlushMappedNamedBufferRange(GLuint buffer, GLintptr offset, GLsizei length)
|
||||
@@ -2015,6 +2110,32 @@ void WrappedOpenGL::glFlushMappedBufferRange(GLenum target, GLintptr offset, GLs
|
||||
return m_Real.glFlushMappedBufferRange(target, offset, length);
|
||||
}
|
||||
|
||||
void WrappedOpenGL::PersistentMapMemoryBarrier(const set<GLResourceRecord *> &maps)
|
||||
{
|
||||
// this function iterates over all the maps, checking for any changes between
|
||||
// the shadow pointers, and propogates that to 'real' GL
|
||||
|
||||
for(set<GLResourceRecord *>::const_iterator it = maps.begin(); it != maps.end(); ++it)
|
||||
{
|
||||
GLResourceRecord *record = *it;
|
||||
|
||||
RDCASSERT(record && record->Map.persistentPtr);
|
||||
|
||||
size_t diffStart = 0, diffEnd = 0;
|
||||
bool found = FindDiffRange(record->GetShadowPtr(0), record->GetShadowPtr(1), (size_t)record->Length, diffStart, diffEnd);
|
||||
if(found)
|
||||
{
|
||||
// update the modified region in the 'comparison' shadow buffer for next check
|
||||
memcpy(record->GetShadowPtr(1) + diffStart, record->GetShadowPtr(0) + diffStart, diffEnd - diffStart);
|
||||
|
||||
// we use our own flush function so it will serialise chunks when necessary, and it
|
||||
// also handles copying into the persistent mapped pointer and flushing the real GL
|
||||
// buffer
|
||||
glFlushMappedNamedBufferRangeEXT(record->Resource.name, GLintptr(diffStart), GLsizeiptr(diffEnd - diffStart));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#pragma endregion
|
||||
|
||||
#pragma region Transform Feedback
|
||||
@@ -3670,6 +3791,17 @@ void WrappedOpenGL::glDeleteBuffers(GLsizei n, const GLuint *buffers)
|
||||
GLResource res = BufferRes(GetCtx(), buffers[i]);
|
||||
if(GetResourceManager()->HasCurrentResource(res))
|
||||
{
|
||||
GLResourceRecord *record = GetResourceManager()->GetResourceRecord(res);
|
||||
if(record)
|
||||
{
|
||||
// if we have a persistent pointer, make sure to unmap it
|
||||
if(record->Map.persistentPtr)
|
||||
m_Real.glUnmapNamedBufferEXT(res.name);
|
||||
|
||||
// free any shadow storage
|
||||
record->FreeShadowStorage();
|
||||
}
|
||||
|
||||
GetResourceManager()->MarkCleanResource(res);
|
||||
if(GetResourceManager()->HasResourceRecord(res))
|
||||
GetResourceManager()->GetResourceRecord(res)->Delete(GetResourceManager());
|
||||
|
||||
@@ -185,6 +185,20 @@ bool WrappedOpenGL::Serialise_glMemoryBarrier(GLbitfield barriers)
|
||||
|
||||
void WrappedOpenGL::glMemoryBarrier(GLbitfield barriers)
|
||||
{
|
||||
if(barriers & GL_CLIENT_MAPPED_BUFFER_BARRIER_BIT)
|
||||
{
|
||||
barriers &= ~GL_CLIENT_MAPPED_BUFFER_BARRIER_BIT;
|
||||
|
||||
// perform a forced flush of all persistent mapped buffers,
|
||||
// coherent or not.
|
||||
PersistentMapMemoryBarrier(m_PersistentMaps);
|
||||
}
|
||||
|
||||
// if it was only GL_CLIENT_MAPPED_BUFFER_BARRIER_BIT and that's been specially
|
||||
// handled then just return. Otherwise, handle the rest
|
||||
if(barriers == 0)
|
||||
return;
|
||||
|
||||
m_Real.glMemoryBarrier(barriers);
|
||||
|
||||
if(m_State == WRITING_CAPFRAME)
|
||||
@@ -210,6 +224,20 @@ bool WrappedOpenGL::Serialise_glMemoryBarrierByRegion(GLbitfield barriers)
|
||||
|
||||
void WrappedOpenGL::glMemoryBarrierByRegion(GLbitfield barriers)
|
||||
{
|
||||
if(barriers & GL_CLIENT_MAPPED_BUFFER_BARRIER_BIT)
|
||||
{
|
||||
barriers &= ~GL_CLIENT_MAPPED_BUFFER_BARRIER_BIT;
|
||||
|
||||
// perform a forced flush of all persistent mapped buffers,
|
||||
// coherent or not.
|
||||
PersistentMapMemoryBarrier(m_PersistentMaps);
|
||||
}
|
||||
|
||||
// if it was only GL_CLIENT_MAPPED_BUFFER_BARRIER_BIT and that's been specially
|
||||
// handled then just return. Otherwise, handle the rest
|
||||
if(barriers == 0)
|
||||
return;
|
||||
|
||||
m_Real.glMemoryBarrierByRegion(barriers);
|
||||
|
||||
if(m_State == WRITING_CAPFRAME)
|
||||
|
||||
Reference in New Issue
Block a user