mirror of
https://github.com/baldurk/renderdoc.git
synced 2026-05-26 20:01:17 +00:00
1729 lines
44 KiB
C++
1729 lines
44 KiB
C++
/******************************************************************************
|
|
* The MIT License (MIT)
|
|
*
|
|
* Copyright (c) 2014 Crytek
|
|
*
|
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
* of this software and associated documentation files (the "Software"), to deal
|
|
* in the Software without restriction, including without limitation the rights
|
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
* copies of the Software, and to permit persons to whom the Software is
|
|
* furnished to do so, subject to the following conditions:
|
|
*
|
|
* The above copyright notice and this permission notice shall be included in
|
|
* all copies or substantial portions of the Software.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
* THE SOFTWARE.
|
|
******************************************************************************/
|
|
|
|
|
|
#include "common/common.h"
|
|
#include "gl_driver.h"
|
|
|
|
#include "common/string_utils.h"
|
|
|
|
#include "replay/type_helpers.h"
|
|
|
|
#include "maths/vec.h"
|
|
|
|
#include "jpeg-compressor/jpge.h"
|
|
|
|
const char *GLChunkNames[] =
|
|
{
|
|
"WrappedOpenGL::Initialisation",
|
|
|
|
"glGenTextures",
|
|
"glBindTexture",
|
|
"glActiveTexture",
|
|
"glTexStorage1D",
|
|
"glTexStorage2D",
|
|
"glTexStorage3D",
|
|
"glTexSubImage1D",
|
|
"glTexSubImage2D",
|
|
"glTexSubImage3D",
|
|
"glCompressedTexSubImage1D",
|
|
"glCompressedTexSubImage2D",
|
|
"glCompressedTexSubImage3D",
|
|
"glTexBufferRange",
|
|
"glPixelStore",
|
|
"glTexParameterf",
|
|
"glTexParameterfv",
|
|
"glTexParameteri",
|
|
"glTexParameteriv",
|
|
"glGenerateMipmap",
|
|
"glCopyImageSubData",
|
|
"glTextureView",
|
|
|
|
"glCreateShader",
|
|
"glCreateProgram",
|
|
"glCreateShaderProgramv",
|
|
"glCompileShader",
|
|
"glShaderSource",
|
|
"glAttachShader",
|
|
"glDetachShader",
|
|
"glUseProgram",
|
|
"glProgramParameter",
|
|
"glBindAttribLocation",
|
|
"glUniformBlockBinding",
|
|
"glProgramUniformMatrix*",
|
|
"glProgramUniformVector*",
|
|
"glLinkProgram",
|
|
|
|
"glGenProgramPipelines",
|
|
"glUseProgramStages",
|
|
"glBindProgramPipeline",
|
|
|
|
"glFenceSync",
|
|
"glClientWaitSync",
|
|
"glWaitSync",
|
|
|
|
"glGenQueries",
|
|
"glBeginQuery",
|
|
"glEndQuery",
|
|
|
|
"glClearColor",
|
|
"glClearDepth",
|
|
"glClear",
|
|
"glClearBufferfv",
|
|
"glClearBufferiv",
|
|
"glClearBufferuiv",
|
|
"glClearBufferfi",
|
|
"glPolygonMode",
|
|
"glPolygonOffset",
|
|
"glCullFace",
|
|
"glHint",
|
|
"glEnable",
|
|
"glDisable",
|
|
"glEnablei",
|
|
"glDisablei",
|
|
"glFrontFace",
|
|
"glBlendFunc",
|
|
"glBlendFunci",
|
|
"glBlendColor",
|
|
"glBlendFuncSeparate",
|
|
"glBlendFuncSeparatei",
|
|
"glBlendEquationSeparate",
|
|
"glBlendEquationSeparatei",
|
|
"glStencilOp",
|
|
"glStencilOpSeparate",
|
|
"glStencilFunc",
|
|
"glStencilFuncSeparate",
|
|
"glStencilMask",
|
|
"glStencilMaskSeparate",
|
|
"glColorMask",
|
|
"glColorMaski",
|
|
"glSampleMaski",
|
|
"glDepthFunc",
|
|
"glDepthMask",
|
|
"glDepthRange",
|
|
"glDepthRangef",
|
|
"glDepthRangeArrayv",
|
|
"glDepthBoundsEXT",
|
|
"glPatchParameteri",
|
|
"glPatchParameterfv",
|
|
"glViewport",
|
|
"glViewportArrayv",
|
|
"glScissor",
|
|
"glScissorArrayv",
|
|
"glBindVertexArray",
|
|
"glBindVertexBuffer",
|
|
"glVertexBindingDivisor",
|
|
"glUniformMatrix*",
|
|
"glUniformVector*",
|
|
"glDrawArrays",
|
|
"glDrawArraysInstanced",
|
|
"glDrawArraysInstancedBaseInstance",
|
|
"glDrawElements",
|
|
"glDrawRangeElements",
|
|
"glDrawElementsInstanced",
|
|
"glDrawElementsInstancedBaseInstance",
|
|
"glDrawElementsBaseVertex",
|
|
"glDrawElementsInstancedBaseVertex",
|
|
"glDrawElementsInstancedBaseVertexBaseInstance",
|
|
|
|
"glGenFramebuffers",
|
|
"glFramebufferTexture",
|
|
"glFramebufferTexture2D",
|
|
"glFramebufferTextureLayer",
|
|
"glReadBuffer",
|
|
"glBindFramebuffer",
|
|
"glDrawBuffer",
|
|
"glDrawBuffers",
|
|
"glBlitFramebuffer",
|
|
|
|
"glGenSamplers",
|
|
"glSamplerParameteri",
|
|
"glSamplerParameterf",
|
|
"glSamplerParameteriv",
|
|
"glSamplerParameterfv",
|
|
"glSamplerParameterIiv",
|
|
"glSamplerParameterIuiv",
|
|
"glBindSampler",
|
|
|
|
"glGenBuffers",
|
|
"glBindBuffer",
|
|
"glBindBufferBase",
|
|
"glBindBufferRange",
|
|
"glBufferStorage",
|
|
"glBufferData",
|
|
"glBufferSubData",
|
|
"glCopyBufferSubData",
|
|
"glUnmapBuffer",
|
|
"glGenVertexArrays",
|
|
"glBindVertexArray",
|
|
"glVertexAttribPointer",
|
|
"glVertexAttribIPointer",
|
|
"glEnableVertexAttribArray",
|
|
"glDisableVertexAttribArray",
|
|
"glVertexAttribFormat",
|
|
"glVertexAttribIFormat",
|
|
"glVertexAttribBinding",
|
|
|
|
|
|
"glObjectLabel",
|
|
"glPushDebugGroup",
|
|
"glDebugMessageInsert",
|
|
"glPopDebugGroup",
|
|
|
|
"Capture",
|
|
"BeginCapture",
|
|
"EndCapture",
|
|
};
|
|
|
|
template<>
|
|
string ToStrHelper<false, WrappedOpenGL::UniformType>::Get(const WrappedOpenGL::UniformType &el)
|
|
{
|
|
switch(el)
|
|
{
|
|
case WrappedOpenGL::UNIFORM_UNKNOWN: return "unk";
|
|
case WrappedOpenGL::VEC1FV: return "1fv";
|
|
case WrappedOpenGL::VEC1IV: return "1iv";
|
|
case WrappedOpenGL::VEC1UIV: return "1uiv";
|
|
case WrappedOpenGL::VEC2FV: return "2fv";
|
|
case WrappedOpenGL::VEC3FV: return "3fv";
|
|
case WrappedOpenGL::VEC4FV: return "4fv";
|
|
case WrappedOpenGL::MAT4FV: return "4fv";
|
|
}
|
|
|
|
char tostrBuf[256] = {0};
|
|
StringFormat::snprintf(tostrBuf, 255, "WrappedOpenGL::UniformType<%d>", el);
|
|
|
|
return tostrBuf;
|
|
}
|
|
|
|
GLInitParams::GLInitParams()
|
|
{
|
|
SerialiseVersion = GL_SERIALISE_VERSION;
|
|
colorBits = 32;
|
|
depthBits = 32;
|
|
stencilBits = 8;
|
|
width = 32;
|
|
height = 32;
|
|
}
|
|
|
|
ReplayCreateStatus GLInitParams::Serialise()
|
|
{
|
|
SERIALISE_ELEMENT(uint32_t, ver, GL_SERIALISE_VERSION); SerialiseVersion = ver;
|
|
|
|
if(ver != GL_SERIALISE_VERSION)
|
|
{
|
|
RDCERR("Incompatible OpenGL serialise version, expected %d got %d", GL_SERIALISE_VERSION, ver);
|
|
return eReplayCreate_APIIncompatibleVersion;
|
|
}
|
|
|
|
SERIALISE_ELEMENT(uint32_t, col, colorBits); colorBits = col;
|
|
SERIALISE_ELEMENT(uint32_t, dpth, depthBits); depthBits = dpth;
|
|
SERIALISE_ELEMENT(uint32_t, stenc, stencilBits); stencilBits = stenc;
|
|
SERIALISE_ELEMENT(uint32_t, w, width); width = w;
|
|
SERIALISE_ELEMENT(uint32_t, h, height); height = h;
|
|
|
|
return eReplayCreate_Success;
|
|
}
|
|
|
|
WrappedOpenGL::WrappedOpenGL(const wchar_t *logfile, const GLHookSet &funcs)
|
|
: m_Real(funcs)
|
|
{
|
|
if(RenderDoc::Inst().GetCrashHandler())
|
|
RenderDoc::Inst().GetCrashHandler()->RegisterMemoryRegion(this, sizeof(WrappedOpenGL));
|
|
|
|
glExts.push_back("GL_ARB_multitexture");
|
|
glExts.push_back("GL_ARB_debug_output");
|
|
glExts.push_back("GL_EXT_direct_state_access");
|
|
glExts.push_back("GL_ARB_internalformat_query");
|
|
glExts.push_back("GL_ARB_internalformat_query2");
|
|
|
|
#if !defined(_RELEASE)
|
|
CaptureOptions &opts = (CaptureOptions &)RenderDoc::Inst().GetCaptureOptions();
|
|
opts.RefAllResources = true;
|
|
#endif
|
|
|
|
m_Replay.SetDriver(this);
|
|
|
|
// TODO: need to check against implementation to ensure we don't claim to support
|
|
// an extension that it doesn't!
|
|
merge(glExts, glExtsString, ' ');
|
|
|
|
m_FrameCounter = 0;
|
|
|
|
m_FrameTimer.Restart();
|
|
|
|
m_TotalTime = m_AvgFrametime = m_MinFrametime = m_MaxFrametime = 0.0;
|
|
|
|
m_CurFileSize = 0;
|
|
|
|
m_RealDebugFunc = NULL;
|
|
m_RealDebugFuncParam = NULL;
|
|
|
|
m_DrawcallStack.push_back(&m_ParentDrawcall);
|
|
|
|
m_CurEventID = 1;
|
|
m_CurDrawcallID = 1;
|
|
|
|
RDCEraseEl(m_TextureRecord);
|
|
RDCEraseEl(m_BufferRecord);
|
|
m_VertexArrayRecord = NULL;
|
|
m_DrawFramebufferRecord = NULL;
|
|
m_ReadFramebufferRecord = NULL;
|
|
m_TextureUnit = 0;
|
|
m_Program = 0;
|
|
|
|
m_LastIndexSize = eGL_NONE;
|
|
m_LastIndexOffset = 0;
|
|
m_LastDrawMode = eGL_NONE;
|
|
|
|
m_DisplayListRecord = NULL;
|
|
|
|
#if defined(RELEASE)
|
|
const bool debugSerialiser = false;
|
|
#else
|
|
const bool debugSerialiser = true;
|
|
#endif
|
|
|
|
if(RenderDoc::Inst().IsReplayApp())
|
|
{
|
|
m_State = READING;
|
|
if(logfile)
|
|
{
|
|
m_pSerialiser = new Serialiser(logfile, Serialiser::READING, debugSerialiser);
|
|
}
|
|
else
|
|
{
|
|
byte dummy[4];
|
|
m_pSerialiser = new Serialiser(4, dummy, false);
|
|
}
|
|
|
|
if(m_Real.glDebugMessageCallback)
|
|
{
|
|
m_Real.glDebugMessageCallback(&DebugSnoopStatic, this);
|
|
m_Real.glEnable(eGL_DEBUG_OUTPUT_SYNCHRONOUS);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
m_State = WRITING_IDLE;
|
|
m_pSerialiser = new Serialiser(NULL, Serialiser::WRITING, debugSerialiser);
|
|
}
|
|
|
|
m_DeviceRecord = NULL;
|
|
|
|
m_ResourceManager = new GLResourceManager(m_State, m_pSerialiser, this);
|
|
|
|
m_DeviceResourceID = GetResourceManager()->RegisterResource(GLResource(NULL, eResSpecial, eSpecialResDevice));
|
|
m_ContextResourceID = GetResourceManager()->RegisterResource(GLResource(NULL, eResSpecial, eSpecialResContext));
|
|
|
|
if(!RenderDoc::Inst().IsReplayApp())
|
|
{
|
|
m_DeviceRecord = GetResourceManager()->AddResourceRecord(m_DeviceResourceID);
|
|
m_DeviceRecord->DataInSerialiser = false;
|
|
m_DeviceRecord->Length = 0;
|
|
m_DeviceRecord->NumSubResources = 0;
|
|
m_DeviceRecord->SpecialResource = true;
|
|
m_DeviceRecord->SubResources = NULL;
|
|
|
|
m_ContextRecord = GetResourceManager()->AddResourceRecord(m_ContextResourceID);
|
|
m_ContextRecord->DataInSerialiser = false;
|
|
m_ContextRecord->Length = 0;
|
|
m_ContextRecord->NumSubResources = 0;
|
|
m_ContextRecord->SpecialResource = true;
|
|
m_ContextRecord->SubResources = NULL;
|
|
}
|
|
else
|
|
{
|
|
m_DeviceRecord = m_ContextRecord = NULL;
|
|
|
|
TrackedResource::SetReplayResourceIDs();
|
|
}
|
|
|
|
m_FakeBB_FBO = 0;
|
|
m_FakeBB_Color = 0;
|
|
m_FakeBB_DepthStencil = 0;
|
|
|
|
RDCDEBUG("Debug Text enabled - for development! remove before release!");
|
|
m_pSerialiser->SetDebugText(true);
|
|
|
|
m_pSerialiser->SetChunkNameLookup(&GetChunkName);
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// Compile time asserts
|
|
|
|
RDCCOMPILE_ASSERT(ARRAY_COUNT(GLChunkNames) == NUM_OPENGL_CHUNKS-FIRST_CHUNK_ID, "Not right number of chunk names");
|
|
}
|
|
|
|
void WrappedOpenGL::Initialise(GLInitParams ¶ms)
|
|
{
|
|
// deliberately want to go through our own wrappers to set up e.g. m_Textures members
|
|
WrappedOpenGL &gl = *this;
|
|
|
|
gl.glGenFramebuffers(1, &m_FakeBB_FBO);
|
|
gl.glBindFramebuffer(eGL_FRAMEBUFFER, m_FakeBB_FBO);
|
|
|
|
gl.glGenTextures(1, &m_FakeBB_Color);
|
|
gl.glBindTexture(eGL_TEXTURE_2D, m_FakeBB_Color);
|
|
|
|
gl.glObjectLabel(eGL_TEXTURE, m_FakeBB_Color, -1, "Backbuffer Color");
|
|
|
|
GLNOTIMP("backbuffer needs to resize if the size is exceeded");
|
|
|
|
GLenum colfmt = eGL_RGBA8;
|
|
|
|
if(params.colorBits == 32)
|
|
colfmt = eGL_RGBA8;
|
|
else if(params.colorBits == 24)
|
|
colfmt = eGL_RGB8;
|
|
else
|
|
RDCERR("Unexpected # colour bits: %d", params.colorBits);
|
|
|
|
gl.glTexStorage2D(eGL_TEXTURE_2D, 1, colfmt, params.width, params.height);
|
|
gl.glTexParameteri(eGL_TEXTURE_2D, eGL_TEXTURE_MIN_FILTER, eGL_NEAREST);
|
|
gl.glTexParameteri(eGL_TEXTURE_2D, eGL_TEXTURE_MAG_FILTER, eGL_NEAREST);
|
|
gl.glTexParameteri(eGL_TEXTURE_2D, eGL_TEXTURE_WRAP_S, eGL_CLAMP_TO_EDGE);
|
|
gl.glTexParameteri(eGL_TEXTURE_2D, eGL_TEXTURE_WRAP_T, eGL_CLAMP_TO_EDGE);
|
|
gl.glFramebufferTexture(eGL_FRAMEBUFFER, eGL_COLOR_ATTACHMENT0, m_FakeBB_Color, 0);
|
|
|
|
gl.glViewport(0, 0, params.width, params.height);
|
|
|
|
if(params.depthBits > 0 || params.stencilBits > 0)
|
|
{
|
|
gl.glGenTextures(1, &m_FakeBB_DepthStencil);
|
|
gl.glBindTexture(eGL_TEXTURE_2D, m_FakeBB_DepthStencil);
|
|
|
|
GLenum depthfmt = eGL_DEPTH32F_STENCIL8;
|
|
bool stencil = false;
|
|
|
|
if(params.stencilBits == 8)
|
|
{
|
|
stencil = true;
|
|
|
|
if(params.depthBits == 32)
|
|
depthfmt = eGL_DEPTH32F_STENCIL8;
|
|
else if(params.depthBits == 24)
|
|
depthfmt = eGL_DEPTH24_STENCIL8;
|
|
else
|
|
RDCERR("Unexpected combination of depth & stencil bits: %d & %d", params.depthBits, params.stencilBits);
|
|
}
|
|
else if(params.stencilBits == 0)
|
|
{
|
|
if(params.depthBits == 32)
|
|
depthfmt = eGL_DEPTH_COMPONENT32F;
|
|
else if(params.depthBits == 24)
|
|
depthfmt = eGL_DEPTH_COMPONENT24;
|
|
else if(params.depthBits == 16)
|
|
depthfmt = eGL_DEPTH_COMPONENT16;
|
|
else
|
|
RDCERR("Unexpected # depth bits: %d", params.depthBits);
|
|
}
|
|
else
|
|
RDCERR("Unexpected # stencil bits: %d", params.stencilBits);
|
|
|
|
if(stencil)
|
|
gl.glObjectLabel(eGL_TEXTURE, m_FakeBB_DepthStencil, -1, "Backbuffer Depth-stencil");
|
|
else
|
|
gl.glObjectLabel(eGL_TEXTURE, m_FakeBB_DepthStencil, -1, "Backbuffer Depth");
|
|
|
|
gl.glTexStorage2D(eGL_TEXTURE_2D, 1, depthfmt, params.width, params.height);
|
|
|
|
if(stencil)
|
|
gl.glFramebufferTexture(eGL_FRAMEBUFFER, eGL_DEPTH_STENCIL_ATTACHMENT, m_FakeBB_DepthStencil, 0);
|
|
else
|
|
gl.glFramebufferTexture(eGL_FRAMEBUFFER, eGL_DEPTH_ATTACHMENT, m_FakeBB_DepthStencil, 0);
|
|
}
|
|
}
|
|
|
|
const char * WrappedOpenGL::GetChunkName(uint32_t idx)
|
|
{
|
|
if(idx < FIRST_CHUNK_ID || idx >= NUM_OPENGL_CHUNKS)
|
|
return "<unknown>";
|
|
return GLChunkNames[idx-FIRST_CHUNK_ID];
|
|
}
|
|
|
|
WrappedOpenGL::~WrappedOpenGL()
|
|
{
|
|
SAFE_DELETE(m_pSerialiser);
|
|
|
|
GetResourceManager()->ReleaseCurrentResource(m_DeviceResourceID);
|
|
GetResourceManager()->ReleaseCurrentResource(m_ContextResourceID);
|
|
|
|
if(m_ContextRecord)
|
|
{
|
|
RDCASSERT(m_ContextRecord->GetRefCount() == 1);
|
|
m_ContextRecord->Delete(GetResourceManager());
|
|
}
|
|
|
|
if(m_DeviceRecord)
|
|
{
|
|
RDCASSERT(m_DeviceRecord->GetRefCount() == 1);
|
|
m_DeviceRecord->Delete(GetResourceManager());
|
|
}
|
|
|
|
m_ResourceManager->Shutdown();
|
|
|
|
SAFE_DELETE(m_ResourceManager);
|
|
|
|
if(RenderDoc::Inst().GetCrashHandler())
|
|
RenderDoc::Inst().GetCrashHandler()->UnregisterMemoryRegion(this);
|
|
}
|
|
|
|
void *WrappedOpenGL::GetCtx()
|
|
{
|
|
return m_ActiveContexts[Threading::GetCurrentID()];
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////
|
|
// Windowing/setup/etc
|
|
////////////////////////////////////////////////////////////////
|
|
|
|
void WrappedOpenGL::DeleteContext(void *contextHandle)
|
|
{
|
|
}
|
|
|
|
void WrappedOpenGL::CreateContext(void *windowHandle, void *contextHandle, void *shareContext, GLInitParams initParams)
|
|
{
|
|
// TODO: support multiple GL contexts more explicitly
|
|
m_InitParams = initParams;
|
|
}
|
|
|
|
void WrappedOpenGL::ActivateContext(void *windowHandle, void *contextHandle)
|
|
{
|
|
m_ActiveContexts[Threading::GetCurrentID()] = contextHandle;
|
|
// TODO: support multiple GL contexts more explicitly
|
|
Keyboard::AddInputWindow(windowHandle);
|
|
}
|
|
|
|
void WrappedOpenGL::WindowSize(void *windowHandle, uint32_t w, uint32_t h)
|
|
{
|
|
// TODO: support multiple window handles
|
|
m_InitParams.width = w;
|
|
m_InitParams.height = h;
|
|
}
|
|
|
|
void WrappedOpenGL::Present(void *windowHandle)
|
|
{
|
|
RenderDoc::Inst().SetCurrentDriver(RDC_OpenGL);
|
|
|
|
if(m_State == WRITING_IDLE)
|
|
RenderDoc::Inst().Tick();
|
|
|
|
m_FrameCounter++; // first present becomes frame #1, this function is at the end of the frame
|
|
|
|
if(m_State == WRITING_IDLE)
|
|
{
|
|
m_FrameTimes.push_back(m_FrameTimer.GetMilliseconds());
|
|
m_TotalTime += m_FrameTimes.back();
|
|
m_FrameTimer.Restart();
|
|
|
|
// update every second
|
|
if(m_TotalTime > 1000.0)
|
|
{
|
|
m_MinFrametime = 10000.0;
|
|
m_MaxFrametime = 0.0;
|
|
m_AvgFrametime = 0.0;
|
|
|
|
m_TotalTime = 0.0;
|
|
|
|
for(size_t i=0; i < m_FrameTimes.size(); i++)
|
|
{
|
|
m_AvgFrametime += m_FrameTimes[i];
|
|
if(m_FrameTimes[i] < m_MinFrametime)
|
|
m_MinFrametime = m_FrameTimes[i];
|
|
if(m_FrameTimes[i] > m_MaxFrametime)
|
|
m_MaxFrametime = m_FrameTimes[i];
|
|
}
|
|
|
|
m_AvgFrametime /= double(m_FrameTimes.size());
|
|
|
|
m_FrameTimes.clear();
|
|
|
|
RDCLOG("Frame: %d. F12/PrtScr to capture. %.2lf ms (%.2lf .. %.2lf) (%.0lf FPS)",
|
|
m_FrameCounter, m_AvgFrametime, m_MinFrametime, m_MaxFrametime, 1000.0f/m_AvgFrametime);
|
|
for(size_t i=0; i < m_FrameRecord.size(); i++)
|
|
RDCLOG("Captured Frame %d. Multiple frame capture not supported.\n", m_FrameRecord[i].frameInfo.frameNumber);
|
|
#if !defined(RELEASE)
|
|
RDCLOG("%llu chunks - %.2f MB", Chunk::NumLiveChunks(), float(Chunk::TotalMem())/1024.0f/1024.0f);
|
|
#endif
|
|
}
|
|
}
|
|
|
|
// kill any current capture
|
|
if(m_State == WRITING_CAPFRAME)
|
|
{
|
|
//if(HasSuccessfulCapture())
|
|
{
|
|
RDCLOG("Finished capture, Frame %u", m_FrameCounter);
|
|
|
|
EndCaptureFrame();
|
|
FinishCapture();
|
|
|
|
const uint32_t maxSize = 1024;
|
|
|
|
byte *thpixels = NULL;
|
|
uint32_t thwidth = 0;
|
|
uint32_t thheight = 0;
|
|
|
|
if(m_Real.glGetIntegerv && m_Real.glReadBuffer && m_Real.glBindFramebuffer && m_Real.glBindBuffer && m_Real.glReadPixels)
|
|
{
|
|
RDCGLenum prevReadBuf = eGL_BACK;
|
|
GLint prevBuf = 0;
|
|
GLint packBufBind = 0;
|
|
GLint prevPackRowLen = 0;
|
|
GLint prevPackSkipRows = 0;
|
|
GLint prevPackSkipPixels = 0;
|
|
GLint prevPackAlignment = 0;
|
|
m_Real.glGetIntegerv(eGL_READ_BUFFER, (GLint *)&prevReadBuf);
|
|
m_Real.glGetIntegerv(eGL_READ_FRAMEBUFFER_BINDING, &prevBuf);
|
|
m_Real.glGetIntegerv(eGL_PIXEL_PACK_BUFFER_BINDING, &packBufBind);
|
|
m_Real.glGetIntegerv(eGL_PACK_ROW_LENGTH, &prevPackRowLen);
|
|
m_Real.glGetIntegerv(eGL_PACK_SKIP_ROWS, &prevPackSkipRows);
|
|
m_Real.glGetIntegerv(eGL_PACK_SKIP_PIXELS, &prevPackSkipPixels);
|
|
m_Real.glGetIntegerv(eGL_PACK_ALIGNMENT, &prevPackAlignment);
|
|
|
|
m_Real.glReadBuffer(eGL_BACK);
|
|
m_Real.glBindFramebuffer(eGL_READ_FRAMEBUFFER, 0);
|
|
m_Real.glBindBuffer(eGL_PIXEL_PACK_BUFFER, 0);
|
|
m_Real.glPixelStorei(eGL_PACK_ROW_LENGTH, 0);
|
|
m_Real.glPixelStorei(eGL_PACK_SKIP_ROWS, 0);
|
|
m_Real.glPixelStorei(eGL_PACK_SKIP_PIXELS, 0);
|
|
m_Real.glPixelStorei(eGL_PACK_ALIGNMENT, 1);
|
|
|
|
thwidth = m_InitParams.width;
|
|
thheight = m_InitParams.height;
|
|
|
|
thpixels = new byte[thwidth*thheight*3];
|
|
|
|
m_Real.glReadPixels(0, 0, thwidth, thheight, eGL_RGB, eGL_UNSIGNED_BYTE, thpixels);
|
|
|
|
for(uint32_t y=0; y <= thheight/2; y++)
|
|
{
|
|
for(uint32_t x=0; x < thwidth; x++)
|
|
{
|
|
byte save[3];
|
|
save[0] = thpixels[y*(thwidth*3) + x*3 + 0];
|
|
save[1] = thpixels[y*(thwidth*3) + x*3 + 1];
|
|
save[2] = thpixels[y*(thwidth*3) + x*3 + 2];
|
|
|
|
thpixels[y*(thwidth*3) + x*3 + 0] = thpixels[(thheight-1-y)*(thwidth*3) + x*3 + 0];
|
|
thpixels[y*(thwidth*3) + x*3 + 1] = thpixels[(thheight-1-y)*(thwidth*3) + x*3 + 1];
|
|
thpixels[y*(thwidth*3) + x*3 + 2] = thpixels[(thheight-1-y)*(thwidth*3) + x*3 + 2];
|
|
|
|
thpixels[(thheight-1-y)*(thwidth*3) + x*3 + 0] = save[0];
|
|
thpixels[(thheight-1-y)*(thwidth*3) + x*3 + 1] = save[1];
|
|
thpixels[(thheight-1-y)*(thwidth*3) + x*3 + 2] = save[2];
|
|
}
|
|
}
|
|
|
|
m_Real.glBindBuffer(eGL_PIXEL_PACK_BUFFER, packBufBind);
|
|
m_Real.glBindFramebuffer(eGL_READ_FRAMEBUFFER, prevBuf);
|
|
m_Real.glReadBuffer(prevReadBuf);
|
|
m_Real.glPixelStorei(eGL_PACK_ROW_LENGTH, prevPackRowLen);
|
|
m_Real.glPixelStorei(eGL_PACK_SKIP_ROWS, prevPackSkipRows);
|
|
m_Real.glPixelStorei(eGL_PACK_SKIP_PIXELS, prevPackSkipPixels);
|
|
m_Real.glPixelStorei(eGL_PACK_ALIGNMENT, prevPackAlignment);
|
|
}
|
|
|
|
byte *jpgbuf = NULL;
|
|
int len = thwidth*thheight;
|
|
|
|
if(len > 0)
|
|
{
|
|
jpgbuf = new byte[len];
|
|
|
|
jpge::params p;
|
|
|
|
p.m_quality = 40;
|
|
|
|
bool success = jpge::compress_image_to_jpeg_file_in_memory(jpgbuf, len, thwidth, thheight, 3, thpixels, p);
|
|
|
|
if(!success)
|
|
{
|
|
RDCERR("Failed to compress to jpg");
|
|
SAFE_DELETE_ARRAY(jpgbuf);
|
|
thwidth = 0;
|
|
thheight = 0;
|
|
}
|
|
}
|
|
|
|
Serialiser *m_pFileSerialiser = RenderDoc::Inst().OpenWriteSerialiser(m_FrameCounter, &m_InitParams, jpgbuf, len, thwidth, thheight);
|
|
|
|
{
|
|
SCOPED_SERIALISE_CONTEXT(DEVICE_INIT);
|
|
|
|
SERIALISE_ELEMENT(ResourceId, immContextId, m_ContextResourceID);
|
|
|
|
m_pFileSerialiser->Insert(scope.Get(true));
|
|
}
|
|
|
|
RDCDEBUG("Inserting Resource Serialisers");
|
|
|
|
GetResourceManager()->InsertReferencedChunks(m_pFileSerialiser);
|
|
|
|
GetResourceManager()->InsertInitialContentsChunks(m_pFileSerialiser);
|
|
|
|
RDCDEBUG("Creating Capture Scope");
|
|
|
|
{
|
|
SCOPED_SERIALISE_CONTEXT(CAPTURE_SCOPE);
|
|
|
|
Serialise_CaptureScope(0);
|
|
|
|
m_pFileSerialiser->Insert(scope.Get(true));
|
|
}
|
|
|
|
{
|
|
RDCDEBUG("Getting Resource Record");
|
|
|
|
GLResourceRecord *record = m_ResourceManager->GetResourceRecord(m_ContextResourceID);
|
|
|
|
RDCDEBUG("Accumulating context resource list");
|
|
|
|
map<int32_t, Chunk *> recordlist;
|
|
record->Insert(recordlist);
|
|
|
|
RDCDEBUG("Flushing %u records to file serialiser", (uint32_t)recordlist.size());
|
|
|
|
for(auto it = recordlist.begin(); it != recordlist.end(); ++it)
|
|
m_pFileSerialiser->Insert(it->second);
|
|
|
|
RDCDEBUG("Done");
|
|
}
|
|
|
|
m_CurFileSize += m_pFileSerialiser->FlushToDisk();
|
|
|
|
RenderDoc::Inst().SuccessfullyWrittenLog();
|
|
|
|
SAFE_DELETE(m_pFileSerialiser);
|
|
|
|
m_State = WRITING_IDLE;
|
|
|
|
GetResourceManager()->MarkUnwrittenResources();
|
|
|
|
GetResourceManager()->ClearReferencedResources();
|
|
}
|
|
}
|
|
|
|
if(RenderDoc::Inst().ShouldTriggerCapture(m_FrameCounter) && m_State == WRITING_IDLE && m_FrameRecord.empty())
|
|
{
|
|
m_State = WRITING_CAPFRAME;
|
|
|
|
FetchFrameRecord record;
|
|
record.frameInfo.frameNumber = m_FrameCounter+1;
|
|
m_FrameRecord.push_back(record);
|
|
|
|
GetResourceManager()->ClearReferencedResources();
|
|
|
|
GetResourceManager()->MarkResourceFrameReferenced(m_DeviceResourceID, eFrameRef_Write);
|
|
GetResourceManager()->PrepareInitialContents();
|
|
|
|
AttemptCapture();
|
|
BeginCaptureFrame();
|
|
|
|
RDCLOG("Starting capture, frame %u", m_FrameCounter);
|
|
}
|
|
}
|
|
|
|
void WrappedOpenGL::Serialise_CaptureScope(uint64_t offset)
|
|
{
|
|
SERIALISE_ELEMENT(uint32_t, FrameNumber, m_FrameCounter);
|
|
|
|
if(m_State >= WRITING)
|
|
{
|
|
GetResourceManager()->Serialise_InitialContentsNeeded();
|
|
}
|
|
else
|
|
{
|
|
FetchFrameRecord record;
|
|
record.frameInfo.fileOffset = offset;
|
|
record.frameInfo.firstEvent = 1;//m_pImmediateContext->GetEventID();
|
|
record.frameInfo.frameNumber = FrameNumber;
|
|
record.frameInfo.immContextId = GetResourceManager()->GetOriginalID(m_ContextResourceID);
|
|
m_FrameRecord.push_back(record);
|
|
|
|
GetResourceManager()->CreateInitialContents();
|
|
}
|
|
}
|
|
|
|
void WrappedOpenGL::EndCaptureFrame()
|
|
{
|
|
SCOPED_SERIALISE_CONTEXT(CONTEXT_CAPTURE_FOOTER);
|
|
|
|
bool HasCallstack = RenderDoc::Inst().GetCaptureOptions().CaptureCallstacks != 0;
|
|
m_pSerialiser->Serialise("HasCallstack", HasCallstack);
|
|
|
|
if(HasCallstack)
|
|
{
|
|
Callstack::Stackwalk *call = Callstack::Collect();
|
|
|
|
RDCASSERT(call->NumLevels() < 0xff);
|
|
|
|
size_t numLevels = call->NumLevels();
|
|
uint64_t *stack = call->GetAddrs();
|
|
|
|
m_pSerialiser->Serialise("callstack", stack, numLevels);
|
|
|
|
delete call;
|
|
}
|
|
|
|
m_ContextRecord->AddChunk(scope.Get());
|
|
}
|
|
|
|
void WrappedOpenGL::AttemptCapture()
|
|
{
|
|
m_State = WRITING_CAPFRAME;
|
|
|
|
{
|
|
RDCDEBUG("Immediate Context %llu Attempting capture", GetContextResourceID());
|
|
|
|
//m_SuccessfulCapture = true;
|
|
|
|
m_ContextRecord->LockChunks();
|
|
while(m_ContextRecord->HasChunks())
|
|
{
|
|
Chunk *chunk = m_ContextRecord->GetLastChunk();
|
|
|
|
SAFE_DELETE(chunk);
|
|
m_ContextRecord->PopChunk();
|
|
}
|
|
m_ContextRecord->UnlockChunks();
|
|
}
|
|
}
|
|
|
|
bool WrappedOpenGL::Serialise_BeginCaptureFrame(bool applyInitialState)
|
|
{
|
|
GLRenderState state(&m_Real, m_pSerialiser, m_State);
|
|
|
|
if(m_State >= WRITING)
|
|
{
|
|
state.FetchState();
|
|
}
|
|
|
|
state.Serialise(m_State, GetCtx(), this);
|
|
|
|
if(m_State <= EXECUTING && applyInitialState)
|
|
{
|
|
m_DoStateVerify = false;
|
|
state.ApplyState();
|
|
m_DoStateVerify = true;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void WrappedOpenGL::BeginCaptureFrame()
|
|
{
|
|
SCOPED_SERIALISE_CONTEXT(CONTEXT_CAPTURE_HEADER);
|
|
|
|
Serialise_BeginCaptureFrame(false);
|
|
|
|
m_ContextRecord->AddChunk(scope.Get(), 1);
|
|
}
|
|
|
|
void WrappedOpenGL::FinishCapture()
|
|
{
|
|
m_State = WRITING_IDLE;
|
|
|
|
//m_SuccessfulCapture = false;
|
|
}
|
|
|
|
void WrappedOpenGL::DebugSnoop(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar *message)
|
|
{
|
|
if(type != eGL_DEBUG_TYPE_OTHER || severity != eGL_DEBUG_SEVERITY_NOTIFICATION)
|
|
{
|
|
RDCLOG("Got a Debug message from %hs, type %hs, ID %d, severity %hs:\n'%hs'",
|
|
ToStr::Get(source).c_str(), ToStr::Get(type).c_str(), id, ToStr::Get(severity).c_str(), message);
|
|
}
|
|
|
|
if(m_RealDebugFunc)
|
|
m_RealDebugFunc(source, type, id, severity, length, message, m_RealDebugFuncParam);
|
|
}
|
|
|
|
void WrappedOpenGL::ReadLogInitialisation()
|
|
{
|
|
uint64_t lastFrame = 0;
|
|
uint64_t firstFrame = 0;
|
|
|
|
m_pSerialiser->SetDebugText(true);
|
|
|
|
m_pSerialiser->Rewind();
|
|
|
|
while(!m_pSerialiser->AtEnd())
|
|
{
|
|
m_pSerialiser->SkipToChunk(CAPTURE_SCOPE);
|
|
|
|
// found a capture chunk
|
|
if(!m_pSerialiser->AtEnd())
|
|
{
|
|
lastFrame = m_pSerialiser->GetOffset();
|
|
if(firstFrame == 0)
|
|
firstFrame = m_pSerialiser->GetOffset();
|
|
|
|
// skip this chunk
|
|
m_pSerialiser->PushContext(NULL, CAPTURE_SCOPE, false);
|
|
m_pSerialiser->SkipCurrentChunk();
|
|
m_pSerialiser->PopContext(NULL, CAPTURE_SCOPE);
|
|
}
|
|
}
|
|
|
|
m_pSerialiser->Rewind();
|
|
|
|
int chunkIdx = 0;
|
|
|
|
struct chunkinfo
|
|
{
|
|
chunkinfo() : count(0), total(0.0) {}
|
|
int count;
|
|
double total;
|
|
};
|
|
|
|
map<GLChunkType,chunkinfo> chunkInfos;
|
|
|
|
SCOPED_TIMER("chunk initialisation");
|
|
|
|
while(1)
|
|
{
|
|
PerformanceTimer timer;
|
|
|
|
uint64_t offset = m_pSerialiser->GetOffset();
|
|
|
|
GLChunkType context = (GLChunkType)m_pSerialiser->PushContext(NULL, 1, false);
|
|
|
|
chunkIdx++;
|
|
|
|
ProcessChunk(offset, context);
|
|
|
|
m_pSerialiser->PopContext(NULL, context);
|
|
|
|
RenderDoc::Inst().SetProgress(FileInitialRead, float(m_pSerialiser->GetOffset())/float(m_pSerialiser->GetSize()));
|
|
|
|
if(context == CAPTURE_SCOPE)
|
|
{
|
|
ContextReplayLog(READING, 0, 0, false);
|
|
|
|
if(m_pSerialiser->GetOffset() > lastFrame)
|
|
break;
|
|
}
|
|
|
|
chunkInfos[context].total += timer.GetMilliseconds();
|
|
chunkInfos[context].count++;
|
|
|
|
if(m_pSerialiser->AtEnd())
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
for(auto it=chunkInfos.begin(); it != chunkInfos.end(); ++it)
|
|
{
|
|
RDCDEBUG("%hs: %.3f total time in %d chunks - %.3f average",
|
|
GetChunkName(it->first), it->second.total, it->second.count,
|
|
it->second.total/double(it->second.count));
|
|
}
|
|
|
|
RDCDEBUG("Allocating %llu persistant bytes of memory for the log.", m_pSerialiser->GetSize() - firstFrame);
|
|
|
|
m_pSerialiser->SetDebugText(false);
|
|
|
|
m_pSerialiser->SetBase(firstFrame);
|
|
}
|
|
|
|
void WrappedOpenGL::ProcessChunk(uint64_t offset, GLChunkType context)
|
|
{
|
|
switch(context)
|
|
{
|
|
case DEVICE_INIT:
|
|
{
|
|
SERIALISE_ELEMENT(ResourceId, immContextId, ResourceId());
|
|
|
|
m_ResourceManager->AddLiveResource(immContextId, GLResource(NULL, eResSpecial, eSpecialResContext));
|
|
break;
|
|
}
|
|
case GEN_TEXTURE:
|
|
Serialise_glGenTextures(0, NULL);
|
|
break;
|
|
case ACTIVE_TEXTURE:
|
|
Serialise_glActiveTexture(eGL_NONE);
|
|
break;
|
|
case BIND_TEXTURE:
|
|
Serialise_glBindTexture(eGL_NONE, 0);
|
|
break;
|
|
case TEXSTORAGE1D:
|
|
Serialise_glTextureStorage1DEXT(0, eGL_NONE, 0, eGL_NONE, 0);
|
|
break;
|
|
case TEXSTORAGE2D:
|
|
Serialise_glTextureStorage2DEXT(0, eGL_NONE, 0, eGL_NONE, 0, 0);
|
|
break;
|
|
case TEXSTORAGE3D:
|
|
Serialise_glTextureStorage3DEXT(0, eGL_NONE, 0, eGL_NONE, 0, 0, 0);
|
|
break;
|
|
case TEXSUBIMAGE1D:
|
|
Serialise_glTextureSubImage1DEXT(0, eGL_NONE, 0, 0, 0, eGL_NONE, eGL_NONE, NULL);
|
|
break;
|
|
case TEXSUBIMAGE2D:
|
|
Serialise_glTextureSubImage2DEXT(0, eGL_NONE, 0, 0, 0, 0, 0, eGL_NONE, eGL_NONE, NULL);
|
|
break;
|
|
case TEXSUBIMAGE3D:
|
|
Serialise_glTextureSubImage3DEXT(0, eGL_NONE, 0, 0, 0, 0, 0, 0, 0, eGL_NONE, eGL_NONE, NULL);
|
|
break;
|
|
case TEXSUBIMAGE1D_COMPRESSED:
|
|
Serialise_glCompressedTextureSubImage1DEXT(0, eGL_NONE, 0, 0, 0, eGL_NONE, 0, NULL);
|
|
break;
|
|
case TEXSUBIMAGE2D_COMPRESSED:
|
|
Serialise_glCompressedTextureSubImage2DEXT(0, eGL_NONE, 0, 0, 0, 0, 0, eGL_NONE, 0, NULL);
|
|
break;
|
|
case TEXSUBIMAGE3D_COMPRESSED:
|
|
Serialise_glCompressedTextureSubImage3DEXT(0, eGL_NONE, 0, 0, 0, 0, 0, 0, 0, eGL_NONE, 0, NULL);
|
|
break;
|
|
case TEXBUFFER_RANGE:
|
|
Serialise_glTextureBufferRangeEXT(0, eGL_NONE, eGL_NONE, 0, 0, 0);
|
|
break;
|
|
case PIXELSTORE:
|
|
Serialise_glPixelStorei(eGL_NONE, 0);
|
|
break;
|
|
case TEXPARAMETERF:
|
|
Serialise_glTextureParameterfEXT(0, eGL_NONE, eGL_NONE, 0);
|
|
break;
|
|
case TEXPARAMETERFV:
|
|
Serialise_glTextureParameterfvEXT(0, eGL_NONE, eGL_NONE, NULL);
|
|
break;
|
|
case TEXPARAMETERI:
|
|
Serialise_glTextureParameteriEXT(0, eGL_NONE, eGL_NONE, 0);
|
|
break;
|
|
case TEXPARAMETERIV:
|
|
Serialise_glTextureParameterivEXT(0, eGL_NONE, eGL_NONE, NULL);
|
|
break;
|
|
case GENERATE_MIPMAP:
|
|
Serialise_glGenerateTextureMipmapEXT(0, eGL_NONE);
|
|
break;
|
|
case COPY_SUBIMAGE:
|
|
Serialise_glCopyImageSubData(0, eGL_NONE, 0, 0, 0, 0, 0, eGL_NONE, 0, 0, 0, 0, 0, 0, 0);
|
|
break;
|
|
case TEXTURE_VIEW:
|
|
Serialise_glTextureView(0, eGL_NONE, 0, eGL_NONE, 0, 0, 0, 0);
|
|
break;
|
|
|
|
case CREATE_SHADER:
|
|
Serialise_glCreateShader(0, eGL_NONE);
|
|
break;
|
|
case CREATE_PROGRAM:
|
|
Serialise_glCreateProgram(0);
|
|
break;
|
|
case CREATE_SHADERPROGRAM:
|
|
Serialise_glCreateShaderProgramv(0, eGL_NONE, 0, NULL);
|
|
break;
|
|
case COMPILESHADER:
|
|
Serialise_glCompileShader(0);
|
|
break;
|
|
case SHADERSOURCE:
|
|
Serialise_glShaderSource(0, 0, NULL, NULL);
|
|
break;
|
|
case ATTACHSHADER:
|
|
Serialise_glAttachShader(0, 0);
|
|
break;
|
|
case DETACHSHADER:
|
|
Serialise_glDetachShader(0, 0);
|
|
break;
|
|
case USEPROGRAM:
|
|
Serialise_glUseProgram(0);
|
|
break;
|
|
case PROGRAMPARAMETER:
|
|
Serialise_glProgramParameteri(0, eGL_NONE, 0);
|
|
break;
|
|
case BINDATTRIB_LOCATION:
|
|
Serialise_glBindAttribLocation(0, 0, NULL);
|
|
break;
|
|
case UNIFORM_BLOCKBIND:
|
|
Serialise_glUniformBlockBinding(0, 0, 0);
|
|
break;
|
|
case PROGRAMUNIFORM_VECTOR:
|
|
Serialise_glProgramUniformVector(0, eGL_NONE, 0, 0, UNIFORM_UNKNOWN);
|
|
break;
|
|
case LINKPROGRAM:
|
|
Serialise_glLinkProgram(0);
|
|
break;
|
|
|
|
case GEN_PROGRAMPIPE:
|
|
Serialise_glGenProgramPipelines(0, NULL);
|
|
break;
|
|
case USE_PROGRAMSTAGES:
|
|
Serialise_glUseProgramStages(0, 0, 0);
|
|
break;
|
|
case BIND_PROGRAMPIPE:
|
|
Serialise_glBindProgramPipeline(0);
|
|
break;
|
|
|
|
case FENCE_SYNC:
|
|
Serialise_glFenceSync(NULL, eGL_NONE, 0);
|
|
break;
|
|
case CLIENTWAIT_SYNC:
|
|
Serialise_glClientWaitSync(NULL, 0, 0);
|
|
break;
|
|
case WAIT_SYNC:
|
|
Serialise_glWaitSync(NULL, 0, 0);
|
|
break;
|
|
|
|
case GEN_QUERIES:
|
|
Serialise_glGenQueries(0, NULL);
|
|
break;
|
|
case BEGIN_QUERY:
|
|
Serialise_glBeginQuery(eGL_NONE, 0);
|
|
break;
|
|
case END_QUERY:
|
|
Serialise_glEndQuery(eGL_NONE);
|
|
break;
|
|
|
|
case CLEAR_COLOR:
|
|
Serialise_glClearColor(0, 0, 0, 0);
|
|
break;
|
|
case CLEAR_DEPTH:
|
|
Serialise_glClearDepth(0);
|
|
break;
|
|
case CLEAR:
|
|
Serialise_glClear(0);
|
|
break;
|
|
case CLEARBUFFERF:
|
|
Serialise_glClearBufferfv(eGL_NONE, 0, NULL);
|
|
break;
|
|
case CLEARBUFFERI:
|
|
Serialise_glClearBufferiv(eGL_NONE, 0, NULL);
|
|
break;
|
|
case CLEARBUFFERUI:
|
|
Serialise_glClearBufferuiv(eGL_NONE, 0, NULL);
|
|
break;
|
|
case CLEARBUFFERFI:
|
|
Serialise_glClearBufferfi(eGL_NONE, 0, 0, 0);
|
|
break;
|
|
case POLYGON_MODE:
|
|
Serialise_glPolygonMode(eGL_NONE, eGL_NONE);
|
|
break;
|
|
case POLYGON_OFFSET:
|
|
Serialise_glPolygonOffset(0, 0);
|
|
break;
|
|
case CULL_FACE:
|
|
Serialise_glCullFace(eGL_NONE);
|
|
break;
|
|
case HINT:
|
|
Serialise_glHint(eGL_NONE, eGL_NONE);
|
|
break;
|
|
case ENABLE:
|
|
Serialise_glEnable(eGL_NONE);
|
|
break;
|
|
case DISABLE:
|
|
Serialise_glDisable(eGL_NONE);
|
|
break;
|
|
case ENABLEI:
|
|
Serialise_glEnablei(eGL_NONE, 0);
|
|
break;
|
|
case DISABLEI:
|
|
Serialise_glDisablei(eGL_NONE, 0);
|
|
break;
|
|
case FRONT_FACE:
|
|
Serialise_glFrontFace(eGL_NONE);
|
|
break;
|
|
case BLEND_FUNC:
|
|
Serialise_glBlendFunc(eGL_NONE, eGL_NONE);
|
|
break;
|
|
case BLEND_FUNCI:
|
|
Serialise_glBlendFunci(0, eGL_NONE, eGL_NONE);
|
|
break;
|
|
case BLEND_COLOR:
|
|
Serialise_glBlendColor(0, 0, 0, 0);
|
|
break;
|
|
case BLEND_FUNC_SEP:
|
|
Serialise_glBlendFuncSeparate(eGL_NONE, eGL_NONE, eGL_NONE, eGL_NONE);
|
|
break;
|
|
case BLEND_FUNC_SEPI:
|
|
Serialise_glBlendFuncSeparatei(0, eGL_NONE, eGL_NONE, eGL_NONE, eGL_NONE);
|
|
break;
|
|
case BLEND_EQ_SEP:
|
|
Serialise_glBlendEquationSeparate(eGL_NONE, eGL_NONE);
|
|
break;
|
|
case BLEND_EQ_SEPI:
|
|
Serialise_glBlendEquationSeparatei(0, eGL_NONE, eGL_NONE);
|
|
break;
|
|
|
|
case STENCIL_OP:
|
|
Serialise_glStencilOp(eGL_NONE, eGL_NONE, eGL_NONE);
|
|
break;
|
|
case STENCIL_OP_SEP:
|
|
Serialise_glStencilOpSeparate(eGL_NONE, eGL_NONE, eGL_NONE, eGL_NONE);
|
|
break;
|
|
case STENCIL_FUNC:
|
|
Serialise_glStencilFunc(eGL_NONE, 0, 0);
|
|
break;
|
|
case STENCIL_FUNC_SEP:
|
|
Serialise_glStencilFuncSeparate(eGL_NONE, eGL_NONE, 0, 0);
|
|
break;
|
|
case STENCIL_MASK:
|
|
Serialise_glStencilMask(0);
|
|
break;
|
|
case STENCIL_MASK_SEP:
|
|
Serialise_glStencilMaskSeparate(eGL_NONE, 0);
|
|
break;
|
|
|
|
case COLOR_MASK:
|
|
Serialise_glColorMask(0, 0, 0, 0);
|
|
break;
|
|
case COLOR_MASKI:
|
|
Serialise_glColorMaski(0, 0, 0, 0, 0);
|
|
break;
|
|
case SAMPLE_MASK:
|
|
Serialise_glSampleMaski(0, 0);
|
|
break;
|
|
case DEPTH_FUNC:
|
|
Serialise_glDepthFunc(eGL_NONE);
|
|
break;
|
|
case DEPTH_MASK:
|
|
Serialise_glDepthMask(0);
|
|
break;
|
|
case DEPTH_RANGE:
|
|
Serialise_glDepthRange(0, 0);
|
|
break;
|
|
case DEPTH_RANGEF:
|
|
Serialise_glDepthRangef(0, 0);
|
|
break;
|
|
case DEPTH_RANGEARRAY:
|
|
Serialise_glDepthRangeArrayv(0, 0, NULL);
|
|
break;
|
|
case DEPTH_BOUNDS:
|
|
Serialise_glDepthBoundsEXT(0, 0);
|
|
break;
|
|
case PATCH_PARAMI:
|
|
Serialise_glPatchParameteri(eGL_NONE, 0);
|
|
break;
|
|
case PATCH_PARAMFV:
|
|
Serialise_glPatchParameterfv(eGL_NONE, NULL);
|
|
break;
|
|
case VIEWPORT:
|
|
Serialise_glViewport(0, 0, 0, 0);
|
|
break;
|
|
case VIEWPORT_ARRAY:
|
|
Serialise_glViewportArrayv(0, 0, 0);
|
|
break;
|
|
case SCISSOR:
|
|
Serialise_glScissor(0, 0, 0, 0);
|
|
break;
|
|
case SCISSOR_ARRAY:
|
|
Serialise_glScissorArrayv(0, 0, 0);
|
|
break;
|
|
case BINDVERTEXARRAY:
|
|
Serialise_glBindVertexArray(0);
|
|
break;
|
|
case BINDVERTEXBUFFER:
|
|
Serialise_glBindVertexBuffer(0, 0, 0, 0);
|
|
break;
|
|
case VERTEXDIVISOR:
|
|
Serialise_glVertexBindingDivisor(0, 0);
|
|
break;
|
|
case UNIFORM_MATRIX:
|
|
Serialise_glUniformMatrix(0, 0, 0, NULL, UNIFORM_UNKNOWN);
|
|
break;
|
|
case UNIFORM_VECTOR:
|
|
Serialise_glUniformVector(0, 0, NULL, UNIFORM_UNKNOWN);
|
|
break;
|
|
case DRAWARRAYS:
|
|
Serialise_glDrawArrays(eGL_NONE, 0, 0);
|
|
break;
|
|
case DRAWARRAYS_INSTANCED:
|
|
Serialise_glDrawArraysInstanced(eGL_NONE, 0, 0, 0);
|
|
break;
|
|
case DRAWARRAYS_INSTANCEDBASEINSTANCE:
|
|
Serialise_glDrawArraysInstancedBaseInstance(eGL_NONE, 0, 0, 0, 0);
|
|
break;
|
|
case DRAWELEMENTS:
|
|
Serialise_glDrawElements(eGL_NONE, 0, eGL_NONE, NULL);
|
|
break;
|
|
case DRAWRANGEELEMENTS:
|
|
Serialise_glDrawRangeElements(eGL_NONE, 0, 0, 0, eGL_NONE, NULL);
|
|
break;
|
|
case DRAWELEMENTS_INSTANCED:
|
|
Serialise_glDrawElementsInstanced(eGL_NONE, 0, eGL_NONE, NULL, 0);
|
|
break;
|
|
case DRAWELEMENTS_INSTANCEDBASEINSTANCE:
|
|
Serialise_glDrawElementsInstancedBaseInstance(eGL_NONE, 0, eGL_NONE, NULL, 0, 0);
|
|
break;
|
|
case DRAWELEMENTS_BASEVERTEX:
|
|
Serialise_glDrawElementsBaseVertex(eGL_NONE, 0, eGL_NONE, NULL, 0);
|
|
break;
|
|
case DRAWELEMENTS_INSTANCEDBASEVERTEX:
|
|
Serialise_glDrawElementsInstancedBaseVertex(eGL_NONE, 0, eGL_NONE, NULL, 0, 0);
|
|
break;
|
|
case DRAWELEMENTS_INSTANCEDBASEVERTEXBASEINSTANCE:
|
|
Serialise_glDrawElementsInstancedBaseVertexBaseInstance(eGL_NONE, 0, eGL_NONE, NULL, 0, 0, 0);
|
|
break;
|
|
|
|
case GEN_FRAMEBUFFERS:
|
|
Serialise_glGenFramebuffers(0, NULL);
|
|
break;
|
|
case FRAMEBUFFER_TEX:
|
|
Serialise_glNamedFramebufferTextureEXT(0, eGL_NONE, 0, 0);
|
|
break;
|
|
case FRAMEBUFFER_TEX2D:
|
|
Serialise_glNamedFramebufferTexture2DEXT(0, eGL_NONE, eGL_NONE, 0, 0);
|
|
break;
|
|
case FRAMEBUFFER_TEXLAYER:
|
|
Serialise_glNamedFramebufferTextureLayerEXT(0, eGL_NONE, 0, 0, 0);
|
|
break;
|
|
case READ_BUFFER:
|
|
Serialise_glReadBuffer(eGL_NONE);
|
|
break;
|
|
case BIND_FRAMEBUFFER:
|
|
Serialise_glBindFramebuffer(eGL_NONE, 0);
|
|
break;
|
|
case DRAW_BUFFER:
|
|
Serialise_glDrawBuffer(eGL_NONE);
|
|
break;
|
|
case DRAW_BUFFERS:
|
|
Serialise_glFramebufferDrawBuffersEXT(0, 0, NULL);
|
|
break;
|
|
case BLIT_FRAMEBUFFER:
|
|
Serialise_glBlitFramebuffer(0, 0, 0, 0, 0, 0, 0, 0, 0, eGL_NONE);
|
|
break;
|
|
|
|
case GEN_SAMPLERS:
|
|
Serialise_glGenSamplers(0, NULL);
|
|
break;
|
|
case SAMPLER_PARAMETERI:
|
|
Serialise_glSamplerParameteri(0, eGL_NONE, 0);
|
|
break;
|
|
case SAMPLER_PARAMETERF:
|
|
Serialise_glSamplerParameterf(0, eGL_NONE, 0);
|
|
break;
|
|
case SAMPLER_PARAMETERIV:
|
|
Serialise_glSamplerParameteriv(0, eGL_NONE, NULL);
|
|
break;
|
|
case SAMPLER_PARAMETERFV:
|
|
Serialise_glSamplerParameterfv(0, eGL_NONE, NULL);
|
|
break;
|
|
case SAMPLER_PARAMETERIIV:
|
|
Serialise_glSamplerParameterIiv(0, eGL_NONE, NULL);
|
|
break;
|
|
case SAMPLER_PARAMETERIUIV:
|
|
Serialise_glSamplerParameterIuiv(0, eGL_NONE, NULL);
|
|
break;
|
|
case BIND_SAMPLER:
|
|
Serialise_glBindSampler(0, 0);
|
|
break;
|
|
|
|
case GEN_BUFFER:
|
|
Serialise_glGenBuffers(0, NULL);
|
|
break;
|
|
case BIND_BUFFER:
|
|
Serialise_glBindBuffer(eGL_NONE, 0);
|
|
break;
|
|
case BIND_BUFFER_BASE:
|
|
Serialise_glBindBufferBase(eGL_NONE, 0, 0);
|
|
break;
|
|
case BIND_BUFFER_RANGE:
|
|
Serialise_glBindBufferRange(eGL_NONE, 0, 0, 0, 0);
|
|
break;
|
|
case BUFFERSTORAGE:
|
|
Serialise_glNamedBufferStorageEXT(0, 0, NULL, 0);
|
|
break;
|
|
case BUFFERDATA:
|
|
Serialise_glNamedBufferDataEXT(eGL_NONE, 0, NULL, eGL_NONE);
|
|
break;
|
|
case BUFFERSUBDATA:
|
|
Serialise_glNamedBufferSubDataEXT(0, 0, 0, NULL);
|
|
break;
|
|
case COPYBUFFERSUBDATA:
|
|
Serialise_glNamedCopyBufferSubDataEXT(0, 0, 0, 0, 0);
|
|
break;
|
|
case UNMAP:
|
|
Serialise_glUnmapNamedBufferEXT(eGL_NONE);
|
|
break;
|
|
case GEN_VERTEXARRAY:
|
|
Serialise_glGenVertexArrays(0, NULL);
|
|
break;
|
|
case BIND_VERTEXARRAY:
|
|
Serialise_glBindVertexArray(0);
|
|
break;
|
|
case VERTEXATTRIBPOINTER:
|
|
Serialise_glVertexAttribPointer(0, 0, eGL_NONE, 0, 0, NULL);
|
|
break;
|
|
case VERTEXATTRIBIPOINTER:
|
|
Serialise_glVertexAttribIPointer(0, 0, eGL_NONE, 0, NULL);
|
|
break;
|
|
case ENABLEVERTEXATTRIBARRAY:
|
|
Serialise_glEnableVertexAttribArray(0);
|
|
break;
|
|
case DISABLEVERTEXATTRIBARRAY:
|
|
Serialise_glDisableVertexAttribArray(0);
|
|
break;
|
|
case VERTEXATTRIBFORMAT:
|
|
Serialise_glVertexAttribFormat(0, 0, eGL_NONE, 0, 0);
|
|
break;
|
|
case VERTEXATTRIBIFORMAT:
|
|
Serialise_glVertexAttribIFormat(0, 0, eGL_NONE, 0);
|
|
break;
|
|
case VERTEXATTRIBBINDING:
|
|
Serialise_glVertexAttribBinding(0, 0);
|
|
break;
|
|
|
|
case OBJECT_LABEL:
|
|
Serialise_glObjectLabel(eGL_NONE, 0, 0, NULL);
|
|
break;
|
|
case BEGIN_EVENT:
|
|
Serialise_glPushDebugGroup(eGL_NONE, 0, 0, NULL);
|
|
break;
|
|
case SET_MARKER:
|
|
Serialise_glDebugMessageInsert(eGL_NONE, eGL_NONE, 0, eGL_NONE, 0, NULL);
|
|
break;
|
|
case END_EVENT:
|
|
Serialise_glPopDebugGroup();
|
|
break;
|
|
|
|
case CAPTURE_SCOPE:
|
|
Serialise_CaptureScope(offset);
|
|
break;
|
|
case CONTEXT_CAPTURE_FOOTER:
|
|
{
|
|
bool HasCallstack = false;
|
|
m_pSerialiser->Serialise("HasCallstack", HasCallstack);
|
|
|
|
if(HasCallstack)
|
|
{
|
|
size_t numLevels = 0;
|
|
uint64_t *stack = NULL;
|
|
|
|
m_pSerialiser->Serialise("callstack", stack, numLevels);
|
|
|
|
m_pSerialiser->SetCallstack(stack, numLevels);
|
|
|
|
SAFE_DELETE_ARRAY(stack);
|
|
}
|
|
|
|
if(m_State == READING)
|
|
{
|
|
AddEvent(CONTEXT_CAPTURE_FOOTER, "SwapBuffers()");
|
|
|
|
FetchDrawcall draw;
|
|
draw.name = L"SwapBuffers()";
|
|
draw.flags |= eDraw_Present;
|
|
|
|
AddDrawcall(draw, true);
|
|
}
|
|
}
|
|
break;
|
|
default:
|
|
// ignore system chunks
|
|
if((int)context == (int)INITIAL_CONTENTS)
|
|
GetResourceManager()->Serialise_InitialState(GLResource(MakeNullResource));
|
|
else if((int)context < (int)FIRST_CHUNK_ID)
|
|
m_pSerialiser->SkipCurrentChunk();
|
|
else
|
|
RDCERR("Unrecognised Chunk type %d", context);
|
|
break;
|
|
}
|
|
}
|
|
|
|
void WrappedOpenGL::ContextReplayLog(LogState readType, uint32_t startEventID, uint32_t endEventID, bool partial)
|
|
{
|
|
m_State = readType;
|
|
|
|
m_DoStateVerify = true;
|
|
|
|
GLChunkType header = (GLChunkType)m_pSerialiser->PushContext(NULL, 1, false);
|
|
RDCASSERT(header == CONTEXT_CAPTURE_HEADER);
|
|
|
|
WrappedOpenGL *context = this;
|
|
|
|
Serialise_BeginCaptureFrame(!partial);
|
|
|
|
m_pSerialiser->PopContext(NULL, header);
|
|
|
|
m_CurEvents.clear();
|
|
|
|
if(m_State == EXECUTING)
|
|
{
|
|
FetchAPIEvent ev = GetEvent(startEventID);
|
|
m_CurEventID = ev.eventID;
|
|
m_pSerialiser->SetOffset(ev.fileOffset);
|
|
}
|
|
else if(m_State == READING)
|
|
{
|
|
m_CurEventID = 1;
|
|
}
|
|
|
|
if(m_State == EXECUTING)
|
|
{
|
|
}
|
|
|
|
GetResourceManager()->MarkInFrame(true);
|
|
|
|
while(1)
|
|
{
|
|
if(m_State == EXECUTING && m_CurEventID > endEventID)
|
|
{
|
|
// we can just break out if we've done all the events desired.
|
|
break;
|
|
}
|
|
|
|
uint64_t offset = m_pSerialiser->GetOffset();
|
|
|
|
GLChunkType context = (GLChunkType)m_pSerialiser->PushContext(NULL, 1, false);
|
|
|
|
ContextProcessChunk(offset, context, false);
|
|
|
|
RenderDoc::Inst().SetProgress(FileInitialRead, float(offset)/float(m_pSerialiser->GetSize()));
|
|
|
|
// for now just abort after capture scope. Really we'd need to support multiple frames
|
|
// but for now this will do.
|
|
if(context == CONTEXT_CAPTURE_FOOTER)
|
|
break;
|
|
|
|
m_CurEventID++;
|
|
}
|
|
|
|
if(m_State == READING)
|
|
{
|
|
GetFrameRecord().back().drawcallList = m_ParentDrawcall.Bake();
|
|
|
|
m_ParentDrawcall.children.clear();
|
|
}
|
|
|
|
GetResourceManager()->MarkInFrame(false);
|
|
|
|
m_State = READING;
|
|
|
|
m_DoStateVerify = false;
|
|
}
|
|
|
|
void WrappedOpenGL::ContextProcessChunk(uint64_t offset, GLChunkType chunk, bool forceExecute)
|
|
{
|
|
/*
|
|
if(chunk < FIRST_CONTEXT_CHUNK && !forceExecute)
|
|
{
|
|
if(m_State == READING)
|
|
{
|
|
GetResourceManager()->MarkInFrame(false);
|
|
|
|
ProcessChunk(offset, chunk);
|
|
m_pSerialiser->PopContext(NULL, chunk);
|
|
|
|
GetResourceManager()->MarkInFrame(true);
|
|
}
|
|
else if(m_State == EXECUTING)
|
|
{
|
|
m_pSerialiser->SkipCurrentChunk();
|
|
m_pSerialiser->PopContext(NULL, chunk);
|
|
}
|
|
return;
|
|
}*/
|
|
|
|
m_CurChunkOffset = offset;
|
|
|
|
uint64_t cOffs = m_pSerialiser->GetOffset();
|
|
|
|
WrappedOpenGL *context = this;
|
|
|
|
LogState state = context->m_State;
|
|
|
|
if(forceExecute)
|
|
context->m_State = EXECUTING;
|
|
else
|
|
context->m_State = m_State;
|
|
|
|
m_AddedDrawcall = false;
|
|
|
|
ProcessChunk(offset, chunk);
|
|
|
|
m_pSerialiser->PopContext(NULL, chunk);
|
|
|
|
if(context->m_State == READING && chunk == SET_MARKER)
|
|
{
|
|
// no push/pop necessary
|
|
}
|
|
else if(context->m_State == READING && chunk == BEGIN_EVENT)
|
|
{
|
|
// push down the drawcallstack to the latest drawcall
|
|
context->m_DrawcallStack.push_back(&context->m_DrawcallStack.back()->children.back());
|
|
}
|
|
else if(context->m_State == READING && chunk == END_EVENT)
|
|
{
|
|
// refuse to pop off further than the root drawcall (mismatched begin/end events e.g.)
|
|
RDCASSERT(context->m_DrawcallStack.size() > 1);
|
|
if(context->m_DrawcallStack.size() > 1)
|
|
context->m_DrawcallStack.pop_back();
|
|
}
|
|
else if(context->m_State == READING)
|
|
{
|
|
if(!m_AddedDrawcall)
|
|
context->AddEvent(chunk, m_pSerialiser->GetDebugStr());
|
|
}
|
|
|
|
m_AddedDrawcall = false;
|
|
|
|
if(forceExecute)
|
|
context->m_State = state;
|
|
}
|
|
|
|
void WrappedOpenGL::AddDrawcall(FetchDrawcall d, bool hasEvents)
|
|
{
|
|
if(d.context == ResourceId()) d.context = GetResourceManager()->GetOriginalID(m_ContextResourceID);
|
|
|
|
m_AddedDrawcall = true;
|
|
|
|
WrappedOpenGL *context = this;
|
|
|
|
FetchDrawcall draw = d;
|
|
draw.eventID = m_CurEventID;
|
|
draw.drawcallID = m_CurDrawcallID;
|
|
|
|
GLuint curCol[8] = { 0 };
|
|
GLuint curDepth = 0;
|
|
|
|
{
|
|
GLint numCols = 8;
|
|
m_Real.glGetIntegerv(eGL_MAX_COLOR_ATTACHMENTS, &numCols);
|
|
|
|
RDCEraseEl(draw.outputs);
|
|
|
|
for(GLint i=0; i < RDCMIN(numCols, 8); i++)
|
|
{
|
|
m_Real.glGetFramebufferAttachmentParameteriv(eGL_DRAW_FRAMEBUFFER, GLenum(eGL_COLOR_ATTACHMENT0+i), eGL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME, (GLint*)&curCol[i]);
|
|
draw.outputs[i] = GetResourceManager()->GetID(TextureRes(GetCtx(), curCol[i]));
|
|
}
|
|
|
|
m_Real.glGetFramebufferAttachmentParameteriv(eGL_DRAW_FRAMEBUFFER, eGL_DEPTH_ATTACHMENT, eGL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME, (GLint*)&curDepth);
|
|
draw.depthOut = GetResourceManager()->GetID(TextureRes(GetCtx(), curDepth));
|
|
}
|
|
|
|
// markers don't increment drawcall ID
|
|
if((draw.flags & (eDraw_SetMarker|eDraw_PushMarker)) == 0)
|
|
m_CurDrawcallID++;
|
|
|
|
if(hasEvents)
|
|
{
|
|
vector<FetchAPIEvent> evs;
|
|
evs.reserve(m_CurEvents.size());
|
|
for(size_t i=0; i < m_CurEvents.size(); )
|
|
{
|
|
if(m_CurEvents[i].context == draw.context)
|
|
{
|
|
evs.push_back(m_CurEvents[i]);
|
|
m_CurEvents.erase(m_CurEvents.begin()+i);
|
|
}
|
|
else
|
|
{
|
|
i++;
|
|
}
|
|
}
|
|
|
|
draw.events = evs;
|
|
}
|
|
|
|
//AddUsage(draw);
|
|
|
|
// should have at least the root drawcall here, push this drawcall
|
|
// onto the back's children list.
|
|
if(!context->m_DrawcallStack.empty())
|
|
{
|
|
DrawcallTreeNode node(draw);
|
|
node.children.insert(node.children.begin(), draw.children.elems, draw.children.elems+draw.children.count);
|
|
context->m_DrawcallStack.back()->children.push_back(node);
|
|
}
|
|
else
|
|
RDCERR("Somehow lost drawcall stack!");
|
|
}
|
|
|
|
void WrappedOpenGL::AddEvent(GLChunkType type, string description, ResourceId ctx)
|
|
{
|
|
if(ctx == ResourceId()) ctx = GetResourceManager()->GetOriginalID(m_ContextResourceID);
|
|
|
|
FetchAPIEvent apievent;
|
|
|
|
apievent.context = ctx;
|
|
apievent.fileOffset = m_CurChunkOffset;
|
|
apievent.eventID = m_CurEventID;
|
|
|
|
apievent.eventDesc = widen(description);
|
|
|
|
Callstack::Stackwalk *stack = m_pSerialiser->GetLastCallstack();
|
|
if(stack)
|
|
{
|
|
create_array(apievent.callstack, stack->NumLevels());
|
|
memcpy(apievent.callstack.elems, stack->GetAddrs(), sizeof(uint64_t)*stack->NumLevels());
|
|
}
|
|
|
|
m_CurEvents.push_back(apievent);
|
|
|
|
if(m_State == READING)
|
|
m_Events.push_back(apievent);
|
|
}
|
|
|
|
FetchAPIEvent WrappedOpenGL::GetEvent(uint32_t eventID)
|
|
{
|
|
for(size_t i=m_Events.size()-1; i > 0; i--)
|
|
{
|
|
if(m_Events[i].eventID <= eventID)
|
|
return m_Events[i];
|
|
}
|
|
|
|
return m_Events[0];
|
|
}
|
|
|
|
void WrappedOpenGL::ReplayLog(uint32_t frameID, uint32_t startEventID, uint32_t endEventID, ReplayLogType replayType)
|
|
{
|
|
RDCASSERT(frameID < (uint32_t)m_FrameRecord.size());
|
|
|
|
uint64_t offs = m_FrameRecord[frameID].frameInfo.fileOffset;
|
|
|
|
m_pSerialiser->SetOffset(offs);
|
|
|
|
bool partial = true;
|
|
|
|
if(startEventID == 0 && (replayType == eReplay_WithoutDraw || replayType == eReplay_Full))
|
|
{
|
|
startEventID = m_FrameRecord[frameID].frameInfo.firstEvent;
|
|
partial = false;
|
|
}
|
|
|
|
GLChunkType header = (GLChunkType)m_pSerialiser->PushContext(NULL, 1, false);
|
|
|
|
RDCASSERT(header == CAPTURE_SCOPE);
|
|
|
|
m_pSerialiser->SkipCurrentChunk();
|
|
|
|
m_pSerialiser->PopContext(NULL, header);
|
|
|
|
if(!partial)
|
|
{
|
|
GetResourceManager()->ApplyInitialContents();
|
|
GetResourceManager()->ReleaseInFrameResources();
|
|
}
|
|
|
|
{
|
|
if(replayType == eReplay_Full)
|
|
ContextReplayLog(EXECUTING, startEventID, endEventID, partial);
|
|
else if(replayType == eReplay_WithoutDraw)
|
|
ContextReplayLog(EXECUTING, startEventID, RDCMAX(1U,endEventID)-1, partial);
|
|
else if(replayType == eReplay_OnlyDraw)
|
|
ContextReplayLog(EXECUTING, endEventID, endEventID, partial);
|
|
else
|
|
RDCFATAL("Unexpected replay type");
|
|
}
|
|
}
|