From 3ca65bd632c6e191de50a5e64c1a72308b343612 Mon Sep 17 00:00:00 2001 From: baldurk Date: Fri, 10 Apr 2015 19:05:21 +0100 Subject: [PATCH] Allow glDrawElements and friends to pass indices as memory. Closes #127 * This is a concession towards non-core capturing, as it's fairly easy to support this fallback case without having a negative effect on the proper path. * We serialise out whether an index buffer was bound, and if it wasn't we serialise the array passed as a parameter, and upload it into a temporary index buffer we generate (so that the replay is correct at least). * I think technically the indirect parameter for glDrawElementsIndirect can point to client memory, and the elements array-of-arrays in glMultiDrawElements can definitely point to client memory, but I'm assuming that no-one would do that. I certainly hope they wouldn't! --- renderdoc/driver/gl/gl_driver.cpp | 15 +++ renderdoc/driver/gl/gl_driver.h | 8 ++ .../driver/gl/wrappers/gl_draw_funcs.cpp | 103 ++++++++++++++++++ 3 files changed, 126 insertions(+) diff --git a/renderdoc/driver/gl/gl_driver.cpp b/renderdoc/driver/gl/gl_driver.cpp index 984ed2037..f9b64bee6 100644 --- a/renderdoc/driver/gl/gl_driver.cpp +++ b/renderdoc/driver/gl/gl_driver.cpp @@ -787,6 +787,14 @@ void WrappedOpenGL::Initialise(GLInitParams ¶ms) gl.glBindVertexArray(m_FakeVAO); gl.glBindVertexArray(0); + // we use this to draw from index data that was 'immediate' passed to the + // draw function, as i na real memory pointer + gl.glGenBuffers(1, &m_FakeIdxBuf); + gl.glBindBuffer(eGL_ELEMENT_ARRAY_BUFFER, m_FakeIdxBuf); + m_FakeIdxSize = 1024*1024; // this buffer is resized up as needed + gl.glNamedBufferStorageEXT(m_FakeIdxBuf, m_FakeIdxSize, NULL, GL_DYNAMIC_STORAGE_BIT); + gl.glBindBuffer(eGL_ELEMENT_ARRAY_BUFFER, 0); + gl.glGenFramebuffers(1, &m_FakeBB_FBO); gl.glBindFramebuffer(eGL_FRAMEBUFFER, m_FakeBB_FBO); @@ -825,6 +833,7 @@ void WrappedOpenGL::Initialise(GLInitParams ¶ms) gl.glViewport(0, 0, params.width, params.height); + m_FakeBB_DepthStencil = 0; if(params.depthBits > 0 || params.stencilBits > 0) { gl.glGenTextures(1, &m_FakeBB_DepthStencil); @@ -884,6 +893,12 @@ const char * WrappedOpenGL::GetChunkName(uint32_t idx) WrappedOpenGL::~WrappedOpenGL() { + m_Real.glDeleteBuffers(1, &m_FakeIdxBuf); + m_Real.glDeleteVertexArrays(1, &m_FakeVAO); + m_Real.glDeleteFramebuffers(1, &m_FakeBB_FBO); + m_Real.glDeleteTextures(1, &m_FakeBB_Color); + if(m_FakeBB_DepthStencil) m_Real.glDeleteTextures(1, &m_FakeBB_DepthStencil); + SAFE_DELETE(m_pSerialiser); GetResourceManager()->ReleaseCurrentResource(m_DeviceResourceID); diff --git a/renderdoc/driver/gl/gl_driver.h b/renderdoc/driver/gl/gl_driver.h index c881b0a66..b441dc14f 100644 --- a/renderdoc/driver/gl/gl_driver.h +++ b/renderdoc/driver/gl/gl_driver.h @@ -286,6 +286,8 @@ class WrappedOpenGL GLuint m_FakeBB_Color; GLuint m_FakeBB_DepthStencil; GLuint m_FakeVAO; + GLuint m_FakeIdxBuf; + GLsizeiptr m_FakeIdxSize; ResourceId m_FakeVAOID; @@ -1113,6 +1115,12 @@ class WrappedOpenGL IMPLEMENT_FUNCTION_SERIALISED(void, glProgramUniformMatrix4x2dv(GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value)); IMPLEMENT_FUNCTION_SERIALISED(void, glProgramUniformMatrix4x3dv(GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value)); + // utility handling functions for glDraw*Elements* to handle pointers to indices being + // passed directly, with no index buffer bound. It's not allowed in core profile but + // it's fairly common and not too hard to support + byte *Common_preElements(GLsizei Count, GLenum Type, uint64_t &IdxOffset); + void Common_postElements(byte *idxDelete); + IMPLEMENT_FUNCTION_SERIALISED(void, glDrawArrays(GLenum mode, GLint first, GLsizei count)); IMPLEMENT_FUNCTION_SERIALISED(void, glDrawArraysInstanced(GLenum mode, GLint first, GLsizei count, GLsizei instancecount)); IMPLEMENT_FUNCTION_SERIALISED(void, glDrawArraysInstancedBaseInstance(GLenum mode, GLint first, GLsizei count, GLsizei instancecount, GLuint baseinstance)); diff --git a/renderdoc/driver/gl/wrappers/gl_draw_funcs.cpp b/renderdoc/driver/gl/wrappers/gl_draw_funcs.cpp index c62e3f56a..d0766e9e6 100644 --- a/renderdoc/driver/gl/wrappers/gl_draw_funcs.cpp +++ b/renderdoc/driver/gl/wrappers/gl_draw_funcs.cpp @@ -724,16 +724,91 @@ void WrappedOpenGL::glDrawArraysInstancedBaseInstance(GLenum mode, GLint first, } } +byte *WrappedOpenGL::Common_preElements(GLsizei Count, GLenum Type, uint64_t &IdxOffset) +{ + GLint idxbuf = 0; + // while writing, check to see if an index buffer is bound + if(m_State >= WRITING) + m_Real.glGetIntegerv(eGL_ELEMENT_ARRAY_BUFFER_BINDING, &idxbuf); + + // serialise whether we're reading indices as memory + SERIALISE_ELEMENT(bool, IndicesFromMemory, idxbuf == 0); + + if(IndicesFromMemory) + { + uint32_t IdxSize = + Type == eGL_UNSIGNED_BYTE ? 1 + : Type == eGL_UNSIGNED_SHORT ? 2 + : /*Type == eGL_UNSIGNED_INT*/ 4; + + // serialise the actual data (IdxOffset is a pointer not an offset in this case) + SERIALISE_ELEMENT_BUF(byte *, idxdata, (void *)IdxOffset, size_t(IdxSize*Count)); + + if(m_State <= EXECUTING) + { + GLsizeiptr idxlen = GLsizeiptr(IdxSize*Count); + + // resize fake index buffer if necessary + if(idxlen > m_FakeIdxSize) + { + m_Real.glBindBuffer(eGL_ELEMENT_ARRAY_BUFFER, 0); + m_Real.glDeleteBuffers(1, &m_FakeIdxBuf); + + m_FakeIdxSize = idxlen; + + m_Real.glGenBuffers(1, &m_FakeIdxBuf); + m_Real.glBindBuffer(eGL_ELEMENT_ARRAY_BUFFER, m_FakeIdxBuf); + m_Real.glNamedBufferStorageEXT(m_FakeIdxBuf, m_FakeIdxSize, NULL, GL_DYNAMIC_STORAGE_BIT); + } + + // bind and update fake index buffer, to draw from the 'immediate' index data + m_Real.glBindBuffer(eGL_ELEMENT_ARRAY_BUFFER, m_FakeIdxBuf); + + m_Real.glNamedBufferSubDataEXT(m_FakeIdxBuf, 0, idxlen, idxdata); + + // Set offset to 0 - means we read data from start of our fake index buffer + IdxOffset = 0; + + // we'll delete this later (only when replaying) + return idxdata; + } + + // can just return NULL, since we don't need to do any cleanup or deletion + } + + return NULL; +} + +void WrappedOpenGL::Common_postElements(byte *idxDelete) +{ + // unbind temporary fake index buffer we used to pass 'immediate' index data + if(idxDelete) + { + m_Real.glBindBuffer(eGL_ELEMENT_ARRAY_BUFFER, 0); + + AddDebugMessage(eDbgCategory_Deprecated, eDbgSeverity_High, eDbgSource_IncorrectAPIUse, + "Assuming GL core profile is used then specifying indices as a raw array, " + "not as offset into element array buffer, is illegal."); + + // delete serialised data + SAFE_DELETE_ARRAY(idxDelete); + } +} + bool WrappedOpenGL::Serialise_glDrawElements(GLenum mode, GLsizei count, GLenum type, const void *indices) { SERIALISE_ELEMENT(GLenum, Mode, mode); SERIALISE_ELEMENT(uint32_t, Count, count); SERIALISE_ELEMENT(GLenum, Type, type); SERIALISE_ELEMENT(uint64_t, IdxOffset, (uint64_t)indices); + + byte *idxDelete = Common_preElements(Count, Type, IdxOffset); if(m_State <= EXECUTING) { m_Real.glDrawElements(Mode, Count, Type, (const void *)IdxOffset); + + Common_postElements(idxDelete); } const string desc = m_pSerialiser->GetDebugStr(); @@ -864,10 +939,14 @@ bool WrappedOpenGL::Serialise_glDrawRangeElements(GLenum mode, GLuint start, GLu SERIALISE_ELEMENT(uint32_t, Count, count); SERIALISE_ELEMENT(GLenum, Type, type); SERIALISE_ELEMENT(uint64_t, IdxOffset, (uint64_t)indices); + + byte *idxDelete = Common_preElements(Count, Type, IdxOffset); if(m_State <= EXECUTING) { m_Real.glDrawRangeElements(Mode, Start, End, Count, Type, (const void *)IdxOffset); + + Common_postElements(idxDelete); } const string desc = m_pSerialiser->GetDebugStr(); @@ -932,9 +1011,13 @@ bool WrappedOpenGL::Serialise_glDrawRangeElementsBaseVertex(GLenum mode, GLuint SERIALISE_ELEMENT(uint64_t, IdxOffset, (uint64_t)indices); SERIALISE_ELEMENT(uint32_t, BaseVtx, basevertex); + byte *idxDelete = Common_preElements(Count, Type, IdxOffset); + if(m_State <= EXECUTING) { m_Real.glDrawRangeElementsBaseVertex(Mode, Start, End, Count, Type, (const void *)IdxOffset, BaseVtx); + + Common_postElements(idxDelete); } const string desc = m_pSerialiser->GetDebugStr(); @@ -997,10 +1080,14 @@ bool WrappedOpenGL::Serialise_glDrawElementsBaseVertex(GLenum mode, GLsizei coun SERIALISE_ELEMENT(GLenum, Type, type); SERIALISE_ELEMENT(uint64_t, IdxOffset, (uint64_t)indices); SERIALISE_ELEMENT(int32_t, BaseVtx, basevertex); + + byte *idxDelete = Common_preElements(Count, Type, IdxOffset); if(m_State <= EXECUTING) { m_Real.glDrawElementsBaseVertex(Mode, Count, Type, (const void *)IdxOffset, BaseVtx); + + Common_postElements(idxDelete); } const string desc = m_pSerialiser->GetDebugStr(); @@ -1064,9 +1151,13 @@ bool WrappedOpenGL::Serialise_glDrawElementsInstanced(GLenum mode, GLsizei count SERIALISE_ELEMENT(uint64_t, IdxOffset, (uint64_t)indices); SERIALISE_ELEMENT(uint32_t, InstCount, instancecount); + byte *idxDelete = Common_preElements(Count, Type, IdxOffset); + if(m_State <= EXECUTING) { m_Real.glDrawElementsInstanced(Mode, Count, Type, (const void *)IdxOffset, InstCount); + + Common_postElements(idxDelete); } const string desc = m_pSerialiser->GetDebugStr(); @@ -1130,10 +1221,14 @@ bool WrappedOpenGL::Serialise_glDrawElementsInstancedBaseInstance(GLenum mode, G SERIALISE_ELEMENT(uint64_t, IdxOffset, (uint64_t)indices); SERIALISE_ELEMENT(uint32_t, InstCount, instancecount); SERIALISE_ELEMENT(uint32_t, BaseInstance, baseinstance); + + byte *idxDelete = Common_preElements(Count, Type, IdxOffset); if(m_State <= EXECUTING) { m_Real.glDrawElementsInstancedBaseInstance(Mode, Count, Type, (const void *)IdxOffset, InstCount, BaseInstance); + + Common_postElements(idxDelete); } const string desc = m_pSerialiser->GetDebugStr(); @@ -1199,9 +1294,13 @@ bool WrappedOpenGL::Serialise_glDrawElementsInstancedBaseVertex(GLenum mode, GLs SERIALISE_ELEMENT(uint32_t, InstCount, instancecount); SERIALISE_ELEMENT(int32_t, BaseVertex, basevertex); + byte *idxDelete = Common_preElements(Count, Type, IdxOffset); + if(m_State <= EXECUTING) { m_Real.glDrawElementsInstancedBaseVertex(Mode, Count, Type, (const void *)IdxOffset, InstCount, BaseVertex); + + Common_postElements(idxDelete); } const string desc = m_pSerialiser->GetDebugStr(); @@ -1267,10 +1366,14 @@ bool WrappedOpenGL::Serialise_glDrawElementsInstancedBaseVertexBaseInstance(GLen SERIALISE_ELEMENT(uint32_t, InstCount, instancecount); SERIALISE_ELEMENT(int32_t, BaseVertex, basevertex); SERIALISE_ELEMENT(uint32_t, BaseInstance, baseinstance); + + byte *idxDelete = Common_preElements(Count, Type, IdxOffset); if(m_State <= EXECUTING) { m_Real.glDrawElementsInstancedBaseVertexBaseInstance(Mode, Count, Type, (const void *)IdxOffset, InstCount, BaseVertex, BaseInstance); + + Common_postElements(idxDelete); } const string desc = m_pSerialiser->GetDebugStr();