From f87aa7a7fe219481c9503019236fb9989cb55a1b Mon Sep 17 00:00:00 2001 From: baldurk Date: Mon, 5 Jun 2023 16:07:01 +0100 Subject: [PATCH] Handle subset indirect draws for GL pixel history replay * This is not needed on Vulkan because Vulkan uses a per-draw callback system that automatically splits up multidraws into single draws for replay. On D3D11 there is no multidraw so it doesn't come up there. * On GL we can e.g. have one draw touching the pixel half-way through a multidraw, and then want to replay from that draw through to the next touching draw which could be much later or in the same multidraw. It assumes we can always replay from one draw to the next. --- .../driver/gl/wrappers/gl_draw_funcs.cpp | 170 +++++++++--------- 1 file changed, 84 insertions(+), 86 deletions(-) diff --git a/renderdoc/driver/gl/wrappers/gl_draw_funcs.cpp b/renderdoc/driver/gl/wrappers/gl_draw_funcs.cpp index e66bed67b..c90c292c1 100644 --- a/renderdoc/driver/gl/wrappers/gl_draw_funcs.cpp +++ b/renderdoc/driver/gl/wrappers/gl_draw_funcs.cpp @@ -2267,24 +2267,23 @@ bool WrappedOpenGL::Serialise_glMultiDrawArrays(SerialiserType &ser, GLenum mode } else { - // otherwise we do the 'hard' case, draw only one multidraw - // note we'll never be asked to do e.g. 3rd-7th of a multidraw. Only ever 0th-nth or - // a single draw. + // otherwise we do the 'hard' case, draw only some subset of multidraws + // we CAN be asked to do an arbitrary subset in the event of pixel history doing a replay + // from Draw N to somewhere after. // // We also need to use the original glMultiDraw command so that gl_DrawID is faithful. In // order to preserve the draw index we write a custom multidraw that specifies count == 0 // for all previous draws. - RDCASSERT(m_LastEventID == m_FirstEventID); + uint32_t firstDrawIdx = m_FirstEventID - baseEventID - 1; + uint32_t lastDrawIdx = RDCMIN((uint32_t)drawcount, m_LastEventID - baseEventID) - 1; - uint32_t drawidx = (m_LastEventID - baseEventID - 1); - - // zero out the count for all previous draws. This won't be used again so we can safely - // write over the serialised array. + // zero out the count for all previous draws up to the first. This won't be used again so we + // can safely write over the serialised array. GLsizei *modcount = (GLsizei *)count; - for(uint32_t d = 0; d < drawidx; d++) + for(uint32_t d = 0; d < firstDrawIdx; d++) modcount[d] = 0; - GL.glMultiDrawArrays(mode, first, count, drawidx + 1); + GL.glMultiDrawArrays(mode, first, count, lastDrawIdx + 1); } m_CurEventID += (uint32_t)drawcount; @@ -2433,25 +2432,24 @@ bool WrappedOpenGL::Serialise_glMultiDrawElements(SerialiserType &ser, GLenum mo } else { - // otherwise we do the 'hard' case, draw only one multidraw - // note we'll never be asked to do e.g. 3rd-7th of a multidraw. Only ever 0th-nth or - // a single draw. + // otherwise we do the 'hard' case, draw only some subset of multidraws + // we CAN be asked to do an arbitrary subset in the event of pixel history doing a replay + // from Draw N to somewhere after. // // We also need to use the original glMultiDraw command so that gl_DrawID is faithful. In // order to preserve the draw index we write a custom multidraw that specifies count == 0 // for all previous draws. - RDCASSERT(m_LastEventID == m_FirstEventID); + uint32_t firstDrawIdx = m_FirstEventID - baseEventID - 1; + uint32_t lastDrawIdx = RDCMIN((uint32_t)drawcount, m_LastEventID - baseEventID) - 1; - uint32_t drawidx = (m_LastEventID - baseEventID - 1); - - // zero out the count for all previous draws. This won't be used again so we can safely - // write over the serialised array. + // zero out the count for all previous draws up to the first. This won't be used again so we + // can safely write over the serialised array. GLsizei *modcount = (GLsizei *)count; - for(uint32_t d = 0; d < drawidx; d++) + for(uint32_t d = 0; d < firstDrawIdx; d++) modcount[d] = 0; if(count == 0 || Check_SafeDraw(true)) - GL.glMultiDrawElements(mode, count, type, inds.data(), drawidx + 1); + GL.glMultiDrawElements(mode, count, type, inds.data(), lastDrawIdx + 1); } m_CurEventID += (uint32_t)drawcount; @@ -2604,25 +2602,25 @@ bool WrappedOpenGL::Serialise_glMultiDrawElementsBaseVertex(SerialiserType &ser, } else { - // otherwise we do the 'hard' case, draw only one multidraw - // note we'll never be asked to do e.g. 3rd-7th of a multidraw. Only ever 0th-nth or - // a single draw. + // otherwise we do the 'hard' case, draw only some subset of multidraws + // we CAN be asked to do an arbitrary subset in the event of pixel history doing a replay + // from Draw N to somewhere after. // // We also need to use the original glMultiDraw command so that gl_DrawID is faithful. In // order to preserve the draw index we write a custom multidraw that specifies count == 0 // for all previous draws. - RDCASSERT(m_LastEventID == m_FirstEventID); + uint32_t firstDrawIdx = m_FirstEventID - baseEventID - 1; + uint32_t lastDrawIdx = RDCMIN((uint32_t)drawcount, m_LastEventID - baseEventID) - 1; - uint32_t drawidx = (m_LastEventID - baseEventID - 1); - - // zero out the count for all previous draws. This won't be used again so we can safely - // write over the serialised array. + // zero out the count for all previous draws up to the first. This won't be used again so we + // can safely write over the serialised array. GLsizei *modcount = (GLsizei *)count; - for(uint32_t d = 0; d < drawidx; d++) + for(uint32_t d = 0; d < firstDrawIdx; d++) modcount[d] = 0; if(count == 0 || Check_SafeDraw(true)) - GL.glMultiDrawElementsBaseVertex(mode, count, type, inds.data(), drawidx + 1, basevertex); + GL.glMultiDrawElementsBaseVertex(mode, count, type, inds.data(), lastDrawIdx + 1, + basevertex); } m_CurEventID += (uint32_t)drawcount; @@ -2792,33 +2790,33 @@ bool WrappedOpenGL::Serialise_glMultiDrawArraysIndirect(SerialiserType &ser, GLe } else { - // otherwise we do the 'hard' case, draw only one multidraw - // note we'll never be asked to do e.g. 3rd-7th of a multidraw. Only ever 0th-nth or - // a single draw. + // otherwise we do the 'hard' case, draw only some subset of multidraws + // we CAN be asked to do an arbitrary subset in the event of pixel history doing a replay + // from Draw N to somewhere after. // // We also need to use the original glMultiDraw command so that gl_DrawID is faithful. In // order to preserve the draw index we write a custom multidraw that specifies count == 0 // for all previous draws. - RDCASSERT(m_LastEventID == m_FirstEventID); + uint32_t firstDrawIdx = m_FirstEventID - baseEventID - 1; + uint32_t lastDrawIdx = RDCMIN((uint32_t)drawcount, m_LastEventID - baseEventID) - 1; - uint32_t drawidx = (m_LastEventID - baseEventID - 1); - - DrawArraysIndirectCommand params = {}; + rdcarray params; + params.resize(lastDrawIdx - firstDrawIdx + 1); GLintptr offs = (GLintptr)offset; if(stride != 0) - offs += stride * drawidx; + offs += stride * firstDrawIdx; else - offs += sizeof(params) * drawidx; + offs += sizeof(DrawArraysIndirectCommand) * firstDrawIdx; - GL.glGetBufferSubData(eGL_DRAW_INDIRECT_BUFFER, offs, sizeof(params), ¶ms); + GL.glGetBufferSubData(eGL_DRAW_INDIRECT_BUFFER, offs, params.byteSize(), params.data()); { GLint prevBuf = 0; GL.glGetIntegerv(eGL_DRAW_INDIRECT_BUFFER_BINDING, &prevBuf); // get an indirect buffer big enough for all the draws - GLsizeiptr bufLength = sizeof(params) * (drawidx + 1); + GLsizeiptr bufLength = sizeof(DrawArraysIndirectCommand) * (lastDrawIdx + 1); BindIndirectBuffer(bufLength); DrawArraysIndirectCommand *cmds = (DrawArraysIndirectCommand *)GL.glMapBufferRange( @@ -2826,18 +2824,18 @@ bool WrappedOpenGL::Serialise_glMultiDrawArraysIndirect(SerialiserType &ser, GLe GL_MAP_WRITE_BIT | GL_MAP_INVALIDATE_BUFFER_BIT); // zero out all prior draws - for(uint32_t d = 0; d < drawidx; d++) + for(uint32_t d = 0; d < firstDrawIdx; d++) memset(cmds + d, 0, sizeof(DrawArraysIndirectCommand)); // write the actual draw's parameters - memcpy(cmds + drawidx, ¶ms, sizeof(params)); + memcpy(cmds + firstDrawIdx, params.data(), params.byteSize()); GL.glUnmapBuffer(eGL_DRAW_INDIRECT_BUFFER); // the offset is 0 because it's referring to our custom buffer, stride is 0 because we // tightly pack. if(Check_SafeDraw(false)) - GL.glMultiDrawArraysIndirect(mode, (const void *)0, drawidx + 1, 0); + GL.glMultiDrawArraysIndirect(mode, (const void *)0, lastDrawIdx + 1, 0); GL.glBindBuffer(eGL_DRAW_INDIRECT_BUFFER, prevBuf); } @@ -3018,33 +3016,33 @@ bool WrappedOpenGL::Serialise_glMultiDrawElementsIndirect(SerialiserType &ser, G } else { - // otherwise we do the 'hard' case, draw only one multidraw - // note we'll never be asked to do e.g. 3rd-7th of a multidraw. Only ever 0th-nth or - // a single draw. + // otherwise we do the 'hard' case, draw only some subset of multidraws + // we CAN be asked to do an arbitrary subset in the event of pixel history doing a replay + // from Draw N to somewhere after. // // We also need to use the original glMultiDraw command so that gl_DrawID is faithful. In // order to preserve the draw index we write a custom multidraw that specifies count == 0 // for all previous draws. - RDCASSERT(m_LastEventID == m_FirstEventID); + uint32_t firstDrawIdx = m_FirstEventID - baseEventID - 1; + uint32_t lastDrawIdx = RDCMIN((uint32_t)drawcount, m_LastEventID - baseEventID) - 1; - uint32_t drawidx = (m_LastEventID - baseEventID - 1); - - DrawElementsIndirectCommand params = {}; + rdcarray params; + params.resize(lastDrawIdx - firstDrawIdx + 1); GLintptr offs = (GLintptr)offset; if(stride != 0) - offs += stride * drawidx; + offs += stride * firstDrawIdx; else - offs += sizeof(params) * drawidx; + offs += sizeof(DrawElementsIndirectCommand) * firstDrawIdx; - GL.glGetBufferSubData(eGL_DRAW_INDIRECT_BUFFER, offs, sizeof(params), ¶ms); + GL.glGetBufferSubData(eGL_DRAW_INDIRECT_BUFFER, offs, params.byteSize(), params.data()); { GLint prevBuf = 0; GL.glGetIntegerv(eGL_DRAW_INDIRECT_BUFFER_BINDING, &prevBuf); // get an indirect buffer big enough for all the draws - GLsizeiptr bufLength = sizeof(params) * (drawidx + 1); + GLsizeiptr bufLength = sizeof(DrawElementsIndirectCommand) * (lastDrawIdx + 1); BindIndirectBuffer(bufLength); DrawElementsIndirectCommand *cmds = (DrawElementsIndirectCommand *)GL.glMapBufferRange( @@ -3052,18 +3050,18 @@ bool WrappedOpenGL::Serialise_glMultiDrawElementsIndirect(SerialiserType &ser, G GL_MAP_WRITE_BIT | GL_MAP_INVALIDATE_BUFFER_BIT); // zero out all prior draws - for(uint32_t d = 0; d < drawidx; d++) + for(uint32_t d = 0; d < firstDrawIdx; d++) memset(cmds + d, 0, sizeof(DrawElementsIndirectCommand)); // write the actual draw's parameters - memcpy(cmds + drawidx, ¶ms, sizeof(params)); + memcpy(cmds + firstDrawIdx, params.data(), params.byteSize()); GL.glUnmapBuffer(eGL_DRAW_INDIRECT_BUFFER); // the offset is 0 because it's referring to our custom buffer, stride is 0 because we // tightly pack. if(Check_SafeDraw(true)) - GL.glMultiDrawElementsIndirect(mode, type, (const void *)0, drawidx + 1, 0); + GL.glMultiDrawElementsIndirect(mode, type, (const void *)0, lastDrawIdx + 1, 0); GL.glBindBuffer(eGL_DRAW_INDIRECT_BUFFER, prevBuf); } @@ -3244,33 +3242,33 @@ bool WrappedOpenGL::Serialise_glMultiDrawArraysIndirectCount(SerialiserType &ser } else { - // otherwise we do the 'hard' case, draw only one multidraw - // note we'll never be asked to do e.g. 3rd-7th of a multidraw. Only ever 0th-nth or - // a single draw. + // otherwise we do the 'hard' case, draw only some subset of multidraws + // we CAN be asked to do an arbitrary subset in the event of pixel history doing a replay + // from Draw N to somewhere after. // // We also need to use the original glMultiDraw command so that gl_DrawID is faithful. In // order to preserve the draw index we write a custom multidraw that specifies count == 0 // for all previous draws. - RDCASSERT(m_LastEventID == m_FirstEventID); + uint32_t firstDrawIdx = m_FirstEventID - baseEventID - 1; + uint32_t lastDrawIdx = RDCMIN((uint32_t)realdrawcount, m_LastEventID - baseEventID) - 1; - uint32_t drawidx = (m_LastEventID - baseEventID - 1); - - DrawArraysIndirectCommand params = {}; + rdcarray params; + params.resize(lastDrawIdx - firstDrawIdx + 1); GLintptr offs = (GLintptr)offset; if(stride != 0) - offs += stride * drawidx; + offs += stride * firstDrawIdx; else - offs += sizeof(params) * drawidx; + offs += sizeof(DrawArraysIndirectCommand) * firstDrawIdx; - GL.glGetBufferSubData(eGL_DRAW_INDIRECT_BUFFER, offs, sizeof(params), ¶ms); + GL.glGetBufferSubData(eGL_DRAW_INDIRECT_BUFFER, offs, params.byteSize(), params.data()); { GLint prevBuf = 0; GL.glGetIntegerv(eGL_DRAW_INDIRECT_BUFFER_BINDING, &prevBuf); // get an indirect buffer big enough for all the draws - GLsizeiptr bufLength = sizeof(params) * (drawidx + 1); + GLsizeiptr bufLength = sizeof(DrawArraysIndirectCommand) * (lastDrawIdx + 1); BindIndirectBuffer(bufLength); DrawArraysIndirectCommand *cmds = (DrawArraysIndirectCommand *)GL.glMapBufferRange( @@ -3278,18 +3276,18 @@ bool WrappedOpenGL::Serialise_glMultiDrawArraysIndirectCount(SerialiserType &ser GL_MAP_WRITE_BIT | GL_MAP_INVALIDATE_BUFFER_BIT); // zero out all prior draws - for(uint32_t d = 0; d < drawidx; d++) + for(uint32_t d = 0; d < firstDrawIdx; d++) memset(cmds + d, 0, sizeof(DrawArraysIndirectCommand)); // write the actual draw's parameters - memcpy(cmds + drawidx, ¶ms, sizeof(params)); + memcpy(cmds + firstDrawIdx, params.data(), params.byteSize()); GL.glUnmapBuffer(eGL_DRAW_INDIRECT_BUFFER); // the offset is 0 because it's referring to our custom buffer, stride is 0 because we // tightly pack. if(Check_SafeDraw(false)) - GL.glMultiDrawArraysIndirect(mode, (const void *)0, drawidx + 1, 0); + GL.glMultiDrawArraysIndirect(mode, (const void *)0, lastDrawIdx + 1, 0); GL.glBindBuffer(eGL_DRAW_INDIRECT_BUFFER, prevBuf); } @@ -3479,33 +3477,33 @@ bool WrappedOpenGL::Serialise_glMultiDrawElementsIndirectCount(SerialiserType &s } else { - // otherwise we do the 'hard' case, draw only one multidraw - // note we'll never be asked to do e.g. 3rd-7th of a multidraw. Only ever 0th-nth or - // a single draw. + // otherwise we do the 'hard' case, draw only some subset of multidraws + // we CAN be asked to do an arbitrary subset in the event of pixel history doing a replay + // from Draw N to somewhere after. // // We also need to use the original glMultiDraw command so that gl_DrawID is faithful. In // order to preserve the draw index we write a custom multidraw that specifies count == 0 // for all previous draws. - RDCASSERT(m_LastEventID == m_FirstEventID); + uint32_t firstDrawIdx = m_FirstEventID - baseEventID - 1; + uint32_t lastDrawIdx = RDCMIN((uint32_t)realdrawcount, m_LastEventID - baseEventID) - 1; - uint32_t drawidx = (m_LastEventID - baseEventID - 1); - - DrawElementsIndirectCommand params = {}; + rdcarray params; + params.resize(lastDrawIdx - firstDrawIdx + 1); GLintptr offs = (GLintptr)offset; if(stride != 0) - offs += stride * drawidx; + offs += stride * firstDrawIdx; else - offs += sizeof(params) * drawidx; + offs += sizeof(DrawElementsIndirectCommand) * firstDrawIdx; - GL.glGetBufferSubData(eGL_DRAW_INDIRECT_BUFFER, offs, sizeof(params), ¶ms); + GL.glGetBufferSubData(eGL_DRAW_INDIRECT_BUFFER, offs, params.byteSize(), params.data()); { GLint prevBuf = 0; GL.glGetIntegerv(eGL_DRAW_INDIRECT_BUFFER_BINDING, &prevBuf); // get an indirect buffer big enough for all the draws - GLsizeiptr bufLength = sizeof(params) * (drawidx + 1); + GLsizeiptr bufLength = sizeof(DrawElementsIndirectCommand) * (lastDrawIdx + 1); BindIndirectBuffer(bufLength); DrawElementsIndirectCommand *cmds = (DrawElementsIndirectCommand *)GL.glMapBufferRange( @@ -3513,18 +3511,18 @@ bool WrappedOpenGL::Serialise_glMultiDrawElementsIndirectCount(SerialiserType &s GL_MAP_WRITE_BIT | GL_MAP_INVALIDATE_BUFFER_BIT); // zero out all prior draws - for(uint32_t d = 0; d < drawidx; d++) + for(uint32_t d = 0; d < firstDrawIdx; d++) memset(cmds + d, 0, sizeof(DrawElementsIndirectCommand)); // write the actual draw's parameters - memcpy(cmds + drawidx, ¶ms, sizeof(params)); + memcpy(cmds + firstDrawIdx, params.data(), params.byteSize()); GL.glUnmapBuffer(eGL_DRAW_INDIRECT_BUFFER); // the offset is 0 because it's referring to our custom buffer, stride is 0 because we // tightly pack. if(maxdrawcount == 0 || Check_SafeDraw(true)) - GL.glMultiDrawElementsIndirect(mode, type, (const void *)0, drawidx + 1, 0); + GL.glMultiDrawElementsIndirect(mode, type, (const void *)0, lastDrawIdx + 1, 0); GL.glBindBuffer(eGL_DRAW_INDIRECT_BUFFER, prevBuf); }