diff --git a/renderdoc/driver/d3d11/d3d11_debug.cpp b/renderdoc/driver/d3d11/d3d11_debug.cpp index a597d7365..0a2816efb 100644 --- a/renderdoc/driver/d3d11/d3d11_debug.cpp +++ b/renderdoc/driver/d3d11/d3d11_debug.cpp @@ -3707,25 +3707,19 @@ void D3D11DebugManager::RenderCheckerboard(Vec3f light, Vec3f dark) } } -PostVSData D3D11DebugManager::GetPostVSBuffers(uint32_t frameID, uint32_t eventID) -{ - auto idx = std::make_pair(frameID, eventID); - if(m_PostVSData.find(idx) != m_PostVSData.end()) - return m_PostVSData[idx]; - - RDCWARN("Post VS Buffers not initialised!"); - PostVSData empty; - RDCEraseEl(empty); - return empty; -} - MeshFormat D3D11DebugManager::GetPostVSBuffers(uint32_t frameID, uint32_t eventID, MeshDataStage stage) { - MeshFormat ret; + PostVSData postvs; + RDCEraseEl(postvs); + + auto idx = std::make_pair(frameID, eventID); + if(m_PostVSData.find(idx) != m_PostVSData.end()) + postvs = m_PostVSData[idx]; - PostVSData postvs = GetPostVSBuffers(frameID, eventID); PostVSData::StageData s = postvs.GetStage(stage); - + + MeshFormat ret; + if(s.useIndices && s.idxBuf) ret.idxbuf = ((WrappedID3D11Buffer *)s.idxBuf)->GetResourceID(); else diff --git a/renderdoc/driver/d3d11/d3d11_debug.h b/renderdoc/driver/d3d11/d3d11_debug.h index f3b8745a5..a3d29a7e9 100644 --- a/renderdoc/driver/d3d11/d3d11_debug.h +++ b/renderdoc/driver/d3d11/d3d11_debug.h @@ -113,7 +113,6 @@ class D3D11DebugManager int GetHeight() { return m_height; } void InitPostVSBuffers(uint32_t frameID, uint32_t eventID); - PostVSData GetPostVSBuffers(uint32_t frameID, uint32_t eventID); MeshFormat GetPostVSBuffers(uint32_t frameID, uint32_t eventID, MeshDataStage stage); uint32_t GetStructCount(ID3D11UnorderedAccessView *uav); diff --git a/renderdoc/driver/gl/gl_debug.cpp b/renderdoc/driver/gl/gl_debug.cpp index d287e4cfc..26c98a9df 100644 --- a/renderdoc/driver/gl/gl_debug.cpp +++ b/renderdoc/driver/gl/gl_debug.cpp @@ -33,6 +33,8 @@ #include "serialise/string_utils.h" +#include + GLuint GLReplay::CreateCShaderProgram(const char *csSrc) { if(m_pDriver == NULL) return 0; @@ -215,7 +217,7 @@ void GLReplay::InitDebugData() gl.glGenBuffers(1, &DebugData.outlineStripVB); gl.glBindBuffer(eGL_ARRAY_BUFFER, DebugData.outlineStripVB); - gl.glBufferData(eGL_ARRAY_BUFFER, sizeof(data), data, eGL_STATIC_DRAW); + gl.glNamedBufferDataEXT(DebugData.outlineStripVB, sizeof(data), data, eGL_STATIC_DRAW); gl.glGenVertexArrays(1, &DebugData.outlineStripVAO); gl.glBindVertexArray(DebugData.outlineStripVAO); @@ -246,7 +248,7 @@ void GLReplay::InitDebugData() for(size_t i=0; i < ARRAY_COUNT(DebugData.UBOs); i++) { gl.glBindBuffer(eGL_UNIFORM_BUFFER, DebugData.UBOs[i]); - gl.glBufferData(eGL_UNIFORM_BUFFER, 512, NULL, eGL_DYNAMIC_DRAW); + gl.glNamedBufferDataEXT(DebugData.UBOs[i], 512, NULL, eGL_DYNAMIC_DRAW); RDCCOMPILE_ASSERT(sizeof(texdisplay) < 512, "texdisplay UBO too large"); RDCCOMPILE_ASSERT(sizeof(FontUniforms) < 512, "texdisplay UBO too large"); RDCCOMPILE_ASSERT(sizeof(HistogramCBufferData) < 512, "texdisplay UBO too large"); @@ -408,6 +410,18 @@ void GLReplay::InitDebugData() gl.glEnableVertexAttribArray(0); DebugData.replayQuadProg = CreateShaderProgram(DebugData.blitvsSource.c_str(), DebugData.genericfsSource.c_str()); + + MakeCurrentReplayContext(&m_ReplayCtx); + + gl.glGenTransformFeedbacks(1, &DebugData.feedbackObj); + gl.glGenBuffers(1, &DebugData.feedbackBuffer); + gl.glGenQueries(1, &DebugData.feedbackQuery); + + gl.glBindTransformFeedback(eGL_TRANSFORM_FEEDBACK, DebugData.feedbackObj); + gl.glBindBuffer(eGL_TRANSFORM_FEEDBACK_BUFFER, DebugData.feedbackBuffer); + gl.glNamedBufferStorageEXT(DebugData.feedbackBuffer, 32*1024*1024, NULL, GL_MAP_READ_BIT); + gl.glBindBufferBase(eGL_TRANSFORM_FEEDBACK_BUFFER, 0, DebugData.feedbackBuffer); + gl.glBindTransformFeedback(eGL_TRANSFORM_FEEDBACK, 0); } void GLReplay::DeleteDebugData() @@ -416,6 +430,16 @@ void GLReplay::DeleteDebugData() WrappedOpenGL &gl = *m_pDriver; + for(auto it=m_PostVSData.begin(); it != m_PostVSData.end(); ++it) + { + gl.glDeleteBuffers(1, &it->second.vsout.buf); + gl.glDeleteBuffers(1, &it->second.vsout.idxBuf); + gl.glDeleteBuffers(1, &it->second.gsout.buf); + gl.glDeleteBuffers(1, &it->second.gsout.idxBuf); + } + + m_PostVSData.clear(); + gl.glDeleteProgram(DebugData.blitProg); for(int i=0; i < 3; i++) @@ -459,6 +483,10 @@ void GLReplay::DeleteDebugData() gl.glDeleteBuffers(1, &DebugData.minmaxResult); gl.glDeleteBuffers(1, &DebugData.histogramBuf); + gl.glDeleteTransformFeedbacks(1, &DebugData.feedbackObj); + gl.glDeleteBuffers(1, &DebugData.feedbackBuffer); + gl.glDeleteQueries(1, &DebugData.feedbackQuery); + gl.glDeleteVertexArrays(1, &DebugData.meshVAO); gl.glDeleteVertexArrays(1, &DebugData.axisVAO); gl.glDeleteVertexArrays(1, &DebugData.frustumVAO); @@ -1548,6 +1576,450 @@ ResourceId GLReplay::RenderOverlay(ResourceId texid, TextureDisplayOverlay overl return m_pDriver->GetResourceManager()->GetID(TextureRes(ctx, DebugData.overlayTex)); } +void GLReplay::InitPostVSBuffers(uint32_t frameID, uint32_t eventID) +{ + auto idx = std::make_pair(frameID, eventID); + if(m_PostVSData.find(idx) != m_PostVSData.end()) + return; + + MakeCurrentReplayContext(&m_ReplayCtx); + + void *ctx = m_ReplayCtx.ctx; + + WrappedOpenGL &gl = *m_pDriver; + GLResourceManager *rm = m_pDriver->GetResourceManager(); + + GLRenderState rs(&gl.GetHookset(), NULL, READING); + rs.FetchState(ctx, &gl); + GLuint elArrayBuffer = 0; + if(rs.VAO) + gl.glGetIntegerv(eGL_ELEMENT_ARRAY_BUFFER_BINDING, (GLint *)&elArrayBuffer); + + // reflection structures + ShaderReflection *vsRefl = NULL; + ShaderReflection *tesRefl = NULL; + ShaderReflection *gsRefl = NULL; + + // non-program used separable programs of each shader. + // we'll add our feedback varings to these programs, relink, + // and combine into a pipeline for use. + GLuint vsProg = 0; + GLuint tcsProg = 0; + GLuint tesProg = 0; + GLuint gsProg = 0; + + // these are the 'real' programs with uniform values that we need + // to copy over to our separable programs. + GLuint vsProgSrc = 0; + GLuint tcsProgSrc = 0; + GLuint tesProgSrc = 0; + GLuint gsProgSrc = 0; + + if(rs.Program == 0) + { + if(rs.Pipeline == 0) + { + return; + } + else + { + ResourceId id = rm->GetID(ProgramPipeRes(ctx, rs.Pipeline)); + auto &pipeDetails = m_pDriver->m_Pipelines[id]; + + if(pipeDetails.stageShaders[0] != ResourceId()) + { + vsRefl = GetShader(pipeDetails.stageShaders[0]); + vsProg = m_pDriver->m_Shaders[pipeDetails.stageShaders[0]].prog; + vsProgSrc = rm->GetCurrentResource(pipeDetails.stagePrograms[0]).name; + } + if(pipeDetails.stageShaders[1] != ResourceId()) + { + tcsProg = m_pDriver->m_Shaders[pipeDetails.stageShaders[1]].prog; + tcsProgSrc = rm->GetCurrentResource(pipeDetails.stagePrograms[1]).name; + } + if(pipeDetails.stageShaders[2] != ResourceId()) + { + tesRefl = GetShader(pipeDetails.stageShaders[2]); + tesProg = m_pDriver->m_Shaders[pipeDetails.stageShaders[2]].prog; + tesProgSrc = rm->GetCurrentResource(pipeDetails.stagePrograms[2]).name; + } + if(pipeDetails.stageShaders[3] != ResourceId()) + { + gsRefl = GetShader(pipeDetails.stageShaders[3]); + gsProg = m_pDriver->m_Shaders[pipeDetails.stageShaders[3]].prog; + gsProgSrc = rm->GetCurrentResource(pipeDetails.stagePrograms[3]).name; + } + } + } + else + { + auto &progDetails = m_pDriver->m_Programs[rm->GetID(ProgramRes(ctx, rs.Program))]; + + if(progDetails.stageShaders[0] != ResourceId()) + { + vsRefl = GetShader(progDetails.stageShaders[0]); + vsProg = m_pDriver->m_Shaders[progDetails.stageShaders[0]].prog; + } + if(progDetails.stageShaders[1] != ResourceId()) + { + tcsProg = m_pDriver->m_Shaders[progDetails.stageShaders[1]].prog; + } + if(progDetails.stageShaders[2] != ResourceId()) + { + tesRefl = GetShader(progDetails.stageShaders[2]); + tesProg = m_pDriver->m_Shaders[progDetails.stageShaders[2]].prog; + } + if(progDetails.stageShaders[3] != ResourceId()) + { + gsRefl = GetShader(progDetails.stageShaders[3]); + gsProg = m_pDriver->m_Shaders[progDetails.stageShaders[3]].prog; + } + + vsProgSrc = tcsProgSrc = tesProgSrc = gsProgSrc = rs.Program; + } + + if(vsRefl == NULL) + { + // no vertex shader bound (no vertex processing - compute only program + // or no program bound, for a clear etc) + m_PostVSData[idx] = GLPostVSData(); + return; + } + + const FetchDrawcall *drawcall = m_pDriver->GetDrawcall(frameID, eventID); + + if(drawcall->numIndices == 0) + { + // draw is 0 length, nothing to do + m_PostVSData[idx] = GLPostVSData(); + return; + } + + GLenum gsOutputType = eGL_NONE; + + if(gsProg) + gl.glGetProgramiv(gsProg, eGL_GEOMETRY_OUTPUT_TYPE, (GLint *)&gsOutputType); + + vector varyings; + + // we don't want to do any work, so just discard before rasterizing + gl.glEnable(eGL_RASTERIZER_DISCARD); + + varyings.clear(); + + uint32_t stride = 0; + uint32_t posoffset = ~0U; + + for(int32_t i=0; i < vsRefl->OutputSig.count; i++) + { + varyings.push_back(vsRefl->OutputSig[i].varName.elems); + + if(!strcmp(vsRefl->OutputSig[i].varName.elems, "gl_Position")) + posoffset = stride; + + stride += sizeof(float)*vsRefl->OutputSig[i].compCount; + } + + gl.glTransformFeedbackVaryings(vsProg, (GLsizei)varyings.size(), &varyings[0], eGL_INTERLEAVED_ATTRIBS); + + // relink separable program with varyings + gl.glLinkProgram(vsProg); + + GLint status = 0; + gl.glGetProgramiv(vsProg, eGL_LINK_STATUS, &status); + if(status == 0) + { + char buffer[1025] = {0}; + gl.glGetProgramInfoLog(vsProg, 1024, NULL, buffer); + RDCERR("Link error making xfb vs program: %s", buffer); + m_PostVSData[idx] = GLPostVSData(); + return; + } + + // make a pipeline to contain just the vertex shader + GLuint vsFeedbackPipe = 0; + gl.glGenProgramPipelines(1, &vsFeedbackPipe); + + // bind the separable vertex program to it + gl.glUseProgramStages(vsFeedbackPipe, eGL_VERTEX_SHADER_BIT, vsProg); + + // copy across any uniform values, bindings etc from the real program containing + // the vertex stage + CopyProgramUniforms(gl.GetHookset(), vsProgSrc, vsProg); + + // bind our program and do the feedback draw + gl.glUseProgram(0); + gl.glBindProgramPipeline(vsFeedbackPipe); + + gl.glBindTransformFeedback(eGL_TRANSFORM_FEEDBACK, DebugData.feedbackObj); + + // need to rebind this here because of an AMD bug that seems to ignore the buffer + // bindings in the feedback object - or at least it errors if the default feedback + // object has no buffers bound. Fortunately the state is still object-local so + // we don't have to restore the buffer binding on the default feedback object. + gl.glBindBufferBase(eGL_TRANSFORM_FEEDBACK_BUFFER, 0, DebugData.feedbackBuffer); + + GLuint idxBuf = 0; + + gl.glBeginQuery(eGL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN, DebugData.feedbackQuery); + gl.glBeginTransformFeedback(eGL_POINTS); + + if((drawcall->flags & eDraw_UseIBuffer) == 0) + { + gl.glDrawArrays(eGL_POINTS, drawcall->vertexOffset, drawcall->numIndices); + } + else // drawcall is indexed + { + ResourceId idxId = rm->GetID(BufferRes(NULL, elArrayBuffer)); + + vector idxdata = GetBufferData(idxId, drawcall->indexOffset*drawcall->indexByteWidth, drawcall->numIndices*drawcall->indexByteWidth); + + vector indices; + + uint8_t *idx8 = (uint8_t *) &idxdata[0]; + uint16_t *idx16 = (uint16_t *)&idxdata[0]; + uint32_t *idx32 = (uint32_t *)&idxdata[0]; + + // only read as many indices as were available in the buffer + uint32_t numIndices = RDCMIN(uint32_t(idxdata.size()/drawcall->indexByteWidth), drawcall->numIndices); + + // grab all unique vertex indices referenced + for(uint32_t i=0; i < numIndices; i++) + { + uint32_t i32 = 0; + if(drawcall->indexByteWidth == 1) i32 = uint32_t(idx8 [i]); + else if(drawcall->indexByteWidth == 2) i32 = uint32_t(idx16[i]); + else if(drawcall->indexByteWidth == 4) i32 = idx32[i]; + + auto it = std::lower_bound(indices.begin(), indices.end(), i32); + + if(it != indices.end() && *it == i32) + continue; + + indices.insert(it, i32); + } + + // An index buffer could be something like: 500, 501, 502, 501, 503, 502 + // in which case we can't use the existing index buffer without filling 499 slots of vertex + // data with padding. Instead we rebase the indices based on the smallest vertex so it becomes + // 0, 1, 2, 1, 3, 2 and then that matches our stream-out'd buffer. + // + // Since we want the indices to be preserved in order to easily match up inputs to outputs, + // but shifted, fill in gaps in our streamout vertex buffer with the lowest index value. + // (use the lowest index value so that even the gaps are a 'valid' vertex, rather than + // potentially garbage data). + uint32_t minindex = indices.empty() ? 0 : indices[0]; + + // indices[] contains ascending unique vertex indices referenced. Fill gaps with minindex + for(size_t i=1; i < indices.size(); i++) + { + if(indices[i]-1 > indices[i-1]) + { + size_t gapsize = size_t( (indices[i]-1) - indices[i-1] ); + + indices.insert(indices.begin()+i, gapsize, minindex); + + i += gapsize; + } + } + + // generate a temporary index buffer with our 'unique index set' indices, + // so we can transform feedback each referenced vertex once + GLuint indexSetBuffer = 0; + gl.glGenBuffers(1, &indexSetBuffer); + gl.glBindBuffer(eGL_ELEMENT_ARRAY_BUFFER, indexSetBuffer); + gl.glNamedBufferStorageEXT(indexSetBuffer, sizeof(uint32_t)*indices.size(), &indices[0], 0); + + gl.glDrawElementsBaseVertex(eGL_POINTS, (GLsizei)indices.size(), eGL_UNSIGNED_INT, NULL, drawcall->vertexOffset); + + // delete the buffer, we don't need it anymore + gl.glBindBuffer(eGL_ELEMENT_ARRAY_BUFFER, elArrayBuffer); + gl.glDeleteBuffers(1, &indexSetBuffer); + + // rebase existing index buffer to point from 0 onwards (which will index into our + // stream-out'd vertex buffer) + if(drawcall->indexByteWidth == 1) + { + for(uint32_t i=0; i < numIndices; i++) + idx8[i] -= uint8_t(minindex&0xff); + } + else if(drawcall->indexByteWidth == 2) + { + for(uint32_t i=0; i < numIndices; i++) + idx16[i] -= uint16_t(minindex&0xffff); + } + else + { + for(uint32_t i=0; i < numIndices; i++) + idx32[i] -= minindex; + } + + // make the index buffer that can be used to render this postvs data - the original + // indices, rebased with minindex being 0 (since we transform feedback to the start + // of our feedback buffer). + gl.glGenBuffers(1, &idxBuf); + gl.glBindBuffer(eGL_ELEMENT_ARRAY_BUFFER, idxBuf); + gl.glNamedBufferStorageEXT(idxBuf, (GLsizeiptr)idxdata.size(), &idxdata[0], 0); + + // restore previous element array buffer binding + gl.glBindBuffer(eGL_ELEMENT_ARRAY_BUFFER, elArrayBuffer); + } + + gl.glEndTransformFeedback(); + gl.glEndQuery(eGL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN); + + // this should be the same as the draw size + GLuint primsWritten = 0; + gl.glGetQueryObjectuiv(DebugData.feedbackQuery, eGL_QUERY_RESULT, &primsWritten); + + // get buffer data from buffer attached to feedback object + float *data = (float *)gl.glMapNamedBufferEXT(DebugData.feedbackBuffer, eGL_READ_ONLY); + + // create a buffer with this data, for future use (typed to ARRAY_BUFFER so we + // can render from it to display previews). + GLuint vsoutBuffer = 0; + gl.glGenBuffers(1, &vsoutBuffer); + gl.glBindBuffer(eGL_ARRAY_BUFFER, vsoutBuffer); + gl.glNamedBufferStorageEXT(vsoutBuffer, stride*primsWritten, data, 0); + + byte *byteData = (byte *)data; + + float nearp = 0.0f; + float farp = 0.0f; + + Vec4f *pos0 = (Vec4f *)(byteData + posoffset); + + for(GLuint i=1; posoffset != ~0U && i < primsWritten; i++) + { + ////////////////////////////////////////////////////////////////////////////////// + // derive near/far, assuming a standard perspective matrix + // + // the transformation from from pre-projection {Z,W} to post-projection {Z,W} + // is linear. So we can say Zpost = Zpre*m + c . Here we assume Wpre = 1 + // and we know Wpost = Zpre from the perspective matrix. + // we can then see from the perspective matrix that + // m = F/(F-N) + // c = -(F*N)/(F-N) + // + // with re-arranging and substitution, we then get: + // N = -c/m + // F = c/(1-m) + // + // so if we can derive m and c then we can determine N and F. We can do this with + // two points, and we pick them reasonably distinct on z to reduce floating-point + // error + + Vec4f *pos = (Vec4f *)(byteData + posoffset + i*stride); + + if(fabs(pos->w - pos0->w) > 0.01f) + { + Vec2f A(pos0->w, pos0->z); + Vec2f B(pos->w, pos->z); + + float m = (B.y-A.y)/(B.x-A.x); + float c = B.y - B.x*m; + + if(m == 1.0f) continue; + + nearp = -c/m; + farp = c/(1-m); + + break; + } + } + + gl.glUnmapNamedBufferEXT(DebugData.feedbackBuffer); + + // store everything out to the PostVS data cache + m_PostVSData[idx].vsin.topo = drawcall->topology; + m_PostVSData[idx].vsout.buf = vsoutBuffer; + m_PostVSData[idx].vsout.posOffset = posoffset; + m_PostVSData[idx].vsout.vertStride = stride; + m_PostVSData[idx].vsout.nearPlane = nearp; + m_PostVSData[idx].vsout.farPlane = farp; + + m_PostVSData[idx].vsout.useIndices = (drawcall->flags & eDraw_UseIBuffer) > 0; + m_PostVSData[idx].vsout.numVerts = drawcall->numIndices; + + m_PostVSData[idx].vsout.idxBuf = 0; + m_PostVSData[idx].vsout.idxByteWidth = drawcall->indexByteWidth; + if(m_PostVSData[idx].vsout.useIndices && idxBuf) + { + m_PostVSData[idx].vsout.idxBuf = idxBuf; + } + + m_PostVSData[idx].vsout.topo = drawcall->topology; + + // set vsProg back to no varyings, for future use + gl.glTransformFeedbackVaryings(vsProg, 0, NULL, eGL_INTERLEAVED_ATTRIBS); + gl.glLinkProgram(vsProg); + + GLuint lastFeedbackPipe = 0; + + // delete temporary pipelines we made + gl.glDeleteProgramPipelines(1, &vsFeedbackPipe); + if(lastFeedbackPipe) gl.glDeleteProgramPipelines(1, &lastFeedbackPipe); + + // restore replay state we trashed + gl.glUseProgram(rs.Program); + gl.glBindProgramPipeline(rs.Pipeline); + + gl.glBindBuffer(eGL_ARRAY_BUFFER, rs.BufferBindings[GLRenderState::eBufIdx_Array]); + gl.glBindBuffer(eGL_ELEMENT_ARRAY_BUFFER, elArrayBuffer); + + gl.glBindTransformFeedback(eGL_TRANSFORM_FEEDBACK, rs.FeedbackObj); + + if(!rs.Enabled[GLRenderState::eEnabled_RasterizerDiscard]) + gl.glDisable(eGL_RASTERIZER_DISCARD); + else + gl.glEnable(eGL_RASTERIZER_DISCARD); +} + +MeshFormat GLReplay::GetPostVSBuffers(uint32_t frameID, uint32_t eventID, MeshDataStage stage) +{ + GLPostVSData postvs; + RDCEraseEl(postvs); + + auto idx = std::make_pair(frameID, eventID); + if(m_PostVSData.find(idx) != m_PostVSData.end()) + postvs = m_PostVSData[idx]; + + GLPostVSData::StageData s = postvs.GetStage(stage); + + MeshFormat ret; + + if(s.useIndices && s.idxBuf) + ret.idxbuf = m_pDriver->GetResourceManager()->GetID(BufferRes(NULL, s.idxBuf)); + else + ret.idxbuf = ResourceId(); + ret.idxoffs = 0; + ret.idxByteWidth = s.idxByteWidth; + + if(s.buf) + ret.buf = m_pDriver->GetResourceManager()->GetID(BufferRes(NULL, s.buf)); + else + ret.buf = ResourceId(); + + ret.offset = s.posOffset; + ret.stride = s.vertStride; + + ret.compCount = 4; + ret.compByteWidth = 4; + ret.compType = eCompType_Float; + ret.specialFormat = eSpecial_Unknown; + + ret.showAlpha = false; + + ret.topo = s.topo; + ret.numVerts = s.numVerts; + + ret.unproject = true; + ret.nearPlane = s.nearPlane; + ret.farPlane = s.farPlane; + + return ret; +} + FloatVector GLReplay::InterpretVertex(byte *data, uint32_t vert, MeshDisplay cfg, byte *end, bool &valid) { FloatVector ret(0.0f, 0.0f, 0.0f, 1.0f); @@ -1666,6 +2138,92 @@ void GLReplay::RenderMesh(uint32_t frameID, uint32_t eventID, const vectorGetResourceManager()->GetCurrentResource(fmt.buf).name; + gl.glBindVertexBuffer(0, vb, fmt.offset, fmt.stride); + + GLenum topo = MakeGLPrimitiveTopology(fmt.topo); + + if(fmt.idxbuf != ResourceId()) + { + GLuint ib = m_pDriver->GetResourceManager()->GetCurrentResource(fmt.idxbuf).name; + gl.glBindBuffer(eGL_ELEMENT_ARRAY_BUFFER, ib); + + GLenum idxtype = eGL_UNSIGNED_BYTE; + if(fmt.idxByteWidth == 2) + idxtype = eGL_UNSIGNED_SHORT; + else if(fmt.idxByteWidth == 4) + idxtype = eGL_UNSIGNED_INT; + + gl.glDrawElements(topo, fmt.numVerts, idxtype, (const void *)(fmt.idxoffs)); + } + else + { + gl.glDrawArrays(topo, 0, fmt.numVerts); + } + } + } + } for(uint32_t i=0; i < 2; i++) { @@ -1743,52 +2301,13 @@ void GLReplay::RenderMesh(uint32_t frameID, uint32_t eventID, const vectorcompCount, eGL_DOUBLE, 0); } - gl.glBindVertexBuffer(i, m_pDriver->GetResourceManager()->GetCurrentResource(fmts[i]->buf).name, fmts[i]->offset, fmts[i]->stride); + GLuint vb = m_pDriver->GetResourceManager()->GetCurrentResource(fmts[i]->buf).name; + gl.glBindVertexBuffer(i, vb, fmts[i]->offset, fmts[i]->stride); } // enable position attribute gl.glEnableVertexAttribArray(0); - - GLenum topo = MakeGLPrimitiveTopology(cfg.position.topo); - - GLuint prog = DebugData.meshProg; - - if(cfg.solidShadeMode == eShade_Lit) - { - // pick program with GS for per-face lighting - prog = DebugData.meshgsProg; - } - - GLint colLoc = gl.glGetUniformLocation(prog, "RENDERDOC_GenericFS_Color"); - GLint mvpLoc = gl.glGetUniformLocation(prog, "ModelViewProj"); - GLint fmtLoc = gl.glGetUniformLocation(prog, "Mesh_DisplayFormat"); - GLint sizeLoc = gl.glGetUniformLocation(prog, "PointSpriteSize"); - GLint homogLoc = gl.glGetUniformLocation(prog, "HomogenousInput"); - - gl.glUseProgram(prog); - - - gl.glEnable(eGL_FRAMEBUFFER_SRGB); - - if(cfg.position.unproject) - { - // the derivation of the projection matrix might not be right (hell, it could be an - // orthographic projection). But it'll be close enough likely. - Matrix4f guessProj = Matrix4f::Perspective(cfg.fov, cfg.position.nearPlane, cfg.position.farPlane, cfg.aspect); - - if(cfg.ortho) - { - guessProj = Matrix4f::Orthographic(cfg.position.nearPlane, cfg.position.farPlane); - } - - guessProjInv = guessProj.Inverse(); - - ModelViewProj = projMat.Mul(camMat.Mul(guessProjInv)); - } - - gl.glUniformMatrix4fv(mvpLoc, 1, GL_FALSE, ModelViewProj.Data()); - gl.glUniform1i(homogLoc, 0); - gl.glUniform2f(sizeLoc, 0.0f, 0.0f); + gl.glDisableVertexAttribArray(1); // solid render if(cfg.solidShadeMode != eShade_None && topo != eGL_PATCHES) @@ -1805,7 +2324,8 @@ void GLReplay::RenderMesh(uint32_t frameID, uint32_t eventID, const vectorGetResourceManager()->GetCurrentResource(cfg.position.idxbuf).name); + GLuint ib = m_pDriver->GetResourceManager()->GetCurrentResource(cfg.position.idxbuf).name; + gl.glBindBuffer(eGL_ELEMENT_ARRAY_BUFFER, ib); gl.glDrawElements(topo, cfg.position.numVerts, idxtype, (const void *)(cfg.position.idxoffs)); } else @@ -1833,7 +2354,7 @@ void GLReplay::RenderMesh(uint32_t frameID, uint32_t eventID, const vectorGetResourceManager()->GetCurrentResource(cfg.position.idxbuf).name); + GLuint ib = m_pDriver->GetResourceManager()->GetCurrentResource(cfg.position.idxbuf).name; + gl.glBindBuffer(eGL_ELEMENT_ARRAY_BUFFER, ib); gl.glDrawElements(topo != eGL_PATCHES ? topo : eGL_POINTS, cfg.position.numVerts, idxtype, (const void *)(cfg.position.idxoffs)); } else @@ -1906,7 +2434,6 @@ void GLReplay::RenderMesh(uint32_t frameID, uint32_t eventID, const vectoreventID == eventID) return draw; + + int32_t count = draw->children.count; + for(int32_t i=0; i < count; i++) + { + const FetchDrawcall *cur = &draw->children.elems[i]; + const FetchDrawcall *next = i+1 < count ? &draw->children.elems[i+1] : NULL; + + if(next && next->eventID <= eventID) + continue; + + cur = GetDrawcall(cur, eventID); + + if(cur) + return cur; + } + + return NULL; +} + +const FetchDrawcall *WrappedOpenGL::GetDrawcall(uint32_t frameID, uint32_t eventID) +{ + if(frameID >= m_FrameRecord.size()) + return NULL; + + int32_t count = m_FrameRecord[frameID].drawcallList.count; + for(int32_t i=0; i < count; i++) + { + const FetchDrawcall *cur = &m_FrameRecord[frameID].drawcallList.elems[i]; + const FetchDrawcall *next = i+1 < count ? &m_FrameRecord[frameID].drawcallList.elems[i+1] : NULL; + + if(next && next->eventID <= eventID) + continue; + + cur = GetDrawcall(cur, eventID); + + if(cur) + return cur; + } + + return NULL; +} + void WrappedOpenGL::ReplayLog(uint32_t frameID, uint32_t startEventID, uint32_t endEventID, ReplayLogType replayType) { RDCASSERT(frameID < (uint32_t)m_FrameRecord.size()); diff --git a/renderdoc/driver/gl/gl_driver.h b/renderdoc/driver/gl/gl_driver.h index c1c0522c2..46cb63943 100644 --- a/renderdoc/driver/gl/gl_driver.h +++ b/renderdoc/driver/gl/gl_driver.h @@ -150,6 +150,8 @@ class WrappedOpenGL vector m_FrameRecord; + const FetchDrawcall *GetDrawcall(const FetchDrawcall *draw, uint32_t eventID); + static const char *GetChunkName(uint32_t idx); // replay @@ -371,6 +373,8 @@ class WrappedOpenGL vector &GetFrameRecord() { return m_FrameRecord; } FetchAPIEvent GetEvent(uint32_t eventID); + const FetchDrawcall *GetDrawcall(uint32_t frameID, uint32_t eventID); + void CreateContext(GLWindowingData winData, void *shareContext, GLInitParams initParams, bool core); void DeleteContext(void *contextHandle); void ActivateContext(GLWindowingData winData); diff --git a/renderdoc/driver/gl/gl_replay.cpp b/renderdoc/driver/gl/gl_replay.cpp index 936dc6cd8..901a03307 100644 --- a/renderdoc/driver/gl/gl_replay.cpp +++ b/renderdoc/driver/gl/gl_replay.cpp @@ -336,13 +336,16 @@ vector GLReplay::GetBufferData(ResourceId buff, uint32_t offset, uint32_t ret.resize(len); WrappedOpenGL &gl = *m_pDriver; - - MakeCurrentReplayContext(m_DebugCtx); + + GLuint oldbuf = 0; + gl.glGetIntegerv(eGL_COPY_READ_BUFFER_BINDING, (GLint *)&oldbuf); gl.glBindBuffer(eGL_COPY_READ_BUFFER, buf.resource.name); gl.glGetBufferSubData(eGL_COPY_READ_BUFFER, (GLintptr)offset, (GLsizeiptr)len, &ret[0]); + gl.glBindBuffer(eGL_COPY_READ_BUFFER, oldbuf); + return ret; } @@ -2159,11 +2162,6 @@ void GLReplay::FillCBufferVariables(ResourceId shader, uint32_t cbufSlot, vector #pragma endregion -void GLReplay::InitPostVSBuffers(uint32_t frameID, uint32_t eventID) -{ - GLNOTIMP("GLReplay::InitPostVSBuffers"); -} - vector GLReplay::GetUsage(ResourceId id) { GLNOTIMP("GetUsage"); @@ -2185,16 +2183,6 @@ void GLReplay::FreeCustomShader(ResourceId id) RDCUNIMPLEMENTED("FreeCustomShader"); } -MeshFormat GLReplay::GetPostVSBuffers(uint32_t frameID, uint32_t eventID, MeshDataStage stage) -{ - MeshFormat ret; - RDCEraseEl(ret); - - GLNOTIMP("GLReplay::GetPostVSBuffers"); - - return ret; -} - byte *GLReplay::GetTextureData(ResourceId tex, uint32_t arrayIdx, uint32_t mip, bool resolve, bool forceRGBA8unorm, float blackPoint, float whitePoint, size_t &dataSize) { RDCUNIMPLEMENTED("GetTextureData"); diff --git a/renderdoc/driver/gl/gl_replay.h b/renderdoc/driver/gl/gl_replay.h index 94cbbf45e..45800af36 100644 --- a/renderdoc/driver/gl/gl_replay.h +++ b/renderdoc/driver/gl/gl_replay.h @@ -32,6 +32,45 @@ class WrappedOpenGL; +struct GLPostVSData +{ + struct StageData + { + GLuint buf; + PrimitiveTopology topo; + + uint32_t numVerts; + uint32_t posOffset; + uint32_t vertStride; + + bool useIndices; + GLuint idxBuf; + uint32_t idxByteWidth; + + float nearPlane; + float farPlane; + } vsin, vsout, gsout; + + GLPostVSData() + { + RDCEraseEl(vsin); + RDCEraseEl(vsout); + RDCEraseEl(gsout); + } + + const StageData &GetStage(MeshDataStage type) + { + if(type == eMeshDataStage_VSOut) + return vsout; + else if(type == eMeshDataStage_GSOut) + return gsout; + else + RDCERR("Unexpected mesh data stage!"); + + return vsin; + } +}; + class GLReplay : public IReplayDriver { public: @@ -216,6 +255,10 @@ class GLReplay : public IReplayDriver GLuint outlineStripVB; GLuint outlineStripVAO; + GLuint feedbackObj; + GLuint feedbackQuery; + GLuint feedbackBuffer; + GLuint pickPixelTex; GLuint pickPixelFBO; @@ -243,6 +286,9 @@ class GLReplay : public IReplayDriver vector data; vector indices; } m_HighlightCache; + + // -> data + std::map, GLPostVSData> m_PostVSData; void InitDebugData(); void DeleteDebugData(); diff --git a/renderdoc/driver/gl/gl_shader_refl.cpp b/renderdoc/driver/gl/gl_shader_refl.cpp index 6feaae47f..a82868bcf 100644 --- a/renderdoc/driver/gl/gl_shader_refl.cpp +++ b/renderdoc/driver/gl/gl_shader_refl.cpp @@ -162,15 +162,16 @@ void CheckVertexOutputUses(vector sources, bool &pointSizeUsed, bool &cl // little utility function that if necessary emulates glCreateShaderProgramv functionality but using glCompileShaderIncludeARB static GLuint CreateSepProgram(const GLHookSet &gl, GLenum type, GLsizei numSources, const char **sources, GLsizei numPaths, const char **paths) { - if(paths == NULL) - return gl.glCreateShaderProgramv(type, numSources, sources); - // definition of glCreateShaderProgramv from the spec GLuint shader = gl.glCreateShader(type); if(shader) { gl.glShaderSource(shader, numSources, sources, NULL); - gl.glCompileShaderIncludeARB(shader, numPaths, paths, NULL); + + if(paths == NULL) + gl.glCompileShader(shader); + else + gl.glCompileShaderIncludeARB(shader, numPaths, paths, NULL); GLuint program = gl.glCreateProgram(); if(program) @@ -184,7 +185,10 @@ static GLuint CreateSepProgram(const GLHookSet &gl, GLenum type, GLsizei numSour { gl.glAttachShader(program, shader); gl.glLinkProgram(program); - gl.glDetachShader(program, shader); + + // we deliberately leave the shaders attached so this program can be re-linked. + // they will be cleaned up when the program is deleted + // gl.glDetachShader(program, shader); } } gl.glDeleteShader(shader); diff --git a/renderdoc/driver/gl/wrappers/gl_buffer_funcs.cpp b/renderdoc/driver/gl/wrappers/gl_buffer_funcs.cpp index 7bea1607c..199d45a3b 100644 --- a/renderdoc/driver/gl/wrappers/gl_buffer_funcs.cpp +++ b/renderdoc/driver/gl/wrappers/gl_buffer_funcs.cpp @@ -77,6 +77,8 @@ void WrappedOpenGL::glGenBuffers(GLsizei n, GLuint *buffers) else { GetResourceManager()->AddLiveResource(id, res); + m_Buffers[id].resource = res; + m_Buffers[id].curType = eGL_NONE; } } } @@ -241,6 +243,10 @@ void WrappedOpenGL::glBindBuffer(GLenum target, GLuint buffer) GetResourceManager()->MarkDirtyResource(r->GetResourceID()); } } + else + { + m_Buffers[GetResourceManager()->GetID(BufferRes(GetCtx(), buffer))].curType = target; + } } bool WrappedOpenGL::Serialise_glNamedBufferStorageEXT(GLuint buffer, GLsizeiptr size, const void *data, GLbitfield flags) @@ -310,6 +316,10 @@ void WrappedOpenGL::glNamedBufferStorageEXT(GLuint buffer, GLsizeiptr size, cons record->Length = (int32_t)size; } } + else + { + m_Buffers[GetResourceManager()->GetID(BufferRes(GetCtx(), buffer))].size = size; + } } void WrappedOpenGL::glBufferStorage(GLenum target, GLsizeiptr size, const void *data, GLbitfield flags) @@ -343,6 +353,10 @@ void WrappedOpenGL::glBufferStorage(GLenum target, GLsizeiptr size, const void * record->Length = (int32_t)size; } } + else + { + RDCERR("Internal buffers should be allocated via dsa interfaces"); + } } bool WrappedOpenGL::Serialise_glNamedBufferDataEXT(GLuint buffer, GLsizeiptr size, const void *data, GLenum usage) @@ -478,6 +492,10 @@ void WrappedOpenGL::glNamedBufferDataEXT(GLuint buffer, GLsizeiptr size, const v record->usage = usage; } } + else + { + m_Buffers[GetResourceManager()->GetID(BufferRes(GetCtx(), buffer))].size = size; + } } void WrappedOpenGL::glBufferData(GLenum target, GLsizeiptr size, const void *data, GLenum usage) @@ -578,6 +596,10 @@ void WrappedOpenGL::glBufferData(GLenum target, GLsizeiptr size, const void *dat record->usage = usage; } } + else + { + RDCERR("Internal buffers should be allocated via dsa interfaces"); + } } bool WrappedOpenGL::Serialise_glNamedBufferSubDataEXT(GLuint buffer, GLintptr offset, GLsizeiptr size, const void *data)