From c47f4f8e0715abd58a1ccc219b040206e019d5fb Mon Sep 17 00:00:00 2001 From: baldurk Date: Wed, 28 Sep 2016 13:18:21 +0200 Subject: [PATCH] Implement proper buffer initial contents, don't save into record data * This fixes a couple of cases - the case where a buffer is modified mid frame would not properly have its contents reset each frame * Also any buffer that isn't modified during the frame would have its initial contents overwritten by some partially recorded updates before a buffer was marked as high traffic/dirty. --- renderdoc/driver/gl/gl_manager.cpp | 131 ++++++++++++++++++++++++----- 1 file changed, 109 insertions(+), 22 deletions(-) diff --git a/renderdoc/driver/gl/gl_manager.cpp b/renderdoc/driver/gl/gl_manager.cpp index a60868c9e..19bfc4d7e 100644 --- a/renderdoc/driver/gl/gl_manager.cpp +++ b/renderdoc/driver/gl/gl_manager.cpp @@ -277,7 +277,7 @@ bool GLResourceManager::SerialisableResource(ResourceId id, GLResourceRecord *re bool GLResourceManager::Need_InitialStateChunk(GLResource res) { - return res.Namespace != eResBuffer; + return true; } bool GLResourceManager::Prepare_InitialState(GLResource res, byte *blob) @@ -437,15 +437,32 @@ bool GLResourceManager::Prepare_InitialState(GLResource res) if(res.Namespace == eResBuffer) { - GLResourceRecord *record = GetResourceRecord(res); + // get the length of the buffer + uint32_t length = 1; + gl.glGetNamedBufferParameterivEXT(res.name, eGL_BUFFER_SIZE, (GLint *)&length); - // TODO copy this to an immutable buffer elsewhere and SetInitialContents() it. - // then only do the readback in Serialise_InitialState + // save old bindings + GLuint oldbuf1 = 0, oldbuf2 = 0; + gl.glGetIntegerv(eGL_COPY_READ_BUFFER_BINDING, (GLint *)&oldbuf1); + gl.glGetIntegerv(eGL_COPY_WRITE_BUFFER_BINDING, (GLint *)&oldbuf2); - GLint length; - gl.glGetNamedBufferParameterivEXT(res.name, eGL_BUFFER_SIZE, &length); + // create a new buffer big enough to hold the contents + GLuint buf = 0; + gl.glGenBuffers(1, &buf); + gl.glBindBuffer(eGL_COPY_WRITE_BUFFER, buf); + gl.glBufferStorage(eGL_COPY_WRITE_BUFFER, (GLsizeiptr)length, NULL, GL_MAP_READ_BIT); - gl.glGetNamedBufferSubDataEXT(res.name, 0, length, record->GetDataPtr()); + // bind the live buffer for copying + gl.glBindBuffer(eGL_COPY_READ_BUFFER, res.name); + + // do the actual copy + gl.glCopyBufferSubData(eGL_COPY_READ_BUFFER, eGL_COPY_WRITE_BUFFER, 0, 0, (GLsizeiptr)length); + + // restore old bindings + gl.glBindBuffer(eGL_COPY_READ_BUFFER, oldbuf1); + gl.glBindBuffer(eGL_COPY_WRITE_BUFFER, oldbuf2); + + SetInitialContents(Id, InitialContentData(BufferRes(res.Context, buf), length, NULL)); } else if(res.Namespace == eResProgram) { @@ -878,19 +895,7 @@ bool GLResourceManager::Force_InitialState(GLResource res) bool GLResourceManager::Serialise_InitialState(ResourceId resid, GLResource res) { - ResourceId Id = ResourceId(); - - if(m_State >= WRITING) - { - Id = GetID(res); - - if(res.Namespace != eResBuffer) - m_pSerialiser->Serialise("Id", Id); - } - else - { - m_pSerialiser->Serialise("Id", Id); - } + SERIALISE_ELEMENT(ResourceId, Id, GetID(res)); if(m_State < WRITING) { @@ -904,7 +909,68 @@ bool GLResourceManager::Serialise_InitialState(ResourceId resid, GLResource res) if(res.Namespace == eResBuffer) { - // Nothing to serialize + // Buffers didn't have serialised initial contents before at all, so although + // this is newly added it's also backwards compatible. + if(m_State >= WRITING) + { + InitialContentData contents = GetInitialContents(Id); + GLuint buf = contents.resource.name; + uint32_t len = contents.num; + + m_pSerialiser->Serialise("len", len); + + // save old binding + GLuint oldbuf = 0; + gl.glGetIntegerv(eGL_COPY_READ_BUFFER_BINDING, (GLint *)&oldbuf); + + // bind the live buffer for readback + gl.glBindBuffer(eGL_COPY_READ_BUFFER, buf); + + byte *readback = (byte *)gl.glMapBuffer(eGL_COPY_READ_BUFFER, eGL_READ_ONLY); + + size_t sz = (size_t)len; + + // readback if possible and serialise + if(readback) + { + m_pSerialiser->SerialiseBuffer("buf", readback, sz); + + gl.glUnmapBuffer(eGL_COPY_READ_BUFFER); + } + else + { + RDCERR("Couldn't map initial contents buffer for readback!"); + + byte *dummy = new byte[len]; + memset(dummy, 0xfe, len); + + m_pSerialiser->SerialiseBuffer("buf", dummy, sz); + + SAFE_DELETE_ARRAY(dummy); + } + + // restore old binding + gl.glBindBuffer(eGL_COPY_READ_BUFFER, oldbuf); + } + else + { + SERIALISE_ELEMENT(uint32_t, len, 0); + + size_t size = 0; + byte *data = NULL; + + m_pSerialiser->SerialiseBuffer("buf", data, size); + + // create a new buffer big enough to hold the contents + GLuint buf = 0; + gl.glGenBuffers(1, &buf); + gl.glBindBuffer(eGL_COPY_WRITE_BUFFER, buf); + gl.glBufferStorage(eGL_COPY_WRITE_BUFFER, (GLsizeiptr)len, data, 0); + + SAFE_DELETE_ARRAY(data); + + SetInitialContents(Id, InitialContentData(BufferRes(m_GL->GetCtx(), buf), len, NULL)); + } } else if(res.Namespace == eResProgram) { @@ -1553,7 +1619,28 @@ void GLResourceManager::Apply_InitialState(GLResource live, InitialContentData i { const GLHookSet &gl = m_GL->m_Real; - if(live.Namespace == eResTexture) + if(live.Namespace == eResBuffer) + { + // save old bindings + GLuint oldbuf1 = 0, oldbuf2 = 0; + gl.glGetIntegerv(eGL_COPY_READ_BUFFER_BINDING, (GLint *)&oldbuf1); + gl.glGetIntegerv(eGL_COPY_WRITE_BUFFER_BINDING, (GLint *)&oldbuf2); + + // bind the immutable contents for copying + gl.glBindBuffer(eGL_COPY_READ_BUFFER, initial.resource.name); + + // bind the live buffer for copying + gl.glBindBuffer(eGL_COPY_WRITE_BUFFER, live.name); + + // do the actual copy + gl.glCopyBufferSubData(eGL_COPY_READ_BUFFER, eGL_COPY_WRITE_BUFFER, 0, 0, + (GLsizeiptr)initial.num); + + // restore old bindings + gl.glBindBuffer(eGL_COPY_READ_BUFFER, oldbuf1); + gl.glBindBuffer(eGL_COPY_WRITE_BUFFER, oldbuf2); + } + else if(live.Namespace == eResTexture) { ResourceId Id = GetID(live); WrappedOpenGL::TextureData &details = m_GL->m_Textures[Id];