diff --git a/renderdoc/api/replay/replay_enums.h b/renderdoc/api/replay/replay_enums.h index fcc1e491a..d6844e615 100644 --- a/renderdoc/api/replay/replay_enums.h +++ b/renderdoc/api/replay/replay_enums.h @@ -327,6 +327,7 @@ enum DrawcallFlags eDraw_SetMarker = 0x10, eDraw_PushMarker = 0x20, eDraw_Present = 0x40, + eDraw_MultiDraw = 0x80, // flags eDraw_UseIBuffer = 0x0100, diff --git a/renderdoc/driver/gl/gl_common.h b/renderdoc/driver/gl/gl_common.h index ad3a2527c..8ded6f920 100644 --- a/renderdoc/driver/gl/gl_common.h +++ b/renderdoc/driver/gl/gl_common.h @@ -122,6 +122,23 @@ void DoVendorChecks(const GLHookSet &gl, GLWindowingData context); void CopyProgramUniforms(const GLHookSet &gl, GLuint progSrc, GLuint progDst); void SerialiseProgramUniforms(const GLHookSet &gl, Serialiser *ser, GLuint prog, map *locTranslate, bool writing); +struct DrawElementsIndirectCommand +{ + uint32_t count; + uint32_t instanceCount; + uint32_t firstIndex; + uint32_t baseVertex; + uint32_t baseInstance; +}; + +struct DrawArraysIndirectCommand +{ + uint32_t count; + uint32_t instanceCount; + uint32_t first; + uint32_t baseInstance; +}; + enum GLChunkType { DEVICE_INIT = FIRST_CHUNK_ID, diff --git a/renderdoc/driver/gl/gl_driver.cpp b/renderdoc/driver/gl/gl_driver.cpp index d5702e461..db293d9ef 100644 --- a/renderdoc/driver/gl/gl_driver.cpp +++ b/renderdoc/driver/gl/gl_driver.cpp @@ -413,6 +413,8 @@ WrappedOpenGL::WrappedOpenGL(const char *logfile, const GLHookSet &funcs) m_CurEventID = 1; m_CurDrawcallID = 1; + m_FirstEventID = 0; + m_LastEventID = ~0U; RDCEraseEl(m_ActiveQueries); m_ActiveConditional = false; @@ -2509,10 +2511,14 @@ void WrappedOpenGL::ContextReplayLog(LogState readType, uint32_t startEventID, u FetchAPIEvent ev = GetEvent(startEventID); m_CurEventID = ev.eventID; m_pSerialiser->SetOffset(ev.fileOffset); + m_FirstEventID = startEventID; + m_LastEventID = endEventID; } else if(m_State == READING) { m_CurEventID = 1; + m_FirstEventID = 0; + m_LastEventID = ~0U; } if(m_State == EXECUTING && !partial) @@ -2686,7 +2692,7 @@ void WrappedOpenGL::AddDrawcall(FetchDrawcall d, bool hasEvents) } // markers don't increment drawcall ID - if((draw.flags & (eDraw_SetMarker|eDraw_PushMarker)) == 0) + if((draw.flags & (eDraw_SetMarker|eDraw_PushMarker|eDraw_MultiDraw)) == 0) m_CurDrawcallID++; if(hasEvents) diff --git a/renderdoc/driver/gl/gl_driver.h b/renderdoc/driver/gl/gl_driver.h index 4e0393626..2278f3aa4 100644 --- a/renderdoc/driver/gl/gl_driver.h +++ b/renderdoc/driver/gl/gl_driver.h @@ -157,6 +157,8 @@ class WrappedOpenGL uint64_t m_CurChunkOffset; uint32_t m_CurEventID, m_CurDrawcallID; + uint32_t m_FirstEventID; + uint32_t m_LastEventID; DrawcallTreeNode m_ParentDrawcall; diff --git a/renderdoc/driver/gl/wrappers/gl_draw_funcs.cpp b/renderdoc/driver/gl/wrappers/gl_draw_funcs.cpp index a5503b96a..0a578761d 100644 --- a/renderdoc/driver/gl/wrappers/gl_draw_funcs.cpp +++ b/renderdoc/driver/gl/wrappers/gl_draw_funcs.cpp @@ -89,17 +89,18 @@ bool WrappedOpenGL::Serialise_glDispatchComputeIndirect(GLintptr indirect) if(m_State == READING) { + uint32_t groupSizes[3]; + m_Real.glGetBufferSubData(eGL_DISPATCH_INDIRECT_BUFFER, (GLintptr)offs, sizeof(uint32_t)*3, groupSizes); + AddEvent(DISPATCH_COMPUTE_INDIRECT, desc); string name = "glDispatchComputeIndirect(" + - ToStr::Get(0) + ", " + - ToStr::Get(0) + ", " + - ToStr::Get(0) + ")"; - - RDCUNIMPLEMENTED("Not fetching indirect data for glDispatchComputeIndirect() display"); + ToStr::Get(groupSizes[0]) + ", " + + ToStr::Get(groupSizes[1]) + ", " + + ToStr::Get(groupSizes[2]) + ")"; FetchDrawcall draw; draw.name = name; - draw.flags |= eDraw_Dispatch; + draw.flags |= eDraw_Dispatch|eDraw_Indirect; draw.debugMessages = debugMessages; @@ -492,21 +493,25 @@ bool WrappedOpenGL::Serialise_glDrawArraysIndirect(GLenum mode, const void *indi if(m_State == READING) { + DrawArraysIndirectCommand params; + m_Real.glGetBufferSubData(eGL_DRAW_INDIRECT_BUFFER, (GLintptr)Offset, sizeof(params), ¶ms); + AddEvent(DRAWARRAYS_INDIRECT, desc); string name = "glDrawArraysIndirect(" + - ( Mode == eGL_POINTS ? "GL_POINTS" : ToStr::Get(Mode) ) + ")"; + ( Mode == eGL_POINTS ? "GL_POINTS" : ToStr::Get(Mode) ) + ", " + + ToStr::Get(params.first) + ", " + + ToStr::Get(params.count) + ", " + + ToStr::Get(params.instanceCount) + ", " + + ToStr::Get(params.baseInstance) + ")"; - RDCUNIMPLEMENTED("Not fetching indirect data for glDrawArraysIndirect() display"); - FetchDrawcall draw; draw.name = name; - draw.numIndices = 1; - draw.numInstances = 1; - draw.indexOffset = 0; - draw.vertexOffset = 0; - draw.instanceOffset = 0; + draw.numIndices = params.count; + draw.numInstances = params.instanceCount; + draw.vertexOffset = params.first; + draw.instanceOffset = params.baseInstance; - draw.flags |= eDraw_Drawcall; + draw.flags |= eDraw_Drawcall|eDraw_Instanced|eDraw_Indirect; draw.debugMessages = debugMessages; @@ -731,22 +736,32 @@ bool WrappedOpenGL::Serialise_glDrawElementsIndirect(GLenum mode, GLenum type, c if(m_State == READING) { + DrawElementsIndirectCommand params; + m_Real.glGetBufferSubData(eGL_DRAW_INDIRECT_BUFFER, (GLintptr)Offset, sizeof(params), ¶ms); + AddEvent(DRAWELEMENTS_INDIRECT, desc); string name = "glDrawElementsIndirect(" + ( Mode == eGL_POINTS ? "GL_POINTS" : ToStr::Get(Mode) ) + ", " + - ToStr::Get(Type) + ")"; + ToStr::Get(params.count) + ", " + + ToStr::Get(Type) + ", " + + ToStr::Get(params.instanceCount) + ", " + + ToStr::Get(params.baseVertex) + ", " + + ToStr::Get(params.baseInstance) + ")"; - RDCUNIMPLEMENTED("Not fetching indirect data for glDrawElementsIndirect() display"); + uint32_t IdxSize = + Type == eGL_UNSIGNED_BYTE ? 1 + : Type == eGL_UNSIGNED_SHORT ? 2 + : /*Type == eGL_UNSIGNED_INT*/ 4; FetchDrawcall draw; draw.name = name; - draw.numIndices = 1; - draw.numInstances = 1; - draw.indexOffset = 0; - draw.vertexOffset = 0; - draw.instanceOffset = 0; + draw.numIndices = params.count; + draw.numInstances = params.instanceCount; + draw.indexOffset = params.firstIndex*IdxSize; + draw.vertexOffset = params.baseVertex; + draw.instanceOffset = params.baseInstance; - draw.flags |= eDraw_Drawcall|eDraw_UseIBuffer; + draw.flags |= eDraw_Drawcall|eDraw_UseIBuffer|eDraw_Instanced|eDraw_Indirect; draw.debugMessages = debugMessages; @@ -755,7 +770,6 @@ bool WrappedOpenGL::Serialise_glDrawElementsIndirect(GLenum mode, GLenum type, c m_LastDrawMode = Mode; m_LastIndexSize = Type; - m_LastIndexOffset = params.firstIndex*IdxSize; return true; } @@ -1260,10 +1274,49 @@ bool WrappedOpenGL::Serialise_glMultiDrawArrays(GLenum mode, const GLint *first, SERIALISE_ELEMENT_ARR(int32_t, firstArray, first, Count); SERIALISE_ELEMENT_ARR(int32_t, countArray, count, Count); - if(m_State <= EXECUTING) + if(m_State == READING) { m_Real.glMultiDrawArrays(Mode, firstArray, countArray, Count); } + else if(m_State <= EXECUTING) + { + size_t i=0; + for(; i < m_Events.size(); i++) + { + if(m_Events[i].eventID >= m_CurEventID) + break; + } + + while(i > 1 && m_Events[i-1].fileOffset == m_Events[i].fileOffset) i--; + + uint32_t baseEventID = m_Events[i].eventID; + + if(m_LastEventID < baseEventID) + { + // To add the multidraw, we made an event N that is the 'parent' marker, then + // N+1, N+2, N+3, ... for each of the sub-draws. If the first sub-draw is selected + // then we'll replay up to N but not N+1, so just do nothing - we DON'T want to draw + // the first sub-draw in that range. + } + else if(m_FirstEventID <= baseEventID && m_LastEventID >= baseEventID) + { + // if we're replaying part-way into a multidraw, we can replay the first part 'easily' + // by just reducing the Count parameter to however many we want to replay. This only + // works if we're replaying from the first multidraw to the nth (n less than Count) + m_Real.glMultiDrawArrays(Mode, firstArray, countArray, RDCMIN(Count, m_LastEventID - baseEventID + 1)); + } + 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. + RDCASSERT(m_LastEventID == m_FirstEventID); + + uint32_t drawidx = (m_LastEventID - baseEventID); + + m_Real.glDrawArrays(Mode, firstArray[drawidx], countArray[drawidx]); + } + } const string desc = m_pSerialiser->GetDebugStr(); @@ -1271,26 +1324,46 @@ bool WrappedOpenGL::Serialise_glMultiDrawArrays(GLenum mode, const GLint *first, if(m_State == READING) { - AddEvent(MULTI_DRAWARRAYS, desc); string name = "glMultiDrawArrays(" + - ( Mode == eGL_POINTS ? "GL_POINTS" : ToStr::Get(Mode) ) + ", " + + ( Mode == eGL_POINTS ? "GL_POINTS" : ToStr::Get(Mode) ) + "," + ToStr::Get(Count) + ")"; - - RDCUNIMPLEMENTED("Not processing multi-draw data for glMultiDrawArrays() display"); FetchDrawcall draw; draw.name = name; - draw.numIndices = 1; - draw.numInstances = 1; - draw.indexOffset = 0; - draw.vertexOffset = 0; - draw.instanceOffset = 0; + draw.flags |= eDraw_MultiDraw; - draw.flags |= eDraw_Drawcall; + AddDrawcall(draw, false); + + m_DrawcallStack.push_back(&m_DrawcallStack.back()->children.back()); + + m_CurEventID++; + + for(uint32_t i=0; i < Count; i++) + { + FetchDrawcall draw; + draw.numIndices = countArray[i]; + draw.vertexOffset = firstArray[i]; - draw.debugMessages = debugMessages; + if(i == 0) + draw.debugMessages = debugMessages; + + draw.name = "glMultiDrawArrays[" + ToStr::Get(i) + "](" + + ToStr::Get(draw.numIndices) + ", " + + ToStr::Get(draw.vertexOffset) + ")"; - AddDrawcall(draw, true); + draw.flags |= eDraw_Drawcall; + + AddEvent(MULTI_DRAWARRAYS, desc); + AddDrawcall(draw, true); + + m_CurEventID++; + } + + m_DrawcallStack.pop_back(); + } + else + { + m_CurEventID += Count+1; } m_LastDrawMode = Mode; @@ -1344,37 +1417,98 @@ bool WrappedOpenGL::Serialise_glMultiDrawElements(GLenum mode, const GLsizei *co } } - if(m_State <= EXECUTING) + if(m_State == READING) { m_Real.glMultiDrawElements(Mode, countArray, Type, idxOffsArray, Count); } + else if(m_State <= EXECUTING) + { + size_t i=0; + for(; i < m_Events.size(); i++) + { + if(m_Events[i].eventID >= m_CurEventID) + break; + } + + while(i > 1 && m_Events[i-1].fileOffset == m_Events[i].fileOffset) i--; + + uint32_t baseEventID = m_Events[i].eventID; + + if(m_LastEventID < baseEventID) + { + // To add the multidraw, we made an event N that is the 'parent' marker, then + // N+1, N+2, N+3, ... for each of the sub-draws. If the first sub-draw is selected + // then we'll replay up to N but not N+1, so just do nothing - we DON'T want to draw + // the first sub-draw in that range. + } + else if(m_FirstEventID <= baseEventID && m_LastEventID >= baseEventID) + { + // if we're replaying part-way into a multidraw, we can replay the first part 'easily' + // by just reducing the Count parameter to however many we want to replay. This only + // works if we're replaying from the first multidraw to the nth (n less than Count) + m_Real.glMultiDrawElements(Mode, countArray, Type, idxOffsArray, RDCMIN(Count, m_LastEventID - baseEventID + 1)); + } + 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. + RDCASSERT(m_LastEventID == m_FirstEventID); + + uint32_t drawidx = (m_LastEventID - baseEventID); + + m_Real.glDrawElements(Mode, countArray[drawidx], Type, idxOffsArray[drawidx]); + } + } const string desc = m_pSerialiser->GetDebugStr(); vector debugMessages = Serialise_DebugMessages(); - + if(m_State == READING) { - AddEvent(MULTI_DRAWELEMENTS, desc); string name = "glMultiDrawElements(" + - ( Mode == eGL_POINTS ? "GL_POINTS" : ToStr::Get(Mode) ) + ", " + + ( Mode == eGL_POINTS ? "GL_POINTS" : ToStr::Get(Mode) ) + "," + + ToStr::Get(Type) + "," + ToStr::Get(Count) + ")"; - - RDCUNIMPLEMENTED("Not processing multi-draw data for glMultiDrawElements() display"); FetchDrawcall draw; draw.name = name; - draw.numIndices = 1; - draw.numInstances = 1; - draw.indexOffset = 0; - draw.vertexOffset = 0; - draw.instanceOffset = 0; - draw.flags |= eDraw_Drawcall; + draw.flags |= eDraw_MultiDraw; - draw.debugMessages = debugMessages; + AddDrawcall(draw, false); - AddDrawcall(draw, true); + m_DrawcallStack.push_back(&m_DrawcallStack.back()->children.back()); + + m_CurEventID++; + + for(uint32_t i=0; i < Count; i++) + { + FetchDrawcall draw; + draw.numIndices = countArray[i]; + draw.indexOffset = (uint32_t) uint64_t(idxOffsArray[i])&0xFFFFFFFF; + + if(i == 0) + draw.debugMessages = debugMessages; + + draw.name = "glMultiDrawElements[" + ToStr::Get(i) + "](" + + ToStr::Get(draw.numIndices) + ", " + + ToStr::Get(draw.indexOffset) + ")"; + + draw.flags |= eDraw_Drawcall|eDraw_UseIBuffer; + + AddEvent(MULTI_DRAWELEMENTS, desc); + AddDrawcall(draw, true); + + m_CurEventID++; + } + + m_DrawcallStack.pop_back(); + } + else + { + m_CurEventID += Count+1; } m_LastDrawMode = Mode; @@ -1429,10 +1563,49 @@ bool WrappedOpenGL::Serialise_glMultiDrawElementsBaseVertex(GLenum mode, const G } } - if(m_State <= EXECUTING) + if(m_State == READING) { m_Real.glMultiDrawElementsBaseVertex(Mode, countArray, Type, idxOffsArray, Count, baseArray); } + else if(m_State <= EXECUTING) + { + size_t i=0; + for(; i < m_Events.size(); i++) + { + if(m_Events[i].eventID >= m_CurEventID) + break; + } + + while(i > 1 && m_Events[i-1].fileOffset == m_Events[i].fileOffset) i--; + + uint32_t baseEventID = m_Events[i].eventID; + + if(m_LastEventID < baseEventID) + { + // To add the multidraw, we made an event N that is the 'parent' marker, then + // N+1, N+2, N+3, ... for each of the sub-draws. If the first sub-draw is selected + // then we'll replay up to N but not N+1, so just do nothing - we DON'T want to draw + // the first sub-draw in that range. + } + else if(m_FirstEventID <= baseEventID && m_LastEventID >= baseEventID) + { + // if we're replaying part-way into a multidraw, we can replay the first part 'easily' + // by just reducing the Count parameter to however many we want to replay. This only + // works if we're replaying from the first multidraw to the nth (n less than Count) + m_Real.glMultiDrawElementsBaseVertex(Mode, countArray, Type, idxOffsArray, RDCMIN(Count, m_LastEventID - baseEventID + 1), baseArray); + } + 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. + RDCASSERT(m_LastEventID == m_FirstEventID); + + uint32_t drawidx = (m_LastEventID - baseEventID); + + m_Real.glDrawElementsBaseVertex(Mode, countArray[drawidx], Type, idxOffsArray[drawidx], baseArray[drawidx]); + } + } const string desc = m_pSerialiser->GetDebugStr(); @@ -1440,28 +1613,53 @@ bool WrappedOpenGL::Serialise_glMultiDrawElementsBaseVertex(GLenum mode, const G if(m_State == READING) { - AddEvent(MULTI_DRAWELEMENTSBASEVERTEX, desc); string name = "glMultiDrawElementsBaseVertex(" + - ( Mode == eGL_POINTS ? "GL_POINTS" : ToStr::Get(Mode) ) + ", " + + ( Mode == eGL_POINTS ? "GL_POINTS" : ToStr::Get(Mode) ) + "," + + ToStr::Get(Type) + "," + ToStr::Get(Count) + ")"; - - RDCUNIMPLEMENTED("Not processing multi-draw data for glMultiDrawElements() display"); FetchDrawcall draw; draw.name = name; - draw.numIndices = 1; - draw.numInstances = 1; - draw.indexOffset = 0; - draw.vertexOffset = 0; - draw.instanceOffset = 0; - draw.flags |= eDraw_Drawcall; + draw.flags |= eDraw_MultiDraw; - draw.debugMessages = debugMessages; + AddDrawcall(draw, false); - AddDrawcall(draw, true); + m_DrawcallStack.push_back(&m_DrawcallStack.back()->children.back()); + + m_CurEventID++; + + for(uint32_t i=0; i < Count; i++) + { + FetchDrawcall draw; + draw.numIndices = countArray[i]; + draw.indexOffset = (uint32_t) uint64_t(idxOffsArray[i])&0xFFFFFFFF; + draw.vertexOffset = baseArray[i]; + + if(i == 0) + draw.debugMessages = debugMessages; + + draw.name = "glMultiDrawElementsBaseVertex[" + ToStr::Get(i) + "](" + + ToStr::Get(draw.numIndices) + ", " + + ToStr::Get(draw.indexOffset) + ", " + + ToStr::Get(draw.vertexOffset) + ")"; + + draw.flags |= eDraw_Drawcall|eDraw_UseIBuffer; + + AddEvent(MULTI_DRAWELEMENTSBASEVERTEX, desc); + AddDrawcall(draw, true); + + m_CurEventID++; + } + + m_DrawcallStack.pop_back(); } - + else + { + m_CurEventID += Count+1; + } + + m_LastIndexSize = Type; m_LastDrawMode = Mode; SAFE_DELETE_ARRAY(countArray); @@ -1491,10 +1689,60 @@ bool WrappedOpenGL::Serialise_glMultiDrawArraysIndirect(GLenum mode, const void SERIALISE_ELEMENT(uint32_t, Count, drawcount); SERIALISE_ELEMENT(uint32_t, Stride, stride); - if(m_State <= EXECUTING) + if(m_State == READING) { m_Real.glMultiDrawArraysIndirect(Mode, (const void *)Offset, Count, Stride); } + else if(m_State <= EXECUTING) + { + size_t i=0; + for(; i < m_Events.size(); i++) + { + if(m_Events[i].eventID >= m_CurEventID) + break; + } + + while(i > 1 && m_Events[i-1].fileOffset == m_Events[i].fileOffset) i--; + + uint32_t baseEventID = m_Events[i].eventID; + + if(m_LastEventID < baseEventID) + { + // To add the multidraw, we made an event N that is the 'parent' marker, then + // N+1, N+2, N+3, ... for each of the sub-draws. If the first sub-draw is selected + // then we'll replay up to N but not N+1, so just do nothing - we DON'T want to draw + // the first sub-draw in that range. + } + else if(m_FirstEventID <= baseEventID && m_LastEventID >= baseEventID) + { + // if we're replaying part-way into a multidraw, we can replay the first part 'easily' + // by just reducing the Count parameter to however many we want to replay. This only + // works if we're replaying from the first multidraw to the nth (n less than Count) + m_Real.glMultiDrawArraysIndirect(Mode, (const void *)Offset, RDCMIN(Count, m_LastEventID - baseEventID + 1), Stride); + } + 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. + RDCASSERT(m_LastEventID == m_FirstEventID); + + uint32_t drawidx = (m_LastEventID - baseEventID); + + DrawArraysIndirectCommand params; + + GLintptr offs = (GLintptr)Offset; + if(Stride != 0) + offs += Stride*drawidx; + else + offs += sizeof(params)*drawidx; + + m_Real.glGetBufferSubData(eGL_DRAW_INDIRECT_BUFFER, offs, sizeof(params), ¶ms); + + m_Real.glDrawArraysInstancedBaseInstance(Mode, params.first, params.count, params.instanceCount, params.baseInstance); + } + } + const string desc = m_pSerialiser->GetDebugStr(); @@ -1502,28 +1750,65 @@ bool WrappedOpenGL::Serialise_glMultiDrawArraysIndirect(GLenum mode, const void if(m_State == READING) { - AddEvent(DRAWARRAYS_INDIRECT, desc); string name = "glMultiDrawArraysIndirect(" + - ( Mode == eGL_POINTS ? "GL_POINTS" : ToStr::Get(Mode) ) + ")"; - - RDCUNIMPLEMENTED("Not fetching indirect data for glMultiDrawArraysIndirect() display"); + ( Mode == eGL_POINTS ? "GL_POINTS" : ToStr::Get(Mode) ) + "," + + ToStr::Get(Count) + ")"; FetchDrawcall draw; draw.name = name; - draw.numIndices = 1; - draw.numInstances = 1; - draw.indexOffset = 0; - draw.vertexOffset = 0; - draw.instanceOffset = 0; - draw.flags |= eDraw_Drawcall; + draw.flags |= eDraw_MultiDraw; + + AddDrawcall(draw, false); + + m_DrawcallStack.push_back(&m_DrawcallStack.back()->children.back()); + + m_CurEventID++; + + GLintptr offs = (GLintptr)Offset; + + for(uint32_t i=0; i < Count; i++) + { + DrawArraysIndirectCommand params; + + m_Real.glGetBufferSubData(eGL_DRAW_INDIRECT_BUFFER, offs, sizeof(params), ¶ms); + + if(Stride) + offs += Stride; + else + offs += sizeof(params); + + FetchDrawcall draw; + draw.numIndices = params.count; + draw.numInstances = params.instanceCount; + draw.vertexOffset = params.first; + draw.instanceOffset = params.baseInstance; - draw.debugMessages = debugMessages; + if(i == 0) + draw.debugMessages = debugMessages; + + draw.name = "glMultiDrawArraysIndirect[" + ToStr::Get(i) + "](" + + ToStr::Get(draw.numIndices) + ", " + + ToStr::Get(draw.numInstances) + ", " + + ToStr::Get(draw.vertexOffset) + ", " + + ToStr::Get(draw.instanceOffset) + ")"; - m_LastDrawMode = Mode; + draw.flags |= eDraw_Drawcall|eDraw_Instanced|eDraw_Indirect; + + AddEvent(MULTI_DRAWARRAYS_INDIRECT, desc); + AddDrawcall(draw, true); - AddDrawcall(draw, true); + m_CurEventID++; + } + + m_DrawcallStack.pop_back(); } + else + { + m_CurEventID += Count+1; + } + + m_LastDrawMode = Mode; return true; } @@ -1548,11 +1833,68 @@ bool WrappedOpenGL::Serialise_glMultiDrawElementsIndirect(GLenum mode, GLenum ty SERIALISE_ELEMENT(uint64_t, Offset, (uint64_t)indirect); SERIALISE_ELEMENT(uint32_t, Count, drawcount); SERIALISE_ELEMENT(uint32_t, Stride, stride); - - if(m_State <= EXECUTING) + + uint32_t IdxSize = + Type == eGL_UNSIGNED_BYTE ? 1 + : Type == eGL_UNSIGNED_SHORT ? 2 + : /*Type == eGL_UNSIGNED_INT*/ 4; + + if(m_State == READING) { m_Real.glMultiDrawElementsIndirect(Mode, Type, (const void *)Offset, Count, Stride); } + else if(m_State <= EXECUTING) + { + size_t i=0; + for(; i < m_Events.size(); i++) + { + if(m_Events[i].eventID >= m_CurEventID) + break; + } + + while(i > 1 && m_Events[i-1].fileOffset == m_Events[i].fileOffset) i--; + + uint32_t baseEventID = m_Events[i].eventID; + + if(m_LastEventID < baseEventID) + { + // To add the multidraw, we made an event N that is the 'parent' marker, then + // N+1, N+2, N+3, ... for each of the sub-draws. If the first sub-draw is selected + // then we'll replay up to N but not N+1, so just do nothing - we DON'T want to draw + // the first sub-draw in that range. + } + else if(m_FirstEventID <= baseEventID && m_LastEventID >= baseEventID) + { + // if we're replaying part-way into a multidraw, we can replay the first part 'easily' + // by just reducing the Count parameter to however many we want to replay. This only + // works if we're replaying from the first multidraw to the nth (n less than Count) + m_Real.glMultiDrawElementsIndirect(Mode, Type, (const void *)Offset, RDCMIN(Count, m_LastEventID - baseEventID + 1), Stride); + } + 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. + RDCASSERT(m_LastEventID == m_FirstEventID); + + uint32_t drawidx = (m_LastEventID - baseEventID); + + DrawElementsIndirectCommand params; + + GLintptr offs = (GLintptr)Offset; + if(Stride != 0) + offs += Stride*drawidx; + else + offs += sizeof(params)*drawidx; + + m_Real.glGetBufferSubData(eGL_DRAW_INDIRECT_BUFFER, offs, sizeof(params), ¶ms); + + m_Real.glDrawElementsInstancedBaseVertexBaseInstance(Mode, params.count, Type, (const void *)ptrdiff_t(params.firstIndex*IdxSize), + params.instanceCount, params.baseVertex, params.baseInstance); + + m_LastIndexOffset = (GLuint)(params.firstIndex*IdxSize); + } + } const string desc = m_pSerialiser->GetDebugStr(); @@ -1560,28 +1902,68 @@ bool WrappedOpenGL::Serialise_glMultiDrawElementsIndirect(GLenum mode, GLenum ty if(m_State == READING) { - AddEvent(DRAWARRAYS_INDIRECT, desc); string name = "glMultiDrawElementsIndirect(" + - ( Mode == eGL_POINTS ? "GL_POINTS" : ToStr::Get(Mode) ) + ")"; - - RDCUNIMPLEMENTED("Not fetching indirect data for glMultiDrawElementsIndirect() display"); + ( Mode == eGL_POINTS ? "GL_POINTS" : ToStr::Get(Mode) ) + "," + + ToStr::Get(Type) + "," + + ToStr::Get(Count) + ")"; FetchDrawcall draw; draw.name = name; - draw.numIndices = 1; - draw.numInstances = 1; - draw.indexOffset = 0; - draw.vertexOffset = 0; - draw.instanceOffset = 0; - draw.flags |= eDraw_Drawcall; + draw.flags |= eDraw_MultiDraw; - draw.debugMessages = debugMessages; + AddDrawcall(draw, false); - m_LastDrawMode = Mode; + m_DrawcallStack.push_back(&m_DrawcallStack.back()->children.back()); - AddDrawcall(draw, true); + m_CurEventID++; + + GLintptr offs = (GLintptr)Offset; + + for(uint32_t i=0; i < Count; i++) + { + DrawElementsIndirectCommand params; + + m_Real.glGetBufferSubData(eGL_DRAW_INDIRECT_BUFFER, offs, sizeof(params), ¶ms); + + if(Stride) + offs += Stride; + else + offs += sizeof(params); + + FetchDrawcall draw; + draw.numIndices = params.count; + draw.numInstances = params.instanceCount; + draw.indexOffset = params.firstIndex*IdxSize; + draw.vertexOffset = params.baseVertex; + draw.instanceOffset = params.baseInstance; + + if(i == 0) + draw.debugMessages = debugMessages; + + draw.name = "glMultiDrawElementsIndirect[" + ToStr::Get(i) + "](" + + ToStr::Get(draw.numIndices) + ", " + + ToStr::Get(draw.numInstances) + ", " + + ToStr::Get(draw.indexOffset) + ", " + + ToStr::Get(draw.instanceOffset) + ")"; + + draw.flags |= eDraw_Drawcall|eDraw_UseIBuffer|eDraw_Instanced|eDraw_Indirect; + + AddEvent(MULTI_DRAWELEMENTS_INDIRECT, desc); + AddDrawcall(draw, true); + + m_CurEventID++; + } + + m_DrawcallStack.pop_back(); } + else + { + m_CurEventID += Count+1; + } + + m_LastIndexSize = Type; + m_LastDrawMode = Mode; return true; } diff --git a/renderdoc/replay/replay_renderer.cpp b/renderdoc/replay/replay_renderer.cpp index 9053233a3..287aabca3 100644 --- a/renderdoc/replay/replay_renderer.cpp +++ b/renderdoc/replay/replay_renderer.cpp @@ -1342,7 +1342,7 @@ FetchDrawcall *ReplayRenderer::SetupDrawcallPointers(FetchFrameInfo frame, rdcty { ret = previous = SetupDrawcallPointers(frame, draw->children, draw, previous); } - else if(draw->flags & (eDraw_PushMarker|eDraw_SetMarker|eDraw_Present)) + else if(draw->flags & (eDraw_PushMarker|eDraw_SetMarker|eDraw_Present|eDraw_MultiDraw)) { // don't want to set up previous/next links for markers } diff --git a/renderdocui/Interop/Enums.cs b/renderdocui/Interop/Enums.cs index 9a1a1fdb3..75a664985 100644 --- a/renderdocui/Interop/Enums.cs +++ b/renderdocui/Interop/Enums.cs @@ -332,6 +332,7 @@ namespace renderdoc SetMarker = 0x10, PushMarker = 0x20, Present = 0x40, + MultiDraw = 0x80, // flags UseIBuffer = 0x100,